diff --git a/BadgeFramework/Demo.py b/BadgeFramework/Demo.py new file mode 100644 index 0000000..78ebbb6 --- /dev/null +++ b/BadgeFramework/Demo.py @@ -0,0 +1,394 @@ +from __future__ import division, absolute_import, print_function +import sys +import threading +import datetime +import random +import time + +from badge import * +from ble_badge_connection import * +from bluepy import * +from bluepy import btle +from bluepy.btle import UUID, Peripheral, DefaultDelegate, AssignedNumbers ,Scanner +from bluepy.btle import BTLEException + +import matplotlib +matplotlib.use("TkAgg") +from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2TkAgg) +from matplotlib.figure import Figure +from Tkinter import Tk, Label, Button, Entry, LEFT, BOTH +import numpy as np + +# It is important to enable the access to the host-display with "xhost +" before starting the docker-container +# You have to configure the ID's and group number of the badges via the terminal correctly to "find" them in a scan --> same group number +# Please enter the MAC-Address of the Badge during the call of this python script + + +MAC_ADDRESS = "" +DISPLAY_COUNTER_MIC = 200 +DISPLAY_TIME_MIC_SEC = 10 +DISPLAY_COUNTER_ACC = 200 +DISPLAY_TIME_ACC_SEC = 10 + + + + + +t_mic = [] +y_mic = [] +t_acc = [] +y_acc = [] +y_scan = [] +scan_alpha = 0.2 + +start_time = time.time() + +scan_color_map=['r', 'g', 'b', 'cyan', 'magenta'] + + +def get_distance(rssi): + n = 2 + p_ref = -65 + d = 10**((p_ref - rssi)/(10*n)) + + return d + + + +class StreamThread(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.running = False + + + + def run(self): + global t_mic + global y_mic + global t_acc + global y_acc + global y_scan + + self.running = True + self.connect() + while(self.running): + try: + tmp = self.badge.get_stream() + while(not (tmp == [])): + #print(tmp) + if(not (tmp.microphone_stream == [])): + mic = tmp.microphone_stream[0].microphone_data.value + t_mic.append(time.time() - start_time) + y_mic.append(mic) + + if(not (tmp.accelerometer_stream == [])): + acc = tmp.accelerometer_stream[0].accelerometer_raw_data.raw_acceleration + t_acc.append(time.time() - start_time) + y_acc.append(acc) + + + + if(not (tmp.scan_stream == [])): + + scan = tmp.scan_stream[0].scan_device + ID = scan.ID + RSSI = scan.rssi + IDs = [row[0] for row in y_scan] + idx = [] + try: + idx = IDs.index(ID) + except Exception as e: + print(e) + + + d = get_distance(RSSI) + + + if(idx == []): + y_scan.append([ID, d]) + # sort ID + y_scan = sorted(y_scan, key=lambda x: x[0]) + + else: + former_d = y_scan[idx][1] + new_d = scan_alpha*d + (1-scan_alpha)* former_d + y_scan[idx]= [ID, new_d] + + print(y_scan) + + #t_scan.append(time.time() - start_time) + # sort to greatest + #y_scan.append(scan) + + + + # TODO: Add here the other data-source (accel...) + tmp = self.badge.get_stream() + except Exception as e: + print(e) + self.connection.disconnect() + self.connect() + + self.connection.disconnect() + + def stop(self): + self.running = False + + def connect(self): + print("Connecting to badge", MAC_ADDRESS) + self.connection = BLEBadgeConnection.get_connection_to_badge(MAC_ADDRESS) + self.connection.connect() + self.badge = OpenBadge(self.connection) + print("Connected!") + print(self.badge.get_status()) + print("Start streaming..") + self.badge.start_microphone_stream(sampling_period_ms=50) + + self.badge.start_accelerometer_stream(timeout_minutes=0, operating_mode=2, full_scale=4, datarate=25, fifo_sampling_period_ms=50) + self.badge.start_scan_stream(timeout_minutes=0, window_ms=100, interval_ms=300,duration_seconds=5, period_seconds=6, aggregation_type=0) + + ############ START other Datasources ############# + + self.clear() + + def clear(self): + global t_mic + global y_mic + global t_acc + global y_acc + global y_scan + print("Clear stream queue") + self.badge.stream_clear() + print("Cleared stream queue") + t_mic = [] + y_mic = [] + t_acc = [] + y_acc = [] + y_scan = [] + + +class App: + def __init__(self): + self.stream_thread_running = 0 + self.plot_thread_running = 0 + + self.f = Figure(figsize=(5,4), dpi=100) + self.axis_mic = self.f.add_subplot(131) + self.axis_mic.set_ylim(0,100) + self.axis_mic.grid(b=True) + + self.axis_acc = self.f.add_subplot(132) + self.axis_acc.set_ylim(-500,500) + self.axis_acc.grid(b=True) + + + self.axis_scan = self.f.add_subplot(133) + self.axis_scan.set_ylim(0,4) + self.axis_scan.grid(b=True) + + + + ########### Add other axis ########## + + self.window = Tk() + self.canvas = FigureCanvasTkAgg(self.f, master = self.window) + self.canvas.draw() + self.canvas.get_tk_widget().pack(fill=BOTH, expand=True) + + + + self.start_button = Button(master=self.window, text="Start", command = self.start_button_pressed) + self.start_button.pack(side=LEFT) + self.stop_button = Button(master=self.window, text="Stop", command = self.stop_button_pressed) + self.stop_button.pack(side=LEFT) + self.window.mainloop() + + + def reset(self): + global t_mic + global y_mic + global t_acc + global y_acc + global y_scan + + t_mic = [] + y_mic = [] + t_acc = [] + y_acc = [] + t_acc_int = [] + y_acc_int = [] + t_bat = [] + y_bat = [] + t_scan = [] + y_scan = [] + + + def start_button_pressed(self): + self.reset() + global start_time + start_time = time.time() + + if(not self.plot_thread_running): + self.plot_thread_running = 1 + self.plot_thread = PlotThread(self.canvas, self.axis_mic, self.axis_acc, self.axis_scan) + self.plot_thread.start() + + + if(not self.stream_thread_running): + self.stream_thread_running = 1 + self.stream_thread = StreamThread() + self.stream_thread.start() + + def stop_button_pressed(self): + self.reset() + + if(self.plot_thread_running): + self.plot_thread_running = 0 + self.plot_thread.stop() + + if(self.stream_thread_running): + self.stream_thread_running = 0 + self.stream_thread.stop() + + +class PlotThread(threading.Thread): + def __init__(self, canvas, axis_mic, axis_acc, axis_scan): + threading.Thread.__init__(self) + self.canvas = canvas + self.axis_mic = axis_mic + self.axis_acc = axis_acc + self.axis_scan = axis_scan + self.running = False + + def run(self): + + global t_mic + global y_mic + global t_acc + global y_acc + global y_scan + self.running = True + while(self.running): + ################# MIC ################### + x_tmp = [] + y_tmp = [] + start_index, end_index = self.get_list_indizes(t_mic, DISPLAY_COUNTER_MIC) + + x_tmp = t_mic[start_index:end_index] + y_tmp = y_mic[start_index:end_index] + t_mic = t_mic[start_index:] + y_mic = y_mic[start_index:] + + + self.axis_mic.clear() + self.axis_mic.plot(x_tmp, y_tmp, "-", label='Mic') + #self.axis_mic.legend(loc="upper right") + y_lim_min, y_lim_max = self.get_y_lim(y_tmp, 0, 90) + self.axis_mic.set_ylim(y_lim_min, y_lim_max + 10) + x_lim_min, x_lim_max = self.get_x_lim_time(DISPLAY_TIME_MIC_SEC, start_time) + self.axis_mic.set_xlim(x_lim_min, x_lim_max) + self.axis_mic.grid(b=True) + self.axis_mic.set_title("Microphone") + self.axis_mic.set_xlabel("Time (s)") + + + ##### ACC ##### + x_tmp = [] + y_tmp = [] + start_index, end_index = self.get_list_indizes(t_acc, DISPLAY_COUNTER_ACC) + + x_tmp = t_acc[start_index:end_index] + y_tmp = y_acc[start_index:end_index] + t_acc = t_acc[start_index:] + y_acc = y_acc[start_index:] + + + self.axis_acc.clear() + self.axis_acc.plot(x_tmp, [row[0] for row in y_tmp] , "-", label='x') + self.axis_acc.plot(x_tmp, [row[1] for row in y_tmp], "-", label='y') + self.axis_acc.plot(x_tmp, [row[2] for row in y_tmp], "-", label='z') + self.axis_acc.legend(loc="upper right") + y_lim_min, y_lim_max = self.get_y_lim(y_tmp, -750, 750) + self.axis_acc.set_ylim(y_lim_min - 10, y_lim_max + 10) + x_lim_min, x_lim_max = self.get_x_lim_time(DISPLAY_TIME_ACC_SEC, start_time) + self.axis_acc.set_xlim(x_lim_min, x_lim_max) + self.axis_acc.grid(b=True) + self.axis_acc.set_title("Acceleration (mg)") + self.axis_acc.set_xlabel("Time (s)") + + + ##### Scan ##### + x_label = [] + x_pos = [] + y = [] + colors = [] + for i in range(0, len(y_scan)): + colors.append(scan_color_map[i%len(scan_color_map)]) + x_label.append("ID: " + str(y_scan[i][0])) + y.append(y_scan[i][1]) + x_pos.append(i) + + + barwidth = 0.5 + self.axis_scan.clear() + self.axis_scan.set_xticks(x_pos) + self.axis_scan.set_xticklabels(x_label) + self.axis_scan.bar(x_pos, y, barwidth, align='center', alpha=0.5, color = colors) + self.axis_scan.set_title("Approximate distance (m)") + + self.canvas.draw() + print("Draw") + + def stop(self): + self.running = False + + def get_list_indizes(self, x, max_values): + l = len(x) + start_index = 0 + end_index = 0 + if(l < max_values): + start_index = 0 + end_index = l + else: + start_index = l-max_values + end_index = l + + return start_index, end_index + + def get_y_lim(self, y, default_min, default_max): + l = len(y) + y_lim_max = default_max + y_lim_min = default_min + if(l == 0): + return y_lim_min, y_lim_max + + maximum = np.max(y) + minimum = np.min(y) + + if(maximum > default_max): + y_lim_max = maximum + if(minimum < default_min): + y_lim_min = minimum + return y_lim_min, y_lim_max + + def get_x_lim_time(self, display_time, start_time): + cur_time = time.time() + x_lim_min = 0 + x_lim_max = cur_time - start_time + + if(x_lim_max < display_time): + x_lim_min = 0 + else: + x_lim_min = x_lim_max - display_time + + return x_lim_min, x_lim_max + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + MAC_ADDRESS = device_addr + app = App() + diff --git a/BadgeFramework/badge.py b/BadgeFramework/badge.py index f5864c0..7f82af5 100644 --- a/BadgeFramework/badge.py +++ b/BadgeFramework/badge.py @@ -2,12 +2,48 @@ import time import logging import sys +import struct +import Queue + +DEFAULT_MICROPHONE_SAMPLING_PERIOD_MS = 50 -# Default scan settings from openbadge-hub-py. DEFAULT_SCAN_WINDOW = 100 DEFAULT_SCAN_INTERVAL = 300 DEFAULT_SCAN_DURATION = 5 DEFAULT_SCAN_PERIOD = 60 +DEFAULT_SCAN_AGGREGATION_TYPE = 0 + +DEFAULT_ACCELEROMETER_OPERATING_MODE = 1 +DEFAULT_ACCELEROMETER_FULL_SCALE = 4 +DEFAULT_ACCELEROMETER_DATARATE = 10 +DEFAULT_ACCELEROMETER_FIFO_SAMPLING_PERIOD_MS = 1000 + +DEFAULT_ACCELEROMETER_INTERRUPT_THRESHOLD_MG = 250 +DEFAULT_ACCELEROMETER_INTERRUPT_MINIMAL_DURATION_MS = 10 +DEFAULT_ACCELEROMETER_INTERRUPT_IGNORE_DURATION_MS = 5000 + +DEFAULT_BATTERY_SAMPLING_PERIOD_MS = 60000 + + + +DEFAULT_MICROPHONE_STREAM_SAMPLING_PERIOD_MS = 50 + +DEFAULT_SCAN_STREAM_WINDOW = 100 +DEFAULT_SCAN_STREAM_INTERVAL = 300 +DEFAULT_SCAN_STREAM_DURATION = 5 +DEFAULT_SCAN_STREAM_PERIOD = 6 +DEFAULT_SCAN_STREAM_AGGREGATION_TYPE = 0 + +DEFAULT_ACCELEROMETER_STREAM_OPERATING_MODE = 1 +DEFAULT_ACCELEROMETER_STREAM_FULL_SCALE = 4 +DEFAULT_ACCELEROMETER_STREAM_DATARATE = 25 +DEFAULT_ACCELEROMETER_STREAM_FIFO_SAMPLING_PERIOD_MS = 50 + +DEFAULT_ACCELEROMETER_INTERRUPT_STREAM_THRESHOLD_MG = 250 +DEFAULT_ACCELEROMETER_INTERRUPT_STREAM_MINIMAL_DURATION_MS = 10 +DEFAULT_ACCELEROMETER_INTERRUPT_STREAM_IGNORE_DURATION_MS = 1000 + +DEFAULT_BATTERY_STREAM_SAMPLING_PERIOD_MS = 5000 from badge_protocol import * @@ -26,19 +62,14 @@ def get_timestamps(): def get_timestamps_from_time(t): timestamp_seconds = int(t) timestamp_fraction_of_second = t - timestamp_seconds - timestamp_miliseconds = int(1000 * timestamp_fraction_of_second) - return (timestamp_seconds, timestamp_miliseconds) + timestamp_ms = int(1000 * timestamp_fraction_of_second) + return (timestamp_seconds, timestamp_ms) # Convert badge timestamp representation to python representation def timestamps_to_time(timestamp_seconds, timestamp_miliseconds): return float(timestamp_seconds) + (float(timestamp_miliseconds) / 1000.0) -# Returns true if a given header message indicates end of stream. (i.e. last chunk) -# See badge communication protocols for more information. -def is_end_header(header_message): - # The end header is defined to be a header full of 0s. This is kind of hacky... - return header_message.serialize_message() == chr(0) * header_message.length() # Represents an OpenBadge currently connected via the BadgeConnection 'connection'. # The 'connection' should already be connected when it is used to initialize this class. @@ -46,6 +77,19 @@ def is_end_header(header_message): class OpenBadge(object): def __init__(self, connection): self.connection = connection + self.status_response_queue = Queue.Queue() + self.start_microphone_response_queue = Queue.Queue() + self.start_scan_response_queue = Queue.Queue() + self.start_accelerometer_response_queue = Queue.Queue() + self.start_accelerometer_interrupt_response_queue = Queue.Queue() + self.start_battery_response_queue = Queue.Queue() + self.microphone_data_response_queue = Queue.Queue() + self.scan_data_response_queue = Queue.Queue() + self.accelerometer_data_response_queue = Queue.Queue() + self.accelerometer_interrupt_data_response_queue = Queue.Queue() + self.battery_data_response_queue = Queue.Queue() + self.test_response_queue = Queue.Queue() + self.stream_response_queue = Queue.Queue() # Helper function to send a BadgeMessage `command_message` to a device, expecting a response # of class `response_type` that is a subclass of BadgeMessage, or None if no response is expected. @@ -63,40 +107,140 @@ def send_command(self, command_message, response_type): else: logger.info("No response expected, transmission successful.") return True - + + + def send_request(self, request_message): + serialized_request = request_message.encode() + + # Adding length header: + serialized_request_len = struct.pack('>H', len(serialized_request)) + serialized_request = serialized_request_len + serialized_request + + logger.debug("Sending: {}, Raw: {}".format(request_message, serialized_request.encode("hex"))) + + self.connection.send(serialized_request, response_len = 0) + + + + def receive_response(self): + response_len = struct.unpack('>H', self.connection.await_data(2))[0] + print("Wait response len: " + str(response_len)) + serialized_response = self.connection.await_data(response_len) + + response_message = Response.decode(serialized_response) + + queue_options = { + Response_status_response_tag: self.status_response_queue, + Response_start_microphone_response_tag: self.start_microphone_response_queue, + Response_start_scan_response_tag: self.start_scan_response_queue, + Response_start_accelerometer_response_tag: self.start_accelerometer_response_queue, + Response_start_accelerometer_interrupt_response_tag: self.start_accelerometer_interrupt_response_queue, + Response_start_battery_response_tag: self.start_battery_response_queue, + Response_microphone_data_response_tag: self.microphone_data_response_queue, + Response_scan_data_response_tag: self.scan_data_response_queue, + Response_accelerometer_data_response_tag: self.accelerometer_data_response_queue, + Response_accelerometer_interrupt_data_response_tag: self.accelerometer_interrupt_data_response_queue, + Response_battery_data_response_tag: self.battery_data_response_queue, + Response_test_response_tag: self.test_response_queue, + Response_stream_response_tag: self.stream_response_queue, + } + response_options = { + Response_status_response_tag: response_message.type.status_response, + Response_start_microphone_response_tag: response_message.type.start_microphone_response, + Response_start_scan_response_tag: response_message.type.start_scan_response, + Response_start_accelerometer_response_tag: response_message.type.start_accelerometer_response, + Response_start_accelerometer_interrupt_response_tag: response_message.type.start_accelerometer_interrupt_response, + Response_start_battery_response_tag: response_message.type.start_battery_response, + Response_microphone_data_response_tag: response_message.type.microphone_data_response, + Response_scan_data_response_tag: response_message.type.scan_data_response, + Response_accelerometer_data_response_tag: response_message.type.accelerometer_data_response, + Response_accelerometer_interrupt_data_response_tag: response_message.type.accelerometer_interrupt_data_response, + Response_battery_data_response_tag: response_message.type.battery_data_response, + Response_test_response_tag: response_message.type.test_response, + Response_stream_response_tag: response_message.type.stream_response, + } + queue_options[response_message.type.which].put(response_options[response_message.type.which]) + + + + # Sends a status request to this Badge. # Optional fields new_id and new_group number will set the badge's id # and group number. They must be sent together. # Returns a StatusResponse() representing badge's response. def get_status(self, t=None, new_id=None, new_group_number=None): if t is None: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + (timestamp_seconds, timestamp_ms) = get_timestamps() else: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) - - status_request = StatusRequest(timestamp_seconds, timestamp_miliseconds, badge_id=new_id, group_number=new_group_number) - - return self.send_command(status_request, StatusResponse) + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_status_request_tag + request.type.status_request = StatusRequest() + request.type.status_request.timestamp = Timestamp() + request.type.status_request.timestamp.seconds = timestamp_seconds + request.type.status_request.timestamp.ms = timestamp_ms + if(not ((new_id is None) or (new_group_number is None)) ): + request.type.status_request.badge_assignement = BadgeAssignement() + request.type.status_request.badge_assignement.ID = new_id + request.type.status_request.badge_assignement.group = new_group_number + request.type.status_request.has_badge_assignement = True + + self.send_request(request) + + # Clear the queue before receiving + with self.status_response_queue.mutex: + self.status_response_queue.queue.clear() + + while(self.status_response_queue.empty()): + self.receive_response() + + return self.status_response_queue.get() + + # Sends a request to the badge to start recording microphone data. # timeout_minutes is the number of minutes the badge is to record, # or 0 if the badge is to scan indefinetely. # Returns a StartRecordResponse() representing the badges response. - def start_recording(self, t=None, timeout_minutes=0): + def start_microphone(self, t=None, timeout_minutes=0, sampling_period_ms=DEFAULT_MICROPHONE_SAMPLING_PERIOD_MS): if t is None: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + (timestamp_seconds, timestamp_ms) = get_timestamps() else: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) - - start_record_request = StartRecordRequest(timestamp_seconds, timestamp_miliseconds, timeout_minutes) - - return self.send_command(start_record_request, StartRecordResponse) + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + request = Request() + request.type.which = Request_start_microphone_request_tag + request.type.start_microphone_request = StartMicrophoneRequest() + request.type.start_microphone_request.timestamp = Timestamp() + request.type.start_microphone_request.timestamp.seconds = timestamp_seconds + request.type.start_microphone_request.timestamp.ms = timestamp_ms + request.type.start_microphone_request.timeout = int(timeout_minutes) + request.type.start_microphone_request.period_ms = sampling_period_ms + + self.send_request(request) + + + with self.start_microphone_response_queue.mutex: + self.start_microphone_response_queue.queue.clear() + + while(self.start_microphone_response_queue.empty()): + self.receive_response() + + return self.start_microphone_response_queue.get() # Sends a request to the badge to stop recording. # Returns True if request was successfuly sent. - def stop_recording(self): - stop_record_request = StopRecordRequest() - return self.send_command(stop_record_request, None) + def stop_microphone(self): + + request = Request() + request.type.which = Request_stop_microphone_request_tag + request.type.stop_microphone_request = StopMicrophoneRequest() + + self.send_request(request) + + # Sends a request to the badge to start performing scans and collecting scan data. # timeout_minutes is the number of minutes the badge is to collect scan data for @@ -106,89 +250,588 @@ def stop_recording(self): # window_miliseconds and interval_miliseconds controls radio duty cycle during scanning (0 for firmware default) # radio is active for [window_miliseconds] every [interval_miliseconds] # Returns a StartScanningResponse() representing the badge's response. - def start_scanning(self, t=None, - timeout_minutes=0, window_miliseconds=DEFAULT_SCAN_WINDOW, interval_miliseconds=DEFAULT_SCAN_INTERVAL, - duration_seconds=DEFAULT_SCAN_DURATION, period_seconds=DEFAULT_SCAN_PERIOD): + def start_scan(self, t=None, + timeout_minutes=0, window_ms=DEFAULT_SCAN_WINDOW, interval_ms=DEFAULT_SCAN_INTERVAL, + duration_seconds=DEFAULT_SCAN_DURATION, period_seconds=DEFAULT_SCAN_PERIOD, aggregation_type=DEFAULT_SCAN_AGGREGATION_TYPE): if t is None: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + (timestamp_seconds, timestamp_ms) = get_timestamps() else: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_start_scan_request_tag + request.type.start_scan_request = StartScanRequest() + request.type.start_scan_request.timestamp = Timestamp() + request.type.start_scan_request.timestamp.seconds = timestamp_seconds + request.type.start_scan_request.timestamp.ms = timestamp_ms + request.type.start_scan_request.timeout = int(timeout_minutes) + request.type.start_scan_request.window = window_ms + request.type.start_scan_request.interval = interval_ms + request.type.start_scan_request.duration = duration_seconds + request.type.start_scan_request.period = period_seconds + request.type.start_scan_request.aggregation_type = aggregation_type + + self.send_request(request) + + # Clear the queue before receiving + with self.start_scan_response_queue.mutex: + self.start_scan_response_queue.queue.clear() + + while(self.start_scan_response_queue.empty()): + self.receive_response() + + return self.start_scan_response_queue.get() + - start_scanning_request = StartScanningRequest(timestamp_seconds, timestamp_miliseconds, timeout_minutes, - window_miliseconds, interval_miliseconds, duration_seconds, period_seconds) - - return self.send_command(start_scanning_request, StartScanningResponse) # Sends a request to the badge to stop scanning. # Returns True if request was successfuly sent. - def stop_scanning(self): - stop_scanning_request = StopScanningRequest() - return self.send_command(stop_scanning_request, None) + def stop_scan(self): + + request = Request() + request.type.which = Request_stop_scan_request_tag + request.type.stop_scan_request = StopScanRequest() + + self.send_request(request) + + + + def start_accelerometer(self, t=None, + timeout_minutes=0, operating_mode=DEFAULT_ACCELEROMETER_OPERATING_MODE, full_scale=DEFAULT_ACCELEROMETER_FULL_SCALE, + datarate=DEFAULT_ACCELEROMETER_DATARATE, fifo_sampling_period_ms=DEFAULT_ACCELEROMETER_FIFO_SAMPLING_PERIOD_MS): + + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_start_accelerometer_request_tag + request.type.start_accelerometer_request = StartAccelerometerRequest() + request.type.start_accelerometer_request.timestamp = Timestamp() + request.type.start_accelerometer_request.timestamp.seconds = timestamp_seconds + request.type.start_accelerometer_request.timestamp.ms = timestamp_ms + request.type.start_accelerometer_request.timeout = int(timeout_minutes) + request.type.start_accelerometer_request.operating_mode = operating_mode + request.type.start_accelerometer_request.full_scale = full_scale + request.type.start_accelerometer_request.datarate = datarate + request.type.start_accelerometer_request.fifo_sampling_period_ms = fifo_sampling_period_ms + + self.send_request(request) + + # Clear the queue before receiving + with self.start_accelerometer_response_queue.mutex: + self.start_accelerometer_response_queue.queue.clear() + + while(self.start_accelerometer_response_queue.empty()): + self.receive_response() + + return self.start_accelerometer_response_queue.get() + + def stop_accelerometer(self): + + request = Request() + request.type.which = Request_stop_accelerometer_request_tag + request.type.stop_accelerometer_request = StopAccelerometerRequest() + + self.send_request(request) + + + + def start_accelerometer_interrupt(self, t=None, + timeout_minutes=0, threshold_mg=DEFAULT_ACCELEROMETER_INTERRUPT_THRESHOLD_MG, + minimal_duration_ms=DEFAULT_ACCELEROMETER_INTERRUPT_MINIMAL_DURATION_MS, + ignore_duration_ms=DEFAULT_ACCELEROMETER_INTERRUPT_IGNORE_DURATION_MS): + + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + + request = Request() + request.type.which = Request_start_accelerometer_interrupt_request_tag + request.type.start_accelerometer_interrupt_request = StartAccelerometerInterruptRequest() + request.type.start_accelerometer_interrupt_request.timestamp = Timestamp() + request.type.start_accelerometer_interrupt_request.timestamp.seconds = timestamp_seconds + request.type.start_accelerometer_interrupt_request.timestamp.ms = timestamp_ms + request.type.start_accelerometer_interrupt_request.timeout = int(timeout_minutes) + request.type.start_accelerometer_interrupt_request.threshold_mg = threshold_mg + request.type.start_accelerometer_interrupt_request.minimal_duration_ms = minimal_duration_ms + request.type.start_accelerometer_interrupt_request.ignore_duration_ms = ignore_duration_ms + + self.send_request(request) + + # Clear the queue before receiving + with self.start_accelerometer_interrupt_response_queue.mutex: + self.start_accelerometer_interrupt_response_queue.queue.clear() + + while(self.start_accelerometer_interrupt_response_queue.empty()): + self.receive_response() + + return self.start_accelerometer_interrupt_response_queue.get() + + def stop_accelerometer_interrupt(self): + + request = Request() + request.type.which = Request_stop_accelerometer_interrupt_request_tag + request.type.stop_accelerometer_interrupt_request = StopAccelerometerInterruptRequest() + + self.send_request(request) + + + + + def start_battery(self, t=None, timeout_minutes=0, sampling_period_ms=DEFAULT_BATTERY_SAMPLING_PERIOD_MS): + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + + request = Request() + request.type.which = Request_start_battery_request_tag + request.type.start_battery_request = StartBatteryRequest() + request.type.start_battery_request.timestamp = Timestamp() + request.type.start_battery_request.timestamp.seconds = timestamp_seconds + request.type.start_battery_request.timestamp.ms = timestamp_ms + request.type.start_battery_request.timeout = int(timeout_minutes) + request.type.start_battery_request.period_ms = sampling_period_ms + + self.send_request(request) + + + with self.start_battery_response_queue.mutex: + self.start_battery_response_queue.queue.clear() + + while(self.start_battery_response_queue.empty()): + self.receive_response() + + return self.start_battery_response_queue.get() + + + + def stop_battery(self): + + request = Request() + request.type.which = Request_stop_battery_request_tag + request.type.stop_battery_request = StopBatteryRequest() + + self.send_request(request) + + return True + + + + # Send a request to the badge to light an led to identify its self. # If duration_seconds == 0, badge will turn off LED if currently lit. # Returns True if request was successfuly sent. def identify(self, duration_seconds=10): - identify_request = IdentifyRequest(duration_seconds) - return self.send_command(identify_request, None) + + request = Request() + request.type.which = Request_identify_request_tag + request.type.identify_request = IdentifyRequest() + request.type.identify_request.timeout = duration_seconds + + self.send_request(request) + + return True + + def test(self): + + request = Request() + request.type.which = Request_test_request_tag + request.type.test_request = TestRequest() + + + self.send_request(request) + + + with self.test_response_queue.mutex: + self.test_response_queue.queue.clear() + + while(self.test_response_queue.empty()): + self.receive_response() + + return self.test_response_queue.get() + + def restart(self): + + request = Request() + request.type.which = Request_restart_request_tag + request.type.restart_request = RestartRequest() + + self.send_request(request) + + return True + # Send a request to the badge for recorded microphone data starting at the given timestamp. # Returns a list of tuples of (MicrophoneDataHeader(), microphone_sample_chunk_data), where each tuple # contains one chunk of microphone data. - def get_mic_data(self, t=None): + def get_microphone_data(self, t=None): if t is None: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + (timestamp_seconds, timestamp_ms) = get_timestamps() else: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) - - - mic_data_request = MicrophoneDataRequest(timestamp_seconds, timestamp_miliseconds) - header = self.send_command(mic_data_request, MicrophoneDataHeader) - - chunks_and_headers = [] - - while not is_end_header(header): - bytes_awaited = header.num_samples_in_chunk - logger.debug("Awaiting {} bytes.".format(bytes_awaited)) - data = self.connection.await_data(bytes_awaited) - - # Note - not using a deserializer here for performance reasons - chunks_and_headers.append((header, map(ord, data))) - - serialized_header = self.connection.await_data(MicrophoneDataHeader.length()) - header = MicrophoneDataHeader.deserialize_message(serialized_header) - - return chunks_and_headers - + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_microphone_data_request_tag + request.type.microphone_data_request = MicrophoneDataRequest() + request.type.microphone_data_request.timestamp = Timestamp() + request.type.microphone_data_request.timestamp.seconds = timestamp_seconds + request.type.microphone_data_request.timestamp.ms = timestamp_ms + + self.send_request(request) + + # Clear the queue before receiving + with self.microphone_data_response_queue.mutex: + self.microphone_data_response_queue.queue.clear() + + microphone_chunks = [] + + while True: + self.receive_response() + if(not self.microphone_data_response_queue.empty()): + microphone_data_response = self.microphone_data_response_queue.get() + if(microphone_data_response.last_response): + break; + microphone_chunks.append(microphone_data_response) + + + return microphone_chunks + # Sends a request to the badge for recorded scan data starting at the the given timestamp. # Returns a list of tuples of (ScanDataHeader(), [ScanDataDevice(), ScanDataDevice(), ...]) # where each tuple contains a header and a list of devices seen from one scan. def get_scan_data(self, t=None): if t is None: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + (timestamp_seconds, timestamp_ms) = get_timestamps() else: - (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) - + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_scan_data_request_tag + request.type.scan_data_request = ScanDataRequest() + request.type.scan_data_request.timestamp = Timestamp() + request.type.scan_data_request.timestamp.seconds = timestamp_seconds + request.type.scan_data_request.timestamp.ms = timestamp_ms + + self.send_request(request) + + # Clear the queue before receiving + with self.scan_data_response_queue.mutex: + self.scan_data_response_queue.queue.clear() + + scan_chunks = [] + + while True: + self.receive_response() + if(not self.scan_data_response_queue.empty()): + scan_data_response = self.scan_data_response_queue.get() + if(scan_data_response.last_response): + break; + scan_chunks.append(scan_data_response) + + + return scan_chunks + + + + def get_accelerometer_data(self, t=None): + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_accelerometer_data_request_tag + request.type.accelerometer_data_request = AccelerometerDataRequest() + request.type.accelerometer_data_request.timestamp = Timestamp() + request.type.accelerometer_data_request.timestamp.seconds = timestamp_seconds + request.type.accelerometer_data_request.timestamp.ms = timestamp_ms + + self.send_request(request) + + # Clear the queue before receiving + with self.accelerometer_data_response_queue.mutex: + self.accelerometer_data_response_queue.queue.clear() + + accelerometer_chunks = [] + + while True: + self.receive_response() + if(not self.accelerometer_data_response_queue.empty()): + accelerometer_data_response = self.accelerometer_data_response_queue.get() + if(accelerometer_data_response.last_response): + break; + accelerometer_chunks.append(accelerometer_data_response) + + + return accelerometer_chunks + + + + def get_accelerometer_interrupt_data(self, t=None): + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_accelerometer_interrupt_data_request_tag + request.type.accelerometer_interrupt_data_request = AccelerometerInterruptDataRequest() + request.type.accelerometer_interrupt_data_request.timestamp = Timestamp() + request.type.accelerometer_interrupt_data_request.timestamp.seconds = timestamp_seconds + request.type.accelerometer_interrupt_data_request.timestamp.ms = timestamp_ms + + self.send_request(request) + + # Clear the queue before receiving + with self.accelerometer_interrupt_data_response_queue.mutex: + self.accelerometer_interrupt_data_response_queue.queue.clear() + + accelerometer_interrupt_chunks = [] + + while True: + self.receive_response() + if(not self.accelerometer_interrupt_data_response_queue.empty()): + accelerometer_interrupt_data_response = self.accelerometer_interrupt_data_response_queue.get() + if(accelerometer_interrupt_data_response.last_response): + break; + accelerometer_interrupt_chunks.append(accelerometer_interrupt_data_response) + + + return accelerometer_interrupt_chunks + + + def get_battery_data(self, t=None): + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + request = Request() + request.type.which = Request_battery_data_request_tag + request.type.battery_data_request = BatteryDataRequest() + request.type.battery_data_request.timestamp = Timestamp() + request.type.battery_data_request.timestamp.seconds = timestamp_seconds + request.type.battery_data_request.timestamp.ms = timestamp_ms + + self.send_request(request) + + # Clear the queue before receiving + with self.battery_data_response_queue.mutex: + self.battery_data_response_queue.queue.clear() + + battery_chunks = [] + + while True: + self.receive_response() + if(not self.battery_data_response_queue.empty()): + battery_data_response = self.battery_data_response_queue.get() + if(battery_data_response.last_response): + break; + battery_chunks.append(battery_data_response) + + + return battery_chunks + + + + + + + + +################# Streaming ################ + def start_microphone_stream(self, t=None, timeout_minutes=0, sampling_period_ms=DEFAULT_MICROPHONE_STREAM_SAMPLING_PERIOD_MS): + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + request = Request() + request.type.which = Request_start_microphone_stream_request_tag + request.type.start_microphone_stream_request = StartMicrophoneStreamRequest() + request.type.start_microphone_stream_request.timestamp = Timestamp() + request.type.start_microphone_stream_request.timestamp.seconds = timestamp_seconds + request.type.start_microphone_stream_request.timestamp.ms = timestamp_ms + request.type.start_microphone_stream_request.timeout = int(timeout_minutes) + request.type.start_microphone_stream_request.period_ms = sampling_period_ms + + self.send_request(request) + - scan_data_request = ScanDataRequest(timestamp_seconds) - header = self.send_command(scan_data_request, ScanDataHeader) + def stop_microphone_stream(self): + + request = Request() + request.type.which = Request_stop_microphone_stream_request_tag + request.type.stop_microphone_stream_request = StopMicrophoneStreamRequest() + + self.send_request(request) + + - scan_headers_and_devices_seen = [] + def start_scan_stream(self, t=None, + timeout_minutes=0, window_ms=DEFAULT_SCAN_STREAM_WINDOW, interval_ms=DEFAULT_SCAN_STREAM_INTERVAL, + duration_seconds=DEFAULT_SCAN_STREAM_DURATION, period_seconds=DEFAULT_SCAN_STREAM_PERIOD, aggregation_type=DEFAULT_SCAN_STREAM_AGGREGATION_TYPE): + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + + request = Request() + request.type.which = Request_start_scan_stream_request_tag + request.type.start_scan_stream_request = StartScanStreamRequest() + request.type.start_scan_stream_request.timestamp = Timestamp() + request.type.start_scan_stream_request.timestamp.seconds = timestamp_seconds + request.type.start_scan_stream_request.timestamp.ms = timestamp_ms + request.type.start_scan_stream_request.timeout = int(timeout_minutes) + request.type.start_scan_stream_request.window = window_ms + request.type.start_scan_stream_request.interval = interval_ms + request.type.start_scan_stream_request.duration = duration_seconds + request.type.start_scan_stream_request.period = period_seconds + request.type.start_scan_stream_request.aggregation_type = aggregation_type + + self.send_request(request) + + - while not is_end_header(header): - bytes_awaited = header.num_devices_seen * ScanDataDevice.length() - logger.debug("Awaiting {} bytes".format(bytes_awaited)) - data = self.connection.await_data(bytes_awaited) - devices = [] - if data: - for i in range(0, len(data), ScanDataDevice.length()): - devices.append(ScanDataDevice.deserialize_message(data[i:i+ScanDataDevice.length()])) + def stop_scan_stream(self): + + request = Request() + request.type.which = Request_stop_scan_stream_request_tag + request.type.stop_scan_stream_request = StopScanStreamRequest() + + self.send_request(request) + + + + def start_accelerometer_stream(self, t=None, + timeout_minutes=0, operating_mode=DEFAULT_ACCELEROMETER_STREAM_OPERATING_MODE, full_scale=DEFAULT_ACCELEROMETER_STREAM_FULL_SCALE, + datarate=DEFAULT_ACCELEROMETER_STREAM_DATARATE, fifo_sampling_period_ms=DEFAULT_ACCELEROMETER_STREAM_FIFO_SAMPLING_PERIOD_MS): + + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + + request = Request() + request.type.which = Request_start_accelerometer_stream_request_tag + request.type.start_accelerometer_stream_request = StartAccelerometerStreamRequest() + request.type.start_accelerometer_stream_request.timestamp = Timestamp() + request.type.start_accelerometer_stream_request.timestamp.seconds = timestamp_seconds + request.type.start_accelerometer_stream_request.timestamp.ms = timestamp_ms + request.type.start_accelerometer_stream_request.timeout = int(timeout_minutes) + request.type.start_accelerometer_stream_request.operating_mode = operating_mode + request.type.start_accelerometer_stream_request.full_scale = full_scale + request.type.start_accelerometer_stream_request.datarate = datarate + request.type.start_accelerometer_stream_request.fifo_sampling_period_ms = fifo_sampling_period_ms + + self.send_request(request) + + + + def stop_accelerometer_stream(self): + + request = Request() + request.type.which = Request_stop_accelerometer_stream_request_tag + request.type.stop_accelerometer_stream_request = StopAccelerometerStreamRequest() + + self.send_request(request) + - scan_headers_and_devices_seen.append((header, devices)) + + def start_accelerometer_interrupt_stream(self, t=None, + timeout_minutes=0, threshold_mg=DEFAULT_ACCELEROMETER_INTERRUPT_STREAM_THRESHOLD_MG, + minimal_duration_ms=DEFAULT_ACCELEROMETER_INTERRUPT_STREAM_MINIMAL_DURATION_MS, + ignore_duration_ms=DEFAULT_ACCELEROMETER_INTERRUPT_STREAM_IGNORE_DURATION_MS): + + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + request = Request() + request.type.which = Request_start_accelerometer_interrupt_stream_request_tag + request.type.start_accelerometer_interrupt_stream_request = StartAccelerometerInterruptStreamRequest() + request.type.start_accelerometer_interrupt_stream_request.timestamp = Timestamp() + request.type.start_accelerometer_interrupt_stream_request.timestamp.seconds = timestamp_seconds + request.type.start_accelerometer_interrupt_stream_request.timestamp.ms = timestamp_ms + request.type.start_accelerometer_interrupt_stream_request.timeout = int(timeout_minutes) + request.type.start_accelerometer_interrupt_stream_request.threshold_mg = threshold_mg + request.type.start_accelerometer_interrupt_stream_request.minimal_duration_ms = minimal_duration_ms + request.type.start_accelerometer_interrupt_stream_request.ignore_duration_ms = ignore_duration_ms + + self.send_request(request) + + + + def stop_accelerometer_interrupt_stream(self): + + request = Request() + request.type.which = Request_stop_accelerometer_interrupt_stream_request_tag + request.type.stop_accelerometer_interrupt_stream_request = StopAccelerometerInterruptStreamRequest() + + self.send_request(request) + - serialized_header = self.connection.await_data(ScanDataHeader.length()) - logger.debug("Attempting to deserialize header {} of length {}".format(serialized_header.encode("hex"), len(serialized_header))) - header = ScanDataHeader.deserialize_message(serialized_header) + + + def start_battery_stream(self, t=None, timeout_minutes=0, sampling_period_ms=DEFAULT_BATTERY_STREAM_SAMPLING_PERIOD_MS): + if t is None: + (timestamp_seconds, timestamp_ms) = get_timestamps() + else: + (timestamp_seconds, timestamp_ms) = get_timestamps_from_time(t) + + + + + request = Request() + request.type.which = Request_start_battery_stream_request_tag + request.type.start_battery_stream_request = StartBatteryStreamRequest() + request.type.start_battery_stream_request.timestamp = Timestamp() + request.type.start_battery_stream_request.timestamp.seconds = timestamp_seconds + request.type.start_battery_stream_request.timestamp.ms = timestamp_ms + request.type.start_battery_stream_request.timeout = int(timeout_minutes) + request.type.start_battery_stream_request.period_ms = sampling_period_ms + + self.send_request(request) + + + + + def stop_battery_stream(self): + + request = Request() + request.type.which = Request_stop_battery_stream_request_tag + request.type.stop_battery_stream_request = StopBatteryStreamRequest() + + self.send_request(request) - return scan_headers_and_devices_seen + + def stream_clear(self): + # Clear the queue + with self.stream_response_queue.mutex: + self.stream_response_queue.queue.clear() + + def get_stream(self): + self.receive_response() + if(not self.stream_response_queue.empty()): + return self.stream_response_queue.get() + else: + return [] + + \ No newline at end of file diff --git a/BadgeFramework/badge_legacy.py b/BadgeFramework/badge_legacy.py new file mode 100644 index 0000000..f697db3 --- /dev/null +++ b/BadgeFramework/badge_legacy.py @@ -0,0 +1,194 @@ +from __future__ import division, absolute_import, print_function +import time +import logging +import sys + +# Default scan settings from openbadge-hub-py. +DEFAULT_SCAN_WINDOW = 100 +DEFAULT_SCAN_INTERVAL = 300 +DEFAULT_SCAN_DURATION = 5 +DEFAULT_SCAN_PERIOD = 60 + +from badge_protocol_legacy import * + +logger = logging.getLogger(__name__) + +# -- Helper methods used often in badge communication -- + +# We generally define timestamp_seconds to be in number of seconds since UTC epoch +# and timestamp_miliseconds to be the miliseconds portion of that UTC timestamp. + +# Returns the current timestamp as two parts - seconds and milliseconds +def get_timestamps(): + return get_timestamps_from_time(time.time()) + +# Returns the given time as two parts - seconds and milliseconds +def get_timestamps_from_time(t): + timestamp_seconds = int(t) + timestamp_fraction_of_second = t - timestamp_seconds + timestamp_miliseconds = int(1000 * timestamp_fraction_of_second) + return (timestamp_seconds, timestamp_miliseconds) + + +# Convert badge timestamp representation to python representation +def timestamps_to_time(timestamp_seconds, timestamp_miliseconds): + return float(timestamp_seconds) + (float(timestamp_miliseconds) / 1000.0) + +# Returns true if a given header message indicates end of stream. (i.e. last chunk) +# See badge communication protocols for more information. +def is_end_header(header_message): + # The end header is defined to be a header full of 0s. This is kind of hacky... + return header_message.serialize_message() == chr(0) * header_message.length() + +# Represents an OpenBadge currently connected via the BadgeConnection 'connection'. +# The 'connection' should already be connected when it is used to initialize this class. +# Implements methods that allow for interaction with that badge. +class OpenBadge(object): + def __init__(self, connection): + self.connection = connection + + # Helper function to send a BadgeMessage `command_message` to a device, expecting a response + # of class `response_type` that is a subclass of BadgeMessage, or None if no response is expected. + def send_command(self, command_message, response_type): + expected_response_length = response_type.length() if response_type else 0 + + serialized_command = command_message.serialize_message() + logger.debug("Sending: {}, Raw: {}".format(command_message, serialized_command.encode("hex"))) + serialized_response = self.connection.send(serialized_command, response_len=expected_response_length) + + if expected_response_length > 0: + response = response_type.deserialize_message(serialized_response) + logger.info("Recieved response {}".format(response)) + return response + else: + logger.info("No response expected, transmission successful.") + return True + + # Sends a status request to this Badge. + # Optional fields new_id and new_group number will set the badge's id + # and group number. They must be sent together. + # Returns a StatusResponse() representing badge's response. + def get_status(self, t=None, new_id=None, new_group_number=None): + if t is None: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + else: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) + + status_request = StatusRequest(timestamp_seconds, timestamp_miliseconds, badge_id=new_id, group_number=new_group_number) + + return self.send_command(status_request, StatusResponse) + + # Sends a request to the badge to start recording microphone data. + # timeout_minutes is the number of minutes the badge is to record, + # or 0 if the badge is to scan indefinetely. + # Returns a StartRecordResponse() representing the badges response. + def start_recording(self, t=None, timeout_minutes=0): + if t is None: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + else: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) + + start_record_request = StartRecordRequest(timestamp_seconds, timestamp_miliseconds, timeout_minutes) + + return self.send_command(start_record_request, StartRecordResponse) + + # Sends a request to the badge to stop recording. + # Returns True if request was successfuly sent. + def stop_recording(self): + stop_record_request = StopRecordRequest() + return self.send_command(stop_record_request, None) + + # Sends a request to the badge to start performing scans and collecting scan data. + # timeout_minutes is the number of minutes the badge is to collect scan data for + # 0 if the badge is to scan and collect indefinetely. + # duration_seconds is how many seconds each scan operation is to last for (0 for firmware default) + # period_seconds is how often the badge should scan (0 for firmware default) + # window_miliseconds and interval_miliseconds controls radio duty cycle during scanning (0 for firmware default) + # radio is active for [window_miliseconds] every [interval_miliseconds] + # Returns a StartScanningResponse() representing the badge's response. + def start_scanning(self, t=None, + timeout_minutes=0, window_miliseconds=DEFAULT_SCAN_WINDOW, interval_miliseconds=DEFAULT_SCAN_INTERVAL, + duration_seconds=DEFAULT_SCAN_DURATION, period_seconds=DEFAULT_SCAN_PERIOD): + if t is None: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + else: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) + + start_scanning_request = StartScanningRequest(timestamp_seconds, timestamp_miliseconds, timeout_minutes, + window_miliseconds, interval_miliseconds, duration_seconds, period_seconds) + + return self.send_command(start_scanning_request, StartScanningResponse) + + # Sends a request to the badge to stop scanning. + # Returns True if request was successfuly sent. + def stop_scanning(self): + stop_scanning_request = StopScanningRequest() + return self.send_command(stop_scanning_request, None) + + # Send a request to the badge to light an led to identify its self. + # If duration_seconds == 0, badge will turn off LED if currently lit. + # Returns True if request was successfuly sent. + def identify(self, duration_seconds=10): + identify_request = IdentifyRequest(duration_seconds) + return self.send_command(identify_request, None) + + # Send a request to the badge for recorded microphone data starting at the given timestamp. + # Returns a list of tuples of (MicrophoneDataHeader(), microphone_sample_chunk_data), where each tuple + # contains one chunk of microphone data. + def get_mic_data(self, t=None): + if t is None: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + else: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) + + + mic_data_request = MicrophoneDataRequest(timestamp_seconds, timestamp_miliseconds) + header = self.send_command(mic_data_request, MicrophoneDataHeader) + + chunks_and_headers = [] + + while not is_end_header(header): + bytes_awaited = header.num_samples_in_chunk + logger.debug("Awaiting {} bytes.".format(bytes_awaited)) + data = self.connection.await_data(bytes_awaited) + + # Note - not using a deserializer here for performance reasons + chunks_and_headers.append((header, map(ord, data))) + + serialized_header = self.connection.await_data(MicrophoneDataHeader.length()) + header = MicrophoneDataHeader.deserialize_message(serialized_header) + + return chunks_and_headers + + # Sends a request to the badge for recorded scan data starting at the the given timestamp. + # Returns a list of tuples of (ScanDataHeader(), [ScanDataDevice(), ScanDataDevice(), ...]) + # where each tuple contains a header and a list of devices seen from one scan. + def get_scan_data(self, t=None): + if t is None: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps() + else: + (timestamp_seconds, timestamp_miliseconds) = get_timestamps_from_time(t) + + + scan_data_request = ScanDataRequest(timestamp_seconds) + header = self.send_command(scan_data_request, ScanDataHeader) + + scan_headers_and_devices_seen = [] + + while not is_end_header(header): + bytes_awaited = header.num_devices_seen * ScanDataDevice.length() + logger.debug("Awaiting {} bytes".format(bytes_awaited)) + data = self.connection.await_data(bytes_awaited) + + devices = [] + if data: + for i in range(0, len(data), ScanDataDevice.length()): + devices.append(ScanDataDevice.deserialize_message(data[i:i+ScanDataDevice.length()])) + + scan_headers_and_devices_seen.append((header, devices)) + + serialized_header = self.connection.await_data(ScanDataHeader.length()) + logger.debug("Attempting to deserialize header {} of length {}".format(serialized_header.encode("hex"), len(serialized_header))) + header = ScanDataHeader.deserialize_message(serialized_header) + + return scan_headers_and_devices_seen diff --git a/BadgeFramework/badge_protocol.py b/BadgeFramework/badge_protocol.py index 4f6fb12..163c8b5 100644 --- a/BadgeFramework/badge_protocol.py +++ b/BadgeFramework/badge_protocol.py @@ -1,258 +1,3393 @@ -from __future__ import division, absolute_import, print_function -from struct import * +import struct -# This file defines much of the structure needed to carry out our communication protocol in Python. -# You can find more information about our communications protocol here: -# https://github.com/HumanDynamics/OpenBadge/wiki/Communication-protocol +PROTOCOL_MICROPHONE_DATA_SIZE = 114 +PROTOCOL_SCAN_DATA_SIZE = 29 +PROTOCOL_ACCELEROMETER_DATA_SIZE = 100 +PROTOCOL_MICROPHONE_STREAM_SIZE = 10 +PROTOCOL_SCAN_STREAM_SIZE = 10 +PROTOCOL_ACCELEROMETER_STREAM_SIZE = 10 +PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE = 10 +PROTOCOL_BATTERY_STREAM_SIZE = 10 -STATUS_REQUEST_HEADER = "s" -START_RECORDING_HEADER = "1" -STOP_RECORDING_HEADER = "0" -START_SCANNING_HEADER = "p" -STOP_SCANNING_HEADER = "q" -REQUEST_MIC_DATA_HEADER = "r" -REQUEST_SCAN_DATA_HEADER = "b" -IDENITFY_HEADER = "i" +Request_status_request_tag = 1 +Request_start_microphone_request_tag = 2 +Request_stop_microphone_request_tag = 3 +Request_start_scan_request_tag = 4 +Request_stop_scan_request_tag = 5 +Request_start_accelerometer_request_tag = 6 +Request_stop_accelerometer_request_tag = 7 +Request_start_accelerometer_interrupt_request_tag = 8 +Request_stop_accelerometer_interrupt_request_tag = 9 +Request_start_battery_request_tag = 10 +Request_stop_battery_request_tag = 11 +Request_microphone_data_request_tag = 12 +Request_scan_data_request_tag = 13 +Request_accelerometer_data_request_tag = 14 +Request_accelerometer_interrupt_data_request_tag = 15 +Request_battery_data_request_tag = 16 +Request_start_microphone_stream_request_tag = 17 +Request_stop_microphone_stream_request_tag = 18 +Request_start_scan_stream_request_tag = 19 +Request_stop_scan_stream_request_tag = 20 +Request_start_accelerometer_stream_request_tag = 21 +Request_stop_accelerometer_stream_request_tag = 22 +Request_start_accelerometer_interrupt_stream_request_tag = 23 +Request_stop_accelerometer_interrupt_stream_request_tag = 24 +Request_start_battery_stream_request_tag = 25 +Request_stop_battery_stream_request_tag = 26 +Request_identify_request_tag = 27 +Request_test_request_tag = 28 +Request_restart_request_tag = 29 +Response_status_response_tag = 1 +Response_start_microphone_response_tag = 2 +Response_start_scan_response_tag = 3 +Response_start_accelerometer_response_tag = 4 +Response_start_accelerometer_interrupt_response_tag = 5 +Response_start_battery_response_tag = 6 +Response_microphone_data_response_tag = 7 +Response_scan_data_response_tag = 8 +Response_accelerometer_data_response_tag = 9 +Response_accelerometer_interrupt_data_response_tag = 10 +Response_battery_data_response_tag = 11 +Response_stream_response_tag = 12 +Response_test_response_tag = 13 -# These fields are used by BadgeMessage objects to serialize and deserialize binary messages to Python objects. -# Fields are composed of (attribute, length [in bytes], optional, serializer, parser) +class _Ostream: + def __init__(self): + self.buf = b'' + def write(self, data): + self.buf += data + +class _Istream: + def __init__(self, buf): + self.buf = buf + def read(self, l): + if(l > len(self.buf)): + raise Exception("Not enough bytes in Istream to read") + ret = self.buf[0:l] + self.buf = self.buf[l:] + return ret + +class Timestamp: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.seconds = 0 + self.ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_seconds(ostream) + self.encode_ms(ostream) + pass + + def encode_seconds(self, ostream): + ostream.write(struct.pack('>I', self.seconds)) + + def encode_ms(self, ostream): + ostream.write(struct.pack('>H', self.ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_seconds(istream) + self.decode_ms(istream) + pass + + def decode_seconds(self, istream): + self.seconds= struct.unpack('>I', istream.read(4))[0] + + def decode_ms(self, istream): + self.ms= struct.unpack('>H', istream.read(2))[0] + + +class BadgeAssignement: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.ID = 0 + self.group = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_ID(ostream) + self.encode_group(ostream) + pass + + def encode_ID(self, ostream): + ostream.write(struct.pack('>H', self.ID)) + + def encode_group(self, ostream): + ostream.write(struct.pack('>B', self.group)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_ID(istream) + self.decode_group(istream) + pass + + def decode_ID(self, istream): + self.ID= struct.unpack('>H', istream.read(2))[0] + + def decode_group(self, istream): + self.group= struct.unpack('>B', istream.read(1))[0] + + +class BatteryData: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.voltage = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_voltage(ostream) + pass + + def encode_voltage(self, ostream): + ostream.write(struct.pack('>f', self.voltage)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_voltage(istream) + pass + + def decode_voltage(self, istream): + self.voltage= struct.unpack('>f', istream.read(4))[0] + + +class MicrophoneData: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.value = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_value(ostream) + pass + + def encode_value(self, ostream): + ostream.write(struct.pack('>B', self.value)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_value(istream) + pass + + def decode_value(self, istream): + self.value= struct.unpack('>B', istream.read(1))[0] + + +class ScanDevice: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.ID = 0 + self.rssi = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_ID(ostream) + self.encode_rssi(ostream) + pass + + def encode_ID(self, ostream): + ostream.write(struct.pack('>H', self.ID)) + + def encode_rssi(self, ostream): + ostream.write(struct.pack('>b', self.rssi)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_ID(istream) + self.decode_rssi(istream) + pass + + def decode_ID(self, istream): + self.ID= struct.unpack('>H', istream.read(2))[0] + + def decode_rssi(self, istream): + self.rssi= struct.unpack('>b', istream.read(1))[0] + + +class ScanResultData: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.scan_device = None + self.count = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_scan_device(ostream) + self.encode_count(ostream) + pass + + def encode_scan_device(self, ostream): + self.scan_device.encode_internal(ostream) + + def encode_count(self, ostream): + ostream.write(struct.pack('>B', self.count)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_scan_device(istream) + self.decode_count(istream) + pass + + def decode_scan_device(self, istream): + self.scan_device = ScanDevice() + self.scan_device.decode_internal(istream) + + def decode_count(self, istream): + self.count= struct.unpack('>B', istream.read(1))[0] + + +class AccelerometerData: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.acceleration = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_acceleration(ostream) + pass + + def encode_acceleration(self, ostream): + ostream.write(struct.pack('>H', self.acceleration)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_acceleration(istream) + pass + + def decode_acceleration(self, istream): + self.acceleration= struct.unpack('>H', istream.read(2))[0] + + +class AccelerometerRawData: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.raw_acceleration = [] + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_raw_acceleration(ostream) + pass + + def encode_raw_acceleration(self, ostream): + count = 3 + for i in range(0, count): + ostream.write(struct.pack('>h', self.raw_acceleration[i])) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_raw_acceleration(istream) + pass + + def decode_raw_acceleration(self, istream): + count = 3 + for i in range(0, count): + self.raw_acceleration.append(struct.unpack('>h', istream.read(2))[0]) + + +class BatteryStream: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.battery_data = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_battery_data(ostream) + pass + + def encode_battery_data(self, ostream): + self.battery_data.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_battery_data(istream) + pass + + def decode_battery_data(self, istream): + self.battery_data = BatteryData() + self.battery_data.decode_internal(istream) + + +class MicrophoneStream: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.microphone_data = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_microphone_data(ostream) + pass + + def encode_microphone_data(self, ostream): + self.microphone_data.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_microphone_data(istream) + pass + + def decode_microphone_data(self, istream): + self.microphone_data = MicrophoneData() + self.microphone_data.decode_internal(istream) + + +class ScanStream: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.scan_device = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_scan_device(ostream) + pass + + def encode_scan_device(self, ostream): + self.scan_device.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_scan_device(istream) + pass + + def decode_scan_device(self, istream): + self.scan_device = ScanDevice() + self.scan_device.decode_internal(istream) + + +class AccelerometerStream: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.accelerometer_raw_data = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_accelerometer_raw_data(ostream) + pass + + def encode_accelerometer_raw_data(self, ostream): + self.accelerometer_raw_data.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_accelerometer_raw_data(istream) + pass + + def decode_accelerometer_raw_data(self, istream): + self.accelerometer_raw_data = AccelerometerRawData() + self.accelerometer_raw_data.decode_internal(istream) + + +class AccelerometerInterruptStream: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class StatusRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.has_badge_assignement = 0 + self.badge_assignement = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_badge_assignement(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_badge_assignement(self, ostream): + ostream.write(struct.pack('>B', self.has_badge_assignement)) + if self.has_badge_assignement: + self.badge_assignement.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_badge_assignement(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_badge_assignement(self, istream): + self.has_badge_assignement= struct.unpack('>B', istream.read(1))[0] + if self.has_badge_assignement: + self.badge_assignement = BadgeAssignement() + self.badge_assignement.decode_internal(istream) + + +class StartMicrophoneRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.period_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_period_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_period_ms(self, ostream): + ostream.write(struct.pack('>H', self.period_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_period_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_period_ms(self, istream): + self.period_ms= struct.unpack('>H', istream.read(2))[0] + + +class StopMicrophoneRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartScanRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.window = 0 + self.interval = 0 + self.duration = 0 + self.period = 0 + self.aggregation_type = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_window(ostream) + self.encode_interval(ostream) + self.encode_duration(ostream) + self.encode_period(ostream) + self.encode_aggregation_type(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_window(self, ostream): + ostream.write(struct.pack('>H', self.window)) + + def encode_interval(self, ostream): + ostream.write(struct.pack('>H', self.interval)) + + def encode_duration(self, ostream): + ostream.write(struct.pack('>H', self.duration)) + + def encode_period(self, ostream): + ostream.write(struct.pack('>H', self.period)) + + def encode_aggregation_type(self, ostream): + ostream.write(struct.pack('>B', self.aggregation_type)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_window(istream) + self.decode_interval(istream) + self.decode_duration(istream) + self.decode_period(istream) + self.decode_aggregation_type(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_window(self, istream): + self.window= struct.unpack('>H', istream.read(2))[0] + + def decode_interval(self, istream): + self.interval= struct.unpack('>H', istream.read(2))[0] + + def decode_duration(self, istream): + self.duration= struct.unpack('>H', istream.read(2))[0] + + def decode_period(self, istream): + self.period= struct.unpack('>H', istream.read(2))[0] + + def decode_aggregation_type(self, istream): + self.aggregation_type= struct.unpack('>B', istream.read(1))[0] + + +class StopScanRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartAccelerometerRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.operating_mode = 0 + self.full_scale = 0 + self.datarate = 0 + self.fifo_sampling_period_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_operating_mode(ostream) + self.encode_full_scale(ostream) + self.encode_datarate(ostream) + self.encode_fifo_sampling_period_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_operating_mode(self, ostream): + ostream.write(struct.pack('>B', self.operating_mode)) + + def encode_full_scale(self, ostream): + ostream.write(struct.pack('>B', self.full_scale)) + + def encode_datarate(self, ostream): + ostream.write(struct.pack('>H', self.datarate)) + + def encode_fifo_sampling_period_ms(self, ostream): + ostream.write(struct.pack('>H', self.fifo_sampling_period_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_operating_mode(istream) + self.decode_full_scale(istream) + self.decode_datarate(istream) + self.decode_fifo_sampling_period_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_operating_mode(self, istream): + self.operating_mode= struct.unpack('>B', istream.read(1))[0] + + def decode_full_scale(self, istream): + self.full_scale= struct.unpack('>B', istream.read(1))[0] + + def decode_datarate(self, istream): + self.datarate= struct.unpack('>H', istream.read(2))[0] + + def decode_fifo_sampling_period_ms(self, istream): + self.fifo_sampling_period_ms= struct.unpack('>H', istream.read(2))[0] + + +class StopAccelerometerRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartAccelerometerInterruptRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.threshold_mg = 0 + self.minimal_duration_ms = 0 + self.ignore_duration_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_threshold_mg(ostream) + self.encode_minimal_duration_ms(ostream) + self.encode_ignore_duration_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_threshold_mg(self, ostream): + ostream.write(struct.pack('>H', self.threshold_mg)) + + def encode_minimal_duration_ms(self, ostream): + ostream.write(struct.pack('>H', self.minimal_duration_ms)) + + def encode_ignore_duration_ms(self, ostream): + ostream.write(struct.pack('>I', self.ignore_duration_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_threshold_mg(istream) + self.decode_minimal_duration_ms(istream) + self.decode_ignore_duration_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_threshold_mg(self, istream): + self.threshold_mg= struct.unpack('>H', istream.read(2))[0] + + def decode_minimal_duration_ms(self, istream): + self.minimal_duration_ms= struct.unpack('>H', istream.read(2))[0] + + def decode_ignore_duration_ms(self, istream): + self.ignore_duration_ms= struct.unpack('>I', istream.read(4))[0] + + +class StopAccelerometerInterruptRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartBatteryRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.period_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_period_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_period_ms(self, ostream): + ostream.write(struct.pack('>I', self.period_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_period_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_period_ms(self, istream): + self.period_ms= struct.unpack('>I', istream.read(4))[0] + + +class StopBatteryRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class MicrophoneDataRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class ScanDataRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class AccelerometerDataRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class AccelerometerInterruptDataRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class BatteryDataRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class StartMicrophoneStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.period_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_period_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_period_ms(self, ostream): + ostream.write(struct.pack('>H', self.period_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_period_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_period_ms(self, istream): + self.period_ms= struct.unpack('>H', istream.read(2))[0] + + +class StopMicrophoneStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartScanStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.window = 0 + self.interval = 0 + self.duration = 0 + self.period = 0 + self.aggregation_type = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_window(ostream) + self.encode_interval(ostream) + self.encode_duration(ostream) + self.encode_period(ostream) + self.encode_aggregation_type(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_window(self, ostream): + ostream.write(struct.pack('>H', self.window)) + + def encode_interval(self, ostream): + ostream.write(struct.pack('>H', self.interval)) + + def encode_duration(self, ostream): + ostream.write(struct.pack('>H', self.duration)) + + def encode_period(self, ostream): + ostream.write(struct.pack('>H', self.period)) + + def encode_aggregation_type(self, ostream): + ostream.write(struct.pack('>B', self.aggregation_type)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_window(istream) + self.decode_interval(istream) + self.decode_duration(istream) + self.decode_period(istream) + self.decode_aggregation_type(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_window(self, istream): + self.window= struct.unpack('>H', istream.read(2))[0] + + def decode_interval(self, istream): + self.interval= struct.unpack('>H', istream.read(2))[0] + + def decode_duration(self, istream): + self.duration= struct.unpack('>H', istream.read(2))[0] + + def decode_period(self, istream): + self.period= struct.unpack('>H', istream.read(2))[0] + + def decode_aggregation_type(self, istream): + self.aggregation_type= struct.unpack('>B', istream.read(1))[0] + + +class StopScanStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartAccelerometerStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.operating_mode = 0 + self.full_scale = 0 + self.datarate = 0 + self.fifo_sampling_period_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_operating_mode(ostream) + self.encode_full_scale(ostream) + self.encode_datarate(ostream) + self.encode_fifo_sampling_period_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_operating_mode(self, ostream): + ostream.write(struct.pack('>B', self.operating_mode)) + + def encode_full_scale(self, ostream): + ostream.write(struct.pack('>B', self.full_scale)) + + def encode_datarate(self, ostream): + ostream.write(struct.pack('>H', self.datarate)) + + def encode_fifo_sampling_period_ms(self, ostream): + ostream.write(struct.pack('>H', self.fifo_sampling_period_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_operating_mode(istream) + self.decode_full_scale(istream) + self.decode_datarate(istream) + self.decode_fifo_sampling_period_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_operating_mode(self, istream): + self.operating_mode= struct.unpack('>B', istream.read(1))[0] + + def decode_full_scale(self, istream): + self.full_scale= struct.unpack('>B', istream.read(1))[0] + + def decode_datarate(self, istream): + self.datarate= struct.unpack('>H', istream.read(2))[0] + + def decode_fifo_sampling_period_ms(self, istream): + self.fifo_sampling_period_ms= struct.unpack('>H', istream.read(2))[0] + + +class StopAccelerometerStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartAccelerometerInterruptStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.threshold_mg = 0 + self.minimal_duration_ms = 0 + self.ignore_duration_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_threshold_mg(ostream) + self.encode_minimal_duration_ms(ostream) + self.encode_ignore_duration_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_threshold_mg(self, ostream): + ostream.write(struct.pack('>H', self.threshold_mg)) + + def encode_minimal_duration_ms(self, ostream): + ostream.write(struct.pack('>H', self.minimal_duration_ms)) + + def encode_ignore_duration_ms(self, ostream): + ostream.write(struct.pack('>I', self.ignore_duration_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_threshold_mg(istream) + self.decode_minimal_duration_ms(istream) + self.decode_ignore_duration_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_threshold_mg(self, istream): + self.threshold_mg= struct.unpack('>H', istream.read(2))[0] + + def decode_minimal_duration_ms(self, istream): + self.minimal_duration_ms= struct.unpack('>H', istream.read(2))[0] + + def decode_ignore_duration_ms(self, istream): + self.ignore_duration_ms= struct.unpack('>I', istream.read(4))[0] + + +class StopAccelerometerInterruptStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class StartBatteryStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.timeout = 0 + self.period_ms = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_timeout(ostream) + self.encode_period_ms(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + def encode_period_ms(self, ostream): + ostream.write(struct.pack('>I', self.period_ms)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_timeout(istream) + self.decode_period_ms(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + def decode_period_ms(self, istream): + self.period_ms= struct.unpack('>I', istream.read(4))[0] + + +class StopBatteryStreamRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class IdentifyRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timeout = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timeout(ostream) + pass + + def encode_timeout(self, ostream): + ostream.write(struct.pack('>H', self.timeout)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timeout(istream) + pass + + def decode_timeout(self, istream): + self.timeout= struct.unpack('>H', istream.read(2))[0] + + +class TestRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class RestartRequest: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + +class Request: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.type = self._type() + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.type.encode_internal(ostream) + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.type.decode_internal(istream) + pass + + class _type: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.which = 0 + self.status_request = None + self.start_microphone_request = None + self.stop_microphone_request = None + self.start_scan_request = None + self.stop_scan_request = None + self.start_accelerometer_request = None + self.stop_accelerometer_request = None + self.start_accelerometer_interrupt_request = None + self.stop_accelerometer_interrupt_request = None + self.start_battery_request = None + self.stop_battery_request = None + self.microphone_data_request = None + self.scan_data_request = None + self.accelerometer_data_request = None + self.accelerometer_interrupt_data_request = None + self.battery_data_request = None + self.start_microphone_stream_request = None + self.stop_microphone_stream_request = None + self.start_scan_stream_request = None + self.stop_scan_stream_request = None + self.start_accelerometer_stream_request = None + self.stop_accelerometer_stream_request = None + self.start_accelerometer_interrupt_stream_request = None + self.stop_accelerometer_interrupt_stream_request = None + self.start_battery_stream_request = None + self.stop_battery_stream_request = None + self.identify_request = None + self.test_request = None + self.restart_request = None + pass + + def encode_internal(self, ostream): + ostream.write(struct.pack('>B', self.which)) + options = { + 1: self.encode_status_request, + 2: self.encode_start_microphone_request, + 3: self.encode_stop_microphone_request, + 4: self.encode_start_scan_request, + 5: self.encode_stop_scan_request, + 6: self.encode_start_accelerometer_request, + 7: self.encode_stop_accelerometer_request, + 8: self.encode_start_accelerometer_interrupt_request, + 9: self.encode_stop_accelerometer_interrupt_request, + 10: self.encode_start_battery_request, + 11: self.encode_stop_battery_request, + 12: self.encode_microphone_data_request, + 13: self.encode_scan_data_request, + 14: self.encode_accelerometer_data_request, + 15: self.encode_accelerometer_interrupt_data_request, + 16: self.encode_battery_data_request, + 17: self.encode_start_microphone_stream_request, + 18: self.encode_stop_microphone_stream_request, + 19: self.encode_start_scan_stream_request, + 20: self.encode_stop_scan_stream_request, + 21: self.encode_start_accelerometer_stream_request, + 22: self.encode_stop_accelerometer_stream_request, + 23: self.encode_start_accelerometer_interrupt_stream_request, + 24: self.encode_stop_accelerometer_interrupt_stream_request, + 25: self.encode_start_battery_stream_request, + 26: self.encode_stop_battery_stream_request, + 27: self.encode_identify_request, + 28: self.encode_test_request, + 29: self.encode_restart_request, + } + options[self.which](ostream) + pass + + def encode_status_request(self, ostream): + self.status_request.encode_internal(ostream) + + def encode_start_microphone_request(self, ostream): + self.start_microphone_request.encode_internal(ostream) + + def encode_stop_microphone_request(self, ostream): + self.stop_microphone_request.encode_internal(ostream) + + def encode_start_scan_request(self, ostream): + self.start_scan_request.encode_internal(ostream) + + def encode_stop_scan_request(self, ostream): + self.stop_scan_request.encode_internal(ostream) + + def encode_start_accelerometer_request(self, ostream): + self.start_accelerometer_request.encode_internal(ostream) + + def encode_stop_accelerometer_request(self, ostream): + self.stop_accelerometer_request.encode_internal(ostream) + + def encode_start_accelerometer_interrupt_request(self, ostream): + self.start_accelerometer_interrupt_request.encode_internal(ostream) + + def encode_stop_accelerometer_interrupt_request(self, ostream): + self.stop_accelerometer_interrupt_request.encode_internal(ostream) + + def encode_start_battery_request(self, ostream): + self.start_battery_request.encode_internal(ostream) + + def encode_stop_battery_request(self, ostream): + self.stop_battery_request.encode_internal(ostream) + + def encode_microphone_data_request(self, ostream): + self.microphone_data_request.encode_internal(ostream) + + def encode_scan_data_request(self, ostream): + self.scan_data_request.encode_internal(ostream) + + def encode_accelerometer_data_request(self, ostream): + self.accelerometer_data_request.encode_internal(ostream) + + def encode_accelerometer_interrupt_data_request(self, ostream): + self.accelerometer_interrupt_data_request.encode_internal(ostream) + + def encode_battery_data_request(self, ostream): + self.battery_data_request.encode_internal(ostream) + + def encode_start_microphone_stream_request(self, ostream): + self.start_microphone_stream_request.encode_internal(ostream) + + def encode_stop_microphone_stream_request(self, ostream): + self.stop_microphone_stream_request.encode_internal(ostream) + + def encode_start_scan_stream_request(self, ostream): + self.start_scan_stream_request.encode_internal(ostream) + + def encode_stop_scan_stream_request(self, ostream): + self.stop_scan_stream_request.encode_internal(ostream) + + def encode_start_accelerometer_stream_request(self, ostream): + self.start_accelerometer_stream_request.encode_internal(ostream) + + def encode_stop_accelerometer_stream_request(self, ostream): + self.stop_accelerometer_stream_request.encode_internal(ostream) + + def encode_start_accelerometer_interrupt_stream_request(self, ostream): + self.start_accelerometer_interrupt_stream_request.encode_internal(ostream) + + def encode_stop_accelerometer_interrupt_stream_request(self, ostream): + self.stop_accelerometer_interrupt_stream_request.encode_internal(ostream) + + def encode_start_battery_stream_request(self, ostream): + self.start_battery_stream_request.encode_internal(ostream) + + def encode_stop_battery_stream_request(self, ostream): + self.stop_battery_stream_request.encode_internal(ostream) + + def encode_identify_request(self, ostream): + self.identify_request.encode_internal(ostream) + + def encode_test_request(self, ostream): + self.test_request.encode_internal(ostream) + + def encode_restart_request(self, ostream): + self.restart_request.encode_internal(ostream) + + + def decode_internal(self, istream): + self.reset() + self.which= struct.unpack('>B', istream.read(1))[0] + options = { + 1: self.decode_status_request, + 2: self.decode_start_microphone_request, + 3: self.decode_stop_microphone_request, + 4: self.decode_start_scan_request, + 5: self.decode_stop_scan_request, + 6: self.decode_start_accelerometer_request, + 7: self.decode_stop_accelerometer_request, + 8: self.decode_start_accelerometer_interrupt_request, + 9: self.decode_stop_accelerometer_interrupt_request, + 10: self.decode_start_battery_request, + 11: self.decode_stop_battery_request, + 12: self.decode_microphone_data_request, + 13: self.decode_scan_data_request, + 14: self.decode_accelerometer_data_request, + 15: self.decode_accelerometer_interrupt_data_request, + 16: self.decode_battery_data_request, + 17: self.decode_start_microphone_stream_request, + 18: self.decode_stop_microphone_stream_request, + 19: self.decode_start_scan_stream_request, + 20: self.decode_stop_scan_stream_request, + 21: self.decode_start_accelerometer_stream_request, + 22: self.decode_stop_accelerometer_stream_request, + 23: self.decode_start_accelerometer_interrupt_stream_request, + 24: self.decode_stop_accelerometer_interrupt_stream_request, + 25: self.decode_start_battery_stream_request, + 26: self.decode_stop_battery_stream_request, + 27: self.decode_identify_request, + 28: self.decode_test_request, + 29: self.decode_restart_request, + } + options[self.which](istream) + pass + + def decode_status_request(self, istream): + self.status_request = StatusRequest() + self.status_request.decode_internal(istream) + + def decode_start_microphone_request(self, istream): + self.start_microphone_request = StartMicrophoneRequest() + self.start_microphone_request.decode_internal(istream) + + def decode_stop_microphone_request(self, istream): + self.stop_microphone_request = StopMicrophoneRequest() + self.stop_microphone_request.decode_internal(istream) + + def decode_start_scan_request(self, istream): + self.start_scan_request = StartScanRequest() + self.start_scan_request.decode_internal(istream) + + def decode_stop_scan_request(self, istream): + self.stop_scan_request = StopScanRequest() + self.stop_scan_request.decode_internal(istream) + + def decode_start_accelerometer_request(self, istream): + self.start_accelerometer_request = StartAccelerometerRequest() + self.start_accelerometer_request.decode_internal(istream) + + def decode_stop_accelerometer_request(self, istream): + self.stop_accelerometer_request = StopAccelerometerRequest() + self.stop_accelerometer_request.decode_internal(istream) + + def decode_start_accelerometer_interrupt_request(self, istream): + self.start_accelerometer_interrupt_request = StartAccelerometerInterruptRequest() + self.start_accelerometer_interrupt_request.decode_internal(istream) + + def decode_stop_accelerometer_interrupt_request(self, istream): + self.stop_accelerometer_interrupt_request = StopAccelerometerInterruptRequest() + self.stop_accelerometer_interrupt_request.decode_internal(istream) + + def decode_start_battery_request(self, istream): + self.start_battery_request = StartBatteryRequest() + self.start_battery_request.decode_internal(istream) + + def decode_stop_battery_request(self, istream): + self.stop_battery_request = StopBatteryRequest() + self.stop_battery_request.decode_internal(istream) + + def decode_microphone_data_request(self, istream): + self.microphone_data_request = MicrophoneDataRequest() + self.microphone_data_request.decode_internal(istream) + + def decode_scan_data_request(self, istream): + self.scan_data_request = ScanDataRequest() + self.scan_data_request.decode_internal(istream) + + def decode_accelerometer_data_request(self, istream): + self.accelerometer_data_request = AccelerometerDataRequest() + self.accelerometer_data_request.decode_internal(istream) + + def decode_accelerometer_interrupt_data_request(self, istream): + self.accelerometer_interrupt_data_request = AccelerometerInterruptDataRequest() + self.accelerometer_interrupt_data_request.decode_internal(istream) + + def decode_battery_data_request(self, istream): + self.battery_data_request = BatteryDataRequest() + self.battery_data_request.decode_internal(istream) + + def decode_start_microphone_stream_request(self, istream): + self.start_microphone_stream_request = StartMicrophoneStreamRequest() + self.start_microphone_stream_request.decode_internal(istream) + + def decode_stop_microphone_stream_request(self, istream): + self.stop_microphone_stream_request = StopMicrophoneStreamRequest() + self.stop_microphone_stream_request.decode_internal(istream) + + def decode_start_scan_stream_request(self, istream): + self.start_scan_stream_request = StartScanStreamRequest() + self.start_scan_stream_request.decode_internal(istream) + + def decode_stop_scan_stream_request(self, istream): + self.stop_scan_stream_request = StopScanStreamRequest() + self.stop_scan_stream_request.decode_internal(istream) + + def decode_start_accelerometer_stream_request(self, istream): + self.start_accelerometer_stream_request = StartAccelerometerStreamRequest() + self.start_accelerometer_stream_request.decode_internal(istream) + + def decode_stop_accelerometer_stream_request(self, istream): + self.stop_accelerometer_stream_request = StopAccelerometerStreamRequest() + self.stop_accelerometer_stream_request.decode_internal(istream) + + def decode_start_accelerometer_interrupt_stream_request(self, istream): + self.start_accelerometer_interrupt_stream_request = StartAccelerometerInterruptStreamRequest() + self.start_accelerometer_interrupt_stream_request.decode_internal(istream) + + def decode_stop_accelerometer_interrupt_stream_request(self, istream): + self.stop_accelerometer_interrupt_stream_request = StopAccelerometerInterruptStreamRequest() + self.stop_accelerometer_interrupt_stream_request.decode_internal(istream) + + def decode_start_battery_stream_request(self, istream): + self.start_battery_stream_request = StartBatteryStreamRequest() + self.start_battery_stream_request.decode_internal(istream) + + def decode_stop_battery_stream_request(self, istream): + self.stop_battery_stream_request = StopBatteryStreamRequest() + self.stop_battery_stream_request.decode_internal(istream) + + def decode_identify_request(self, istream): + self.identify_request = IdentifyRequest() + self.identify_request.decode_internal(istream) + + def decode_test_request(self, istream): + self.test_request = TestRequest() + self.test_request.decode_internal(istream) + + def decode_restart_request(self, istream): + self.restart_request = RestartRequest() + self.restart_request.decode_internal(istream) + + +class StatusResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.clock_status = 0 + self.microphone_status = 0 + self.scan_status = 0 + self.accelerometer_status = 0 + self.accelerometer_interrupt_status = 0 + self.battery_status = 0 + self.timestamp = None + self.battery_data = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_clock_status(ostream) + self.encode_microphone_status(ostream) + self.encode_scan_status(ostream) + self.encode_accelerometer_status(ostream) + self.encode_accelerometer_interrupt_status(ostream) + self.encode_battery_status(ostream) + self.encode_timestamp(ostream) + self.encode_battery_data(ostream) + pass + + def encode_clock_status(self, ostream): + ostream.write(struct.pack('>B', self.clock_status)) + + def encode_microphone_status(self, ostream): + ostream.write(struct.pack('>B', self.microphone_status)) + + def encode_scan_status(self, ostream): + ostream.write(struct.pack('>B', self.scan_status)) + + def encode_accelerometer_status(self, ostream): + ostream.write(struct.pack('>B', self.accelerometer_status)) + + def encode_accelerometer_interrupt_status(self, ostream): + ostream.write(struct.pack('>B', self.accelerometer_interrupt_status)) + + def encode_battery_status(self, ostream): + ostream.write(struct.pack('>B', self.battery_status)) + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_battery_data(self, ostream): + self.battery_data.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_clock_status(istream) + self.decode_microphone_status(istream) + self.decode_scan_status(istream) + self.decode_accelerometer_status(istream) + self.decode_accelerometer_interrupt_status(istream) + self.decode_battery_status(istream) + self.decode_timestamp(istream) + self.decode_battery_data(istream) + pass + + def decode_clock_status(self, istream): + self.clock_status= struct.unpack('>B', istream.read(1))[0] + + def decode_microphone_status(self, istream): + self.microphone_status= struct.unpack('>B', istream.read(1))[0] + + def decode_scan_status(self, istream): + self.scan_status= struct.unpack('>B', istream.read(1))[0] + + def decode_accelerometer_status(self, istream): + self.accelerometer_status= struct.unpack('>B', istream.read(1))[0] + + def decode_accelerometer_interrupt_status(self, istream): + self.accelerometer_interrupt_status= struct.unpack('>B', istream.read(1))[0] + + def decode_battery_status(self, istream): + self.battery_status= struct.unpack('>B', istream.read(1))[0] + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_battery_data(self, istream): + self.battery_data = BatteryData() + self.battery_data.decode_internal(istream) + + +class StartMicrophoneResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class StartScanResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class StartAccelerometerResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class StartAccelerometerInterruptResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class StartBatteryResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + +class MicrophoneDataResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.last_response = 0 + self.timestamp = None + self.sample_period_ms = 0 + self.microphone_data = [] + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_last_response(ostream) + self.encode_timestamp(ostream) + self.encode_sample_period_ms(ostream) + self.encode_microphone_data(ostream) + pass + + def encode_last_response(self, ostream): + ostream.write(struct.pack('>B', self.last_response)) + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_sample_period_ms(self, ostream): + ostream.write(struct.pack('>H', self.sample_period_ms)) + + def encode_microphone_data(self, ostream): + count = len(self.microphone_data) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.microphone_data[i].encode_internal(ostream) -# chr field is used for convinience, but can be confusing. We use the chr field to represnet an object that is an actual -# character, or a string of length 1 (as opposed to a numeric object). For signed numeric char (int8), use the int8_field -def char_field(attribute, optional=False): - return (attribute, 1, optional, lambda x: chr(x) if type(x) is int else x, lambda x: chr(x) if type (x) is int else x) -def ulong_field(attribute, optional=False): - return (attribute, 4, optional, lambda x: pack("B', istream.read(1))[0] + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_sample_period_ms(self, istream): + self.sample_period_ms= struct.unpack('>H', istream.read(2))[0] + + def decode_microphone_data(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = MicrophoneData() + tmp.decode_internal(istream) + self.microphone_data.append(tmp) + + +class ScanDataResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.last_response = 0 + self.timestamp = None + self.scan_result_data = [] + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_last_response(ostream) + self.encode_timestamp(ostream) + self.encode_scan_result_data(ostream) + pass + + def encode_last_response(self, ostream): + ostream.write(struct.pack('>B', self.last_response)) + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_scan_result_data(self, ostream): + count = len(self.scan_result_data) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.scan_result_data[i].encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_last_response(istream) + self.decode_timestamp(istream) + self.decode_scan_result_data(istream) + pass + + def decode_last_response(self, istream): + self.last_response= struct.unpack('>B', istream.read(1))[0] + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_scan_result_data(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = ScanResultData() + tmp.decode_internal(istream) + self.scan_result_data.append(tmp) + + +class AccelerometerDataResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.last_response = 0 + self.timestamp = None + self.accelerometer_data = [] + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_last_response(ostream) + self.encode_timestamp(ostream) + self.encode_accelerometer_data(ostream) + pass + + def encode_last_response(self, ostream): + ostream.write(struct.pack('>B', self.last_response)) + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_accelerometer_data(self, ostream): + count = len(self.accelerometer_data) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.accelerometer_data[i].encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_last_response(istream) + self.decode_timestamp(istream) + self.decode_accelerometer_data(istream) + pass + + def decode_last_response(self, istream): + self.last_response= struct.unpack('>B', istream.read(1))[0] + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_accelerometer_data(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = AccelerometerData() + tmp.decode_internal(istream) + self.accelerometer_data.append(tmp) + + +class AccelerometerInterruptDataResponse: + + def __init__(self): + self.reset() + + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.last_response = 0 + self.timestamp = None + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_last_response(ostream) + self.encode_timestamp(ostream) + pass + + def encode_last_response(self, ostream): + ostream.write(struct.pack('>B', self.last_response)) + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) -def ushort_field(attribute, optional=False): - return (attribute, 2, optional, lambda x: pack("B', istream.read(1))[0] -def uint8_field(attribute, optional=False): - return (attribute, 1, optional, lambda x: pack("B', self.last_response)) + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_battery_data(self, ostream): + self.battery_data.encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_last_response(istream) + self.decode_timestamp(istream) + self.decode_battery_data(istream) pass - # Override Python's __repr__() for maximum prettiness. + def decode_last_response(self, istream): + self.last_response= struct.unpack('>B', istream.read(1))[0] + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_battery_data(self, istream): + self.battery_data = BatteryData() + self.battery_data.decode_internal(istream) + + +class StreamResponse: + + def __init__(self): + self.reset() + def __repr__(self): - fields_dict = {} - for attribute, length, optional, serializer, parser in self.message_fields: - if hasattr(self, attribute): - fields_dict[attribute] = getattr(self, attribute) + return str(self.__dict__) + + def reset(self): + self.timestamp = None + self.battery_stream = [] + self.microphone_stream = [] + self.scan_stream = [] + self.accelerometer_stream = [] + self.accelerometer_interrupt_stream = [] + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_timestamp(ostream) + self.encode_battery_stream(ostream) + self.encode_microphone_stream(ostream) + self.encode_scan_stream(ostream) + self.encode_accelerometer_stream(ostream) + self.encode_accelerometer_interrupt_stream(ostream) + pass + + def encode_timestamp(self, ostream): + self.timestamp.encode_internal(ostream) + + def encode_battery_stream(self, ostream): + count = len(self.battery_stream) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.battery_stream[i].encode_internal(ostream) + + def encode_microphone_stream(self, ostream): + count = len(self.microphone_stream) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.microphone_stream[i].encode_internal(ostream) + + def encode_scan_stream(self, ostream): + count = len(self.scan_stream) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.scan_stream[i].encode_internal(ostream) + + def encode_accelerometer_stream(self, ostream): + count = len(self.accelerometer_stream) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.accelerometer_stream[i].encode_internal(ostream) + + def encode_accelerometer_interrupt_stream(self, ostream): + count = len(self.accelerometer_interrupt_stream) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.accelerometer_interrupt_stream[i].encode_internal(ostream) - string_representation = "[BadgeMessage (" + self.__class__.__name__ + ") " + str(fields_dict) + "]" - return string_representation - # Returns the length of the binary representation of this type of BadgeMessage. @classmethod - def length(cls): - return reduce(lambda x, y: x + y[1], cls.message_fields, 0) + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_timestamp(istream) + self.decode_battery_stream(istream) + self.decode_microphone_stream(istream) + self.decode_scan_stream(istream) + self.decode_accelerometer_stream(istream) + self.decode_accelerometer_interrupt_stream(istream) + pass + + def decode_timestamp(self, istream): + self.timestamp = Timestamp() + self.timestamp.decode_internal(istream) + + def decode_battery_stream(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = BatteryStream() + tmp.decode_internal(istream) + self.battery_stream.append(tmp) + + def decode_microphone_stream(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = MicrophoneStream() + tmp.decode_internal(istream) + self.microphone_stream.append(tmp) + + def decode_scan_stream(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = ScanStream() + tmp.decode_internal(istream) + self.scan_stream.append(tmp) + + def decode_accelerometer_stream(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = AccelerometerStream() + tmp.decode_internal(istream) + self.accelerometer_stream.append(tmp) + + def decode_accelerometer_interrupt_stream(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = AccelerometerInterruptStream() + tmp.decode_internal(istream) + self.accelerometer_interrupt_stream.append(tmp) + + +class TestResponse: + + def __init__(self): + self.reset() - # Returns the binary representation of this BadgeMessage as a string according to the - # badge communication protocol. - def serialize_message(self): - serialized_message = "" - for attribute, length, optional, serializer, parser in self.message_fields: - if hasattr(self, attribute): - serialized_message += serializer(getattr(self, attribute)) + def __repr__(self): + return str(self.__dict__) + + def reset(self): + self.test_failed = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_test_failed(ostream) + pass + + def encode_test_failed(self, ostream): + ostream.write(struct.pack('>B', self.test_failed)) - return serialized_message - # Returns an instance of the given derived class 'cls' of BadgeMessage initialized from the - # given string 'serialized_message' according to the badge communication protocol. @classmethod - def deserialize_message(cls, serialized_message): - message_attributes = {} + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_test_failed(istream) + pass + + def decode_test_failed(self, istream): + self.test_failed= struct.unpack('>B', istream.read(1))[0] - pos = 0 - for attribute, length, optional, serializer, parser in cls.message_fields: - if pos < len(serialized_message): - raw_field = serialized_message[pos:pos + length] - message_attributes[attribute] = parser(raw_field) - pos += length - else: - if optional != True: raise ValueError("Serialized messsage is malformed. {}".format(serialized_message.encode("hex"))) - return cls(**message_attributes) +class Response: -# The propoer values of the fields needed to initialize these objects are not documented here. -# They are documented in the communications protocol documentation on the GitHub Wiki instead. -# https://github.com/HumanDynamics/OpenBadge/wiki/Communication-protocol + def __init__(self): + self.reset() -class StatusRequest(BadgeMessage): - message_fields = [char_field("header"), ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds"), - ushort_field("badge_id", optional=True), uint8_field("group_number", optional=True)] + def __repr__(self): + return str(self.__dict__) - def __init__(self, timestamp_seconds, timestamp_miliseconds, header=STATUS_REQUEST_HEADER, badge_id=None, group_number=None): - self.header = header - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds - if badge_id: self.badge_id = badge_id - if group_number: self.group_number = group_number + def reset(self): + self.type = self._type() + pass - BadgeMessage.__init__(self) + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf -class StatusResponse(BadgeMessage): - message_fields = [bool_field("clock_status"), bool_field("scanner_status"), bool_field("collector_status"), - ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds"), float_field("battery_voltage")] + def encode_internal(self, ostream): + self.type.encode_internal(ostream) + pass - def __init__(self, clock_status, scanner_status, collector_status, timestamp_seconds, - timestamp_miliseconds, battery_voltage): - self.clock_status = clock_status - self.scanner_status = scanner_status - self.collector_status = collector_status - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds - self.battery_voltage = battery_voltage - BadgeMessage.__init__(self) + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj -class StartRecordRequest(BadgeMessage): - message_fields = [char_field("header"), ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds"), ushort_field("timeout_minutes")] + def decode_internal(self, istream): + self.reset() + self.type.decode_internal(istream) + pass - def __init__(self, timestamp_seconds, timestamp_miliseconds, timeout_minutes, header=START_RECORDING_HEADER): - self.header = header - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds - self.timeout_minutes = timeout_minutes + class _type: - BadgeMessage.__init__(self) + def __init__(self): + self.reset() -class StartRecordResponse(BadgeMessage): - message_fields = [ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds")] + def __repr__(self): + return str(self.__dict__) - def __init__(self, timestamp_seconds, timestamp_miliseconds): - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds + def reset(self): + self.which = 0 + self.status_response = None + self.start_microphone_response = None + self.start_scan_response = None + self.start_accelerometer_response = None + self.start_accelerometer_interrupt_response = None + self.start_battery_response = None + self.microphone_data_response = None + self.scan_data_response = None + self.accelerometer_data_response = None + self.accelerometer_interrupt_data_response = None + self.battery_data_response = None + self.stream_response = None + self.test_response = None + pass - BadgeMessage.__init__(self) + def encode_internal(self, ostream): + ostream.write(struct.pack('>B', self.which)) + options = { + 1: self.encode_status_response, + 2: self.encode_start_microphone_response, + 3: self.encode_start_scan_response, + 4: self.encode_start_accelerometer_response, + 5: self.encode_start_accelerometer_interrupt_response, + 6: self.encode_start_battery_response, + 7: self.encode_microphone_data_response, + 8: self.encode_scan_data_response, + 9: self.encode_accelerometer_data_response, + 10: self.encode_accelerometer_interrupt_data_response, + 11: self.encode_battery_data_response, + 12: self.encode_stream_response, + 13: self.encode_test_response, + } + options[self.which](ostream) + pass -class StopRecordRequest(BadgeMessage): - message_fields = [char_field("header")] + def encode_status_response(self, ostream): + self.status_response.encode_internal(ostream) - def __init__(self, header=STOP_RECORDING_HEADER): - self.header = header + def encode_start_microphone_response(self, ostream): + self.start_microphone_response.encode_internal(ostream) - BadgeMessage.__init__(self) + def encode_start_scan_response(self, ostream): + self.start_scan_response.encode_internal(ostream) -class StartScanningRequest(BadgeMessage): - message_fields = [char_field("header"), ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds"), - ushort_field("timeout_minutes"), ushort_field("window_miliseconds"), ushort_field("interval_miliseconds"), - ushort_field("duration_seconds"), ushort_field("period_seconds")] + def encode_start_accelerometer_response(self, ostream): + self.start_accelerometer_response.encode_internal(ostream) - def __init__(self, timestamp_seconds, timestamp_miliseconds, timeout_minutes, window_miliseconds, interval_miliseconds, - duration_seconds, period_seconds, header=START_SCANNING_HEADER): - self.header = header - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds - self.timeout_minutes = timeout_minutes - self.window_miliseconds = window_miliseconds - self.interval_miliseconds = interval_miliseconds - self.duration_seconds = duration_seconds - self.period_seconds = period_seconds + def encode_start_accelerometer_interrupt_response(self, ostream): + self.start_accelerometer_interrupt_response.encode_internal(ostream) - BadgeMessage.__init__(self) + def encode_start_battery_response(self, ostream): + self.start_battery_response.encode_internal(ostream) -class StartScanningResponse(BadgeMessage): - message_fields = [ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds")] + def encode_microphone_data_response(self, ostream): + self.microphone_data_response.encode_internal(ostream) - def __init__(self, timestamp_seconds, timestamp_miliseconds): - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds + def encode_scan_data_response(self, ostream): + self.scan_data_response.encode_internal(ostream) - BadgeMessage.__init__(self) + def encode_accelerometer_data_response(self, ostream): + self.accelerometer_data_response.encode_internal(ostream) -class StopScanningRequest(BadgeMessage): - message_fields = [char_field("header")] + def encode_accelerometer_interrupt_data_response(self, ostream): + self.accelerometer_interrupt_data_response.encode_internal(ostream) - def __init__(self, header=STOP_SCANNING_HEADER): - self.header = header + def encode_battery_data_response(self, ostream): + self.battery_data_response.encode_internal(ostream) - BadgeMessage.__init__(self) + def encode_stream_response(self, ostream): + self.stream_response.encode_internal(ostream) -class IdentifyRequest(BadgeMessage): - message_fields = [char_field("header"), ushort_field("duration_seconds")] + def encode_test_response(self, ostream): + self.test_response.encode_internal(ostream) - def __init__(self, duration_seconds, header=IDENITFY_HEADER): - self.header = header - self.duration_seconds = duration_seconds - BadgeMessage.__init__(self) + def decode_internal(self, istream): + self.reset() + self.which= struct.unpack('>B', istream.read(1))[0] + options = { + 1: self.decode_status_response, + 2: self.decode_start_microphone_response, + 3: self.decode_start_scan_response, + 4: self.decode_start_accelerometer_response, + 5: self.decode_start_accelerometer_interrupt_response, + 6: self.decode_start_battery_response, + 7: self.decode_microphone_data_response, + 8: self.decode_scan_data_response, + 9: self.decode_accelerometer_data_response, + 10: self.decode_accelerometer_interrupt_data_response, + 11: self.decode_battery_data_response, + 12: self.decode_stream_response, + 13: self.decode_test_response, + } + options[self.which](istream) + pass -class MicrophoneDataRequest(BadgeMessage): - message_fields = [char_field("header"), ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds")] + def decode_status_response(self, istream): + self.status_response = StatusResponse() + self.status_response.decode_internal(istream) - def __init__(self, timestamp_seconds, timestamp_miliseconds, header=REQUEST_MIC_DATA_HEADER): - self.header = header - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds + def decode_start_microphone_response(self, istream): + self.start_microphone_response = StartMicrophoneResponse() + self.start_microphone_response.decode_internal(istream) - BadgeMessage.__init__(self) + def decode_start_scan_response(self, istream): + self.start_scan_response = StartScanResponse() + self.start_scan_response.decode_internal(istream) -class MicrophoneDataHeader(BadgeMessage): - message_fields = [ulong_field("timestamp_seconds"), ushort_field("timestamp_miliseconds"), - float_field("battery_voltage"), ushort_field("sample_period_miliseconds"), - uint8_field("num_samples_in_chunk")] + def decode_start_accelerometer_response(self, istream): + self.start_accelerometer_response = StartAccelerometerResponse() + self.start_accelerometer_response.decode_internal(istream) - def __init__(self, timestamp_seconds, timestamp_miliseconds, battery_voltage, - sample_period_miliseconds, num_samples_in_chunk): - self.timestamp_seconds = timestamp_seconds - self.timestamp_miliseconds = timestamp_miliseconds - self.battery_voltage = battery_voltage - self.sample_period_miliseconds = sample_period_miliseconds - self.num_samples_in_chunk = num_samples_in_chunk + def decode_start_accelerometer_interrupt_response(self, istream): + self.start_accelerometer_interrupt_response = StartAccelerometerInterruptResponse() + self.start_accelerometer_interrupt_response.decode_internal(istream) - BadgeMessage.__init__(self) + def decode_start_battery_response(self, istream): + self.start_battery_response = StartBatteryResponse() + self.start_battery_response.decode_internal(istream) -class ScanDataRequest(BadgeMessage): - message_fields = [char_field("header"), ulong_field("timestamp_seconds")] + def decode_microphone_data_response(self, istream): + self.microphone_data_response = MicrophoneDataResponse() + self.microphone_data_response.decode_internal(istream) - def __init__(self, timestamp_seconds, header=REQUEST_SCAN_DATA_HEADER): - self.header = header - self.timestamp_seconds = timestamp_seconds + def decode_scan_data_response(self, istream): + self.scan_data_response = ScanDataResponse() + self.scan_data_response.decode_internal(istream) - BadgeMessage.__init__(self) + def decode_accelerometer_data_response(self, istream): + self.accelerometer_data_response = AccelerometerDataResponse() + self.accelerometer_data_response.decode_internal(istream) -class ScanDataHeader(BadgeMessage): - message_fields = [ulong_field("timestamp_seconds"), float_field("battery_voltage"), uint8_field("num_devices_seen")] + def decode_accelerometer_interrupt_data_response(self, istream): + self.accelerometer_interrupt_data_response = AccelerometerInterruptDataResponse() + self.accelerometer_interrupt_data_response.decode_internal(istream) - def __init__(self, timestamp_seconds, battery_voltage, num_devices_seen): - self.timestamp_seconds = timestamp_seconds - self.battery_voltage = battery_voltage - self.num_devices_seen = num_devices_seen + def decode_battery_data_response(self, istream): + self.battery_data_response = BatteryDataResponse() + self.battery_data_response.decode_internal(istream) - BadgeMessage.__init__(self) + def decode_stream_response(self, istream): + self.stream_response = StreamResponse() + self.stream_response.decode_internal(istream) -class ScanDataDevice(BadgeMessage): - message_fields = [ushort_field("device_id"), int8_field("average_rssi"), int8_field("num_times_seen")] + def decode_test_response(self, istream): + self.test_response = TestResponse() + self.test_response.decode_internal(istream) - def __init__(self, device_id, average_rssi, num_times_seen): - self.device_id = device_id - self.average_rssi = average_rssi - self.num_times_seen = num_times_seen - BadgeMessage.__init__(self) \ No newline at end of file diff --git a/BadgeFramework/badge_protocol.tb b/BadgeFramework/badge_protocol.tb new file mode 100644 index 0000000..fb50795 --- /dev/null +++ b/BadgeFramework/badge_protocol.tb @@ -0,0 +1,371 @@ + +message Timestamp { + required uint32 seconds; + required uint16 ms; +} + +message BadgeAssignement { + required uint16 ID; + required uint8 group; +} + +message BatteryData { + required float voltage; +} + +message MicrophoneData { + required uint8 value; +} + + +message ScanDevice { + required uint16 ID; + required int8 rssi; +} + +message ScanResultData { + required ScanDevice scan_device; + required uint8 count; +} + +message AccelerometerData { + required uint16 acceleration; +} + +message AccelerometerRawData { + fixed_repeated int16 raw_acceleration[3]; +} + + + +message BatteryStream { + required BatteryData battery_data; +} + +message MicrophoneStream { + required MicrophoneData microphone_data; +} + +message ScanStream { + required ScanDevice scan_device; +} + +message AccelerometerStream { + required AccelerometerRawData accelerometer_raw_data; +} + +message AccelerometerInterruptStream { + required Timestamp timestamp; +} + + + +define { + PROTOCOL_MICROPHONE_DATA_SIZE = 114; + PROTOCOL_SCAN_DATA_SIZE = 29; + PROTOCOL_ACCELEROMETER_DATA_SIZE = 100; +} + +define { + PROTOCOL_MICROPHONE_STREAM_SIZE = 10; + PROTOCOL_SCAN_STREAM_SIZE = 10; + PROTOCOL_ACCELEROMETER_STREAM_SIZE = 10; + PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE = 10; + PROTOCOL_BATTERY_STREAM_SIZE = 10; +} + + +message StatusRequest { + required Timestamp timestamp; + optional BadgeAssignement badge_assignement; +} + +message StartMicrophoneRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 period_ms; +} + +message StopMicrophoneRequest { +} + +message StartScanRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 window; + required uint16 interval; + required uint16 duration; + required uint16 period; + required uint8 aggregation_type; +} + +message StopScanRequest { +} + +message StartAccelerometerRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint8 operating_mode; + required uint8 full_scale; + required uint16 datarate; + required uint16 fifo_sampling_period_ms; +} + +message StopAccelerometerRequest { +} + + +message StartAccelerometerInterruptRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 threshold_mg; + required uint16 minimal_duration_ms; + required uint32 ignore_duration_ms; +} + +message StopAccelerometerInterruptRequest { +} + +message StartBatteryRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint32 period_ms; +} + +message StopBatteryRequest { +} + + + +message MicrophoneDataRequest { + required Timestamp timestamp; +} + +message ScanDataRequest { + required Timestamp timestamp; +} + +message AccelerometerDataRequest { + required Timestamp timestamp; +} + +message AccelerometerInterruptDataRequest { + required Timestamp timestamp; +} + +message BatteryDataRequest { + required Timestamp timestamp; +} + + + +message StartMicrophoneStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 period_ms; +} + +message StopMicrophoneStreamRequest { +} + +message StartScanStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 window; + required uint16 interval; + required uint16 duration; + required uint16 period; + required uint8 aggregation_type; +} + +message StopScanStreamRequest { +} + +message StartAccelerometerStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint8 operating_mode; + required uint8 full_scale; + required uint16 datarate; + required uint16 fifo_sampling_period_ms; +} + +message StopAccelerometerStreamRequest { +} + + +message StartAccelerometerInterruptStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 threshold_mg; + required uint16 minimal_duration_ms; + required uint32 ignore_duration_ms; +} + +message StopAccelerometerInterruptStreamRequest { +} + +message StartBatteryStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint32 period_ms; +} + +message StopBatteryStreamRequest { +} + + +message IdentifyRequest { + required uint16 timeout; +} + +message TestRequest { +} + +message RestartRequest { +} + +message Request { + oneof type { + StatusRequest status_request (1); + StartMicrophoneRequest start_microphone_request (2); + StopMicrophoneRequest stop_microphone_request (3); + StartScanRequest start_scan_request (4); + StopScanRequest stop_scan_request (5); + StartAccelerometerRequest start_accelerometer_request (6); + StopAccelerometerRequest stop_accelerometer_request (7); + StartAccelerometerInterruptRequest start_accelerometer_interrupt_request (8); + StopAccelerometerInterruptRequest stop_accelerometer_interrupt_request (9); + StartBatteryRequest start_battery_request (10); + StopBatteryRequest stop_battery_request (11); + MicrophoneDataRequest microphone_data_request (12); + ScanDataRequest scan_data_request (13); + AccelerometerDataRequest accelerometer_data_request (14); + AccelerometerInterruptDataRequest accelerometer_interrupt_data_request (15); + BatteryDataRequest battery_data_request (16); + StartMicrophoneStreamRequest start_microphone_stream_request (17); + StopMicrophoneStreamRequest stop_microphone_stream_request (18); + StartScanStreamRequest start_scan_stream_request (19); + StopScanStreamRequest stop_scan_stream_request (20); + StartAccelerometerStreamRequest start_accelerometer_stream_request (21); + StopAccelerometerStreamRequest stop_accelerometer_stream_request (22); + StartAccelerometerInterruptStreamRequest start_accelerometer_interrupt_stream_request (23); + StopAccelerometerInterruptStreamRequest stop_accelerometer_interrupt_stream_request (24); + StartBatteryStreamRequest start_battery_stream_request (25); + StopBatteryStreamRequest stop_battery_stream_request (26); + IdentifyRequest identify_request (27); + TestRequest test_request (28); + RestartRequest restart_request (29); + } +} + + + + + + + +message StatusResponse { + required uint8 clock_status; + required uint8 microphone_status; + required uint8 scan_status; + required uint8 accelerometer_status; + required uint8 accelerometer_interrupt_status; + required uint8 battery_status; + required Timestamp timestamp; + required BatteryData battery_data; +} + +message StartMicrophoneResponse { + required Timestamp timestamp; +} + + +message StartScanResponse { + required Timestamp timestamp; +} + + +message StartAccelerometerResponse { + required Timestamp timestamp; +} + + +message StartAccelerometerInterruptResponse { + required Timestamp timestamp; +} + + +message StartBatteryResponse { + required Timestamp timestamp; +} + + + + +message MicrophoneDataResponse { + required uint8 last_response; + required Timestamp timestamp; + required uint16 sample_period_ms; + repeated MicrophoneData microphone_data [PROTOCOL_MICROPHONE_DATA_SIZE]; +} + +message ScanDataResponse { + required uint8 last_response; + required Timestamp timestamp; + repeated ScanResultData scan_result_data[PROTOCOL_SCAN_DATA_SIZE]; +} + +message AccelerometerDataResponse { + required uint8 last_response; + required Timestamp timestamp; + repeated AccelerometerData accelerometer_data[PROTOCOL_ACCELEROMETER_DATA_SIZE]; +} + +message AccelerometerInterruptDataResponse { + required uint8 last_response; + required Timestamp timestamp; +} + +message BatteryDataResponse { + required uint8 last_response; + required Timestamp timestamp; + required BatteryData battery_data; +} + + + +message StreamResponse { + required Timestamp timestamp; + repeated BatteryStream battery_stream[PROTOCOL_BATTERY_STREAM_SIZE]; + repeated MicrophoneStream microphone_stream[PROTOCOL_MICROPHONE_STREAM_SIZE]; + repeated ScanStream scan_stream[PROTOCOL_SCAN_STREAM_SIZE]; + repeated AccelerometerStream accelerometer_stream[PROTOCOL_ACCELEROMETER_STREAM_SIZE]; + repeated AccelerometerInterruptStream accelerometer_interrupt_stream[PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE]; +} + + + +message TestResponse { + required uint8 test_failed; +} + + + +message Response { + oneof type { + StatusResponse status_response (1); + StartMicrophoneResponse start_microphone_response (2); + StartScanResponse start_scan_response (3); + StartAccelerometerResponse start_accelerometer_response (4); + StartAccelerometerInterruptResponse start_accelerometer_interrupt_response (5); + StartBatteryResponse start_battery_response (6); + MicrophoneDataResponse microphone_data_response (7); + ScanDataResponse scan_data_response (8); + AccelerometerDataResponse accelerometer_data_response (9); + AccelerometerInterruptDataResponse accelerometer_interrupt_data_response (10); + BatteryDataResponse battery_data_response (11); + StreamResponse stream_response (12); + TestResponse test_response (13); + } +} diff --git a/BadgeFramework/badge_protocol_legacy.py b/BadgeFramework/badge_protocol_legacy.py new file mode 100644 index 0000000..4f6fb12 --- /dev/null +++ b/BadgeFramework/badge_protocol_legacy.py @@ -0,0 +1,258 @@ +from __future__ import division, absolute_import, print_function +from struct import * + +# This file defines much of the structure needed to carry out our communication protocol in Python. +# You can find more information about our communications protocol here: +# https://github.com/HumanDynamics/OpenBadge/wiki/Communication-protocol + +STATUS_REQUEST_HEADER = "s" +START_RECORDING_HEADER = "1" +STOP_RECORDING_HEADER = "0" +START_SCANNING_HEADER = "p" +STOP_SCANNING_HEADER = "q" +REQUEST_MIC_DATA_HEADER = "r" +REQUEST_SCAN_DATA_HEADER = "b" +IDENITFY_HEADER = "i" + +# These fields are used by BadgeMessage objects to serialize and deserialize binary messages to Python objects. +# Fields are composed of (attribute, length [in bytes], optional, serializer, parser) + +# chr field is used for convinience, but can be confusing. We use the chr field to represnet an object that is an actual +# character, or a string of length 1 (as opposed to a numeric object). For signed numeric char (int8), use the int8_field +def char_field(attribute, optional=False): + return (attribute, 1, optional, lambda x: chr(x) if type(x) is int else x, lambda x: chr(x) if type (x) is int else x) + +def ulong_field(attribute, optional=False): + return (attribute, 4, optional, lambda x: pack("= self.rx_bytes_expected: - self.rx_message = self.rx_buffer[0:self.rx_bytes_expected] - self.rx_buffer = self.rx_buffer[self.rx_bytes_expected:] - self.rx_bytes_expected = 0 - + + for b in data: + self.rx_queue.put(b) + + # Implements BadgeConnection's connect() spec. def connect(self): @@ -110,11 +107,10 @@ def disconnect(self): self.uart = None self.tx = None self.rx = None - - self.rx_buffer = "" - self.rx_message = None - self.rx_bytes_expected = 0 - + + with self.rx_queue.mutex: + self.rx_queue.queue.clear() + #self.ble_device.disconnect() self.conn.disconnect() self.ble_device = None @@ -134,14 +130,18 @@ def await_data(self, data_len): if not self.is_connected(): raise RuntimeError("BLEBadgeConnection not connected before await_data()!") - self.rx_bytes_expected = data_len - self.rx_message = None - - if data_len > 0: - while(self.rx_message is None): + rx_message = "" + rx_bytes_expected = data_len + + if rx_bytes_expected > 0: + while True: + while(not self.rx_queue.empty()): + rx_message += self.rx_queue.get() + if(len(rx_message) == rx_bytes_expected): + return rx_message + self.conn.waitForNotifications(5.0) - - return self.rx_message + # Implements BadgeConnection's send() spec. @@ -149,13 +149,19 @@ def send(self, message, response_len=0): if not self.is_connected(): raise RuntimeError("BLEBadgeConnection not connected before send()!") - self.rx_bytes_expected = response_len - self.rx_message = None - + rx_message = "" + rx_bytes_expected = response_len + self.tx.write(message,withResponse=True) - - if response_len > 0: - while(self.rx_message is None): + + + if rx_bytes_expected > 0: + while True: + while(not self.rx_queue.empty()): + rx_message += self.rx_queue.get() + if(len(rx_message) == rx_bytes_expected): + return rx_message + self.conn.waitForNotifications(5.0) - - return self.rx_message + + \ No newline at end of file diff --git a/BadgeFramework/terminal.py b/BadgeFramework/terminal.py index 60a1a33..eaaed93 100644 --- a/BadgeFramework/terminal.py +++ b/BadgeFramework/terminal.py @@ -37,15 +37,36 @@ def main(): def print_help(args): print("Available commands: [optional arguments]") print(" status [new badge id] [group number] (id + group must be set together)") - print(" start_record [timeout in minutes]") - print(" stop_record") + print(" start_microphone") + print(" stop_microphone") print(" start_scan") print(" stop_scan") - print(" get_mic_data [seconds of mic data to request]") - print(" get_audio_stream [seconds of mic data to request]") + print(" start_accelerometer") + print(" stop_accelerometer") + print(" start_accelerometer_interrupt") + print(" stop_accelerometer_interrupt") + print(" start_battery") + print(" stop_battery") + print(" get_microphone_data [seconds of mic data to request]") print(" get_scan_data [seconds of scan data to request]") + print(" get_accelerometer_data [seconds of accelerometer data to request]") + print(" get_accelerometer_interrupt_data [seconds of accelerometer interrupt data to request]") + print(" get_battery_data [seconds of battery data to request]") print(" identify [led duration seconds | 'off']") + print(" test") + print(" restart") print(" help") + print(" start_microphone_stream") + print(" stop_microphone_stream") + print(" start_scan_stream") + print(" stop_scan_stream") + print(" start_accelerometer_stream") + print(" stop_accelerometer_stream") + print(" start_accelerometer_interrupt_stream") + print(" stop_accelerometer_interrupt_stream") + print(" start_battery_stream") + print(" stop_battery_stream") + print(" stream") print("All commands use current system time as transmitted time.") print("Default arguments used where not specified.") @@ -61,62 +82,50 @@ def handle_status_request(args): else: print("Invalid Syntax: status [new badge id] [group number]") - def handle_start_record_request(args): - if len(args) == 1: - print(badge.start_recording()) - elif len(args) == 2: - print(badge.start_recording(timeout_minutes=int(args[1]))) - else: - print("Invalid Syntax: start_record [timeout in minutes]") + def handle_start_microphone_request(args): + print(badge.start_microphone()) - def handle_stop_record_request(args): - if badge.stop_recording(): - print("Stop request request sent!") - else: - print("Stop record request failed. :(") - def handle_start_scanning_request(args): - print(badge.start_scanning()) + def handle_stop_microphone_request(args): + badge.stop_microphone() + - def handle_stop_scanning_request(args): - if badge.stop_scanning(): - print("Stop scanning request sent!") - else: - print("Stop scanning request failed. :(") + def handle_start_scan_request(args): + print(badge.start_scan()) - def handle_get_mic_data(args): - if len(args) == 1: - print(badge.get_mic_data()) - elif len(args) == 2: - start_time_to_request = int(time.time()) - int(args[1]) - print(badge.get_mic_data(start_time_to_request)) - else: - print("Invalid Syntax: get_mic_data [seconds of mic data to request]") + def handle_stop_scan_request(args): + badge.stop_scan() + + + def handle_start_accelerometer_request(args): + print(badge.start_accelerometer()) - def handle_get_audio_stream(args): - mic_data = None + def handle_stop_accelerometer_request(args): + badge.stop_accelerometer() + + + def handle_start_accelerometer_interrupt_request(args): + print(badge.start_accelerometer_interrupt()) + + def handle_stop_accelerometer_interrupt_request(args): + badge.stop_accelerometer_interrupt() + + def handle_start_battery_request(args): + print(badge.start_battery()) + + def handle_stop_battery_request(args): + badge.stop_battery() + + + def handle_get_microphone_data(args): if len(args) == 1: - mic_data = badge.get_mic_data() + print(badge.get_microphone_data()) elif len(args) == 2: start_time_to_request = int(time.time()) - int(args[1]) - mic_data = badge.get_mic_data(start_time_to_request) + print(badge.get_microphone_data(start_time_to_request)) else: - print("Invalid Syntax: get_audio_stream [seconds of audio to request]") - return - - audio_stream = [] - last_chunk_end_time = None - for header, samples in mic_data: - this_chunk_start_time = timestamps_to_time(header.timestamp_seconds, header.timestamp_miliseconds) - if last_chunk_end_time and this_chunk_start_time - last_chunk_end_time > 0.1: - audio_stream.append(("GAP", this_chunk_start_time - last_chunk_end_time)) + print("Invalid Syntax: get_microphone_data [seconds of microphone data to request]") - audio_stream.extend(samples) - - chunk_duration = (header.num_samples_in_chunk * (float(header.sample_period_miliseconds) / 1000.0)) - last_chunk_end_time = this_chunk_start_time + chunk_duration - - print(audio_stream) def handle_get_scan_data(args): if len(args) == 1: @@ -126,35 +135,129 @@ def handle_get_scan_data(args): print(badge.get_scan_data(start_time_to_request)) else: print("Invalid Syntax: get_scan_data [seconds of scan data to request]") + + def handle_get_accelerometer_data(args): + if len(args) == 1: + print(badge.get_accelerometer_data()) + elif len(args) == 2: + start_time_to_request = int(time.time()) - int(args[1]) + print(badge.get_accelerometer_data(start_time_to_request)) + else: + print("Invalid Syntax: get_accelerometer_data [seconds of accelerometer data to request]") + + def handle_get_accelerometer_interrupt_data(args): + if len(args) == 1: + print(badge.get_accelerometer_interrupt_data()) + elif len(args) == 2: + start_time_to_request = int(time.time()) - int(args[1]) + print(badge.get_accelerometer_interrupt_data(start_time_to_request)) + else: + print("Invalid Syntax: get_accelerometer_interrupt_data [seconds of accelerometer interrupt data to request]") + + def handle_get_battery_data(args): + if len(args) == 1: + print(badge.get_battery_data()) + elif len(args) == 2: + start_time_to_request = int(time.time()) - int(args[1]) + print(badge.get_battery_data(start_time_to_request)) + else: + print("Invalid Syntax: get_battery_data [seconds of battery data to request]") + + def handle_identify_request(args): if len(args) == 1: - request_success = badge.identify() + badge.identify() elif len(args) == 2: if args[1] == "off": - request_success = badge.identify(duration_seconds=0) + badge.identify(duration_seconds=0) else: - request_success = badge.identify(duration_seconds=int(args[1])) + badge.identify(duration_seconds=int(args[1])) else: print("Invalid Syntax: identify [led duration seconds | 'off']") return - if request_success: - print("Identify request sent!") - else: - print("Identify request failed. :(") + def handle_test_request(args): + print(badge.test()) + + def handle_restart_request(args): + print(badge.restart()) + + + + def handle_start_microphone_stream_request(args): + badge.start_microphone_stream() + + + def handle_stop_microphone_stream_request(args): + badge.stop_microphone_stream() + + + def handle_start_scan_stream_request(args): + badge.start_scan_stream() + + def handle_stop_scan_stream_request(args): + badge.stop_scan_stream() + + + def handle_start_accelerometer_stream_request(args): + badge.start_accelerometer_stream() + + def handle_stop_accelerometer_stream_request(args): + badge.stop_accelerometer_stream() + + + def handle_start_accelerometer_interrupt_stream_request(args): + badge.start_accelerometer_interrupt_stream() + + def handle_stop_accelerometer_interrupt_stream_request(args): + badge.stop_accelerometer_interrupt_stream() + + def handle_start_battery_stream_request(args): + badge.start_battery_stream() + + def handle_stop_battery_stream_request(args): + badge.stop_battery_stream() + + def handle_stream_request(args): + badge.stream_clear() + stream = badge.get_stream() + while(not stream == []): + print(stream) + stream = badge.get_stream() command_handlers = { "help": print_help, "status": handle_status_request, - "start_record": handle_start_record_request, - "stop_record": handle_stop_record_request, - "start_scan": handle_start_scanning_request, - "stop_scan": handle_stop_scanning_request, - "get_mic_data": handle_get_mic_data, - "get_audio_stream": handle_get_audio_stream, + "start_microphone": handle_start_microphone_request, + "stop_microphone": handle_stop_microphone_request, + "start_scan": handle_start_scan_request, + "stop_scan": handle_stop_scan_request, + "start_accelerometer": handle_start_accelerometer_request, + "stop_accelerometer": handle_stop_accelerometer_request, + "start_accelerometer_interrupt": handle_start_accelerometer_interrupt_request, + "stop_accelerometer_interrupt": handle_stop_accelerometer_interrupt_request, + "start_battery": handle_start_battery_request, + "stop_battery": handle_stop_battery_request, + "get_microphone_data": handle_get_microphone_data, "get_scan_data": handle_get_scan_data, + "get_accelerometer_data": handle_get_accelerometer_data, + "get_accelerometer_interrupt_data": handle_get_accelerometer_interrupt_data, + "get_battery_data": handle_get_battery_data, "identify": handle_identify_request, + "test": handle_test_request, + "restart": handle_restart_request, + "start_microphone_stream": handle_start_microphone_stream_request, + "stop_microphone_stream": handle_stop_microphone_stream_request, + "start_scan_stream": handle_start_scan_stream_request, + "stop_scan_stream": handle_stop_scan_stream_request, + "start_accelerometer_stream": handle_start_accelerometer_stream_request, + "stop_accelerometer_stream": handle_stop_accelerometer_stream_request, + "start_accelerometer_interrupt_stream": handle_start_accelerometer_interrupt_stream_request, + "stop_accelerometer_interrupt_stream": handle_stop_accelerometer_interrupt_stream_request, + "start_battery_stream": handle_start_battery_stream_request, + "stop_battery_stream": handle_stop_battery_stream_request, + "stream": handle_stream_request, } while True: diff --git a/BadgeFramework/terminal_legacy.py b/BadgeFramework/terminal_legacy.py new file mode 100644 index 0000000..879d858 --- /dev/null +++ b/BadgeFramework/terminal_legacy.py @@ -0,0 +1,179 @@ +from __future__ import division, absolute_import, print_function +import logging +import sys +import threading + + +from badge_legacy import * +from ble_badge_connection import * +from bluepy import * +from bluepy import btle +from bluepy.btle import UUID, Peripheral, DefaultDelegate, AssignedNumbers ,Scanner +from bluepy.btle import BTLEException + +# Enable debug output. +logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) +# Main Loop of Badge Terminal + +def main(): + num_args = len(sys.argv) # Get the arguments list + if num_args != 2: + print("Please enter badge MAC address") + return + + device_addr = sys.argv[1] + print("Connecting to badge", device_addr) + connection = BLEBadgeConnection.get_connection_to_badge(device_addr) + + if not connection: + print("Could not find nearby OpenBadge. :( Please try again!") + return + + connection.connect() + badge = OpenBadge(connection) + + print("Connected!") + + def print_help(args): + print("Available commands: [optional arguments]") + print(" status [new badge id] [group number] (id + group must be set together)") + print(" start_record [timeout in minutes]") + print(" stop_record") + print(" start_scan") + print(" stop_scan") + print(" get_mic_data [seconds of mic data to request]") + print(" get_audio_stream [seconds of mic data to request]") + print(" get_scan_data [seconds of scan data to request]") + print(" identify [led duration seconds | 'off']") + print(" help") + print("All commands use current system time as transmitted time.") + print("Default arguments used where not specified.") + + def handle_status_request(args): + if len(args) == 1: + print(badge.get_status()) + elif len(args) == 2: + print("Badge ID and Group Number Must Be Set Simultaneously") + elif len(args) == 3: + new_id = int(args[1]) + group_number = int(args[2]) + print(badge.get_status(new_id=new_id, new_group_number=group_number)) + else: + print("Invalid Syntax: status [new badge id] [group number]") + + def handle_start_record_request(args): + if len(args) == 1: + print(badge.start_recording()) + elif len(args) == 2: + print(badge.start_recording(timeout_minutes=int(args[1]))) + else: + print("Invalid Syntax: start_record [timeout in minutes]") + + def handle_stop_record_request(args): + if badge.stop_recording(): + print("Stop request request sent!") + else: + print("Stop record request failed. :(") + + def handle_start_scanning_request(args): + print(badge.start_scanning()) + + def handle_stop_scanning_request(args): + if badge.stop_scanning(): + print("Stop scanning request sent!") + else: + print("Stop scanning request failed. :(") + + def handle_get_mic_data(args): + if len(args) == 1: + print(badge.get_mic_data()) + elif len(args) == 2: + start_time_to_request = int(time.time()) - int(args[1]) + print(badge.get_mic_data(start_time_to_request)) + else: + print("Invalid Syntax: get_mic_data [seconds of mic data to request]") + + def handle_get_audio_stream(args): + mic_data = None + if len(args) == 1: + mic_data = badge.get_mic_data() + elif len(args) == 2: + start_time_to_request = int(time.time()) - int(args[1]) + mic_data = badge.get_mic_data(start_time_to_request) + else: + print("Invalid Syntax: get_audio_stream [seconds of audio to request]") + return + + audio_stream = [] + last_chunk_end_time = None + for header, samples in mic_data: + this_chunk_start_time = timestamps_to_time(header.timestamp_seconds, header.timestamp_miliseconds) + if last_chunk_end_time and this_chunk_start_time - last_chunk_end_time > 0.1: + audio_stream.append(("GAP", this_chunk_start_time - last_chunk_end_time)) + + audio_stream.extend(samples) + + chunk_duration = (header.num_samples_in_chunk * (float(header.sample_period_miliseconds) / 1000.0)) + last_chunk_end_time = this_chunk_start_time + chunk_duration + + print(audio_stream) + + def handle_get_scan_data(args): + if len(args) == 1: + print(badge.get_scan_data()) + elif len(args) == 2: + start_time_to_request = int(time.time()) - int(args[1]) + print(badge.get_scan_data(start_time_to_request)) + else: + print("Invalid Syntax: get_scan_data [seconds of scan data to request]") + + def handle_identify_request(args): + if len(args) == 1: + request_success = badge.identify() + elif len(args) == 2: + if args[1] == "off": + request_success = badge.identify(duration_seconds=0) + else: + request_success = badge.identify(duration_seconds=int(args[1])) + else: + print("Invalid Syntax: identify [led duration seconds | 'off']") + return + + if request_success: + print("Identify request sent!") + else: + print("Identify request failed. :(") + + command_handlers = { + "help": print_help, + "status": handle_status_request, + "start_record": handle_start_record_request, + "stop_record": handle_stop_record_request, + "start_scan": handle_start_scanning_request, + "stop_scan": handle_stop_scanning_request, + "get_mic_data": handle_get_mic_data, + "get_audio_stream": handle_get_audio_stream, + "get_scan_data": handle_get_scan_data, + "identify": handle_identify_request, + } + + while True: + sys.stdout.write("> ") + # [:-1] removes newline character + command = sys.stdin.readline()[:-1] + if command == "exit": + connection.disconnect() + break + + command_args = command.split(" ") + if command_args[0] in command_handlers: + command_handlers[command_args[0]](command_args) + else: + print("Command Not Found!") + print_help({}) + + + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/IntegrationTests/disabled_test_1_debug_log.py b/IntegrationTests/LegacyProtocol/disabled_test_1_debug_log.py similarity index 100% rename from IntegrationTests/disabled_test_1_debug_log.py rename to IntegrationTests/LegacyProtocol/disabled_test_1_debug_log.py diff --git a/IntegrationTests/LegacyProtocol/integration_test.py b/IntegrationTests/LegacyProtocol/integration_test.py new file mode 100644 index 0000000..7deffb6 --- /dev/null +++ b/IntegrationTests/LegacyProtocol/integration_test.py @@ -0,0 +1,82 @@ +from __future__ import division, absolute_import, print_function +import unittest +import sys +import logging +import time +import serial +import serial.tools.list_ports +import threading + +sys.path.append('../../BadgeFramework') +from ble_badge_connection import BLEBadgeConnection +from badge_legacy import OpenBadge + +logging.basicConfig(filename="integration_test.log", level=logging.DEBUG) +logger = logging.getLogger(__name__) + +# Enable log output to terminal +stdout_handler = logging.StreamHandler(sys.stdout) +stdout_handler.setLevel(logging.DEBUG) +logger.addHandler(stdout_handler) + +# Uncomment this line to make logging very verbose. +# logging.getLogger().addHandler(stdout_handler) + +# Special badge restart command only used for testing purposes +def restart_badge(serial): + serial.write("restart\n") + time.sleep(5) + +class IntegrationTest(unittest.TestCase): + def __init__(self, device_addr): + self.device_addr = device_addr + unittest.TestCase.__init__(self) + + def runTest(self): + self.runTest_startUART() + # AdaFruit has this really handy helper function, but we should probably write our own, so that + # we don't have to propogate the AdaFruit dependency everywhere. + #Adafruit_BluefruitLE.get_provider().run_mainloop_with(self.runTest_MainLoop) + self.runTest_MainLoop() + + + def runTest_startUART(self): + uartPort = list(serial.tools.list_ports.comports())[0] + self.uartSerial = serial.Serial(uartPort.device, 115200, timeout=1) + + + def uartRXTarget(): + while True: + # Some slight implicit control flow going on here: + # uartSerial.readline() will sometimes timeout, and then we'll just loop around. + + rx_data = self.uartSerial.readline() + + if rx_data: + # We truncate the ending newline. + self.onUartLineRx(rx_data[:-1]) + + uartRXThread = threading.Thread(target=uartRXTarget) + uartRXThread.setDaemon(True) + uartRXThread.start() + + def onUartLineRx(self, data): + logger.info("UART:" + data) + + def runTest_MainLoop(self): + restart_badge(self.uartSerial) + + connection = BLEBadgeConnection.get_connection_to_badge(self.device_addr) + connection.connect() + badge = OpenBadge(connection) + + try: + self.testCase(badge, logger) + print("Test Passed! :)") + except Exception as e: + self.onTestFailure(badge, logger) + raise AssertionError("Test Failure") + + def onTestFailure(self, badge, logger): + logger.exception("Exception during test!") + logger.info("Badge Status after Failure: {}".format(badge.get_status())) diff --git a/IntegrationTests/LegacyProtocol/run_all_tests.py b/IntegrationTests/LegacyProtocol/run_all_tests.py new file mode 100644 index 0000000..0121ed7 --- /dev/null +++ b/IntegrationTests/LegacyProtocol/run_all_tests.py @@ -0,0 +1,30 @@ +from __future__ import division, absolute_import, print_function + +from fnmatch import fnmatch +from importlib import import_module +from integration_test import IntegrationTest +import inspect +import os +import sys +import subprocess + +os.system("rm -rf testResults") +os.system("mkdir testResults") + +if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + +device_addr = sys.argv[1] + +for file in os.listdir("."): + if fnmatch(file, "test_*.py"): + test_case_name = file.replace(".py", "") + print("Running " + test_case_name + "...") + test_status = os.system("python {}.py {} > testResults/{}.log".format(test_case_name, device_addr, test_case_name)) + if not test_status == 0: + raise AssertionError("Test {} Failed!".format(test_case_name)) + else: + print(" Test "+ test_case_name + " PASSED! :)") + +print("All integration tests passed! :)") diff --git a/IntegrationTests/test_2_sync_start_stop.py b/IntegrationTests/LegacyProtocol/test_2_sync_start_stop.py similarity index 100% rename from IntegrationTests/test_2_sync_start_stop.py rename to IntegrationTests/LegacyProtocol/test_2_sync_start_stop.py diff --git a/IntegrationTests/test_3_scan_record_timeout.py b/IntegrationTests/LegacyProtocol/test_3_scan_record_timeout.py similarity index 100% rename from IntegrationTests/test_3_scan_record_timeout.py rename to IntegrationTests/LegacyProtocol/test_3_scan_record_timeout.py diff --git a/IntegrationTests/test_4_clock_test.py b/IntegrationTests/LegacyProtocol/test_4_clock_test.py similarity index 100% rename from IntegrationTests/test_4_clock_test.py rename to IntegrationTests/LegacyProtocol/test_4_clock_test.py diff --git a/IntegrationTests/LegacyProtocol/test_5_record_no_gaps.py b/IntegrationTests/LegacyProtocol/test_5_record_no_gaps.py new file mode 100644 index 0000000..118ff0b --- /dev/null +++ b/IntegrationTests/LegacyProtocol/test_5_record_no_gaps.py @@ -0,0 +1,64 @@ +from __future__ import division, absolute_import, print_function +import time +import sys +from integration_test import * + +sys.path.append('../BadgeFramework') +from badge import timestamps_to_time + +TEST_LENGTH_SECONDS = 3 * 60 +SAMPLE_PERIOD_TICKS = 1638.0 +NRF_CLOCK_FREQ = 32768.0 +SAMPLE_PERIOD_MS = SAMPLE_PERIOD_TICKS * (1000.0 / NRF_CLOCK_FREQ) +SAMPLES_PER_SECOND = 1000 / SAMPLE_PERIOD_MS + +# Maximum allowed delay between recording start command sent and first sample recorded in seconds. +MAX_ALLOWED_STARTUP_DELAY = 10 + +class RecordNoGapsTestCase(IntegrationTest): + def testCase(self, badge, logger): + # Sync time + status = badge.get_status() + + test_start_time = time.time() # Set this here (before 0.25sec wait) because of the moving average clock mechanism on the badge + time.sleep(.25) + badge.start_recording() + time.sleep(TEST_LENGTH_SECONDS) + badge.stop_recording() + + mic_data = badge.get_mic_data(t=test_start_time) + + num_samples_taken = 0 + + # We give extra leway on the first chunk to allow for startup time. + first_chunk_header, first_chunk_data = mic_data[0] + first_chunk_time = timestamps_to_time(first_chunk_header.timestamp_seconds, first_chunk_header.timestamp_miliseconds) + self.assertAlmostEqual(test_start_time, first_chunk_time, delta=MAX_ALLOWED_STARTUP_DELAY) + # If we passed our startup delay test, use our first_chunk_time to calibrate all other expected times + expected_next_chunk_time = first_chunk_time + + for header, data in mic_data: + # Check that there's the correct number of samples + self.assertEqual(header.num_samples_in_chunk, len(data)) + num_samples_taken += header.num_samples_in_chunk + + # Check that timestamps are continous + sample_time = timestamps_to_time(header.timestamp_seconds, header.timestamp_miliseconds) + self.assertAlmostEqual(expected_next_chunk_time, sample_time, delta=0.005) + print("Chunk {}: OK".format(header)) + expected_next_chunk_time = sample_time + (float(header.num_samples_in_chunk) / SAMPLES_PER_SECOND) + + # Check that there were the correct number of total samples for the amount of time spent recording + actual_test_duration = expected_next_chunk_time - first_chunk_time + expected_num_samples = actual_test_duration * SAMPLES_PER_SECOND + self.assertAlmostEqual(TEST_LENGTH_SECONDS, actual_test_duration, delta=5) # Increased to 5 becuase we don't send partial chunks + self.assertAlmostEqual(num_samples_taken, expected_num_samples, delta=1) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + + testCase = RecordNoGapsTestCase(device_addr) + testCase.runTest() \ No newline at end of file diff --git a/IntegrationTests/integration_test.py b/IntegrationTests/integration_test.py index c0f2ebf..b7fc908 100644 --- a/IntegrationTests/integration_test.py +++ b/IntegrationTests/integration_test.py @@ -9,6 +9,7 @@ sys.path.append('../BadgeFramework') from ble_badge_connection import BLEBadgeConnection +from bluepy.btle import BTLEException from badge import OpenBadge logging.basicConfig(filename="integration_test.log", level=logging.DEBUG) @@ -22,16 +23,36 @@ # Uncomment this line to make logging very verbose. # logging.getLogger().addHandler(stdout_handler) -# Special badge restart command only used for testing purposes -def restart_badge(serial): - serial.write("restart\n") - time.sleep(5) - class IntegrationTest(unittest.TestCase): def __init__(self, device_addr): self.device_addr = device_addr unittest.TestCase.__init__(self) + def restart_badge(self, badge): + try: + badge.restart() + except BTLEException as e: + # let it restart + time.sleep(0.25) + self.connection.connect() + return OpenBadge(self.connection) + + def assertStatusesEqual(self, status, expected_values): + """ + Takes a status response from a badge + And a dict with expected values + Note that the keys in must match + those in exactly + """ + for key, expected_val in expected_values.iteritems(): + actual_val = getattr(status, key) + self.assertEqual(actual_val, expected_val, msg=""" + Actual and expected status values did not match for + status: {}\n + Expected value: {}\n + Actual value: {} + """.format(key, expected_val, actual_val)) + def runTest(self): self.runTest_startUART() # AdaFruit has this really handy helper function, but we should probably write our own, so that @@ -64,11 +85,13 @@ def onUartLineRx(self, data): logger.info("UART:" + data) def runTest_MainLoop(self): - restart_badge(self.uartSerial) - connection = BLEBadgeConnection.get_connection_to_badge(self.device_addr) - connection.connect() - badge = OpenBadge(connection) + self.connection = BLEBadgeConnection.get_connection_to_badge(self.device_addr) + self.connection.connect() + badge = OpenBadge(self.connection) + + badge = self.restart_badge(badge) + try: self.testCase(badge, logger) diff --git a/IntegrationTests/manual_badge_speed_test.py b/IntegrationTests/manual_badge_speed_test.py new file mode 100644 index 0000000..53387bd --- /dev/null +++ b/IntegrationTests/manual_badge_speed_test.py @@ -0,0 +1,51 @@ +from __future__ import division, absolute_import, print_function +import time +from integration_test import * + + +class BadgeSpeedTestCase(IntegrationTest): + + def __init__(self, device_addr, test_duration_minutes=5): + self.test_duration_minutes = test_duration_minutes + super(BadgeSpeedTestCase, self).__init__(device_addr) + + + def testCase(self, badge, logger): + """ + Record five minutes worth of microphone data, + make sure it pulls it at a reasonable speed. + """ + # sync time + badge.get_status() + + test_start_time = time.time() + badge.start_microphone() + time.sleep(self.test_duration_minutes * 60) + badge.stop_microphone() + + pull_start_time = time.time() + badge_data = badge.get_microphone_data(t=test_start_time) + pull_end_time = time.time() + + pull_duration = pull_end_time - pull_start_time # seconds + + print("It took {} seconds to pull {} minutes worth of data" + .format(pull_duration, self.test_duration_minutes)) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + + + if len(sys.argv) == 3: + test_duration = sys.argv[2] + print("starting speed test w/ length {} minutes".format(test_duration)) + testCase = BadgeSpeedTestCase(device_addr, test_duration_minutes=test_duration) + testCase.runTest() + else: + print("starting speed test w/ length 5 minutes") + testCase = BadgeSpeedTestCase(device_addr) + testCase.runTest() + diff --git a/IntegrationTests/run_all_tests.py b/IntegrationTests/run_all_tests.py index 0121ed7..0341af8 100644 --- a/IntegrationTests/run_all_tests.py +++ b/IntegrationTests/run_all_tests.py @@ -17,7 +17,7 @@ device_addr = sys.argv[1] -for file in os.listdir("."): +for file in sorted(os.listdir(".")): if fnmatch(file, "test_*.py"): test_case_name = file.replace(".py", "") print("Running " + test_case_name + "...") diff --git a/IntegrationTests/test_1_sync_start_stop.py b/IntegrationTests/test_1_sync_start_stop.py new file mode 100644 index 0000000..892aeba --- /dev/null +++ b/IntegrationTests/test_1_sync_start_stop.py @@ -0,0 +1,109 @@ +from __future__ import division, absolute_import, print_function +import time +from integration_test import * + +class SyncStartStopTestCase(IntegrationTest): + + + + def testCase(self, badge, logger): + # Sync time + logger.info("Syncing time %f", time.time()) + status = badge.get_status() + time.sleep(.25) + + expected_values = { + 'microphone_status': False, + 'scan_status': False, + 'accelerometer_status': False, + 'accelerometer_interrupt_status': False, + 'battery_status': False, + } + + # Check that badge now has correct time. + status = badge.get_status() + logger.info("Status after time set: {}".format(status)) + # FIRMWARE BUG: Badge time is always off by four seconds. (Why?) + # TODO - is this still true? + self.assertAlmostEqual(status.timestamp.seconds, time.time(), delta=1) + + # Start microphone, check that status changes. + badge.start_microphone() + time.sleep(.25) + status = badge.get_status() + expected_values['microphone_status'] = True + self.assertStatusesEqual(status, expected_values) + + # Start scanner, check that status changes. + badge.start_scan() + time.sleep(.25) + status = badge.get_status() + expected_values['scan_status'] = True + self.assertStatusesEqual(status, expected_values) + + # Start accelerometer, check that status changes. + badge.start_accelerometer() + time.sleep(.25) + status = badge.get_status() + expected_values['accelerometer_status'] = True + self.assertStatusesEqual(status, expected_values) + + # Start accelerometer, check that status changes. + badge.start_accelerometer_interrupt() + time.sleep(.25) + status = badge.get_status() + expected_values['accelerometer_interrupt_status'] = True + self.assertStatusesEqual(status, expected_values) + + # Start battery, check that status changes & prev statuses remain. + badge.start_battery() + time.sleep(.25) + status = badge.get_status() + expected_values['battery_status'] = True + self.assertStatusesEqual(status, expected_values) + + # Stop scanner, check that status changes. + badge.stop_scan() + time.sleep(.25) + status = badge.get_status() + expected_values['scan_status'] = False + self.assertStatusesEqual(status, expected_values) + + # Stop battery, check that status changes. + badge.stop_battery() + time.sleep(.25) + status = badge.get_status() + expected_values['battery_status'] = False + self.assertStatusesEqual(status, expected_values) + + # Stop accelerometer, check that status changes. + badge.stop_accelerometer() + time.sleep(.25) + status = badge.get_status() + expected_values['accelerometer_status'] = False + self.assertStatusesEqual(status, expected_values) + + # Stop accelerometer_interrupt, check that status changes. + badge.stop_accelerometer_interrupt() + time.sleep(.25) + status = badge.get_status() + expected_values['accelerometer_interrupt_status'] = False + self.assertStatusesEqual(status, expected_values) + + # Stop microphone, check that status changes. + # Note that microphone will only stop after scanning and recording does. + # TODO is this still true? + badge.stop_microphone() + time.sleep(.25) + status = badge.get_status() + expected_values['microphone_status'] = False + self.assertStatusesEqual(status, expected_values) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + + testCase = SyncStartStopTestCase(device_addr) + testCase.runTest() diff --git a/IntegrationTests/test_2_clock_test.py b/IntegrationTests/test_2_clock_test.py new file mode 100644 index 0000000..8d9198d --- /dev/null +++ b/IntegrationTests/test_2_clock_test.py @@ -0,0 +1,29 @@ +from __future__ import division, absolute_import, print_function +import time +from integration_test import * + +class ClockTestCase(IntegrationTest): + def testCase(self, badge, logger): + # Sync current time. + badge.get_status() + + time.sleep(.25) + + start_time = badge.get_status().timestamp.seconds + logger.info("Start time: %f", start_time) + + time.sleep(5) + + end_time = badge.get_status().timestamp.seconds + logger.info("End time: %f", end_time) + + self.assertAlmostEqual(end_time, start_time + 5, delta=1) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + + testCase = ClockTestCase(device_addr) + testCase.runTest() diff --git a/IntegrationTests/test_3_send_old_timestamp_on_sync.py b/IntegrationTests/test_3_send_old_timestamp_on_sync.py new file mode 100644 index 0000000..3f36752 --- /dev/null +++ b/IntegrationTests/test_3_send_old_timestamp_on_sync.py @@ -0,0 +1,48 @@ +from __future__ import division, absolute_import, print_function +import time +from integration_test import * + + +MAXIMUM_SECONDS_AFTER_START = 5000 +class SendOldTimestampOnSyncTestCase(IntegrationTest): + """ + When a badge updates its internal time, it should respond with the old + timestamp it is replacing. + """ + def testCase(self, badge, logger): + # Badge is currently unsynced. + # We expect the initial timestamp to be a small value + # (should be number of seconds passed since + # the badge was turned on) + + + # sync timestamps + status = badge.get_status() + initial_time = status.timestamp.seconds + + self.assertEqual(status.clock_status, 0, msg=""" + Initial clock status should be 0 + """) + + self.assertTrue(initial_time < MAXIMUM_SECONDS_AFTER_START, msg=""" + Initial timestamp should be a very small value. + Actual value: {} + """.format(initial_time)) + + time.sleep(0.25) + + # just make sure the clock synced afterward + synced_status = badge.get_status() + self.assertEqual(synced_status.clock_status, 1, msg=""" + Synced clock status should be 1, was {} + """.format(synced_status.clock_status)) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + + testCase = SendOldTimestampOnSyncTestCase(device_addr) + testCase.runTest() diff --git a/IntegrationTests/test_4_scan_record_timeout.py b/IntegrationTests/test_4_scan_record_timeout.py new file mode 100644 index 0000000..1fd238b --- /dev/null +++ b/IntegrationTests/test_4_scan_record_timeout.py @@ -0,0 +1,55 @@ +from __future__ import division, absolute_import, print_function +import time +from integration_test import * + +class ScanRecordTimeoutTestCase(IntegrationTest): + + def testCase(self, badge, logger): + # sync time + badge.get_status() + + # start recording all for 1 minute + badge.start_microphone(timeout_minutes=1) + badge.start_scan(timeout_minutes=1) + badge.start_accelerometer(timeout_minutes=1) + badge.start_accelerometer_interrupt(timeout_minutes=1) + badge.start_battery(timeout_minutes=1) + + time.sleep(.25) + + # just make sure it all got started + status = badge.get_status() + expected_values = { + 'microphone_status': True, + 'scan_status': True, + 'accelerometer_status': True, + 'accelerometer_interrupt_status': True, + 'battery_status': True, + } + self.assertStatusesEqual(status, expected_values) + + + # badge should still be recording + time.sleep(55) + status = badge.get_status() + self.assertStatusesEqual(status, expected_values) + + # ok, we should be finished now + # note that every time you call status (or send any message to the badge), + # the timer resets + time.sleep(122) + + for key, val in expected_values.iteritems(): + expected_values[key] = False + + status = badge.get_status() + self.assertStatusesEqual(status, expected_values) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + + testCase = ScanRecordTimeoutTestCase(device_addr) + testCase.runTest() diff --git a/IntegrationTests/test_5_record_no_gaps.py b/IntegrationTests/test_5_record_no_gaps.py index f619d22..8fbe3db 100644 --- a/IntegrationTests/test_5_record_no_gaps.py +++ b/IntegrationTests/test_5_record_no_gaps.py @@ -19,39 +19,44 @@ class RecordNoGapsTestCase(IntegrationTest): def testCase(self, badge, logger): # Sync time status = badge.get_status() - time.sleep(.25) - badge.start_recording() + # Set this here (before 0.25sec wait) because of the moving average clock mechanism on the badge + # TODO what? test_start_time = time.time() + time.sleep(.25) + badge.start_microphone() time.sleep(TEST_LENGTH_SECONDS) - badge.stop_recording() + badge.stop_microphone() - mic_data = badge.get_mic_data(t=test_start_time) + mic_data = badge.get_microphone_data(t=test_start_time) num_samples_taken = 0 # We give extra leway on the first chunk to allow for startup time. - first_chunk_header, first_chunk_data = mic_data[0] - first_chunk_time = timestamps_to_time(first_chunk_header.timestamp_seconds, first_chunk_header.timestamp_miliseconds) + first_chunk = mic_data[0] + first_chunk_time = timestamps_to_time(first_chunk.timestamp.seconds, first_chunk.timestamp.ms) self.assertAlmostEqual(test_start_time, first_chunk_time, delta=MAX_ALLOWED_STARTUP_DELAY) # If we passed our startup delay test, use our first_chunk_time to calibrate all other expected times expected_next_chunk_time = first_chunk_time + for data in mic_data: + # has four keys: + # 'sample_period_ms': an int + # 'timestamp': an object with two key/val pairs, 'seconds' and 'ms' + # 'microphone_data': an array of objects with a single key/val pair, 'value' + # 'last_response': an int, TODO on what it means - for header, data in mic_data: - # Check that there's the correct number of samples - self.assertEqual(header.num_samples_in_chunk, len(data)) - num_samples_taken += header.num_samples_in_chunk + num_samples_taken += len(data.microphone_data) # Check that timestamps are continous - sample_time = timestamps_to_time(header.timestamp_seconds, header.timestamp_miliseconds) + sample_time = timestamps_to_time(data.timestamp.seconds, data.timestamp.ms) self.assertAlmostEqual(expected_next_chunk_time, sample_time, delta=0.005) - print("Chunk {}: OK".format(header)) - expected_next_chunk_time = sample_time + (float(header.num_samples_in_chunk) / SAMPLES_PER_SECOND) + print("Chunk {}: OK".format(data)) + expected_next_chunk_time = sample_time + (float(len(data.microphone_data)) / SAMPLES_PER_SECOND) # Check that there were the correct number of total samples for the amount of time spent recording actual_test_duration = expected_next_chunk_time - first_chunk_time expected_num_samples = actual_test_duration * SAMPLES_PER_SECOND - self.assertAlmostEqual(TEST_LENGTH_SECONDS, actual_test_duration, delta=2.5) + self.assertAlmostEqual(TEST_LENGTH_SECONDS, actual_test_duration, delta=5) # Increased to 5 becuase we don't send partial chunks self.assertAlmostEqual(num_samples_taken, expected_num_samples, delta=1) if __name__ == "__main__": @@ -61,4 +66,4 @@ def testCase(self, badge, logger): device_addr = sys.argv[1] testCase = RecordNoGapsTestCase(device_addr) - testCase.runTest() \ No newline at end of file + testCase.runTest() diff --git a/IntegrationTests/test_6_handle_repeated_requests.py b/IntegrationTests/test_6_handle_repeated_requests.py new file mode 100644 index 0000000..d0dab2f --- /dev/null +++ b/IntegrationTests/test_6_handle_repeated_requests.py @@ -0,0 +1,161 @@ +from __future__ import division, absolute_import, print_function +import time +from integration_test import * + +sys.path.append('../BadgeFramework') +from badge import timestamps_to_time + +BADGE_TIMER_TICK_RATE=101 + +def sync(badge): + status = badge.get_status() + time.sleep(.25) + +class RepeatedRecordRequestTestCase(IntegrationTest): + + def startAllRecorders(self, badge, timeout): + badge.start_microphone(timeout_minutes=timeout) + badge.start_scan(timeout_minutes=timeout) + badge.start_accelerometer(timeout_minutes=timeout) + badge.start_accelerometer_interrupt(timeout_minutes=timeout) + badge.start_battery(timeout_minutes=timeout) + + def testTimeout(self, badge, logger): + sync(badge) + + # set to some relatively long timeout + # we're going to change it anyway + print("starting rec") + self.startAllRecorders(badge, 20) + + print("confirming start worked") + status = badge.get_status() + expected_values = { + 'microphone_status': True, + 'scan_status': True, + 'accelerometer_status': True, + 'accelerometer_interrupt_status': True, + 'battery_status': True, + } + self.assertStatusesEqual(status, expected_values) + + print("pass a newer, shorter timeout") + self.startAllRecorders(badge, 1) + + # in case we got bad RNG, wait for the maximum + # timer tick rate + # otherwise we get false failures + # TODO maybe this changed in new implementation? + time.sleep(BADGE_TIMER_TICK_RATE) + + print("We expect the badge to have used the more recent, shorter timeout") + status = badge.get_status() + for key, val in expected_values.iteritems(): + expected_values[key] = False + self.assertStatusesEqual(status, expected_values) + + + print("Start all recorders w/ 1 minutes timeout") + # repeat as above but from shorter -> longer + self.startAllRecorders(badge, 1) + + time.sleep(0.25) + status = badge.get_status() + # check to make sure start worked + for key, val in expected_values.iteritems(): + expected_values[key] = True + self.assertStatusesEqual(status, expected_values) + + print("Change timeout to 3 minutes") + self.startAllRecorders(badge, 3) + + time.sleep(BADGE_TIMER_TICK_RATE) + + print("We expect the badge to have used the more recent, longer timeout") + status = badge.get_status() + self.assertStatusesEqual(status, expected_values) + + + def checkTimestampContinuity(self, data, expected_delta): + first_chunk = data[0] + second_chunk = data[1] + + first_time = timestamps_to_time( + first_chunk.timestamp.seconds, + first_chunk.timestamp.ms) + + last_time = timestamps_to_time( + second_chunk.timestamp.seconds, + second_chunk.timestamp.ms) + + last_diff = last_time - first_time + + for ele in data[2:]: + secs = ele.timestamp.seconds + millis = ele.timestamp.ms + + current_time = timestamps_to_time(secs, millis) + + current_diff = current_time - last_time + + self.assertAlmostEqual(current_diff, last_diff, delta=expected_delta, msg=""" + The difference between the sample timestamps should be approximately equal + Actual difference: {} + Expected within: {} + """.format(abs(current_diff - last_diff), expected_delta)) + + last_time = current_time + last_diff = current_diff + + def testRecordNotRestarted(self, badge, logger): + sync(badge) + + badge.start_microphone(timeout_minutes=5) + + test_start_time = time.time() + time.sleep(60) + + # allow the badges to run the scan for a little while + + # send new record/scan requests, sleep for a min, repeat + # doing this multiple times to make sure we're not + # getting false positive from RNG + for i in range(3): + badge.start_microphone(timeout_minutes=5) + time.sleep(60) + + # stop recording/scanning and inspect the data + badge.stop_microphone() + + # Normally, the start-timepoints that are written to the chunk + # should be more or less equidistant. + # So if the sampling is restarted, + # this shouldn't be the case anymore. + print("checking mic data") + mic_data = badge.get_microphone_data(t=test_start_time) + self.checkTimestampContinuity(mic_data, 0.3) + + + + def testCase(self, badge, logger): + """ + When the badge receives a repeated record request, + - it should use the timeout value of the newest request + - it should not restart the actual recording process + i.e. we should see no disruption in the data + """ + + self.testRecordNotRestarted(badge, logger) + badge = self.restart_badge(badge) + self.testTimeout(badge, logger) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + device_addr = sys.argv[1] + + print("creating testcase") + testCase = RepeatedRecordRequestTestCase(device_addr) + testCase.runTest() diff --git a/IntegrationTests/test_7_receive_data.py b/IntegrationTests/test_7_receive_data.py new file mode 100644 index 0000000..a101b6a --- /dev/null +++ b/IntegrationTests/test_7_receive_data.py @@ -0,0 +1,55 @@ +from __future__ import division, absolute_import, print_function +import time +from integration_test import * + + +# badges sample voltage every minute iirc +WAIT_FOR_SAMPLE = 65 + +class ReceiveDataTestCase(IntegrationTest): + """ + Ensure we receive data from the badges + + Ignore proximity/scan because we don't receive data + if there are no other devices around + + Ignore accelerometer_interrupt because we don't receive + interrupt data unless someone moves the badge during testing + """ + + def testCase(self, badge, logger): + # sync badge + badge.get_status() + + ts = time.time() + time.sleep(WAIT_FOR_SAMPLE) + + # sanity check - should not have any data because we havent been recording + self.assertTrue(len(badge.get_microphone_data(ts)) == 0) + self.assertTrue(len(badge.get_battery_data(ts)) == 0) + self.assertTrue(len(badge.get_accelerometer_data(ts)) == 0) + + # record + badge.start_battery() + badge.start_microphone() + badge.start_accelerometer() + + # make sure we wait long enough to have collected samples + time.sleep(WAIT_FOR_SAMPLE) + + self.assertFalse(len(badge.get_microphone_data(ts)) == 0) + self.assertFalse(len(badge.get_battery_data(ts)) == 0) + self.assertFalse(len(badge.get_accelerometer_data(ts)) == 0) + + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Please enter badge MAC address") + exit(1) + + device_addr = sys.argv[1] + + testCase = ReceiveDataTestCase(device_addr) + testCase.runTest() + diff --git a/README.md b/README.md index 750ee4a..127b183 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,29 @@ The framework includes two tools for testing firmware code Note that in order to run the integration tests, you'll need to have a badge connected with a serial interface. A regular J-Link debugger/programmer does not include these. We recommend using a nRF51 dev-kit as a programmer for this purpose (see our wiki/documentations on how to make one). +## Badge testing procedure ## +The badge firmware includes a tester mode that can be used for testing new badges. +To compile, use the badge_03v6_tester flag (or badge_03v4_tester, for older hardware). For example: +``` +make badge_03v6_noDebug flashUnlock flashErase flashS130 flashAPP +``` + +Or, when using docker: +``` +docker-compose run nrf make badge_03v6_noDebug flashUnlock flashErase flashS130 flashAPP +``` + +Once programmed, the badge uses the LEDs to indicate the status of the test: +1. Blink red LED several times, one for each test (EEPROM, Flash, etc) +2. Microphone test. When it starts, both green and red LEDs will turn on +It then looks for quiet->noise->quiet->noise pattern, each section must be at least 200 ms long. +This must be completed within 10 seconds. +In a quiet place, simply say "ahhhh" twice, with pause in between. Every time the noise passed the threshold, the LEDs +will turn off +3. Accelerometer test. When it starts, both green and red LEDs will turn on. Simply shake the badge +4. Once done, the green LED will turn off for several seconds and then turn off + +Example for known issues with badge manufacturing: +* LEDs not turning on - might be a problem with the LEDs themselves +* When microphone test starts, both LEDs will momentarily turn on and then off +This might indicate a problem in the microphone filter or amplifier circuit (the microphone saturates and noise level says high) \ No newline at end of file diff --git a/compose/Dockerfile b/compose/Dockerfile index 22fce96..db3c91a 100644 --- a/compose/Dockerfile +++ b/compose/Dockerfile @@ -20,6 +20,7 @@ RUN apt-get update && apt-get install -y \ cmake \ minicom screen \ vim \ + lcov \ wget RUN mkdir -p /opt/downloads @@ -31,17 +32,22 @@ RUN wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q3-update/+download RUN tar -C /usr/local -xjf downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 # nRF SDK -ENV NRF_SDK_PATH=/opt/nrf/sdk/nrf_sdk_8_1 +ENV NRF_SDK_PATH=/opt/nrf/sdk/nrf_sdk_12_3/nrf5_12.3.0_d7731ad run mkdir -p $NRF_SDK_PATH -RUN wget https://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/nRF51_SDK_8.1.0_b6ed55f.zip -O downloads/nRF51_SDK_8.1.0_b6ed55f.zip -RUN unzip -q downloads/nRF51_SDK_8.1.0_b6ed55f.zip -d $NRF_SDK_PATH +RUN wget https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v12.x.x/nRF5_SDK_12.3.0_d7731ad.zip -O downloads/nRF5_SDK_12.3.0_d7731ad.zip +RUN unzip -q downloads/nRF5_SDK_12.3.0_d7731ad.zip -d $NRF_SDK_PATH +#RUN wget https://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/nRF51_SDK_8.1.0_b6ed55f.zip -O downloads/nRF51_SDK_8.1.0_b6ed55f.zip +#RUN unzip -q downloads/nRF51_SDK_8.1.0_b6ed55f.zip -d $NRF_SDK_PATH COPY compose/Makefile.posix $NRF_SDK_PATH/components/toolchain/gcc/Makefile.posix # SEGGER copy compose/JLink_Linux_V616i_x86_64.deb /opt/downloads/JLink_Linux_V616i_x86_64.deb + RUN dpkg -i downloads/JLink_Linux_V616i_x86_64.deb +ENV DEBIAN_FRONTEND=noninteractive + ################################ ## Bluetooth Python environment ################################ @@ -50,6 +56,7 @@ RUN apt-get update && apt-get install -y \ python-dev \ python-pip \ python-setuptools \ + python-tk \ gcc \ build-essential \ libglib2.0-dev \ @@ -59,3 +66,5 @@ RUN apt-get update && apt-get install -y \ COPY ./requirements.txt /tmp/requirements.txt RUN pip install -r /tmp/requirements.txt + +ENV DISPLAY :0 diff --git a/compose/Dockerfile_BACKUP_13100 b/compose/Dockerfile_BACKUP_13100 new file mode 100644 index 0000000..ba39932 --- /dev/null +++ b/compose/Dockerfile_BACKUP_13100 @@ -0,0 +1,71 @@ +FROM ubuntu:18.04 + +MAINTAINER Oren Lederman +# Based on https://github.com/FruityLoopers/fruityfactory + +########################### +## Compilation environment +########################### +RUN apt-get update && apt-get install -y \ + build-essential \ + libbluetooth-dev \ + bluez \ + rfkill \ + bluetooth \ + lib32z1 \ + lib32ncurses5 \ + unzip \ + binutils-avr \ + git \ + cmake \ + minicom screen \ + vim \ +<<<<<<< HEAD + lcov +======= + wget +>>>>>>> upstream/master + +RUN mkdir -p /opt/downloads + +WORKDIR /opt + +# GCC ARM +RUN wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q3-update/+download/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 -O downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 +RUN tar -C /usr/local -xjf downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 + +# nRF SDK +ENV NRF_SDK_PATH=/opt/nrf/sdk/nrf_sdk_12_3/nrf5_12.3.0_d7731ad +run mkdir -p $NRF_SDK_PATH +RUN wget https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v12.x.x/nRF5_SDK_12.3.0_d7731ad.zip -O downloads/nRF5_SDK_12.3.0_d7731ad.zip +RUN unzip -q downloads/nRF5_SDK_12.3.0_d7731ad.zip -d $NRF_SDK_PATH +#RUN wget https://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/nRF51_SDK_8.1.0_b6ed55f.zip -O downloads/nRF51_SDK_8.1.0_b6ed55f.zip +#RUN unzip -q downloads/nRF51_SDK_8.1.0_b6ed55f.zip -d $NRF_SDK_PATH + +COPY compose/Makefile.posix $NRF_SDK_PATH/components/toolchain/gcc/Makefile.posix + +# SEGGER +copy compose/JLink_Linux_V616i_x86_64.deb /opt/downloads/JLink_Linux_V616i_x86_64.deb +<<<<<<< HEAD +RUN dpkg -i downloads/JLink_Linux_V616i_x86_64.deb +======= +RUN dpkg -i downloads/JLink_Linux_V616i_x86_64.deb + +################################ +## Bluetooth Python environment +################################ +RUN apt-get update && apt-get install -y \ + python \ + python-dev \ + python-pip \ + python-setuptools \ + gcc \ + build-essential \ + libglib2.0-dev \ + bluez \ + --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* + +COPY ./requirements.txt /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt +>>>>>>> upstream/master diff --git a/compose/Dockerfile_BASE_13100 b/compose/Dockerfile_BASE_13100 new file mode 100644 index 0000000..212d228 --- /dev/null +++ b/compose/Dockerfile_BASE_13100 @@ -0,0 +1,42 @@ +FROM ubuntu:12.04 + +MAINTAINER Oren Lederman +# Based on https://github.com/FruityLoopers/fruityfactory + +RUN apt-get update && apt-get install -y \ + build-essential \ + libbluetooth-dev \ + bluez \ + rfkill \ + bluetooth \ + lib32z1 \ + lib32ncurses5 \ + unzip \ + binutils-avr \ + git \ + cmake \ + minicom screen \ + vim + +RUN mkdir -p /opt/downloads + +WORKDIR /opt + +# GCC ARM +RUN wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q3-update/+download/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 -O downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 +RUN tar -C /usr/local -xjf downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 + +# nRF SDK +ENV NRF_SDK_PATH=/opt/nrf/sdk/nrf_sdk_8_1 +run mkdir -p $NRF_SDK_PATH +RUN wget https://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/nRF51_SDK_8.1.0_b6ed55f.zip -O downloads/nRF51_SDK_8.1.0_b6ed55f.zip +RUN unzip -q downloads/nRF51_SDK_8.1.0_b6ed55f.zip -d $NRF_SDK_PATH + +COPY compose/Makefile.posix $NRF_SDK_PATH/components/toolchain/gcc/Makefile.posix + +# Fix missing library error +RUN ln -s /lib/x86_64-linux-gnu/libudev.so.0 /lib/x86_64-linux-gnu/libudev.so.1 + +# SEGGER +copy compose/JLink_Linux_V616i_x86_64.deb /opt/downloads/JLink_Linux_V616i_x86_64.deb +RUN dpkg -i downloads/JLink_Linux_V616i_x86_64.deb diff --git a/compose/Dockerfile_LOCAL_13100 b/compose/Dockerfile_LOCAL_13100 new file mode 100644 index 0000000..0999f42 --- /dev/null +++ b/compose/Dockerfile_LOCAL_13100 @@ -0,0 +1,45 @@ +FROM ubuntu:12.04 + +MAINTAINER Oren Lederman +# Based on https://github.com/FruityLoopers/fruityfactory + +RUN apt-get update && apt-get install -y \ + build-essential \ + libbluetooth-dev \ + bluez \ + rfkill \ + bluetooth \ + lib32z1 \ + lib32ncurses5 \ + unzip \ + binutils-avr \ + git \ + cmake \ + minicom screen \ + vim \ + lcov + +RUN mkdir -p /opt/downloads + +WORKDIR /opt + +# GCC ARM +RUN wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q3-update/+download/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 -O downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 +RUN tar -C /usr/local -xjf downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 + +# nRF SDK +ENV NRF_SDK_PATH=/opt/nrf/sdk/nrf_sdk_12_3/nrf5_12.3.0_d7731ad +run mkdir -p $NRF_SDK_PATH +RUN wget https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v12.x.x/nRF5_SDK_12.3.0_d7731ad.zip -O downloads/nRF5_SDK_12.3.0_d7731ad.zip +RUN unzip -q downloads/nRF5_SDK_12.3.0_d7731ad.zip -d $NRF_SDK_PATH +#RUN wget https://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/nRF51_SDK_8.1.0_b6ed55f.zip -O downloads/nRF51_SDK_8.1.0_b6ed55f.zip +#RUN unzip -q downloads/nRF51_SDK_8.1.0_b6ed55f.zip -d $NRF_SDK_PATH + +COPY compose/Makefile.posix $NRF_SDK_PATH/components/toolchain/gcc/Makefile.posix + +# Fix missing library error +RUN ln -s /lib/x86_64-linux-gnu/libudev.so.0 /lib/x86_64-linux-gnu/libudev.so.1 + +# SEGGER +copy compose/JLink_Linux_V616i_x86_64.deb /opt/downloads/JLink_Linux_V616i_x86_64.deb +RUN dpkg -i downloads/JLink_Linux_V616i_x86_64.deb \ No newline at end of file diff --git a/compose/Dockerfile_REMOTE_13100 b/compose/Dockerfile_REMOTE_13100 new file mode 100644 index 0000000..22fce96 --- /dev/null +++ b/compose/Dockerfile_REMOTE_13100 @@ -0,0 +1,61 @@ +FROM ubuntu:18.04 + +MAINTAINER Oren Lederman +# Based on https://github.com/FruityLoopers/fruityfactory + +########################### +## Compilation environment +########################### +RUN apt-get update && apt-get install -y \ + build-essential \ + libbluetooth-dev \ + bluez \ + rfkill \ + bluetooth \ + lib32z1 \ + lib32ncurses5 \ + unzip \ + binutils-avr \ + git \ + cmake \ + minicom screen \ + vim \ + wget + +RUN mkdir -p /opt/downloads + +WORKDIR /opt + +# GCC ARM +RUN wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q3-update/+download/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 -O downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 +RUN tar -C /usr/local -xjf downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 + +# nRF SDK +ENV NRF_SDK_PATH=/opt/nrf/sdk/nrf_sdk_8_1 +run mkdir -p $NRF_SDK_PATH +RUN wget https://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/nRF51_SDK_8.1.0_b6ed55f.zip -O downloads/nRF51_SDK_8.1.0_b6ed55f.zip +RUN unzip -q downloads/nRF51_SDK_8.1.0_b6ed55f.zip -d $NRF_SDK_PATH + +COPY compose/Makefile.posix $NRF_SDK_PATH/components/toolchain/gcc/Makefile.posix + +# SEGGER +copy compose/JLink_Linux_V616i_x86_64.deb /opt/downloads/JLink_Linux_V616i_x86_64.deb +RUN dpkg -i downloads/JLink_Linux_V616i_x86_64.deb + +################################ +## Bluetooth Python environment +################################ +RUN apt-get update && apt-get install -y \ + python \ + python-dev \ + python-pip \ + python-setuptools \ + gcc \ + build-essential \ + libglib2.0-dev \ + bluez \ + --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* + +COPY ./requirements.txt /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt diff --git a/compose/entry_point.sh b/compose/entry_point.sh index 34d41ff..9b52c7d 100755 --- a/compose/entry_point.sh +++ b/compose/entry_point.sh @@ -15,6 +15,9 @@ elif [ "$1" = 'terminal' ]; then elif [ "$1" = 'run_all_tests' ]; then cd /app/IntegrationTests python ./run_all_tests.py ${@:2} +elif [ "$1" = 'run_speed_test' ]; then + cd /app/IntegrationTests + python ./manual_badge_speed_test.py ${@:2} ${@:3} else exec "$@" fi diff --git a/firmware/nRF_badge/data_collector/Makefile b/firmware/nRF_badge/data_collector/Makefile old mode 100644 new mode 100755 index 5af65c2..be801e6 --- a/firmware/nRF_badge/data_collector/Makefile +++ b/firmware/nRF_badge/data_collector/Makefile @@ -5,7 +5,13 @@ $(warning NRF_SDK_PATH environment variable undefined. Default: /usr/local/nRF5 endif NRF_SDK_PATH ?= /usr/local/nRF51_SDK_8.1.0_b6ed55f -SDK_PATH := $(NRF_SDK_PATH) +# Michael: ADDED /nRF5_SDK_12.3.0_d7731ad +SDK_PATH := $(NRF_SDK_PATH)/nRF5_SDK_12.3.0_d7731ad + +# The tinybuf directories +TINYBUF_PATH := tinybuf +TINYBUF_SRC_PATH := $(TINYBUF_PATH)/incl + export OUTPUT_FILENAME #MAKEFILE_NAME := $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) @@ -19,7 +25,7 @@ else include $(TEMPLATE_PATH)/Makefile.posix endif -APP_MEMORY_OFFSET = 1c000 ##application memory offset for S130 softdevice +APP_MEMORY_OFFSET = 1b000 ##application memory offset for S130 softdevice JLINK_OPTIONS = -device nrf51422_xxac -if swd -speed 500 JLINK = JLinkExe $(JLINK_OPTIONS) @@ -90,78 +96,224 @@ helpDetailed: +###################################################### SOURCE ##################################### + +#source files +#C_SOURCE_FILES += \ +#main.c \ +#$(SDK_PATH)/components/toolchain/system_nrf51.c \ # Needed for the Assembly file, because there is the SystemInit function! +#$(SDK_PATH)/components/drivers_nrf/uart/nrf_drv_uart.c \ # the uart driver abstraction library/c-file +#$(SDK_PATH)/components/drivers_nrf/common/nrf_drv_common.c \ # the function nrf_drv_common_irq_enable must be explicity included (maybe because it is called from an inline function) +#$(SDK_PATH)/components/drivers_nrf/hal/nrf_adc.c \ # the adc abstraction library +#$(SDK_PATH)/components/drivers_nrf/spi_master/nrf_drv_spi.c \ # the spi driver abstraction library +#$(SDK_PATH)/components/libraries/fstorage/fstorage.c \ # the flash abstraction library +#$(SDK_PATH)/components/softdevice/common/softdevice_handler/softdevice_handler.c \ # needed for +#$(SDK_PATH)/components/libraries/util/app_error.c \ # needed for softdevice_handler.c +#$(SDK_PATH)/components/libraries/util/app_error_weak.c \ # needed for app_error.c +#$(SDK_PATH)/components/drivers_nrf/clock/nrf_drv_clock.c \ # needed for softdevice_handler.c +#$(SDK_PATH)/components/libraries\util\app_util_platform.c \ # needed for the initialization of the softdevice via SOFTDEVICE_HANDLER_INIT() +#$(SDK_PATH)/components/drivers_nrf/gpiote/nrf_drv_gpiote.c \ # needed to set a the interrupt pins for the accelerometer +#$(SDK_PATH)/components/libraries/timer/app_timer.c \ # needed for the timer-abstraction +#$(SDK_PATH)/components/ble/ble_advertising/ble_advertising.c \ # needed for ble_lib to abstract the ble_advertising-functionality +#$(SDK_PATH)/components/ble/ble_services/ble_nus/ble_nus.c \ # needed for ble_lib to abstract the BLE Nordic Uart Service functionality + + + #source files C_SOURCE_FILES += \ main.c \ -incl/accel.c \ -incl/analog.c \ -incl/app_fifo_util.c \ -incl/rtc_timing.c \ -incl/battery.c \ -incl/ble_setup.c \ -incl/debug_log.c \ -incl/ext_eeprom.c \ -incl/collector.c \ -incl/storer.c \ -incl/sender.c \ -incl/self_test.c \ -incl/scanner.c \ -incl/uart_commands.c \ -$(SDK_PATH)/components/libraries/crc16/crc16.c \ -$(SDK_PATH)/components/libraries/scheduler/app_scheduler.c \ -$(SDK_PATH)/components/libraries/timer/app_timer.c \ -$(SDK_PATH)/components/libraries/util/app_error.c \ -$(SDK_PATH)/components/libraries/util/nrf_assert.c \ -$(SDK_PATH)/components/libraries/uart/retarget.c \ -$(SDK_PATH)/components/drivers_nrf/hal/nrf_adc.c \ -$(SDK_PATH)/components/drivers_nrf/hal/nrf_delay.c \ -$(SDK_PATH)/components/drivers_nrf/uart/app_uart_fifo.c \ -$(SDK_PATH)/components/libraries/fifo/app_fifo.c \ +incl/adc_lib.c \ +incl/spi_lib.c \ +incl/uart_lib.c \ +incl/flash_lib.c \ +incl/system_event_lib.c \ +incl/eeprom_lib.c \ +incl/debug_lib.c \ +incl/storage1_lib.c \ +incl/storage2_lib.c \ +incl/storage_lib.c \ +incl/filesystem_lib.c \ +incl/accel_lib.c \ +incl/chunk_fifo_lib.c \ +incl/systick_lib.c \ +incl/ble_lib.c \ +incl/sender_lib.c \ +incl/request_handler_lib_01v1.c \ +incl/request_handler_lib_02v1.c \ +incl/protocol_messages_01v1.c \ +incl/protocol_messages_02v1.c \ +incl/common_messages.c \ +incl/chunk_messages.c \ +incl/stream_messages.c \ +incl/storer_lib.c \ +incl/advertiser_lib.c \ +incl/scanner_lib.c \ +incl/battery_lib.c \ +incl/microphone_lib.c \ +incl/circular_fifo_lib.c \ +incl/sampling_lib.c \ +incl/processing_lib.c \ +incl/timeout_lib.c \ +incl/selftest_lib.c \ +incl/uart_commands_lib.c \ +$(TINYBUF_SRC_PATH)/tinybuf.c \ +$(SDK_PATH)/components/toolchain/system_nrf51.c \ +$(SDK_PATH)/components/drivers_nrf/uart/nrf_drv_uart.c \ $(SDK_PATH)/components/drivers_nrf/common/nrf_drv_common.c \ +$(SDK_PATH)/components/drivers_nrf/hal/nrf_adc.c \ +$(SDK_PATH)/components/drivers_nrf/spi_master/nrf_drv_spi.c \ +$(SDK_PATH)/components/libraries/fstorage/fstorage.c \ +$(SDK_PATH)/components/softdevice/common/softdevice_handler/softdevice_handler.c \ +$(SDK_PATH)/components/libraries/util/app_error.c \ +$(SDK_PATH)/components/libraries/util/app_error_weak.c \ +$(SDK_PATH)/components/drivers_nrf/clock/nrf_drv_clock.c \ +$(SDK_PATH)/components/libraries/util/app_util_platform.c \ $(SDK_PATH)/components/drivers_nrf/gpiote/nrf_drv_gpiote.c \ -$(SDK_PATH)/components/drivers_nrf/ble_flash/ble_flash.c \ -$(SDK_PATH)/components/drivers_nrf/spi_master/spi_master.c \ -$(SDK_PATH)/components/drivers_nrf/pstorage/pstorage.c \ -$(SDK_PATH)/components/ble/common/ble_advdata.c \ +$(SDK_PATH)/components/libraries/timer/app_timer.c \ $(SDK_PATH)/components/ble/ble_advertising/ble_advertising.c \ -$(SDK_PATH)/components/ble/common/ble_srv_common.c \ -$(SDK_PATH)/components/toolchain/system_nrf51.c \ -$(SDK_PATH)/components/softdevice/common/softdevice_handler/softdevice_handler.c \ -$(SDK_PATH)/components/ble/ble_services/ble_bas/ble_bas.c \ +$(SDK_PATH)/components/ble/common/ble_advdata.c \ $(SDK_PATH)/components/ble/ble_services/ble_nus/ble_nus.c \ +$(SDK_PATH)/components/ble/common/ble_srv_common.c \ +$(SDK_PATH)/components/libraries/fifo/app_fifo.c \ +$(SDK_PATH)/components/libraries/scheduler/app_scheduler.c \ + + +#incl/accel.c \ +#incl/analog.c \ +#incl/app_fifo_util.c \ +#incl/rtc_timing.c \ +#incl/battery.c \ +#incl/ble_setup.c \ +#incl/debug_log.c \ +#incl/ext_eeprom.c \ +#incl/collector.c \ +#incl/storer.c \ +#incl/sender.c \ +#incl/self_test.c \ +#incl/scanner.c \ +#incl/uart_commands.c \ +#$(SDK_PATH)/components/libraries/crc16/crc16.c \ +#$(SDK_PATH)/components/libraries/scheduler/app_scheduler.c \ +#$(SDK_PATH)/components/libraries/timer/app_timer.c \ +#$(SDK_PATH)/components/libraries/util/app_error.c \ +#$(SDK_PATH)/components/libraries/util/nrf_assert.c \ +#$(SDK_PATH)/components/libraries/uart/retarget.c \ +#$(SDK_PATH)/components/drivers_nrf/hal/nrf_adc.c \ +#$(SDK_PATH)/components/drivers_nrf/hal/nrf_delay.c \ +#$(SDK_PATH)/components/drivers_nrf/uart/app_uart_fifo.c \ +#$(SDK_PATH)/components/libraries/fifo/app_fifo.c \ +#$(SDK_PATH)/components/drivers_nrf/common/nrf_drv_common.c \ +#$(SDK_PATH)/components/drivers_nrf/gpiote/nrf_drv_gpiote.c \ +#$(SDK_PATH)/components/drivers_nrf/ble_flash/ble_flash.c \ +#$(SDK_PATH)/components/drivers_nrf/spi_master/spi_master.c \ +#$(SDK_PATH)/components/drivers_nrf/pstorage/pstorage.c \ +#$(SDK_PATH)/components/ble/common/ble_advdata.c \ +#$(SDK_PATH)/components/ble/ble_advertising/ble_advertising.c \ +#$(SDK_PATH)/components/ble/common/ble_srv_common.c \ +#$(SDK_PATH)/components/toolchain/system_nrf51.c \ +#$(SDK_PATH)/components/softdevice/common/softdevice_handler/softdevice_handler.c \ +#$(SDK_PATH)/components/ble/ble_services/ble_bas/ble_bas.c \ +#$(SDK_PATH)/components/ble/ble_services/ble_nus/ble_nus.c \ + +###################################################### SOURCE ##################################### #assembly files -ASM_SOURCE_FILES = $(SDK_PATH)/components/toolchain/gcc/gcc_startup_nrf51.s +#Changed Michael --> to .S! +ASM_SOURCE_FILES = $(SDK_PATH)/components/toolchain/gcc/gcc_startup_nrf51.S +#ASM_SOURCE_FILES = $(SDK_PATH)/components/toolchain/gcc/gcc_startup_nrf51.s + +###################################################### INCLUDE ##################################### + +#includes +# Doku: +#INC_PATHS = -Iconfig # Include all files in the config directory +#INC_PATHS += -Iincl # Include all files in the incl directory +#INC_PATHS += -I$(SDK_PATH)/components/device # nrf51822_peripherals.h (for sdk_config.h), nrf.h, nrf51 (for main, because it includes the core_cm0 peripheral (NVIC_SystemReset)) +#INC_PATHS += -I$(SDK_PATH)/components/toolchain # system_nrf51.h/.c for SystemInit of the assembly file +#INC_PATHS += -I$(SDK_PATH)/components/toolchain/gcc # the assembly startup-code: gcc_startup_nrf51 +#INC_PATHS += -I$(SDK_PATH)/components/toolchain/cmsis/include # the core_cm0-files needed for other files, e.g. nrf51.h +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/uart # nrf_drv_uart.h/.c +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/hal # nrf_uart.h/.c (needed for nrf_drv_uart), nrf_adc.h/.c +#INC_PATHS += -I$(SDK_PATH)/components/libraries/util # app_error.h/.c, sdk_errors.h/.c, app_util_platform.h/.c (APP_PRIORITY_LOW...) +#INC_PATHS += -I$(SDK_PATH)/components/softdevice/s130/headers # nrf_soc.h, nrf_nvic.h, nrf_errors.h (to interface with the softdevice) +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/common # nrf_drv_common.h/.c +#INC_PATHS += -I$(SDK_PATH)/components/libraries/log # nrf_log.h, nrf_log_ctrl.c (for almost any file) +#INC_PATHS += -I$(SDK_PATH)/components/libraries/log/src # nrf_log_internal (needed for nrf_log) +#INC_PATHS += -I$(SDK_PATH)/components/boards # boards.h (include in main.c, that there is a redirect to custom_board.h and so the badge_vxx-file) +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/delay # nrf_delay.h --> implementation directly in header --> no source file to add! +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/spi_master # nrf_drv_spi.c/.h --> implementation of SPI master driver +#INC_PATHS += -I$(SDK_PATH)/components/libraries/fstorage # fstorage.c/.h +#INC_PATHS += -I$(SDK_PATH)/components/libraries/experimental_section_vars # needed for fstorage.c +#INC_PATHS += -I$(SDK_PATH)/components/softdevice/common/softdevice_handler # needed for system_event_lib.h/.c +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/clock # needed for softdevice_handler.c +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/gpiote # needed for nrf_drv_gpiote.c/.h +#INC_PATHS += -I$(SDK_PATH)/components/libraries/timer # needed for app_timer.h #includes INC_PATHS = -Iconfig INC_PATHS += -Iincl +INC_PATHS += -I$(TINYBUF_SRC_PATH) INC_PATHS += -I$(SDK_PATH)/components/device INC_PATHS += -I$(SDK_PATH)/components/toolchain INC_PATHS += -I$(SDK_PATH)/components/toolchain/gcc -INC_PATHS += -I$(SDK_PATH)/components/softdevice/s130/headers -INC_PATHS += -I$(SDK_PATH)/components/softdevice/common/softdevice_handler -INC_PATHS += -I$(SDK_PATH)/components/libraries/crc16/ +INC_PATHS += -I$(SDK_PATH)/components/toolchain/cmsis/include +INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/uart +INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/hal INC_PATHS += -I$(SDK_PATH)/components/libraries/util -INC_PATHS += -I$(SDK_PATH)/components/libraries/fifo -INC_PATHS += -I$(SDK_PATH)/components/libraries/scheduler -INC_PATHS += -I$(SDK_PATH)/components/libraries/trace -INC_PATHS += -I$(SDK_PATH)/components/libraries/timer +INC_PATHS += -I$(SDK_PATH)/components/softdevice/s130/headers INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/common -INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/rtc -INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/hal -INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/uart -INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/config -INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/gpiote -INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/ble_flash +INC_PATHS += -I$(SDK_PATH)/components/libraries/log +INC_PATHS += -I$(SDK_PATH)/components/libraries/log/src +INC_PATHS += -I$(SDK_PATH)/components/boards +INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/delay INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/spi_master -INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/pstorage -INC_PATHS += -I$(SDK_PATH)/components/ble/common +INC_PATHS += -I$(SDK_PATH)/components/libraries/fstorage +INC_PATHS += -I$(SDK_PATH)/components/libraries/experimental_section_vars +INC_PATHS += -I$(SDK_PATH)/components/softdevice/common/softdevice_handler +INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/clock +INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/gpiote +INC_PATHS += -I$(SDK_PATH)/components/libraries/timer INC_PATHS += -I$(SDK_PATH)/components/ble/ble_advertising -INC_PATHS += -I$(SDK_PATH)/components/ble/ble_services/ble_bas +INC_PATHS += -I$(SDK_PATH)/components/ble/common INC_PATHS += -I$(SDK_PATH)/components/ble/ble_services/ble_nus -INC_PATHS += -I$(SDK_PATH)/examples/bsp +INC_PATHS += -I$(SDK_PATH)/components/libraries/fifo +INC_PATHS += -I$(SDK_PATH)/components/libraries/scheduler + + + +#INC_PATHS += -I$(SDK_PATH)/components/libraries/uart +#INC_PATHS += -I$(SDK_PATH)/components/libraries/util +#INC_PATHS += -I$(SDK_PATH)/components/softdevice/s130/headers +#INC_PATHS += -I$(SDK_PATH)/components/libraries/log +#INC_PATHS += -I$(SDK_PATH)/components/libraries/log/src +#INC_PATHS += -I$(SDK_PATH)/components/softdevice/common/softdevice_handler +#INC_PATHS += -I$(SDK_PATH)/components/libraries/crc16/ +#INC_PATHS += -I$(SDK_PATH)/components/libraries/util +#INC_PATHS += -I$(SDK_PATH)/components/libraries/fifo +#INC_PATHS += -I$(SDK_PATH)/components/libraries/scheduler +#INC_PATHS += -I$(SDK_PATH)/components/libraries/trace +#INC_PATHS += -I$(SDK_PATH)/components/libraries/timer +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/common +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/rtc +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/hal +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/config +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/gpiote +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/ble_flash +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/spi_master +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/pstorage +#INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/delay +#INC_PATHS += -I$(SDK_PATH)/components/boards +#INC_PATHS += -I$(SDK_PATH)/components/ble/common +#INC_PATHS += -I$(SDK_PATH)/components/ble/ble_advertising +#INC_PATHS += -I$(SDK_PATH)/components/ble/ble_services/ble_bas +#INC_PATHS += -I$(SDK_PATH)/components/ble/ble_services/ble_nus +#INC_PATHS += -I$(SDK_PATH)/examples/bsp +#INC_PATHS += -I$(SDK_PATH)/examples/bsp + + +###################################################### INCLUDE ##################################### + OBJECT_DIRECTORY = _build LISTING_DIRECTORY = $(OBJECT_DIRECTORY) @@ -171,8 +323,50 @@ OUTPUT_BINARY_DIRECTORY = $(OBJECT_DIRECTORY) BUILD_DIRECTORIES := $(sort $(OBJECT_DIRECTORY) $(OUTPUT_BINARY_DIRECTORY) $(LISTING_DIRECTORY) ) +compile_flags: +ifeq ($(PROTOCOL_VERSION), 2) + @echo PROTOCOL_VERSION = 2 + $(eval COMPILEFLAGS += -DPROTOCOL_02v1) +else + @echo PROTOCOL_VERSION = 1 + $(eval COMPILEFLAGS += -DPROTOCOL_01v1) +endif + + +#========== Add the compile flags that are given via cmd-line +NRF51DK: compile_flags +NRF51DK: CFLAGS += $(COMPILEFLAGS) + +badge_03: compile_flags +badge_03: CFLAGS += $(COMPILEFLAGS) +badge_03_noDebug: compile_flags +badge_03_noDebug: CFLAGS += $(COMPILEFLAGS) + +badge_03v2_rigado: compile_flags +badge_03v2_rigado: CFLAGS += $(COMPILEFLAGS) +badge_03v2_rigado_noDebug: compile_flags +badge_03v2_rigado_noDebug: CFLAGS += $(COMPILEFLAGS) +badge_03v2_rigado_tester: compile_flags +badge_03v2_rigado_tester: CFLAGS += $(COMPILEFLAGS) +badge_03v2_dynastream: compile_flags +badge_03v2_dynastream: CFLAGS += $(COMPILEFLAGS) + +badge_03v4: compile_flags +badge_03v4: CFLAGS += $(COMPILEFLAGS) +badge_03v4_noDebug: compile_flags +badge_03v4_noDebug: CFLAGS += $(COMPILEFLAGS) +badge_03v4_tester: compile_flags +badge_03v4_tester: CFLAGS += $(COMPILEFLAGS) + +badge_03v6: compile_flags +badge_03v6: CFLAGS += $(COMPILEFLAGS) +badge_03v6_noDebug: compile_flags +badge_03v6_noDebug: CFLAGS += $(COMPILEFLAGS) +badge_03v6_tester: compile_flags +badge_03v6_tester: CFLAGS += $(COMPILEFLAGS) + #======= Target-dependent compiler flags -NRF51DK: CFLAGS += -DBOARD_PCA10028 +NRF51DK: CFLAGS += -DBOARD_PCA10028 badge_03: CFLAGS += -DBOARD_CUSTOM badge_03: CFLAGS += -DBOARD_BADGE_03 @@ -215,12 +409,15 @@ badge_03v6_noDebug: CFLAGS += -UDEBUG_LOG_ENABLE #======= Other compiler flags CFLAGS += -DSOFTDEVICE_PRESENT CFLAGS += -DNRF51 +# Added Michel (for the nrf_peripherals.h to include the nrf51822_peripherals) +CFLAGS += -DNRF51822 CFLAGS += -DS130 CFLAGS += -DBLE_STACK_SUPPORT_REQD CFLAGS += -DSWI_DISABLE0 CFLAGS += -mcpu=cortex-m0 CFLAGS += -mthumb -mabi=aapcs --std=gnu99 -CFLAGS += -Wall -Werror -O3 +#CFLAGS += -Wall -Werror -O3 +CFLAGS += -Wall -Werror -O1 CFLAGS += -mfloat-abi=soft # keep every function in separate section. This will allow linker to dump unused functions CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing @@ -233,8 +430,13 @@ CFLAGS += -DGIT_COMMIT='"$(GIT_COMMIT)"' CFLAGS += -DGIT_BRANCH='"$(GIT_BRANCH)"' CFLAGS += -DGIT_TAG='"$(GIT_TAG)"' + +#Removed Michael #enable SPI -CFLAGS += -DSPI_MASTER_0_ENABLE +#CFLAGS += -DSPI_MASTER_0_ENABLE + +#Added Michael +LDFLAGS += -u _printf_float # keep every function in separate section. This will allow linker to dump unused functions LDFLAGS += -Xlinker -Map=$(LISTING_DIRECTORY)/$(OUTPUT_FILENAME).map @@ -250,6 +452,7 @@ ASMFLAGS += -x assembler-with-cpp # ! vvv this has been here since badge 03, hasn't been updated, but hasn't seemed to cause any issues? ASMFLAGS += -DBOARD_PCA10028 #default to NRF51DK board badge_03: ASMFLAGS += -UBOARD_PCA10028 #undefine NRF51DK, to define custom board flag +badge_03v6: ASMFLAGS += -UBOARD_PCA10028 #undefine NRF51DK, to define custom board flag badge_03: ASMFLAGS += -DBOARD_CUSTOM badge_03: ASMFLAGS += -DBOARD_BADGE_03 ASMFLAGS += -DSOFTDEVICE_PRESENT @@ -265,17 +468,24 @@ C_OBJECTS = $(addprefix $(OBJECT_DIRECTORY)/, $(C_SOURCE_FILE_NAMES:.c=.o) ) ASM_SOURCE_FILE_NAMES = $(notdir $(ASM_SOURCE_FILES)) ASM_PATHS = $(call remduplicates, $(dir $(ASM_SOURCE_FILES) )) -ASM_OBJECTS = $(addprefix $(OBJECT_DIRECTORY)/, $(ASM_SOURCE_FILE_NAMES:.s=.o) ) +#Changed Michael +#ASM_OBJECTS = $(addprefix $(OBJECT_DIRECTORY)/, $(ASM_SOURCE_FILE_NAMES:.s=.o) ) +ASM_OBJECTS = $(addprefix $(OBJECT_DIRECTORY)/, $(ASM_SOURCE_FILE_NAMES:.S=.o) ) vpath %.c $(C_PATHS) -vpath %.s $(ASM_PATHS) +#Changed Michael +#vpath %.s $(ASM_PATHS) +vpath %.S $(ASM_PATHS) OBJECTS = $(C_OBJECTS) $(ASM_OBJECTS) + + nrf51422_xxac_s130: OUTPUT_FILENAME := nrf51422_xxac_s130 #NRF51 DK uses 51422 xxac variant - 256KB flash, 32KB RAM, ANT/BLE SoC #nrf51422_xxac_s130: LINKER_SCRIPT=nrf51_xxac_S130_linker.ld -nrf51422_xxac_s130: LINKER_SCRIPT=$(SDK_PATH)/components/softdevice/s130/toolchain/armgcc/armgcc_s130_nrf51422_xxac.ld +#nrf51422_xxac_s130: LINKER_SCRIPT=$(SDK_PATH)/components/softdevice/s130/toolchain/armgcc/armgcc_s130_nrf51422_xxac.ld +nrf51422_xxac_s130: LINKER_SCRIPT=armgcc_s130_nrf51422_xxac.ld nrf51422_xxac_s130: $(BUILD_DIRECTORIES) $(OBJECTS) @echo Linking target: $(OUTPUT_FILENAME).out $(NO_ECHO)$(CC) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out @@ -292,7 +502,9 @@ $(OBJECT_DIRECTORY)/%.o: %.c $(NO_ECHO)$(CC) $(CFLAGS) $(INC_PATHS) -c -o $@ $< # Assemble files -$(OBJECT_DIRECTORY)/%.o: %.s +#Changed Michael +#$(OBJECT_DIRECTORY)/%.o: %.s +$(OBJECT_DIRECTORY)/%.o: %.S @echo Compiling file: $(notdir $<) $(NO_ECHO)$(CC) $(ASMFLAGS) $(INC_PATHS) -c -o $@ $< @@ -382,9 +594,12 @@ flashAPP.jlink: flashS130: flashS130.jlink $(JLINK) $(OBJECT_DIRECTORY)/flashS130.jlink +#Changed Michaelnew SDK-version! #create SoftDevice flashing script for JLinkExe flashS130.jlink: - printf "loadbin $(SDK_PATH)/components/softdevice/s130/hex/s130_softdevice.hex 0\nr\ng\nexit\n" > $(OBJECT_DIRECTORY)/flashS130.jlink + printf "loadbin $(SDK_PATH)/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex 0\nr\ng\nexit\n" > $(OBJECT_DIRECTORY)/flashS130.jlink + #printf "loadbin $(SDK_PATH)/components/softdevice/s130/hex/s130_softdevice.hex 0\nr\ng\nexit\n" > $(OBJECT_DIRECTORY)/flashS130.jlink + #erase all flash diff --git a/firmware/nRF_badge/data_collector/README.md b/firmware/nRF_badge/data_collector/README.md new file mode 100644 index 0000000..65dd087 --- /dev/null +++ b/firmware/nRF_badge/data_collector/README.md @@ -0,0 +1,37 @@ +Protocol: +- The Protocol (current version 02v1) is based on Tinybuf messages for the de-/serialization of structured data. 02v1 uses Big endian for the integer/floating point number representation (it is the badge_protocol.py of the BadgeFramework generated from badge_protocol.tb in the same directory). 01v1 uses Little endian (it is the badge_protocol_legacy.py of the BadgeFramework). + +- The definition of the 02v1 protocol messages is in incl/protocol_messages_02v1.tb. The most important messages are the Request message and the Repsonse message: Both contains a oneof-field of the different messages: sent to the badge (request) and sent from the badge (response) + +Description: + +| Request Response | Description | +|--|--| +| Status Request | Request to retrieve the current status of the badge, setting the time, and optionally setting the ID and group number. | +| Status Response | Response returns the current status of the badge: clock_status, microphone_status, scan_status, accelerometer_status, accelerometer_interrupt_status, battery status. Additionally, a local timestamp of the badge and the current battery-data is returned. | +| Start Request | Request to start a certain data source for recording and storing. Each data source has its own parameters that are specified via the request. Additionally, the time is synchronized via these requests. The timeout-parameter specifies the timeout in minutes: If this time expires without contact to a hub the data recording stops. | +| Start Response | Response containing the local timestamp of the badge. | +| Stop Request | Request to stop recording + storing of a certain data source. | +| --- | Stop Request has no response! | +| Data Request | Request to retrieve the stored data since a certain timestamp in the past. | +| Data Response | Response containing the stored data chunks. Additionally, a flag "last_response" is set or not. If it is set, it was the last chunk. | +| Start Stream Request | Starts the streaming of a data source with certain parameters. Can be enabled while storing is active. Watch out that the selected parameters influence the recording for storage. | +| Stream Response | This is a message that contains all data points of the streamed data sources. It is transmitted continously. (If a data source is not activated, the repeated field is empty) +| Stop Stream Request | Stops the streaming of a certain data source. | +| --- | Stop Stream Request has no response! | +| Identify Request| Identifies the badge by blinking its LED for a certain time. | +| --- | Identify Request has no response! | +| Test Request | Starts the peripheral testing of the badge. | +| Test Response | Repsonse indicating whether the test failed or not. | +| Restart Request | Request to restart the badge. | +| --- | Restart Request has no response! | + + + + + +Unit Test: +- To invoke a Unit Test (e.g. TEST_NAME), enter directory /unit_test and call make badge_03v6 TEST_NAME run_TEST_NAME (optionally: LCOV=TRUE). +- You can run all unit tests by: make badge_03v6 all run_all. +- If LCOV=TRUE, the source code coverage analysis is done: It is written into the _build/LCOV-directory for each test. + diff --git a/firmware/nRF_badge/data_collector/armgcc_s130_nrf51422_xxac.ld b/firmware/nRF_badge/data_collector/armgcc_s130_nrf51422_xxac.ld new file mode 100644 index 0000000..0b826af --- /dev/null +++ b/firmware/nRF_badge/data_collector/armgcc_s130_nrf51422_xxac.ld @@ -0,0 +1,28 @@ +/* Linker script to configure memory regions. */ + +SEARCH_DIR(.) +GROUP(-lgcc -lc -lnosys) + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x1b000, LENGTH = 0x25000 + RAM (rwx) : ORIGIN = 0x20001fe8, LENGTH = 0x6018 +} + +SECTIONS +{ + .fs_data : + { + PROVIDE(__start_fs_data = .); + KEEP(*(.fs_data)) + PROVIDE(__stop_fs_data = .); + } > RAM + .pwr_mgmt_data : + { + PROVIDE(__start_pwr_mgmt_data = .); + KEEP(*(.pwr_mgmt_data)) + PROVIDE(__stop_pwr_mgmt_data = .); + } > RAM +} INSERT AFTER .data; + +INCLUDE "nrf5x_common.ld" diff --git a/firmware/nRF_badge/data_collector/config/nrf_drv_config.h b/firmware/nRF_badge/data_collector/config/nrf_drv_config.h deleted file mode 100644 index b4b1a60..0000000 --- a/firmware/nRF_badge/data_collector/config/nrf_drv_config.h +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * - */ - -#ifndef NRF_DRV_CONFIG_H -#define NRF_DRV_CONFIG_H - -/* CLOCK */ -#define CLOCK_CONFIG_XTAL_FREQ NRF_CLOCK_XTALFREQ_16MHz -#define CLOCK_CONFIG_LF_SRC NRF_CLOCK_LF_SRC_Xtal -#define CLOCK_CONFIG_LF_RC_CAL_INTERVAL RC_2000MS_CALIBRATION_INTERVAL -#define CLOCK_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW - -/* GPIOTE */ -#define GPIOTE_ENABLED 1 - -#if (GPIOTE_ENABLED == 1) -#define GPIOTE_CONFIG_USE_SWI_EGU false -#define GPIOTE_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_HIGH -#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 5 -#endif - -/* TIMER */ -#define TIMER0_ENABLED 0 - -#if (TIMER0_ENABLED == 1) -#define TIMER0_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz -#define TIMER0_CONFIG_MODE TIMER_MODE_MODE_Timer -#define TIMER0_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_32Bit -#define TIMER0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW - -#define TIMER0_INSTANCE_INDEX 0 -#endif - -#define TIMER1_ENABLED 0 - -#if (TIMER1_ENABLED == 1) -#define TIMER1_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz -#define TIMER1_CONFIG_MODE TIMER_MODE_MODE_Timer -#define TIMER1_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_16Bit -#define TIMER1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW - -#define TIMER1_INSTANCE_INDEX (TIMER0_ENABLED) -#endif - -#define TIMER2_ENABLED 0 - -#if (TIMER2_ENABLED == 1) -#define TIMER2_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz -#define TIMER2_CONFIG_MODE TIMER_MODE_MODE_Timer -#define TIMER2_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_16Bit -#define TIMER2_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW - -#define TIMER2_INSTANCE_INDEX (TIMER1_ENABLED+TIMER0_ENABLED) -#endif - -#define TIMER_COUNT (TIMER0_ENABLED + TIMER1_ENABLED + TIMER2_ENABLED) - -/* RTC */ -#define RTC0_ENABLED 0 - -#if (RTC0_ENABLED == 1) -#define RTC0_CONFIG_FREQUENCY 32678 -#define RTC0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW -#define RTC0_CONFIG_RELIABLE false - -#define RTC0_INSTANCE_INDEX 0 -#endif - -#define RTC1_ENABLED 1 - -#if (RTC1_ENABLED == 1) -#define RTC1_CONFIG_FREQUENCY 32768 -#define RTC1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW -#define RTC1_CONFIG_RELIABLE false - -#define RTC1_INSTANCE_INDEX (RTC0_ENABLED) -#endif - -#define RTC_COUNT (RTC0_ENABLED+RTC1_ENABLED) - -#define NRF_MAXIMUM_LATENCY_US 2000 - -/* RNG */ -#define RNG_ENABLED 0 - -#if (RNG_ENABLED == 1) -#define RNG_CONFIG_ERROR_CORRECTION true -#define RNG_CONFIG_POOL_SIZE 8 -#define RNG_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW -#endif - - -/* QDEC */ -#define QDEC_ENABLED 0 - -#if (QDEC_ENABLED == 1) -#define QDEC_CONFIG_REPORTPER NRF_QDEC_REPORTPER_10 -#define QDEC_CONFIG_SAMPLEPER NRF_QDEC_SAMPLEPER_16384us -#define QDEC_CONFIG_PIO_A 1 -#define QDEC_CONFIG_PIO_B 2 -#define QDEC_CONFIG_PIO_LED 3 -#define QDEC_CONFIG_LEDPRE 511 -#define QDEC_CONFIG_LEDPOL NRF_QDEC_LEPOL_ACTIVE_HIGH -#define QDEC_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW -#define QDEC_CONFIG_DBFEN false -#define QDEC_CONFIG_SAMPLE_INTEN false -#endif - -/* LPCOMP */ -#define LPCOMP_ENABLED 0 - -#if (LPCOMP_ENABLED == 1) -#define LPCOMP_CONFIG_REFERENCE NRF_LPCOMP_REF_SUPPLY_FOUR_EIGHT -#define LPCOMP_CONFIG_DETECTION NRF_LPCOMP_DETECT_DOWN -#define LPCOMP_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW -#define LPCOMP_CONFIG_INPUT NRF_LPCOMP_INPUT_0 -#endif - -/* WDT */ -#define WDT_ENABLED 0 - -#if (WDT_ENABLED == 1) -#define WDT_CONFIG_BEHAVIOUR NRF_WDT_BEHAVIOUR_RUN_SLEEP -#define WDT_CONFIG_RELOAD_VALUE 2000 -#define WDT_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_HIGH -#endif - -#endif // NRF_DRV_CONFIG_H diff --git a/firmware/nRF_badge/data_collector/config/pstorage_platform.h b/firmware/nRF_badge/data_collector/config/pstorage_platform.h deleted file mode 100644 index 21c4e3e..0000000 --- a/firmware/nRF_badge/data_collector/config/pstorage_platform.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * - */ - - /** @cond To make doxygen skip this file */ - -/** @file - * This header contains defines with respect persistent storage that are specific to - * persistent storage implementation and application use case. - */ -#ifndef PSTORAGE_PL_H__ -#define PSTORAGE_PL_H__ - -#include -#include "nrf.h" - -static __INLINE uint16_t pstorage_flash_page_size() -{ - return (uint16_t)NRF_FICR->CODEPAGESIZE; -} - -#define PSTORAGE_FLASH_PAGE_SIZE pstorage_flash_page_size() /**< Size of one flash page. */ -#define PSTORAGE_FLASH_EMPTY_MASK 0xFFFFFFFF /**< Bit mask that defines an empty address in flash. */ - -#define PSTORAGE_FLASH_PAGE_END \ - ((NRF_UICR->BOOTLOADERADDR != PSTORAGE_FLASH_EMPTY_MASK) \ - ? (NRF_UICR->BOOTLOADERADDR / PSTORAGE_FLASH_PAGE_SIZE) \ - : NRF_FICR->CODESIZE) - - -#define PSTORAGE_MAX_APPLICATIONS 2 /**< Maximum number of applications that can be registered with the module, configurable based on system requirements. */ -#define PSTORAGE_MIN_BLOCK_SIZE 0x0010 /**< Minimum size of block that can be registered with the module. Should be configured based on system requirements, recommendation is not have this value to be at least size of word. */ - -#define PSTORAGE_DATA_START_ADDR ((PSTORAGE_FLASH_PAGE_END - PSTORAGE_MAX_APPLICATIONS - 1) \ - * PSTORAGE_FLASH_PAGE_SIZE) /**< Start address for persistent data, configurable according to system requirements. */ -#define PSTORAGE_DATA_END_ADDR ((PSTORAGE_FLASH_PAGE_END - 1) * PSTORAGE_FLASH_PAGE_SIZE) /**< End address for persistent data, configurable according to system requirements. */ -#define PSTORAGE_SWAP_ADDR PSTORAGE_DATA_END_ADDR /**< Top-most page is used as swap area for clear and update. */ - -#define PSTORAGE_MAX_BLOCK_SIZE PSTORAGE_FLASH_PAGE_SIZE /**< Maximum size of block that can be registered with the module. Should be configured based on system requirements. And should be greater than or equal to the minimum size. */ -#define PSTORAGE_CMD_QUEUE_SIZE 30 /**< Maximum number of flash access commands that can be maintained by the module for all applications. Configurable. */ - - -/** Abstracts persistently memory block identifier. */ -typedef uint32_t pstorage_block_t; - -typedef struct -{ - uint32_t module_id; /**< Module ID.*/ - pstorage_block_t block_id; /**< Block ID.*/ -} pstorage_handle_t; - -typedef uint16_t pstorage_size_t; /** Size of length and offset fields. */ - -/**@brief Handles Flash Access Result Events. To be called in the system event dispatcher of the application. */ -void pstorage_sys_event_handler (uint32_t sys_evt); - -#endif // PSTORAGE_PL_H__ - -/** @} */ -/** @endcond */ diff --git a/firmware/nRF_badge/data_collector/config/sdk_config.h b/firmware/nRF_badge/data_collector/config/sdk_config.h new file mode 100644 index 0000000..d2a5702 --- /dev/null +++ b/firmware/nRF_badge/data_collector/config/sdk_config.h @@ -0,0 +1,1815 @@ + + +#ifndef SDK_CONFIG_H +#define SDK_CONFIG_H +// <<< Use Configuration Wizard in Context Menu >>>\n +#ifdef USE_APP_CONFIG +#include "app_config.h" +#endif +// nRF_Drivers + + + +//#include is included in every peripheral module, so don't need to include it here! +//#include + + +// APP FIFO ENABLED +#ifndef APP_FIFO_ENABLED +#define APP_FIFO_ENABLED 1 +#endif + + + +// nRF_BLE + +//========================================================== +// BLE_ADVERTISING_ENABLED - ble_advertising - Advertising module + + +#ifndef BLE_ADVERTISING_ENABLED +#define BLE_ADVERTISING_ENABLED 1 +#endif + +// +//========================================================== + + + +// nRF_BLE_Services + +//========================================================== +// BLE_NUS_ENABLED - ble_nus - Nordic UART Service + + +#ifndef BLE_NUS_ENABLED +#define BLE_NUS_ENABLED 1 +#endif + +// +//========================================================== + + +//========================================================== +// ADC_ENABLED - nrf_drv_adc - Driver for ADC peripheral (nRF51) +//========================================================== +#ifndef ADC_ENABLED +#define ADC_ENABLED 1 +#endif +#if ADC_ENABLED +// ADC_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef ADC_CONFIG_IRQ_PRIORITY +#define ADC_CONFIG_IRQ_PRIORITY 3 +#endif + +// ADC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef ADC_CONFIG_LOG_ENABLED +#define ADC_CONFIG_LOG_ENABLED 0 +#endif +#if ADC_CONFIG_LOG_ENABLED +// ADC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef ADC_CONFIG_LOG_LEVEL +#define ADC_CONFIG_LOG_LEVEL 3 +#endif + +// ADC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef ADC_CONFIG_INFO_COLOR +#define ADC_CONFIG_INFO_COLOR 0 +#endif + +// ADC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef ADC_CONFIG_DEBUG_COLOR +#define ADC_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //ADC_CONFIG_LOG_ENABLED +// + +#endif //ADC_ENABLED +// + + +//========================================================== +// APP_TIMER_ENABLED - app_timer - Application timer functionality +//========================================================== +#ifndef APP_TIMER_ENABLED +#define APP_TIMER_ENABLED 1 +#endif +#if APP_TIMER_ENABLED +// APP_TIMER_WITH_PROFILER - Enable app_timer profiling + + +#ifndef APP_TIMER_WITH_PROFILER +#define APP_TIMER_WITH_PROFILER 0 +#endif + +// APP_TIMER_KEEPS_RTC_ACTIVE - Enable RTC always on + + +// If option is enabled RTC is kept running even if there is no active timers. +// This option can be used when app_timer is used for timestamping. + +#ifndef APP_TIMER_KEEPS_RTC_ACTIVE +#define APP_TIMER_KEEPS_RTC_ACTIVE 0 +#endif + +#endif //APP_TIMER_ENABLED +// + + +//========================================================== +// APP_SCHEDULER_ENABLED - app_scheduler - Application scheduler functionality +//========================================================== +#ifndef APP_SCHEDULER_ENABLED +#define APP_SCHEDULER_ENABLED 1 +#endif //APP_SCHEDULER_ENABLED +// + + +//========================================================== +// CLOCK_ENABLED - nrf_drv_clock - CLOCK peripheral driver +//========================================================== +#ifndef CLOCK_ENABLED +#define CLOCK_ENABLED 1 +#endif +#if CLOCK_ENABLED +// CLOCK_CONFIG_XTAL_FREQ - HF XTAL Frequency + +// <0=> Default (64 MHz) +// <255=> Default (16 MHz) +// <0=> 32 MHz + +#ifndef CLOCK_CONFIG_XTAL_FREQ +#define CLOCK_CONFIG_XTAL_FREQ 255 +#endif + +// CLOCK_CONFIG_LF_SRC - LF Clock Source + +// <0=> RC +// <1=> XTAL +// <2=> Synth + +#ifndef CLOCK_CONFIG_LF_SRC +#define CLOCK_CONFIG_LF_SRC 1 +#endif + +// CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef CLOCK_CONFIG_IRQ_PRIORITY +#define CLOCK_CONFIG_IRQ_PRIORITY 3 +#endif + +// CLOCK_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef CLOCK_CONFIG_LOG_ENABLED +#define CLOCK_CONFIG_LOG_ENABLED 0 +#endif +#if CLOCK_CONFIG_LOG_ENABLED +// CLOCK_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef CLOCK_CONFIG_LOG_LEVEL +#define CLOCK_CONFIG_LOG_LEVEL 3 +#endif + +// CLOCK_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_INFO_COLOR +#define CLOCK_CONFIG_INFO_COLOR 0 +#endif + +// CLOCK_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_DEBUG_COLOR +#define CLOCK_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //CLOCK_CONFIG_LOG_ENABLED +// + +#endif //CLOCK_ENABLED +// + + + +// FSTORAGE_ENABLED - fstorage - Flash storage module +//========================================================== +#ifndef FSTORAGE_ENABLED +#define FSTORAGE_ENABLED 1 +#endif +#if FSTORAGE_ENABLED +// FS_QUEUE_SIZE - Configures the size of the internal queue. +// Increase this if there are many users, or if it is likely that many +// operation will be queued at once without waiting for the previous operations +// to complete. In general, increase the queue size if you frequently receive +// @ref FS_ERR_QUEUE_FULL errors when calling @ref fs_store or @ref fs_erase. + +#ifndef FS_QUEUE_SIZE +#define FS_QUEUE_SIZE 4 +#endif + +// FS_OP_MAX_RETRIES - Number attempts to execute an operation if the SoftDevice fails. +// Increase this value if events return the @ref FS_ERR_OPERATION_TIMEOUT +// error often. The SoftDevice may fail to schedule flash access due to high BLE activity. + +#ifndef FS_OP_MAX_RETRIES +#define FS_OP_MAX_RETRIES 5 +#endif + +// FS_MAX_WRITE_SIZE_WORDS - Maximum number of words to be written to flash in a single operation. +// Tweaking this value can increase the chances of the SoftDevice being +// able to fit flash operations in between radio activity. This value is bound by the +// maximum number of words which the SoftDevice can write to flash in a single call to +// @ref sd_flash_write, which is 256 words for nRF51 ICs and 1024 words for nRF52 ICs. + +#ifndef FS_MAX_WRITE_SIZE_WORDS +#define FS_MAX_WRITE_SIZE_WORDS 256 +#endif + + + +#endif //FSTORAGE_ENABLED +// + +//========================================================== +// GPIOTE_ENABLED - nrf_drv_gpiote - GPIOTE peripheral driver +//========================================================== +#ifndef GPIOTE_ENABLED +#define GPIOTE_ENABLED 1 +#endif +#if GPIOTE_ENABLED +// GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS - Number of lower power input pins +#ifndef GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS +#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 5 //1 +#endif + +// GPIOTE_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef GPIOTE_CONFIG_IRQ_PRIORITY +#define GPIOTE_CONFIG_IRQ_PRIORITY 1 // 3 +#endif + +// GPIOTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef GPIOTE_CONFIG_LOG_ENABLED +#define GPIOTE_CONFIG_LOG_ENABLED 0 +#endif +#if GPIOTE_CONFIG_LOG_ENABLED +// GPIOTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef GPIOTE_CONFIG_LOG_LEVEL +#define GPIOTE_CONFIG_LOG_LEVEL 3 +#endif + +// GPIOTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef GPIOTE_CONFIG_INFO_COLOR +#define GPIOTE_CONFIG_INFO_COLOR 0 +#endif + +// GPIOTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef GPIOTE_CONFIG_DEBUG_COLOR +#define GPIOTE_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //GPIOTE_CONFIG_LOG_ENABLED +// + +#endif //GPIOTE_ENABLED +// + +//========================================================== +// LPCOMP_ENABLED - nrf_drv_lpcomp - LPCOMP peripheral driver +//========================================================== +#ifndef LPCOMP_ENABLED +#define LPCOMP_ENABLED 0 +#endif +#if LPCOMP_ENABLED +// LPCOMP_CONFIG_REFERENCE - Reference voltage + +// <0=> Supply 1/8 +// <1=> Supply 2/8 +// <2=> Supply 3/8 +// <3=> Supply 4/8 +// <4=> Supply 5/8 +// <5=> Supply 6/8 +// <6=> Supply 7/8 +// <8=> Supply 1/16 (nRF52) +// <9=> Supply 3/16 (nRF52) +// <10=> Supply 5/16 (nRF52) +// <11=> Supply 7/16 (nRF52) +// <12=> Supply 9/16 (nRF52) +// <13=> Supply 11/16 (nRF52) +// <14=> Supply 13/16 (nRF52) +// <15=> Supply 15/16 (nRF52) +// <7=> External Ref 0 +// <65543=> External Ref 1 + +#ifndef LPCOMP_CONFIG_REFERENCE +#define LPCOMP_CONFIG_REFERENCE 3 +#endif + +// LPCOMP_CONFIG_DETECTION - Detection + +// <0=> Crossing +// <1=> Up +// <2=> Down + +#ifndef LPCOMP_CONFIG_DETECTION +#define LPCOMP_CONFIG_DETECTION 2 +#endif + +// LPCOMP_CONFIG_INPUT - Analog input + +// <0=> 0 +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef LPCOMP_CONFIG_INPUT +#define LPCOMP_CONFIG_INPUT 0 +#endif + +// LPCOMP_CONFIG_HYST - Hysteresis + + +#ifndef LPCOMP_CONFIG_HYST +#define LPCOMP_CONFIG_HYST 0 +#endif + +// LPCOMP_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef LPCOMP_CONFIG_IRQ_PRIORITY +#define LPCOMP_CONFIG_IRQ_PRIORITY 3 +#endif + +// LPCOMP_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef LPCOMP_CONFIG_LOG_ENABLED +#define LPCOMP_CONFIG_LOG_ENABLED 0 +#endif +#if LPCOMP_CONFIG_LOG_ENABLED +// LPCOMP_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef LPCOMP_CONFIG_LOG_LEVEL +#define LPCOMP_CONFIG_LOG_LEVEL 3 +#endif + +// LPCOMP_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef LPCOMP_CONFIG_INFO_COLOR +#define LPCOMP_CONFIG_INFO_COLOR 0 +#endif + +// LPCOMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef LPCOMP_CONFIG_DEBUG_COLOR +#define LPCOMP_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //LPCOMP_CONFIG_LOG_ENABLED +// + +#endif //LPCOMP_ENABLED +// + +//========================================================== +// PERIPHERAL_RESOURCE_SHARING_ENABLED - nrf_drv_common - Peripheral drivers common module +//========================================================== +#ifndef PERIPHERAL_RESOURCE_SHARING_ENABLED +#define PERIPHERAL_RESOURCE_SHARING_ENABLED 0 +#endif +#if PERIPHERAL_RESOURCE_SHARING_ENABLED +// COMMON_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef COMMON_CONFIG_LOG_ENABLED +#define COMMON_CONFIG_LOG_ENABLED 0 +#endif +#if COMMON_CONFIG_LOG_ENABLED +// COMMON_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef COMMON_CONFIG_LOG_LEVEL +#define COMMON_CONFIG_LOG_LEVEL 3 +#endif + +// COMMON_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMMON_CONFIG_INFO_COLOR +#define COMMON_CONFIG_INFO_COLOR 0 +#endif + +// COMMON_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMMON_CONFIG_DEBUG_COLOR +#define COMMON_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //COMMON_CONFIG_LOG_ENABLED +// + +#endif //PERIPHERAL_RESOURCE_SHARING_ENABLED +// + + +//========================================================== +// PPI_ENABLED - nrf_drv_ppi - PPI peripheral driver +//========================================================== +#ifndef PPI_ENABLED +#define PPI_ENABLED 1 +#endif +#if PPI_ENABLED +// PPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef PPI_CONFIG_LOG_ENABLED +#define PPI_CONFIG_LOG_ENABLED 0 +#endif +#if PPI_CONFIG_LOG_ENABLED +// PPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PPI_CONFIG_LOG_LEVEL +#define PPI_CONFIG_LOG_LEVEL 3 +#endif + +// PPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PPI_CONFIG_INFO_COLOR +#define PPI_CONFIG_INFO_COLOR 0 +#endif + +// PPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PPI_CONFIG_DEBUG_COLOR +#define PPI_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //PPI_CONFIG_LOG_ENABLED +// + +#endif //PPI_ENABLED +// + + +//========================================================== +// QDEC_ENABLED - nrf_drv_qdec - QDEC peripheral driver +//========================================================== +#ifndef QDEC_ENABLED +#define QDEC_ENABLED 0 +#endif +#if QDEC_ENABLED +// QDEC_CONFIG_REPORTPER - Report period + +// <0=> 10 Samples +// <1=> 40 Samples +// <2=> 80 Samples +// <3=> 120 Samples +// <4=> 160 Samples +// <5=> 200 Samples +// <6=> 240 Samples +// <7=> 280 Samples + +#ifndef QDEC_CONFIG_REPORTPER +#define QDEC_CONFIG_REPORTPER 0 +#endif + +// QDEC_CONFIG_SAMPLEPER - Sample period + +// <0=> 128 us +// <1=> 256 us +// <2=> 512 us +// <3=> 1024 us +// <4=> 2048 us +// <5=> 4096 us +// <6=> 8192 us +// <7=> 16384 us + +#ifndef QDEC_CONFIG_SAMPLEPER +#define QDEC_CONFIG_SAMPLEPER 7 +#endif + +// QDEC_CONFIG_PIO_A - A pin <0-31> + + +#ifndef QDEC_CONFIG_PIO_A +#define QDEC_CONFIG_PIO_A 31 +#endif + +// QDEC_CONFIG_PIO_B - B pin <0-31> + + +#ifndef QDEC_CONFIG_PIO_B +#define QDEC_CONFIG_PIO_B 31 +#endif + +// QDEC_CONFIG_PIO_LED - LED pin <0-31> + + +#ifndef QDEC_CONFIG_PIO_LED +#define QDEC_CONFIG_PIO_LED 31 +#endif + +// QDEC_CONFIG_LEDPRE - LED pre +#ifndef QDEC_CONFIG_LEDPRE +#define QDEC_CONFIG_LEDPRE 511 +#endif + +// QDEC_CONFIG_LEDPOL - LED polarity + +// <0=> Active low +// <1=> Active high + +#ifndef QDEC_CONFIG_LEDPOL +#define QDEC_CONFIG_LEDPOL 1 +#endif + +// QDEC_CONFIG_DBFEN - Debouncing enable + + +#ifndef QDEC_CONFIG_DBFEN +#define QDEC_CONFIG_DBFEN 0 +#endif + +// QDEC_CONFIG_SAMPLE_INTEN - Sample ready interrupt enable + + +#ifndef QDEC_CONFIG_SAMPLE_INTEN +#define QDEC_CONFIG_SAMPLE_INTEN 0 +#endif + +// QDEC_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef QDEC_CONFIG_IRQ_PRIORITY +#define QDEC_CONFIG_IRQ_PRIORITY 3 +#endif + +// QDEC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef QDEC_CONFIG_LOG_ENABLED +#define QDEC_CONFIG_LOG_ENABLED 0 +#endif +#if QDEC_CONFIG_LOG_ENABLED +// QDEC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef QDEC_CONFIG_LOG_LEVEL +#define QDEC_CONFIG_LOG_LEVEL 3 +#endif + +// QDEC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef QDEC_CONFIG_INFO_COLOR +#define QDEC_CONFIG_INFO_COLOR 0 +#endif + +// QDEC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef QDEC_CONFIG_DEBUG_COLOR +#define QDEC_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //QDEC_CONFIG_LOG_ENABLED +// + +#endif //QDEC_ENABLED +// + + +//========================================================== +// RNG_ENABLED - nrf_drv_rng - RNG peripheral driver +//========================================================== +#ifndef RNG_ENABLED +#define RNG_ENABLED 0 +#endif +#if RNG_ENABLED +// RNG_CONFIG_ERROR_CORRECTION - Error correction + + +#ifndef RNG_CONFIG_ERROR_CORRECTION +#define RNG_CONFIG_ERROR_CORRECTION 1 +#endif + +// RNG_CONFIG_POOL_SIZE - Pool size +#ifndef RNG_CONFIG_POOL_SIZE +#define RNG_CONFIG_POOL_SIZE 8 +#endif + +// RNG_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef RNG_CONFIG_IRQ_PRIORITY +#define RNG_CONFIG_IRQ_PRIORITY 3 +#endif + +// RNG_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef RNG_CONFIG_LOG_ENABLED +#define RNG_CONFIG_LOG_ENABLED 0 +#endif +#if RNG_CONFIG_LOG_ENABLED +// RNG_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef RNG_CONFIG_LOG_LEVEL +#define RNG_CONFIG_LOG_LEVEL 3 +#endif + +// RNG_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_INFO_COLOR +#define RNG_CONFIG_INFO_COLOR 0 +#endif + +// RNG_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_DEBUG_COLOR +#define RNG_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //RNG_CONFIG_LOG_ENABLED +// + +#endif //RNG_ENABLED +// + + +//========================================================== +// RTC_ENABLED - nrf_drv_rtc - RTC peripheral driver +//========================================================== +#ifndef RTC_ENABLED +#define RTC_ENABLED 1 +#endif +#if RTC_ENABLED +// RTC_DEFAULT_CONFIG_FREQUENCY - Frequency <16-32768> + + +#ifndef RTC_DEFAULT_CONFIG_FREQUENCY +#define RTC_DEFAULT_CONFIG_FREQUENCY 32768 // 10 +#endif + +// RTC_DEFAULT_CONFIG_RELIABLE - Ensures safe compare event triggering + + +#ifndef RTC_DEFAULT_CONFIG_RELIABLE +#define RTC_DEFAULT_CONFIG_RELIABLE 0 +#endif + +// RTC_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef RTC_DEFAULT_CONFIG_IRQ_PRIORITY +#define RTC_DEFAULT_CONFIG_IRQ_PRIORITY 3 +#endif + +// RTC0_ENABLED - Enable RTC0 instance + + +#ifndef RTC0_ENABLED +#define RTC0_ENABLED 0 +#endif + +// RTC1_ENABLED - Enable RTC1 instance + + +#ifndef RTC1_ENABLED +#define RTC1_ENABLED 1 +#endif + +// RTC2_ENABLED - Enable RTC2 instance + + +#ifndef RTC2_ENABLED +#define RTC2_ENABLED 0 +#endif + +// NRF_MAXIMUM_LATENCY_US - Maximum possible time[us] in highest priority interrupt +#ifndef NRF_MAXIMUM_LATENCY_US +#define NRF_MAXIMUM_LATENCY_US 2000 +#endif + +// RTC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef RTC_CONFIG_LOG_ENABLED +#define RTC_CONFIG_LOG_ENABLED 0 +#endif +#if RTC_CONFIG_LOG_ENABLED +// RTC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef RTC_CONFIG_LOG_LEVEL +#define RTC_CONFIG_LOG_LEVEL 3 +#endif + +// RTC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RTC_CONFIG_INFO_COLOR +#define RTC_CONFIG_INFO_COLOR 0 +#endif + +// RTC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RTC_CONFIG_DEBUG_COLOR +#define RTC_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //RTC_CONFIG_LOG_ENABLED +// + +#endif //RTC_ENABLED +// + + + +// SPI_ENABLED - nrf_drv_spi - SPI/SPIM peripheral driver +//========================================================== +#ifndef SPI_ENABLED +#define SPI_ENABLED 1 +#endif +#if SPI_ENABLED +// SPI_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef SPI_DEFAULT_CONFIG_IRQ_PRIORITY +#define SPI_DEFAULT_CONFIG_IRQ_PRIORITY 3 +#endif + +// SPI0_ENABLED - Enable SPI0 instance +//========================================================== +#ifndef SPI0_ENABLED +#define SPI0_ENABLED 1 +#endif +#if SPI0_ENABLED +// SPI0_USE_EASY_DMA - Use EasyDMA + + +#ifndef SPI0_USE_EASY_DMA +#define SPI0_USE_EASY_DMA 0 +#endif + +// SPI0_DEFAULT_FREQUENCY - SPI frequency + +// <33554432=> 125 kHz +// <67108864=> 250 kHz +// <134217728=> 500 kHz +// <268435456=> 1 MHz +// <536870912=> 2 MHz +// <1073741824=> 4 MHz +// <2147483648=> 8 MHz + +#ifndef SPI0_DEFAULT_FREQUENCY +#define SPI0_DEFAULT_FREQUENCY 2147483648 +#endif + +#endif //SPI0_ENABLED +// + +// SPI1_ENABLED - Enable SPI1 instance +//========================================================== +#ifndef SPI1_ENABLED +#define SPI1_ENABLED 0 +#endif +#if SPI1_ENABLED +// SPI1_USE_EASY_DMA - Use EasyDMA + + +#ifndef SPI1_USE_EASY_DMA +#define SPI1_USE_EASY_DMA 0 +#endif + +// SPI1_DEFAULT_FREQUENCY - SPI frequency + +// <33554432=> 125 kHz +// <67108864=> 250 kHz +// <134217728=> 500 kHz +// <268435456=> 1 MHz +// <536870912=> 2 MHz +// <1073741824=> 4 MHz +// <2147483648=> 8 MHz + +#ifndef SPI1_DEFAULT_FREQUENCY +#define SPI1_DEFAULT_FREQUENCY 1073741824 +#endif + +#endif //SPI1_ENABLED +// + +// SPI2_ENABLED - Enable SPI2 instance +//========================================================== +#ifndef SPI2_ENABLED +#define SPI2_ENABLED 0 +#endif +#if SPI2_ENABLED +// SPI2_USE_EASY_DMA - Use EasyDMA + + +#ifndef SPI2_USE_EASY_DMA +#define SPI2_USE_EASY_DMA 0 +#endif + +// SPI2_DEFAULT_FREQUENCY - Use EasyDMA + + +#ifndef SPI2_DEFAULT_FREQUENCY +#define SPI2_DEFAULT_FREQUENCY 0 +#endif + +#endif //SPI2_ENABLED +// + +// SPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SPI_CONFIG_LOG_ENABLED +#define SPI_CONFIG_LOG_ENABLED 0 +#endif +#if SPI_CONFIG_LOG_ENABLED +// SPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SPI_CONFIG_LOG_LEVEL +#define SPI_CONFIG_LOG_LEVEL 3 +#endif + +// SPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPI_CONFIG_INFO_COLOR +#define SPI_CONFIG_INFO_COLOR 0 +#endif + +// SPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPI_CONFIG_DEBUG_COLOR +#define SPI_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //SPI_CONFIG_LOG_ENABLED +// + +#endif //SPI_ENABLED +// + +//========================================================== +// TIMER_ENABLED - nrf_drv_timer - TIMER periperal driver +//========================================================== +#ifndef TIMER_ENABLED +#define TIMER_ENABLED 0 +#endif +#if TIMER_ENABLED +// TIMER_DEFAULT_CONFIG_FREQUENCY - Timer frequency if in Timer mode + +// <0=> 16 MHz +// <1=> 8 MHz +// <2=> 4 MHz +// <3=> 2 MHz +// <4=> 1 MHz +// <5=> 500 kHz +// <6=> 250 kHz +// <7=> 125 kHz +// <8=> 62.5 kHz +// <9=> 31.25 kHz + +#ifndef TIMER_DEFAULT_CONFIG_FREQUENCY +#define TIMER_DEFAULT_CONFIG_FREQUENCY 0 +#endif + +// TIMER_DEFAULT_CONFIG_MODE - Timer mode or operation + +// <0=> Timer +// <1=> Counter + +#ifndef TIMER_DEFAULT_CONFIG_MODE +#define TIMER_DEFAULT_CONFIG_MODE 0 +#endif + +// TIMER_DEFAULT_CONFIG_BIT_WIDTH - Timer counter bit width + +// <0=> 16 bit +// <1=> 8 bit +// <2=> 24 bit +// <3=> 32 bit + +#ifndef TIMER_DEFAULT_CONFIG_BIT_WIDTH +#define TIMER_DEFAULT_CONFIG_BIT_WIDTH 0 //3 +#endif + +// TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef TIMER_DEFAULT_CONFIG_IRQ_PRIORITY +#define TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 3 +#endif + +// TIMER0_ENABLED - Enable TIMER0 instance + + +#ifndef TIMER0_ENABLED +#define TIMER0_ENABLED 0 +#endif + +// TIMER1_ENABLED - Enable TIMER1 instance + + +#ifndef TIMER1_ENABLED +#define TIMER1_ENABLED 0 +#endif + +// TIMER2_ENABLED - Enable TIMER2 instance + + +#ifndef TIMER2_ENABLED +#define TIMER2_ENABLED 0 +#endif + +// TIMER3_ENABLED - Enable TIMER3 instance + + +#ifndef TIMER3_ENABLED +#define TIMER3_ENABLED 0 +#endif + +// TIMER4_ENABLED - Enable TIMER4 instance + + +#ifndef TIMER4_ENABLED +#define TIMER4_ENABLED 0 +#endif + +// TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TIMER_CONFIG_LOG_ENABLED +#define TIMER_CONFIG_LOG_ENABLED 0 +#endif +#if TIMER_CONFIG_LOG_ENABLED +// TIMER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TIMER_CONFIG_LOG_LEVEL +#define TIMER_CONFIG_LOG_LEVEL 3 +#endif + +// TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TIMER_CONFIG_INFO_COLOR +#define TIMER_CONFIG_INFO_COLOR 0 +#endif + +// TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TIMER_CONFIG_DEBUG_COLOR +#define TIMER_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //TIMER_CONFIG_LOG_ENABLED +// + +#endif //TIMER_ENABLED +// + +//========================================================== +// UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver +//========================================================== +#ifndef UART_ENABLED +#define UART_ENABLED 1 +#endif +#if UART_ENABLED + +//#define UART_PRESENT + + +// UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef UART_DEFAULT_CONFIG_HWFC +#define UART_DEFAULT_CONFIG_HWFC 0 +#endif + +// UART_DEFAULT_CONFIG_PARITY - Parity + +// <0=> Excluded +// <14=> Included + +#ifndef UART_DEFAULT_CONFIG_PARITY +#define UART_DEFAULT_CONFIG_PARITY 0 +#endif + +// UART_DEFAULT_CONFIG_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <10289152=> 38400 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30924800=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 57600 baud + +#ifndef UART_DEFAULT_CONFIG_BAUDRATE +#define UART_DEFAULT_CONFIG_BAUDRATE 30924800 +#endif + +// UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef UART_DEFAULT_CONFIG_IRQ_PRIORITY +#define UART_DEFAULT_CONFIG_IRQ_PRIORITY 3 +#endif + +// UART_EASY_DMA_SUPPORT - Driver supporting EasyDMA + + +#ifndef UART_EASY_DMA_SUPPORT +#define UART_EASY_DMA_SUPPORT 0 +#endif + +// UART_LEGACY_SUPPORT - Driver supporting Legacy mode + + +#ifndef UART_LEGACY_SUPPORT +#define UART_LEGACY_SUPPORT 0 +#endif + +// UART0_ENABLED - Enable UART0 instance +//========================================================== +#ifndef UART0_ENABLED +#define UART0_ENABLED 1 +#endif +#if UART0_ENABLED +// UART0_CONFIG_USE_EASY_DMA - Default setting for using EasyDMA + + +#ifndef UART0_CONFIG_USE_EASY_DMA +#define UART0_CONFIG_USE_EASY_DMA 0 +#endif + +#endif //UART0_ENABLED +// + +// UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef UART_CONFIG_LOG_ENABLED +#define UART_CONFIG_LOG_ENABLED 0 +#endif +#if UART_CONFIG_LOG_ENABLED +// UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef UART_CONFIG_LOG_LEVEL +#define UART_CONFIG_LOG_LEVEL 3 +#endif + +// UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef UART_CONFIG_INFO_COLOR +#define UART_CONFIG_INFO_COLOR 0 +#endif + +// UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef UART_CONFIG_DEBUG_COLOR +#define UART_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //UART_CONFIG_LOG_ENABLED +// + +#endif //UART_ENABLED +// + + +//========================================================== +// WDT_ENABLED - nrf_drv_wdt - WDT peripheral driver +//========================================================== +#ifndef WDT_ENABLED +#define WDT_ENABLED 0 +#endif +#if WDT_ENABLED +// WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode + +// <1=> Run in SLEEP, Pause in HALT +// <8=> Pause in SLEEP, Run in HALT +// <9=> Run in SLEEP and HALT +// <0=> Pause in SLEEP and HALT + +#ifndef WDT_CONFIG_BEHAVIOUR +#define WDT_CONFIG_BEHAVIOUR 1 +#endif + +// WDT_CONFIG_RELOAD_VALUE - Reload value <15-4294967295> + + +#ifndef WDT_CONFIG_RELOAD_VALUE +#define WDT_CONFIG_RELOAD_VALUE 2000 +#endif + +// WDT_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef WDT_CONFIG_IRQ_PRIORITY +#define WDT_CONFIG_IRQ_PRIORITY 1 +#endif + +// WDT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef WDT_CONFIG_LOG_ENABLED +#define WDT_CONFIG_LOG_ENABLED 0 +#endif +#if WDT_CONFIG_LOG_ENABLED +// WDT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef WDT_CONFIG_LOG_LEVEL +#define WDT_CONFIG_LOG_LEVEL 3 +#endif + +// WDT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef WDT_CONFIG_INFO_COLOR +#define WDT_CONFIG_INFO_COLOR 0 +#endif + +// WDT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef WDT_CONFIG_DEBUG_COLOR +#define WDT_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //WDT_CONFIG_LOG_ENABLED +// + +#endif //WDT_ENABLED +// + +// +//========================================================== + +// nRF_Log + +//========================================================== +// NRF_LOG_ENABLED - nrf_log - Logging +//========================================================== +#ifndef NRF_LOG_ENABLED +#define NRF_LOG_ENABLED 0 +#endif +#if NRF_LOG_ENABLED +// NRF_LOG_USES_COLORS - If enabled then ANSI escape code for colors is prefixed to every string +//========================================================== +#ifndef NRF_LOG_USES_COLORS +#define NRF_LOG_USES_COLORS 0 +#endif +#if NRF_LOG_USES_COLORS +// NRF_LOG_COLOR_DEFAULT - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_COLOR_DEFAULT +#define NRF_LOG_COLOR_DEFAULT 0 +#endif + +// NRF_LOG_ERROR_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_ERROR_COLOR +#define NRF_LOG_ERROR_COLOR 0 +#endif + +// NRF_LOG_WARNING_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_WARNING_COLOR +#define NRF_LOG_WARNING_COLOR 0 +#endif + +#endif //NRF_LOG_USES_COLORS +// + +// NRF_LOG_DEFAULT_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_LOG_DEFAULT_LEVEL +#define NRF_LOG_DEFAULT_LEVEL 3 +#endif + +// NRF_LOG_DEFERRED - Enable deffered logger. + +// Log data is buffered and can be processed in idle. +//========================================================== +#ifndef NRF_LOG_DEFERRED +#define NRF_LOG_DEFERRED 1 +#endif +#if NRF_LOG_DEFERRED +// NRF_LOG_DEFERRED_BUFSIZE - Size of the buffer for logs in words. +// Must be power of 2 + +#ifndef NRF_LOG_DEFERRED_BUFSIZE +#define NRF_LOG_DEFERRED_BUFSIZE 256 +#endif + +#endif //NRF_LOG_DEFERRED +// + +// NRF_LOG_USES_TIMESTAMP - Enable timestamping + + +// Function for getting the timestamp is provided by the user + +#ifndef NRF_LOG_USES_TIMESTAMP +#define NRF_LOG_USES_TIMESTAMP 0 +#endif + +#endif //NRF_LOG_ENABLED +// + +// nrf_log_backend - Logging sink + +//========================================================== +// NRF_LOG_BACKEND_MAX_STRING_LENGTH - Buffer for storing single output string +// Logger backend RAM usage is determined by this value. + +#ifndef NRF_LOG_BACKEND_MAX_STRING_LENGTH +#define NRF_LOG_BACKEND_MAX_STRING_LENGTH 256 +#endif + +// NRF_LOG_TIMESTAMP_DIGITS - Number of digits for timestamp +// If higher resolution timestamp source is used it might be needed to increase that + +#ifndef NRF_LOG_TIMESTAMP_DIGITS +#define NRF_LOG_TIMESTAMP_DIGITS 8 +#endif + +// NRF_LOG_BACKEND_SERIAL_USES_UART - If enabled data is printed over UART +//========================================================== +#ifndef NRF_LOG_BACKEND_SERIAL_USES_UART +#define NRF_LOG_BACKEND_SERIAL_USES_UART 1 +#endif +#if NRF_LOG_BACKEND_SERIAL_USES_UART +// NRF_LOG_BACKEND_SERIAL_UART_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <10289152=> 38400 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30924800=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 57600 baud + +#ifndef NRF_LOG_BACKEND_SERIAL_UART_BAUDRATE +#define NRF_LOG_BACKEND_SERIAL_UART_BAUDRATE 30924800 +#endif + +// NRF_LOG_BACKEND_SERIAL_UART_TX_PIN - UART TX pin +#ifndef NRF_LOG_BACKEND_SERIAL_UART_TX_PIN +#define NRF_LOG_BACKEND_SERIAL_UART_TX_PIN 9 +#endif + +// NRF_LOG_BACKEND_SERIAL_UART_RX_PIN - UART RX pin +#ifndef NRF_LOG_BACKEND_SERIAL_UART_RX_PIN +#define NRF_LOG_BACKEND_SERIAL_UART_RX_PIN 11 +#endif + +// NRF_LOG_BACKEND_SERIAL_UART_RTS_PIN - UART RTS pin +#ifndef NRF_LOG_BACKEND_SERIAL_UART_RTS_PIN +#define NRF_LOG_BACKEND_SERIAL_UART_RTS_PIN 8 +#endif + +// NRF_LOG_BACKEND_SERIAL_UART_CTS_PIN - UART CTS pin +#ifndef NRF_LOG_BACKEND_SERIAL_UART_CTS_PIN +#define NRF_LOG_BACKEND_SERIAL_UART_CTS_PIN 10 +#endif + +// NRF_LOG_BACKEND_SERIAL_UART_FLOW_CONTROL - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef NRF_LOG_BACKEND_SERIAL_UART_FLOW_CONTROL +#define NRF_LOG_BACKEND_SERIAL_UART_FLOW_CONTROL 0 +#endif + +// NRF_LOG_BACKEND_UART_INSTANCE - UART instance used + +// <0=> 0 + +#ifndef NRF_LOG_BACKEND_UART_INSTANCE +#define NRF_LOG_BACKEND_UART_INSTANCE 0 +#endif + +#endif //NRF_LOG_BACKEND_SERIAL_USES_UART +// + +// NRF_LOG_BACKEND_SERIAL_USES_RTT - If enabled data is printed using RTT +//========================================================== +#ifndef NRF_LOG_BACKEND_SERIAL_USES_RTT +#define NRF_LOG_BACKEND_SERIAL_USES_RTT 0 +#endif +#if NRF_LOG_BACKEND_SERIAL_USES_RTT +// NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE - RTT output buffer size. +// Should be equal or bigger than \ref NRF_LOG_BACKEND_MAX_STRING_LENGTH. +// This value is used in Segger RTT configuration to set the buffer size +// if it is bigger than default RTT buffer size. + +#ifndef NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE +#define NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE 512 +#endif + +#endif //NRF_LOG_BACKEND_SERIAL_USES_RTT +// + +// +//========================================================== + +// +//========================================================== + +// nRF_Segger_RTT + +//========================================================== +// segger_rtt - SEGGER RTT + +//========================================================== +// SEGGER_RTT_CONFIG_BUFFER_SIZE_UP - Size of upstream buffer. +#ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_UP +#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 64 +#endif + +// SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS - Size of upstream buffer. +#ifndef SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS +#define SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS 2 +#endif + +// SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN - Size of upstream buffer. +#ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN +#define SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN 16 +#endif + +// SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS - Size of upstream buffer. +#ifndef SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS +#define SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS 2 +#endif + +// +//========================================================== + +// +//========================================================== + +// <<< end of configuration section >>> +#endif //SDK_CONFIG_H + diff --git a/firmware/nRF_badge/data_collector/incl/accel.c b/firmware/nRF_badge/data_collector/incl/accel.c deleted file mode 100644 index 4b33f58..0000000 --- a/firmware/nRF_badge/data_collector/incl/accel.c +++ /dev/null @@ -1,192 +0,0 @@ -#include "accel.h" -#include "self_test.h" - -bool self_test_enable = false; -// ====== Accel LIS2DH and SPI functions ====== -volatile bool transmission_completed_spi = false; -//=== event handler ==== -void accel_spi_evt_handler(spi_master_evt_t spi_master_evt) -{ - switch(spi_master_evt.evt_type){ - case SPI_MASTER_EVT_TRANSFER_COMPLETED: - //Transmission done. - transmission_completed_spi = true; - //rx_buffer must have received data now. - break; - default: - // no implementation is required for now. - break; - } -} - -// ==== Init SPI for LIS2DH comunication ==== -void accel_spi_init(){ - uint32_t err_code = NRF_SUCCESS; - - //manually handle SS pin - nrf_gpio_pin_set(SPIM0_SS_PIN_2); - nrf_gpio_cfg_output(SPIM0_SS_PIN_2); - - // Configure SPI master. - spi_master_config_t spi_config = { - SPI_FREQUENCY_FREQUENCY_M8, // Serial clock frequency 8 Mbps. - SPIM0_SCK_PIN, // Defined in badge_03v6.h - SPIM0_MISO_PIN, - SPIM0_MOSI_PIN, - SPIM0_SS_PIN_2, // // SS, we'll handle that manually - APP_IRQ_PRIORITY_LOW, // Interrupt priority LOW. - SPI_CONFIG_ORDER_MsbFirst, // Bits order ? - SPI_CONFIG_CPOL_ActiveLow, // Serial clock polarity ACTIVELOW. - SPI_CONFIG_CPHA_Trailing, // Serial clock phase TRAILING. - 0 // Don't disable all IRQs. - }; - - err_code = spi_master_open(SPI_MASTER_0, &spi_config); - if (err_code != NRF_SUCCESS) { - nrf_gpio_pin_write(LED_2,LED_ON); //turn on LED - debug_log("Err\r\n"); - } - - // Register event handler for SPI master. - spi_master_evt_handler_reg(SPI_MASTER_0, accel_spi_evt_handler); -} - -//==== Funcion write registers and send value ==== -void writeRegister8(uint8_t reg, uint8_t value) { - uint8_t tx[2] = {reg & ~0x80, value}; // tx[1] = register to write / tx[2] = value to send //Array to send - uint8_t rx[2] = {0,0}; //Array to receive - spi_master_send_recv(SPI_MASTER_0,tx,sizeof(tx),rx,2); //Send and receive over SPI protocol - while(spi_busy()); //Wait while spi is bussy -} - -//==== Funcion read registers ==== -uint8_t readRegister8(uint8_t reg){ - uint8_t txBuf[1] = {reg | 0x80}; //Array to send - uint8_t rxBuf[2] = {0,0}; //Array to receive - spi_master_send_recv(SPI_MASTER_0,txBuf,sizeof(txBuf),rxBuf,2); //Send and receive over SPI protocol - while(spi_busy()); //Wait while spi is bussy - return rxBuf[1]; // Value retorned from spi register -} - -// ==== Set data rate 400hz ==== -void setDataRate(){ - uint8_t ctl1 = readRegister8(LIS2DH_REG_CTRL1); - ctl1 &= ~(0xF0); // mask off bits - ctl1 |= (0b0111 << 4); // 0b0111 = 400hz - writeRegister8(LIS2DH_REG_CTRL1, ctl1); -} - -// ==== Set Internal movement dectection and Interrupt activation ==== -void accel_set_int_motion() { - accel_spi_init();//Init SPI protocol - - // configurations for control registers - writeRegister8(LIS2DH_REG_CTRL2, 0x00); // High-pass filter (HPF) enabled - writeRegister8(LIS2DH_REG_CTRL3, 0x40); // ACC AOI1 interrupt signal is routed to INT1 pin. - writeRegister8(LIS2DH_REG_CTRL4, 0x00); // Full Scale = +/-2 g - writeRegister8(LIS2DH_REG_CTRL5, 0x00); // Default value is 00 for no latching. Interrupt signals on INT1 pin is not latched. - - // configurations for wakeup and motionless detection - writeRegister8(INT1_THS, 0x10); // Threshold interrupt - writeRegister8(INT1_DURATION, 0x00); // Duration = 1LSBs * (1/10Hz) = 0.1s. - writeRegister8(INT1_CFG, 0xA0); // Enable ZHIE interrupt generation, AND logic. - writeRegister8(LIS2DH_REG_CTRL1, 0x5C); // Turn on the sensor, Z axis with ODR = 100Hz low power mode. - writeRegister8(INT1_CFG, 0x82); // Enable XHIE interrupt generation, AND logic. - writeRegister8(LIS2DH_REG_CTRL1, 0x59); // Turn on the sensor, X axis with ODR = 100Hz low power mode. -} - -void accel_init(){ - //==== Set basic registers ==== - writeRegister8(LIS2DH_REG_CTRL1, 0x07);// enable all axes, normal mode - setDataRate();// 400Hz rate - writeRegister8(LIS2DH_REG_CTRL4, 0x88);// High res & BDU enabled - writeRegister8(LIS2DH_REG_CTRL3, 0x10);// DRDY on INT1 - writeRegister8(LIS2DH_REG_TEMPCFG, 0x80);// enable adcss -} - -// ==== Set registers for internal tap detection ==== -void accel_set_int_tap(){ - //==== Set click (sigle tap) ==== - writeRegister8(LIS2DH_REG_CTRL3, 0x80); // turn on int1 click - writeRegister8(LIS2DH_REG_CTRL5, 0x08); // latch interrupt on int1 - writeRegister8(LIS2DH_REG_CLICKCFG, 0x15); // turn on all axes & tap detection - writeRegister8(LIS2DH_REG_CLICKTHS, CLICKTHRESHHOLD); // arbitrary - writeRegister8(LIS2DH_REG_TIMELIMIT, 10); // arbitrary - writeRegister8(LIS2DH_REG_TIMELATENCY, 20); // arbitrary - writeRegister8(LIS2DH_REG_TIMEWINDOW, 255); // arbitrary -} - -// ==== Accelerometer Internal Self-Test ==== -void acc_self_test(){ - debug_log("Init self test\r\n"); - nrf_gpio_pin_write(GREEN_LED,LED_ON); - - if (self_test_enable==false){ //Ask if self-test is eneable - writeRegister8(LIS2DH_REG_CTRL4, 0x8A); // enable self-test register - } - - //==== Set Interrupt as Input ==== - nrf_gpio_pin_set(INT_PIN_INPUT); - nrf_gpio_cfg_input(INT_PIN_INPUT, NRF_GPIO_PIN_PULLUP); - nrf_delay_ms(200); //Wait for the movement simulation is ready - - //==== Interrupt Read ==== - if(nrf_gpio_pin_read(INT_PIN_INPUT)!=0){ //Is eneable interrupt pin? // Blink both LEDs - debug_log(" Success\r\n"); - - writeRegister8(LIS2DH_REG_CTRL4, 0x88);//Deseable self-test register - self_test_enable=true; - } else { - debug_log(" Failed\r\n"); - while(1) {}; - } - - nrf_delay_ms(LED_BLINK_MS); - nrf_gpio_pin_write(GREEN_LED,LED_OFF); - nrf_delay_ms(LED_BLINK_MS); - } - -// ==== Init accel test and set movement detection ==== -void accel_test(){ - debug_log("Init accel test\r\n"); - nrf_gpio_pin_write(GREEN_LED,LED_ON); - - //==== Check conection by Whoami ==== - if (readRegister8(LIS2DH_WHO_AM_I_REGISTER) != LIS2DH_WHO_AM_I_VALUE){ //Call howami register - debug_log(" Success\r\n"); - } - else { - debug_log(" Failed\r\n"); - while(1) {}; - } - - nrf_delay_ms(LED_BLINK_MS); - nrf_gpio_pin_write(GREEN_LED,LED_OFF); - nrf_delay_ms(LED_BLINK_MS); - - accel_init();//Set basic registers -} - -// ==== Tap detection function ==== -void tap_accel(){ - //==== Read tap ==== - uint8_t click = readRegister8(LIS2DH_REG_CLICKSRC); - if (click!=0) { // Movement detected? - nrf_gpio_pin_write(GREEN_LED,LED_ON); //turn on LED - }else{ - nrf_gpio_pin_write(GREEN_LED,LED_OFF); //turn off LED - } -} - -// ==== Internal motion detection with Interrupt output ==== -void motion_interrupt(){ - //==== Set Interrupt as Input ==== - nrf_gpio_pin_set(INT_PIN_INPUT); - nrf_gpio_cfg_input(INT_PIN_INPUT, NRF_GPIO_PIN_PULLUP); - //==== Interrupt Read ==== - if(nrf_gpio_pin_read(INT_PIN_INPUT)!=0){ //Is eneable interrupt pin? - nrf_gpio_pin_write(GREEN_LED,LED_ON); //turn on LED - } else { - nrf_gpio_pin_write(GREEN_LED,LED_OFF); //turn off LED - } -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/accel.h b/firmware/nRF_badge/data_collector/incl/accel.h deleted file mode 100644 index e5536d4..0000000 --- a/firmware/nRF_badge/data_collector/incl/accel.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef ACCEL_H -#define ACCEL_H - -#define SPIM0_SS_PIN_2 2 // accelerometer -#define LIS2DH_WHO_AM_I_REGISTER 0x0F -#define LIS2DH_WHO_AM_I_VALUE 0x33 -#define CLICKTHRESHHOLD 20 //Threshhold for tap accel -#define LIS2DH_REG_CLICKSRC 0x39 -#define LIS2DH_REG_CTRL1 0x20 -#define LIS2DH_REG_CTRL2 0x21 -#define LIS2DH_REG_CTRL3 0x22 -#define LIS2DH_REG_CTRL4 0x23 -#define LIS2DH_REG_CTRL5 0x24 -#define LIS2DH_REG_TEMPCFG 0x1F -#define LIS2DH_REG_CLICKCFG 0x38 -#define LIS2DH_REG_CLICKTHS 0x3A -#define LIS2DH_REG_TIMELIMIT 0x3B -#define LIS2DH_REG_TIMELATENCY 0x3C -#define LIS2DH_REG_TIMEWINDOW 0x3D -#define LIS2DH_REG_ACTTHS 0x3E -#define LIS2DH_REG_ACTDUR 0x3F -#define INT1_THS 0x32 -#define INT1_DURATION 0x00 -#define INT1_CFG 0x30 - -void accel_set_int_motion(); -void acc_self_test(); -void setDataRate(); -void accel_test(); -void tap_accel(); -void motion_interrupt(); -void accel_spi_init(); -void accel_init(); -void accel_set_int_tap(); - -#endif //ACCEL_H diff --git a/firmware/nRF_badge/data_collector/incl/accel_lib.c b/firmware/nRF_badge/data_collector/incl/accel_lib.c new file mode 100644 index 0000000..e720b46 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/accel_lib.c @@ -0,0 +1,1021 @@ +#include "accel_lib.h" +#include "spi_lib.h" + +#include "debug_lib.h" + +#include "nrf_drv_gpiote.h" // Needed for the INT-Pin + + +#include "app_util_platform.h" + +#include "systick_lib.h" // Needed for accel_selftest() + + + + +#if ACCELEROMETER_PRESENT +#define ACCEL_INT1_PIN ACCELEROMETER_INT_PIN +#else +#define ACCEL_INT1_PIN 25 +#endif + +/**< The default configuration parameters for the accelerometer */ +#define ACCEL_DATARATE_DEFAULT ACCEL_DATARATE_10_HZ +#define ACCEL_OPERATING_MODE_DEFAULT ACCEL_POWER_DOWN_MODE +#define ACCEL_AXIS_DEFAULT (ACCEL_X_AXIS_ENABLE | ACCEL_Y_AXIS_ENABLE | ACCEL_Z_AXIS_ENABLE) +#define ACCEL_FULL_SCALE_DEFAULT ACCEL_FULL_SCALE_2G +#define ACCEL_FIFO_DEFAULT ACCEL_FIFO_DISABLE +#define ACCEL_HP_FILTER_DEFAULT ACCEL_HP_FILTER_DISABLE +#define ACCEL_INTERRUPT_EVENT_DEFAULT ACCEL_NO_INTERRUPT +#define ACCEL_MOTION_INTERRUPT_PARAMETER_THRESHOLD_MG_DEFAULT 250 +#define ACCEL_MOTION_INTERRUPT_PARAMETER_MINIMAL_DURATION_MS_DEFAULT 0 + + +/**< Parameters for the selftest-function of the accelerometer */ +#define ACCEL_SELFTEST_TIME_FOR_INTERRUPT_GENERATION_MS 10000 +#define ACCEL_SELFTEST_INTERRUPT_THRESHOLD_MG 250 +#define ACCEL_SELFTEST_INTERRUPT_MINIMAL_DURATION_MS 20 + + + + + +#define LIS2DH12_WHO_AM_I_VALUE 0x33 /**< The value of the WHO_AM_I-register */ + + +/**< All register addresses of LIS2DH12 */ +#define LIS2DH12_STATUS_REG_AUX_ADDR 0x07 +#define LIS2DH12_OUT_TMEP_L_ADDR 0x0C +#define LIS2DH12_OUT_TMEP_H_ADDR 0x0D +#define LIS2DH12_WHO_AM_I_ADDR 0x0F +#define LIS2DH12_CTRL_REG0_ADDR 0x1E +#define LIS2DH12_TEMP_CFG_REG_ADDR 0x1F +#define LIS2DH12_CTRL_REG1_ADDR 0x20 +#define LIS2DH12_CTRL_REG2_ADDR 0x21 +#define LIS2DH12_CTRL_REG3_ADDR 0x22 +#define LIS2DH12_CTRL_REG4_ADDR 0x23 +#define LIS2DH12_CTRL_REG5_ADDR 0x24 +#define LIS2DH12_CTRL_REG6_ADDR 0x25 +#define LIS2DH12_REFERENCE_ADDR 0x26 +#define LIS2DH12_STATUS_REG_ADDR 0x27 +#define LIS2DH12_OUT_X_L_ADDR 0x28 +#define LIS2DH12_OUT_X_H_ADDR 0x29 +#define LIS2DH12_OUT_Y_L_ADDR 0x2A +#define LIS2DH12_OUT_Y_H_ADDR 0x2B +#define LIS2DH12_OUT_Z_L_ADDR 0x2C +#define LIS2DH12_OUT_Z_H_ADDR 0x2D +#define LIS2DH12_FIFO_CTRL_REG_ADDR 0x2E +#define LIS2DH12_FIFO_SRC_REG_ADDR 0x2F +#define LIS2DH12_INT1_CFG_ADDR 0x30 +#define LIS2DH12_INT1_SRC_ADDR 0x31 +#define LIS2DH12_INT1_THS_ADDR 0x32 +#define LIS2DH12_INT1_DURATION_ADDR 0x33 +#define LIS2DH12_INT2_CFG_ADDR 0x34 +#define LIS2DH12_INT2_SRC_ADDR 0x35 +#define LIS2DH12_INT2_THS_ADDR 0x36 +#define LIS2DH12_INT2_DURATION_ADDR 0x37 +#define LIS2DH12_CLICK_CFG_ADDR 0x38 +#define LIS2DH12_CLICK_SRC_ADDR 0x39 +#define LIS2DH12_CLICK_THS_ADDR 0x3A +#define LIS2DH12_TIME_LIMIT_ADDR 0x3B +#define LIS2DH12_TIME_LATENCY_ADDR 0x3C +#define LIS2DH12_TIME_WINDOW_ADDR 0x3D +#define LIS2DH12_ACT_THS_ADDR 0x3E +#define LIS2DH12_ACT_DUR_ADDR 0x3F + +#define FIFO_SIZE 32 /**< The number of levels in the accelerometer FIFO */ +#define FIFO_BUFFER_SIZE (1 + FIFO_SIZE * 6) /**< The buffer size of the spi operation (read/write registers). "1+" for the header byte that characterize the operation. */ + + + +static accel_datarate_t datarate = ACCEL_DATARATE_DEFAULT; /**< The current datarate */ +static accel_full_scale_t full_scale = ACCEL_FULL_SCALE_DEFAULT; /**< The current full-scale */ +static accel_operating_mode_t operating_mode = ACCEL_OPERATING_MODE_DEFAULT; /**< The current operating-mode */ +static accel_HP_filter_t HP_filter = ACCEL_HP_FILTER_DEFAULT; /**< The current HP filter mode */ + + +static volatile accel_interrupt_event_t interrupt_event = ACCEL_INTERRUPT_EVENT_DEFAULT; /**< The current interrupt event that is generated, when the interrupt-pin switches from low to high */ +static volatile accel_interrupt_handler_t interrupt_handler = NULL; /**< The current interrupt handler callback function */ + +static uint16_t motion_interrupt_parameter_threshold_mg = ACCEL_MOTION_INTERRUPT_PARAMETER_THRESHOLD_MG_DEFAULT; /**< The current threshold for the motion interrupt */ +static uint16_t motion_interrupt_parameter_minimal_duration_ms = ACCEL_MOTION_INTERRUPT_PARAMETER_MINIMAL_DURATION_MS_DEFAULT; /**< The current minimal duration for the motion interrupt */ + + +static spi_instance_t spi_instance; /**< The spi instance used to communicate with the LIS2DH12-IC via SPI */ + +static uint8_t fifo_buf[FIFO_BUFFER_SIZE]; /**< The buffer for the FIFO-read operation */ + + + +/**@brief Function for reading a 8-bit register value from an accelerometer register. + * + * @param[in] addr The address of the accelerometer register. + * @param[out] value Pointer to memory where to store the read value. + * + * + * @retval NRF_SUCCESS If the spi-read operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +static ret_code_t accel_read_reg_8(uint8_t addr, uint8_t* value) { + + uint8_t tx_buf[1]= {addr | 0x80}; + uint8_t rx_buf[2]= {0, 0}; + + ret_code_t ret = spi_transmit_receive(&spi_instance, tx_buf, 1, rx_buf, 2); + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY or NRF_ERROR_INVALID_ADDR + if(ret == NRF_ERROR_INVALID_ADDR) + ret = NRF_ERROR_INTERNAL; + + *value = rx_buf[1]; + + return ret; +} + + +/**@brief Function for writing a 8-bit register value to an accelerometer register. + * + * @param[in] addr The address of the accelerometer register. + * @param[in] value The value that should be written to the register. + * + * + * @retval NRF_SUCCESS If the spi-write operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +static ret_code_t accel_write_reg_8(uint8_t addr, uint8_t value) { + uint8_t tx_buf[2] = {addr & (~0x80), value}; + uint8_t rx_buf[2] = {0, 0}; + + + ret_code_t ret = spi_transmit_receive(&spi_instance, tx_buf, 2, rx_buf, 2); + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY or NRF_ERROR_INVALID_ADDR + if(ret == NRF_ERROR_INVALID_ADDR) + ret = NRF_ERROR_INTERNAL; + + + return ret; +} + + + +/**@brief The interrupt handler function of the INT1-interrupt pin. + * + * @param[in] pin The gpio-pin where the event/interrupt was detected. + * @param[in] action The action of the pin (e.g. NRF_GPIOTE_POLARITY_LOTOHI) + * + */ +void accel_int1_event_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { + if(pin == ACCEL_INT1_PIN && action == NRF_GPIOTE_POLARITY_LOTOHI) { + if(interrupt_event != ACCEL_NO_INTERRUPT) { + // TODO: remove + //nrf_gpio_pin_toggle(9); + + + if(interrupt_handler != NULL) { + accel_interrupt_event_t evt = interrupt_event; // A temporary copy, so the called handler can't change the internal interrupt_event + interrupt_handler(&evt); + } + } + + } +} + +/**@brief Function for retrieving the shift-factor and the sensitvity for a specific accelerometer configuration. + * + * @param[out] shift_factor The number of bits the raw accelerometer values should be shifted to left. + * @param[out] sensitivity The sensitivity the raw accelerometer values should be multiplied with [mg/LSB]. + * @param[in] accel_operating_mode The current operating-mode of the accelerometer. + * @param[in] accel_full_scale The current full-scale of the accelerometer. + */ +static void get_shift_factor_and_sensitivity(int16_t* shift_factor, int16_t* sensitivity, accel_operating_mode_t accel_operating_mode, accel_full_scale_t accel_full_scale) { + // The values in this function are from the Driver of the ST-package + switch(accel_operating_mode) { + case ACCEL_LOW_POWER_MODE: + *shift_factor = 8; + switch(accel_full_scale) { + case ACCEL_FULL_SCALE_2G: + *sensitivity = 16; + break; + case ACCEL_FULL_SCALE_4G: + *sensitivity = 32; + break; + case ACCEL_FULL_SCALE_8G: + *sensitivity = 64; + break; + case ACCEL_FULL_SCALE_16G: + *sensitivity = 192; + break; + default: + *sensitivity = 0; + break; + } + break; + + case ACCEL_NORMAL_MODE: + *shift_factor = 6; + switch(accel_full_scale) { + case ACCEL_FULL_SCALE_2G: + *sensitivity = 4; + break; + case ACCEL_FULL_SCALE_4G: + *sensitivity = 8; + break; + case ACCEL_FULL_SCALE_8G: + *sensitivity = 16; + break; + case ACCEL_FULL_SCALE_16G: + *sensitivity = 48; + break; + default: + *sensitivity = 0; + break; + } + break; + case ACCEL_HIGH_RESOLUTION_MODE: + *shift_factor = 4; + switch(accel_full_scale) { + case ACCEL_FULL_SCALE_2G: + *sensitivity = 1; + break; + case ACCEL_FULL_SCALE_4G: + *sensitivity = 2; + break; + case ACCEL_FULL_SCALE_8G: + *sensitivity = 4; + break; + case ACCEL_FULL_SCALE_16G: + *sensitivity = 12; + break; + default: + *sensitivity = 0; + break; + } + break; + default: + *shift_factor = 0; + *sensitivity = 0; + break; + } +} + + +/**@brief Function for retrieving the mg per LSB for the threshold register for interrupt configuration. + * + * @param[in] accel_full_scale The current full-scale of the accelerometer. + * + * @retval mg per LSB for the current configuration. + */ +static uint16_t get_threshold_mg_per_lsb(accel_full_scale_t accel_full_scale) { + + uint16_t mg_per_lsb = 16; + switch(accel_full_scale) { // See Datasheet LIS2DH12: chapter 8.21, p.43/53 + case ACCEL_FULL_SCALE_2G: + mg_per_lsb = 16; + break; + case ACCEL_FULL_SCALE_4G: + mg_per_lsb = 32; + break; + case ACCEL_FULL_SCALE_8G: + mg_per_lsb = 62; + break; + case ACCEL_FULL_SCALE_16G: + mg_per_lsb = 186; + break; + default: + mg_per_lsb = 16; + break; + + } + return mg_per_lsb; + +} + + +/**@brief Function for retrieving the datarate as integer. + * + * @param[in] accel_datarate The current datarate of the accelerometer. + * @param[in] accel_operating_mode The current operating-mode of the accelerometer. + * + * @retval The datarate as integer. + */ +static uint16_t get_datarate_as_number(accel_datarate_t accel_datarate, accel_operating_mode_t accel_operating_mode) { + uint16_t datarate_as_number = 1; + switch(accel_datarate) { + case ACCEL_DATARATE_1_HZ: + datarate_as_number = 1; + break; + case ACCEL_DATARATE_10_HZ: + datarate_as_number = 10; + break; + case ACCEL_DATARATE_25_HZ: + datarate_as_number = 25; + break; + case ACCEL_DATARATE_50_HZ: + datarate_as_number = 50; + break; + case ACCEL_DATARATE_100_HZ: + datarate_as_number = 100; + break; + case ACCEL_DATARATE_200_HZ: + datarate_as_number = 200; + break; + case ACCEL_DATARATE_400_HZ: + datarate_as_number = 400; + break; + case ACCEL_DATARATE_1600_HZ: + datarate_as_number = 1600; + break; + case ACCEL_DATARATE_1344_HZ_5376_HZ: + if(accel_operating_mode == ACCEL_LOW_POWER_MODE) + datarate_as_number = 5376; + else + datarate_as_number = 1344; + break; + default: + datarate_as_number = 1; + break; + } + + return datarate_as_number; +} + + + + + +ret_code_t accel_init(void) { + + // TODO: retrieve Pin-numbers from the custom_board-file! + // TODO: Interrupt pin configuration! + + static uint8_t init_done = 0; + + if(init_done == 0) { + + #if ACCELEROMETER_PRESENT + + // SPI-module intitalization + spi_instance.spi_peripheral = 0; + spi_instance.nrf_drv_spi_config.frequency = NRF_DRV_SPI_FREQ_8M; + spi_instance.nrf_drv_spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST; + spi_instance.nrf_drv_spi_config.mode = NRF_DRV_SPI_MODE_3; + spi_instance.nrf_drv_spi_config.orc = 0; + spi_instance.nrf_drv_spi_config.irq_priority = APP_IRQ_PRIORITY_MID; + spi_instance.nrf_drv_spi_config.ss_pin = SPIM0_ACCELEROMETER_SS_PIN; + spi_instance.nrf_drv_spi_config.miso_pin = SPIM0_MISO_PIN; + spi_instance.nrf_drv_spi_config.mosi_pin = SPIM0_MOSI_PIN; + spi_instance.nrf_drv_spi_config.sck_pin = SPIM0_SCK_PIN; + + ret_code_t ret = spi_init(&spi_instance); + + // ret could be NRF_SUCCESS or NRF_ERROR_INVALID_PARAM + if(ret != NRF_SUCCESS) + return NRF_ERROR_INTERNAL; + + // Check the WHO_AM_I-register value + uint8_t value = 0; + ret = accel_read_reg_8(LIS2DH12_WHO_AM_I_ADDR, &value); + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY or NRF_ERROR_INTERNAL + if(ret != NRF_SUCCESS) + return ret; + + if(value != LIS2DH12_WHO_AM_I_VALUE) { + return NRF_ERROR_INTERNAL; + } + + + // Interrupt pin configuration and initialization: + if(!nrf_drv_gpiote_is_init()) + nrf_drv_gpiote_init(); + + + // GPIOTE_CONFIG_IN_SENSE_LOTOHI(true) or GPIOTE_CONFIG_IN_SENSE_HITOLO(true) + nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(false); + in_config.pull = NRF_GPIO_PIN_NOPULL; + ret = nrf_drv_gpiote_in_init(ACCEL_INT1_PIN, &in_config, accel_int1_event_handler); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + nrf_drv_gpiote_in_event_enable(ACCEL_INT1_PIN, true); + + + + // Enable the default axis + ret = accel_set_axis(ACCEL_AXIS_DEFAULT); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Set the default full scale + full_scale = ACCEL_FULL_SCALE_DEFAULT; + ret = accel_set_full_scale(ACCEL_FULL_SCALE_DEFAULT); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Set the default datarate + datarate = ACCEL_DATARATE_DEFAULT; + ret = accel_set_datarate(ACCEL_DATARATE_DEFAULT); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Set the default operating mode + ret = accel_set_operating_mode(ACCEL_OPERATING_MODE_DEFAULT); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Set the default FIFO-configuration + ret = accel_set_fifo(ACCEL_FIFO_DEFAULT); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Set the default HP-filter configuration + HP_filter = ACCEL_HP_FILTER_DEFAULT; + ret = accel_set_HP_filter(ACCEL_HP_FILTER_DEFAULT); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Set the default motion interrupt threshold and duration-parameters + motion_interrupt_parameter_threshold_mg = ACCEL_MOTION_INTERRUPT_PARAMETER_THRESHOLD_MG_DEFAULT; + motion_interrupt_parameter_minimal_duration_ms = ACCEL_MOTION_INTERRUPT_PARAMETER_MINIMAL_DURATION_MS_DEFAULT; + accel_set_motion_interrupt_parameters(ACCEL_MOTION_INTERRUPT_PARAMETER_THRESHOLD_MG_DEFAULT, ACCEL_MOTION_INTERRUPT_PARAMETER_MINIMAL_DURATION_MS_DEFAULT); + + // Set the default interrupt operation/event + interrupt_handler = NULL; + interrupt_event = ACCEL_INTERRUPT_EVENT_DEFAULT; + ret = accel_set_interrupt(ACCEL_INTERRUPT_EVENT_DEFAULT); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + #endif + } + init_done = 1; + + return NRF_SUCCESS; +} + + + +ret_code_t accel_set_datarate(accel_datarate_t accel_datarate) { + + ret_code_t ret; + uint8_t ctrl_reg1_val; + ret = accel_read_reg_8(LIS2DH12_CTRL_REG1_ADDR, &ctrl_reg1_val); + if(ret != NRF_SUCCESS) return ret; + + ctrl_reg1_val &= ~(0xF0); // Clear all ODR-bits + ctrl_reg1_val |= ((accel_datarate << 4) & 0xF0); // Set ODR-bits according to the datarate + + ret = accel_write_reg_8(LIS2DH12_CTRL_REG1_ADDR, ctrl_reg1_val); + if(ret != NRF_SUCCESS) return ret; + + datarate = accel_datarate; + + return NRF_SUCCESS; +} + + +ret_code_t accel_set_operating_mode(accel_operating_mode_t accel_operating_mode) { + + + ret_code_t ret; + // Always set the datarate before changing the operating mode, because if + // the application switches from power-down to another operating mode, + // the former datarate has to be restored. + ret = accel_set_datarate(datarate); + if(ret != NRF_SUCCESS) return ret; + + // See: Table 3: Operating mode selection + uint8_t ctrl_reg1_val; + uint8_t ctrl_reg4_val; + + ret = accel_read_reg_8(LIS2DH12_CTRL_REG1_ADDR, &ctrl_reg1_val); + if(ret != NRF_SUCCESS) return ret; + ret = accel_read_reg_8(LIS2DH12_CTRL_REG4_ADDR, &ctrl_reg4_val); + if(ret != NRF_SUCCESS) return ret; + + switch(accel_operating_mode) { + case ACCEL_POWER_DOWN_MODE: + { + ctrl_reg1_val &= ~(0x08); // Clear LPen-bit (default: normal mode) + ctrl_reg4_val &= ~(0x08); // Clear HR-bit (default: normal mode) + ctrl_reg1_val &= ~(0xF0); // Clear all ODR bits for power-down mode --> see: Table 4: Data rate configuration + } + break; + case ACCEL_LOW_POWER_MODE: + { + ctrl_reg1_val |= (0x08); // Set LPen-bit + ctrl_reg4_val &= ~(0x08); // Clear HR-bit + } + break; + case ACCEL_NORMAL_MODE: + { + ctrl_reg1_val &= ~(0x08); // Clear LPen-bit + ctrl_reg4_val &= ~(0x08); // Clear HR-bit + } + break; + case ACCEL_HIGH_RESOLUTION_MODE: + { + ctrl_reg1_val &= ~(0x08); // Clear LPen-bit + ctrl_reg4_val |= (0x08); // Set HR-bit + } + break; + } + + ctrl_reg4_val |= (0x80); // Set BDU-bit + + + ret = accel_write_reg_8(LIS2DH12_CTRL_REG1_ADDR, ctrl_reg1_val); + if(ret != NRF_SUCCESS) return ret; + ret = accel_write_reg_8(LIS2DH12_CTRL_REG4_ADDR, ctrl_reg4_val); + if(ret != NRF_SUCCESS) return ret; + + operating_mode = accel_operating_mode; + + return NRF_SUCCESS; +} + +ret_code_t accel_set_axis(accel_axis_t accel_axis) { + ret_code_t ret; + uint8_t ctrl_reg1_val; + ret = accel_read_reg_8(LIS2DH12_CTRL_REG1_ADDR, &ctrl_reg1_val); + if(ret != NRF_SUCCESS) return ret; + + if(accel_axis & ACCEL_X_AXIS_ENABLE) { + ctrl_reg1_val |= (1 << 0); + } + if(accel_axis & ACCEL_Y_AXIS_ENABLE) { + ctrl_reg1_val |= (1 << 1); + } + if(accel_axis & ACCEL_Z_AXIS_ENABLE) { + ctrl_reg1_val |= (1 << 2); + } + + if(accel_axis & ACCEL_X_AXIS_DISABLE) { + ctrl_reg1_val &= ~(1 << 0); + } + if(accel_axis & ACCEL_Y_AXIS_DISABLE) { + ctrl_reg1_val &= ~(1 << 1); + } + if(accel_axis & ACCEL_Z_AXIS_DISABLE) { + ctrl_reg1_val &= ~(1 << 2); + } + + + ret = accel_write_reg_8(LIS2DH12_CTRL_REG1_ADDR, ctrl_reg1_val); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + + + +ret_code_t accel_set_full_scale(accel_full_scale_t accel_full_scale) { + ret_code_t ret; + + uint8_t ctrl_reg4_val; + + ret = accel_read_reg_8(LIS2DH12_CTRL_REG4_ADDR, &ctrl_reg4_val); + if(ret != NRF_SUCCESS) return ret; + + + ctrl_reg4_val &= ~(0x30); // Clear the Full-scale bits + ctrl_reg4_val |= ((accel_full_scale << 4) & 0xF0); // Set the Full-scale bits accordingly + + ret = accel_write_reg_8(LIS2DH12_CTRL_REG4_ADDR, ctrl_reg4_val); + if(ret != NRF_SUCCESS) return ret; + + full_scale = accel_full_scale; + + return NRF_SUCCESS; + +} + +ret_code_t accel_set_HP_filter(accel_HP_filter_t accel_HP_filter) { + ret_code_t ret; + uint8_t tmp; + switch(accel_HP_filter) { + case ACCEL_HP_FILTER_ENABLE: + // Enable the High-pass filter for FIFO data: + ret = accel_read_reg_8(LIS2DH12_CTRL_REG2_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0xF0); // High pass filter: Normal mode, (CutOff 2Hz@100Hz) + tmp |= 0x08; // High pass filter: HP-data enabled for FIFO + ret = accel_write_reg_8(LIS2DH12_CTRL_REG2_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + + // Dummy read the reference register to reset the HP-filter: + ret = accel_read_reg_8(LIS2DH12_REFERENCE_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + break; + + case ACCEL_HP_FILTER_DISABLE: + + default: + // Disable the High-pass filter for FIFO data: + ret = accel_read_reg_8(LIS2DH12_CTRL_REG2_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0x08); // High pass filter: HP-data disabled for FIFO + ret = accel_write_reg_8(LIS2DH12_CTRL_REG2_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + break; + } + + HP_filter = accel_HP_filter; + + return NRF_SUCCESS; +} + + +void accel_set_interrupt_handler(accel_interrupt_handler_t accel_interrupt_handler) { + interrupt_handler = accel_interrupt_handler; +} + + + +ret_code_t accel_set_motion_interrupt_parameters(uint16_t threshold_mg, uint16_t minimal_duration_ms) { + + + ret_code_t ret; + + // Calculate and write the interrupt-threshold: + uint16_t mg_per_lsb = get_threshold_mg_per_lsb(full_scale); + uint16_t threshold = threshold_mg/mg_per_lsb; + if(threshold > 0x7F) threshold = 0x7F; + ret = accel_write_reg_8(LIS2DH12_INT1_THS_ADDR, (uint8_t) threshold); + if(ret != NRF_SUCCESS) return ret; + + // Calculate and write the interrupt-duration: + uint16_t datarate_as_number = get_datarate_as_number(datarate, operating_mode); + uint16_t duration = (uint16_t)(((uint32_t)(((uint32_t)datarate_as_number) * ((uint32_t) minimal_duration_ms))) / 1000); + if(duration > 0x7F) duration = 0x7F; + ret = accel_write_reg_8(LIS2DH12_INT1_DURATION_ADDR, (uint8_t) duration); + if(ret != NRF_SUCCESS) return ret; + + + motion_interrupt_parameter_threshold_mg = threshold_mg; + motion_interrupt_parameter_minimal_duration_ms = minimal_duration_ms; + + return NRF_SUCCESS; + +} + + + +ret_code_t accel_set_interrupt(accel_interrupt_event_t accel_interrupt_event) { + ret_code_t ret; + uint8_t tmp; + + // Disable everything (so that the former config of antoher interrupt source is replaced) + + // Disable HP_IA1 interrupt of HP-Filter: + ret = accel_read_reg_8(LIS2DH12_CTRL_REG2_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0x01); + ret = accel_write_reg_8(LIS2DH12_CTRL_REG2_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + + // Disable Interrupt Pin: + ret = accel_write_reg_8(LIS2DH12_CTRL_REG3_ADDR, 0x00); + if(ret != NRF_SUCCESS) return ret; + + // Reset the interupt 1 latched bit (although it should not be set) + ret = accel_read_reg_8(LIS2DH12_CTRL_REG5_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0x08); // Clear LIR_INT1-bit + ret = accel_write_reg_8(LIS2DH12_CTRL_REG5_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + + // Reset the threshold: + ret = accel_write_reg_8(LIS2DH12_INT1_THS_ADDR, 0x00); + if(ret != NRF_SUCCESS) return ret; + + // Reset the duration to 0: + ret = accel_write_reg_8(LIS2DH12_INT1_DURATION_ADDR, 0x00); + if(ret != NRF_SUCCESS) return ret; + + + // Disbale interrupt 1: + ret = accel_write_reg_8(LIS2DH12_INT1_CFG_ADDR, 0x00); + if(ret != NRF_SUCCESS) return ret; + + + + + switch(accel_interrupt_event) { + case ACCEL_NO_INTERRUPT: + { + } + break; + + + + case ACCEL_MOTION_INTERRUPT: + { + // Enable the High-pass filter: + ret = accel_read_reg_8(LIS2DH12_CTRL_REG2_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0xF0); // High pass filter: Normal mode, (CutOff 2Hz@100Hz) + tmp |= 0x01; // High pass filter: HP-data enabled for INT1 + ret = accel_write_reg_8(LIS2DH12_CTRL_REG2_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + + // Enable interrupt on INT1 Pin: + ret = accel_write_reg_8(LIS2DH12_CTRL_REG3_ADDR, 0x40); + if(ret != NRF_SUCCESS) return ret; + + // Set interrupt 1 latched (So a new interrupt is only then generated if the INT1_SRC-register is read, e.g. by calling accel_reset_interrupt()) + ret = accel_read_reg_8(LIS2DH12_CTRL_REG5_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp |= (0x08); // Set LIR_INT1-bit + ret = accel_write_reg_8(LIS2DH12_CTRL_REG5_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + + // Set the motion interrupt parameters (threshold_mg and minimal_duration_ms) + ret = accel_set_motion_interrupt_parameters(motion_interrupt_parameter_threshold_mg, motion_interrupt_parameter_minimal_duration_ms); + if(ret != NRF_SUCCESS) return ret; + + // Dummy read the reference register to reset the HP-filter: + ret = accel_read_reg_8(LIS2DH12_REFERENCE_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + + // Enable interrupt 1 on High X, Y and Z axis with OR-combination + ret = accel_write_reg_8(LIS2DH12_INT1_CFG_ADDR, 0x2A); + if(ret != NRF_SUCCESS) return ret; + + // Reset, only in case there was a reset before that was not cleared + ret = accel_reset_interrupt(); + if(ret != NRF_SUCCESS) return ret; + } + break; + case ACCEL_SINGLE_TAP_INTERRUPT: + { + // TODO: implement/configure the single-tap interrupt generation of the accelerometer + } + break; + + case ACCEL_DOUBLE_TAP_INTERRUPT: + { + // TODO: implement/configure the double-tap interrupt generation of the accelerometer + } + break; + } + + + interrupt_event = accel_interrupt_event; + + return NRF_SUCCESS; +} + +ret_code_t accel_reset_interrupt(void) { + // Resetting a latched-interrupt by reading the INT1_SRC-register value + ret_code_t ret; + uint8_t tmp; + ret = accel_read_reg_8(LIS2DH12_INT1_SRC_ADDR, &tmp); // The read value could also be used to get information about the causing axis. + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + + + +ret_code_t accel_set_fifo(accel_fifo_t accel_fifo) { + ret_code_t ret; + uint8_t tmp; + + switch(accel_fifo) { + + case ACCEL_FIFO_STREAM_ENABLE: + // Enable the FIFO + ret = accel_read_reg_8(LIS2DH12_CTRL_REG5_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp |= (0x40); // Set FIFO_EN-bit + ret = accel_write_reg_8(LIS2DH12_CTRL_REG5_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + + // Set the FIFO mode to Stream mode + ret = accel_read_reg_8(LIS2DH12_FIFO_CTRL_REG_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0xC0); // Clear the FM1 and FM0 bit + tmp |= (0x80); // Set FM1 bit + ret = accel_write_reg_8(LIS2DH12_FIFO_CTRL_REG_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + break; + + case ACCEL_FIFO_DISABLE: + default: + // Disable the FIFO + ret = accel_read_reg_8(LIS2DH12_CTRL_REG5_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0x40); // Clear FIFO_EN-bit + ret = accel_write_reg_8(LIS2DH12_CTRL_REG5_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + + // Set the FIFO mode to Bypass mode (default) + ret = accel_read_reg_8(LIS2DH12_FIFO_CTRL_REG_ADDR, &tmp); + if(ret != NRF_SUCCESS) return ret; + tmp &= ~(0xC0); // Clear the FM1 and FM0 bit + ret = accel_write_reg_8(LIS2DH12_FIFO_CTRL_REG_ADDR, tmp); + if(ret != NRF_SUCCESS) return ret; + break; + } + + return NRF_SUCCESS; +} + + +ret_code_t accel_read_acceleration(int16_t* accel_x, int16_t* accel_y, int16_t* accel_z, uint8_t* num_samples, uint32_t max_num_samples) { + ret_code_t ret; + *num_samples = 0; + + + // First read out the number of available samples in FIFO through FIFO_SRC_REG + uint8_t fifo_src_reg; + ret = accel_read_reg_8(LIS2DH12_FIFO_SRC_REG_ADDR, &fifo_src_reg); + if(ret != NRF_SUCCESS) return ret; + + *num_samples = (fifo_src_reg & 0x1F); // Extract the FSS-bits that contains the number of unread samples stored in FIFO-buffer + + // Increment because the maximum number of samples stored in FIFO is 32, but the maximum value in the FIFO_SRC_REG is 31. If there is only one sample available, the value is 0. + *num_samples += 1; + if(*num_samples > FIFO_SIZE) { // Only for safety reasons + *num_samples = FIFO_SIZE; + } + + if((uint32_t) *num_samples > max_num_samples) { // Only read maximal max_num_samples from the accelerometer + *num_samples = (uint8_t) max_num_samples; + } + + // Now read available number of samples from the fifo + uint8_t tx_buf[FIFO_BUFFER_SIZE]; + tx_buf[0] = LIS2DH12_OUT_X_L_ADDR | 0x80 | 0x40; // 0x40 for multiple sequential register read + + + + ret = spi_transmit_receive(&spi_instance, tx_buf, 1, fifo_buf, (*num_samples * 6) + 1); // *6, because the accelerometer-data is stored in 6 sequential registers (see chapter 9.5 in AN5005). + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY or NRF_ERROR_INVALID_ADDR + if(ret == NRF_ERROR_INVALID_ADDR) + ret = NRF_ERROR_INTERNAL; + if(ret != NRF_SUCCESS) return ret; + + // Get the shift_factor and the sensitivity for the current configuration, to convert the acceleration to mg + int16_t shift_factor, sensitivity; + get_shift_factor_and_sensitivity(&shift_factor, &sensitivity, operating_mode, full_scale); + + // Set the output values of the acceleration: + for(uint16_t i = 0; i < *num_samples; i++) { + accel_x[i] = (((int16_t) ((((int16_t)fifo_buf[(i*6+1) + 1]) << 8) | ((int16_t) fifo_buf[(i*6+0) + 1]))) >> shift_factor) * sensitivity; + accel_y[i] = (((int16_t) ((((int16_t)fifo_buf[(i*6+3) + 1]) << 8) | ((int16_t) fifo_buf[(i*6+2) + 1]))) >> shift_factor) * sensitivity; + accel_z[i] = (((int16_t) ((((int16_t)fifo_buf[(i*6+5) + 1]) << 8) | ((int16_t) fifo_buf[(i*6+4) + 1]))) >> shift_factor) * sensitivity; + } + + return NRF_SUCCESS; +} + + + +static volatile uint32_t selftest_event_counter = 0; +static void selftest_interrupt_handler(accel_interrupt_event_t const * event) { + selftest_event_counter ++; +} + +bool accel_selftest(void) { + debug_log("ACCEL: Start accel selftest...\n\r"); + + uint8_t selftest_failed = 0; + + // Backup the old configuration: + accel_operating_mode_t former_operating_mode = operating_mode; + accel_datarate_t former_datarate = datarate; + accel_full_scale_t former_full_scale = full_scale; + accel_HP_filter_t former_HP_filter = HP_filter; + accel_interrupt_event_t former_interrupt_event = interrupt_event; + accel_interrupt_handler_t former_interrupt_handler = interrupt_handler; + uint16_t former_motion_interrupt_parameter_threshold_mg = motion_interrupt_parameter_threshold_mg; + uint16_t former_motion_interrupt_parameter_minimal_duration_ms = motion_interrupt_parameter_minimal_duration_ms; + + + + ret_code_t ret; + + // Configure for selftest + ret = accel_set_operating_mode(ACCEL_LOW_POWER_MODE); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_operating_mode failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_datarate(ACCEL_DATARATE_100_HZ); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_datarate failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_full_scale(ACCEL_FULL_SCALE_2G); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_full_scale failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_HP_filter(ACCEL_HP_FILTER_DISABLE); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_HP_filter failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_motion_interrupt_parameters(ACCEL_SELFTEST_INTERRUPT_THRESHOLD_MG, ACCEL_SELFTEST_INTERRUPT_MINIMAL_DURATION_MS); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_motion_interrupt_parameters failed!\n\r"); + selftest_failed = 1; + } + + accel_set_interrupt_handler(selftest_interrupt_handler); + + ret = accel_set_interrupt(ACCEL_MOTION_INTERRUPT); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_interrupt failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_reset_interrupt(); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_reset_interrupt failed!\n\r"); + selftest_failed = 1; + } + + debug_log("ACCEL: Waiting for an wake-up interrupt for %u ms.\n\r", ACCEL_SELFTEST_TIME_FOR_INTERRUPT_GENERATION_MS); + + // This is the actual test: + // Waiting for an interrupt: + uint32_t end_ms = systick_get_continuous_millis() + ACCEL_SELFTEST_TIME_FOR_INTERRUPT_GENERATION_MS; + selftest_event_counter = 0; + while(selftest_event_counter == 0 && systick_get_continuous_millis() < end_ms); + + + // Reading accelerometer data: + int16_t x[32] = {0}; + int16_t y[32] = {0}; + int16_t z[32] = {0}; + uint8_t num_samples = 0; + ret = accel_read_acceleration(x, y, z, &num_samples, 32); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_read_acceleration failed!\n\r"); + selftest_failed = 1; + } + + + ret = accel_reset_interrupt(); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_reset_interrupt failed!\n\r"); + selftest_failed = 1; + } + + + + // Restore the former configuration + ret = accel_set_operating_mode(former_operating_mode); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_operating_mode failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_datarate(former_datarate); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_datarate failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_full_scale(former_full_scale); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_full_scale failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_HP_filter(former_HP_filter); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_HP_filter failed!\n\r"); + selftest_failed = 1; + } + + ret = accel_set_motion_interrupt_parameters(former_motion_interrupt_parameter_threshold_mg, former_motion_interrupt_parameter_minimal_duration_ms); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_motion_interrupt_parameters failed!\n\r"); + selftest_failed = 1; + } + + accel_set_interrupt_handler(former_interrupt_handler); + + ret = accel_set_interrupt(former_interrupt_event); + if(ret != NRF_SUCCESS) { + debug_log("ACCEL: Accel_set_interrupt failed!\n\r"); + selftest_failed = 1; + } + + + // Check if the selftest was successful + if(selftest_failed == 1) { + debug_log("ACCEL: Accel selftest failed!\n\r"); + return 0; + } + + if(selftest_event_counter == 0 || (x[0] == 0 && y[0] == 0 && z[0] == 0)) { + debug_log("ACCEL: Accel selftest failed: No interrupt was generated in %u ms OR the accelerometer reading failed!\n\r", ACCEL_SELFTEST_TIME_FOR_INTERRUPT_GENERATION_MS); + return 0; + } + debug_log("ACCEL: x %d, y %d, z %d\n\r", x[0], y[0], z[0]); + + debug_log("ACCEL: Accel selftest successful!\n"); + + return 1; +} + + diff --git a/firmware/nRF_badge/data_collector/incl/accel_lib.h b/firmware/nRF_badge/data_collector/incl/accel_lib.h new file mode 100644 index 0000000..ba4a2c5 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/accel_lib.h @@ -0,0 +1,274 @@ +#ifndef __ACCEL_LIB_H +#define __ACCEL_LIB_H + + +#include +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + +#ifndef UNIT_TEST +#include "custom_board.h" +#else +#define ACCELEROMETER_PRESENT 1 +#endif + +#ifndef ACCELEROMETER_PRESENT +#define ACCELEROMETER_PRESENT 0 +#endif + +/**@brief Accelerometer datarate type. + * + * @details For more information see: Table 4: Data rate configuration at p.9/59 in AN5005 + */ +typedef enum { + ACCEL_DATARATE_1_HZ = 0b0001, + ACCEL_DATARATE_10_HZ = 0b0010, + ACCEL_DATARATE_25_HZ = 0b0011, + ACCEL_DATARATE_50_HZ = 0b0100, + ACCEL_DATARATE_100_HZ = 0b0101, + ACCEL_DATARATE_200_HZ = 0b0110, + ACCEL_DATARATE_400_HZ = 0b0111, + ACCEL_DATARATE_1600_HZ = 0b1000, /**< Only available in low-power mode */ + ACCEL_DATARATE_1344_HZ_5376_HZ = 0b1001, /**< In normal-mode/high-resolution-mode -> 1344Hz and in low-power mode -> 5376Hz */ +} accel_datarate_t; + + +/**@brief Accelerometer operating mode type. */ +typedef enum { + ACCEL_POWER_DOWN_MODE = 0, + ACCEL_LOW_POWER_MODE = 1, + ACCEL_NORMAL_MODE = 2, + ACCEL_HIGH_RESOLUTION_MODE = 3, +} accel_operating_mode_t; + + +/**@brief Accelerometer axis type. */ +typedef enum { + ACCEL_X_AXIS_ENABLE = (1 << 0), + ACCEL_Y_AXIS_ENABLE = (1 << 1), + ACCEL_Z_AXIS_ENABLE = (1 << 2), + ACCEL_X_AXIS_DISABLE = (1 << 3), + ACCEL_Y_AXIS_DISABLE = (1 << 4), + ACCEL_Z_AXIS_DISABLE = (1 << 5), +} accel_axis_t; + + +/**@brief Accelerometer full scale type. */ +typedef enum { + ACCEL_FULL_SCALE_2G = 0b00, + ACCEL_FULL_SCALE_4G = 0b01, + ACCEL_FULL_SCALE_8G = 0b10, + ACCEL_FULL_SCALE_16G = 0b11, +} accel_full_scale_t; + + +/**@brief Accelerometer FIFO type/mode. */ +typedef enum { + ACCEL_FIFO_DISABLE = 0, /**< FIFO disabled == Bypass mode */ + ACCEL_FIFO_STREAM_ENABLE = 1, /**< FIFO stream mode */ +} accel_fifo_t; + + +/**@brief Accelerometer high-pass filter type. */ +typedef enum { + ACCEL_HP_FILTER_DISABLE = 0, + ACCEL_HP_FILTER_ENABLE = 1, +} accel_HP_filter_t; + + +/**@brief Accelerometer interrupt event type. */ +typedef enum { + ACCEL_NO_INTERRUPT = 0, /**< No interrupt will be generated */ + ACCEL_MOTION_INTERRUPT = 1, /**< Wake up interrupt on motion */ + ACCEL_SINGLE_TAP_INTERRUPT = 2, /**< Tap interrupt on single tap */ + ACCEL_DOUBLE_TAP_INTERRUPT = 3, /**< Tap interrupt on double tap */ +} accel_interrupt_event_t; + + +/**@brief Accelerometer event handler type. */ +typedef void (*accel_interrupt_handler_t) (accel_interrupt_event_t const * p_event); + + + +/**@brief Function for initializing the acceleromenter module. + * + * @details This functions initializes the underlying spi-module. + * The spi peripheral has to be enabled in the config-file: sdk_config.h. + * Furthermore, it reads out the WHO_AM_I-register to check whether the communication works. + * + * + * @retval NRF_SUCCESS If the module was successfully initialized. + * @retval NRF_ERROR_BUSY If the register-read operation failed (because of an ongoing spi operation). + * @retval NRF_ERROR_INTERNAL If there was an error while initializing the spi-module (e.g. bad configuration), the WHO_AM_I-register mismatch or there was antother internal problem. + */ +ret_code_t accel_init(void); + + +/**@brief Function for setting the datarate of the accelerometer. + * + * @param[in] accel_datarate The datarate that should be set. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_datarate(accel_datarate_t accel_datarate); + + +/**@brief Function for setting the operating mode of the accelerometer. + * + * @param[in] accel_operating_mode The operating-mode that should be set. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_operating_mode(accel_operating_mode_t accel_operating_mode); + + + +/**@brief Function for enabling/disabling one or more axis of the accelerometer. + * + * @param[in] accel_axis A logical or concatenation of different axis (e.g. (ACCEL_X_AXIS_ENABLE | ACCEL_Y_AXIS_ENABLE | ACCEL_Z_AXIS_ENABLE)) + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_axis(accel_axis_t accel_axis); + + +/**@brief Function for setting the full-scale value of the accelerometer. + * + * @param[in] accel_full_scale The full-scale value that should be set. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_full_scale(accel_full_scale_t accel_full_scale); + + +/**@brief Function for enabling/disabling the high-pass filter. + * + * @param[in] accel_HP_filter ACCEL_HP_FILTER_DISABLE or ACCEL_HP_FILTER_ENABLE. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_HP_filter(accel_HP_filter_t accel_HP_filter); + + +/**@brief Function for setting the interrupt-handler that is called when an interrupt is generated. + * + * @param[in] accel_interrupt_handler The handler that is called when an interrupt is generated (called with the event-source). + * + */ +void accel_set_interrupt_handler(accel_interrupt_handler_t accel_interrupt_handler); + + + +/**@brief Function for setting an interrupt operation/event. + * + * @param[in] threshold_mg The threshold in mg to generate an interrupt. + * @param[in] minimal_duration_ms The minimal duration in ms the acceleration has to be above the threshold to generate an interrupt. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_motion_interrupt_parameters(uint16_t threshold_mg, uint16_t minimal_duration_ms); + + +/**@brief Function for setting an interrupt operation/event. + * + * @details This function sets the interrupt according to the input parameter accel_interrupt_event. + * Before it sets the required configuration for the interrupt, it clears the bits of the registers + * that are needed by this function. This allows the switch between different interrupt events without + * problems. + * + * @note The interrupt events ACCEL_SINGLE_TAP_INTERRUPT and ACCEL_DOUBLE_TAP_INTERRUPT are not implemented yet. + * For these two events also a function like accel_set_motion_interrupt_parameters() must be implemented, + * to set the required parameters. + * + * @param[in] accel_interrupt_event The interrupt event that should be enabled. If no interrupt should be generated call with ACCEL_NO_INTERRUPT. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_interrupt(accel_interrupt_event_t accel_interrupt_event); + + +/**@brief Function for resetting the INT1-interrupt source of the accelerometer for latched interrupts. + * + * @details If the interrupt is latched (e.g. motion interrupt) this function must be called to reset the interrupt, + * so that a new interrupt could be generated by the accelerometer. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_reset_interrupt(void); + + + +/**@brief Function for enabling/disabling the internal FIFO of the accelerometer. + * + * @details Enables the stream mode of the internal 32-level FIFO. + * + * @param[in] accel_fifo ACCEL_FIFO_DISABLE or ACCEL_FIFO_ENABLE. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_set_fifo(accel_fifo_t accel_fifo); + + + + +/**@brief Function for reading the current acceleration in mg. + * + * @details This function reads the acceleration data from the accelerometer and converts it to mg. + * If the FIFO is enabled this function reads everything from the FIFO and returns the number of samples + * in the output variable num_samples. If the FIFO is not enabled num_samples is always 1. + * + * @warning The output-buffers (accel_x, accel_y, accel_z) have to be a size of at least 32 elements. + * + * + * @param[out] accel_x Pointer to array with at least 32 entries for acceleration data in x-direction. + * @param[out] accel_y Pointer to array with at least 32 entries for acceleration data in y-direction. + * @param[out] accel_z Pointer to array with at least 32 entries for acceleration data in z-direction. + * @param[out] num_samples Pointer to memory where to store the number of samples read from the accelerometer. + * @param[in] max_num_samples Maximal number of samples to read from the accelerometer. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If the spi-module is busy. + * @retval NRF_ERROR_INTERNAL If there were some internal problems, because the buffers weren't in RAM-section (should not happen!). + */ +ret_code_t accel_read_acceleration(int16_t* accel_x, int16_t* accel_y, int16_t* accel_z, uint8_t* num_samples, uint32_t max_num_samples); + + + +/**@brief Function for testing the accelerometer module. + * + * @details The function configures the accelerometer for a selftest. + * The selftest contains a wake-up interrupt test, so the user + * has to generate a wake-up interrupt by moving the device. + * The selftest can be configured by the following parameters: + * - ACCEL_SELFTEST_TIME_FOR_INTERRUPT_GENERATION_MS: The time the wake-up event has to appear after function-call + * - ACCEL_SELFTEST_INTERRUPT_THRESHOLD_MG: The threshold in mg to generate the wake-up interrupt + * - ACCEL_SELFTEST_INTERRUPT_MINIMAL_DURATION_MS: Minimal duration must be above the threshold, before generating an interrupt + * + * + * @retval 0 If selftest failed. + * @retval 1 If selftest passed. + * + * @note systick_init() has to be called before. + */ +bool accel_selftest(void); + + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/adc_lib.c b/firmware/nRF_badge/data_collector/incl/adc_lib.c new file mode 100644 index 0000000..9cccc18 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/adc_lib.c @@ -0,0 +1,140 @@ +#include "adc_lib.h" + + +#define ADC_PERIPHERAL_NUMBER ADC_COUNT /**< Number of activated adc peripherals in sdk_config.h. */ + +/**@brief The different ADC operations. These operations will be used to set the peripheral busy or not. */ +typedef enum { + ADC_NO_OPERATION = 0, /**< Currently no adc operation ongoing. */ + ADC_READING_OPERATION = (1 << 0), /**< Currently there is an adc operation ongoing. */ +} adc_operation_t; + + +static volatile adc_operation_t adc_operations[ADC_PERIPHERAL_NUMBER] = {0}; /**< Array to save the current adc_operations (needed to check if there is an ongoing adc operation) */ +static const adc_instance_t * adc_default_instances[ADC_PERIPHERAL_NUMBER] = {NULL}; /**< Array of pointers to the current adc_instances (needed to check whether the configuration and input-selection has to be done again) */ +static uint32_t adc_instance_number = 1; /**< adc_instance_number starts at 1 not 0 because all entries in the spi_instances-arrays are 0. So the check for the adc_instance_id-element may not work correctly. */ + + + +ret_code_t adc_init(adc_instance_t* adc_instance, uint8_t default_instance) { + + // Check if the selected peripheral exists + if(adc_instance->adc_peripheral == 0) { + + #if ADC_ENABLED + #else + return NRF_ERROR_INVALID_PARAM; + #endif + } else { + return NRF_ERROR_INVALID_PARAM; + } + + adc_instance->adc_instance_id = adc_instance_number; + if(default_instance) { + adc_default_instances[adc_instance->adc_peripheral] = adc_instance; + nrf_adc_configure((nrf_adc_config_t *) &(adc_default_instances[adc_instance->adc_peripheral]->nrf_adc_config)); + nrf_adc_input_select(adc_default_instances[adc_instance->adc_peripheral]->nrf_adc_config_input); + } + adc_instance_number++; + + + return NRF_SUCCESS; +} + +ret_code_t adc_read_raw(const adc_instance_t* adc_instance, int32_t* raw) { + uint8_t peripheral_index = adc_instance->adc_peripheral; + + // Check if there is an operation ongoing on this peripheral + if(adc_operations[peripheral_index] != ADC_NO_OPERATION){ + return NRF_ERROR_BUSY; + } + + // Set the operation + adc_operations[peripheral_index] = ADC_READING_OPERATION; + + + nrf_adc_configure((nrf_adc_config_t *) &(adc_instance->nrf_adc_config)); + + // Only configure the input, if we have to + nrf_adc_input_select(adc_instance->nrf_adc_config_input); + + // Do the ADC conversion on the configured channel + nrf_adc_start(); + while(!nrf_adc_conversion_finished()); + *raw = nrf_adc_result_get(); + nrf_adc_conversion_event_clean(); + nrf_adc_stop(); + + + // Check if we have an default instance. If yes, configure it again. + if(adc_default_instances[peripheral_index] != NULL) { + nrf_adc_configure((nrf_adc_config_t *) &(adc_default_instances[peripheral_index]->nrf_adc_config)); + nrf_adc_input_select(adc_default_instances[peripheral_index]->nrf_adc_config_input); + } + + + // Reset the ADC Operation + adc_operations[peripheral_index] = ADC_NO_OPERATION; + + + + return NRF_SUCCESS; +} + +ret_code_t adc_read_raw_default(const adc_instance_t* adc_instance, int32_t* raw) { + + + uint8_t peripheral_index = adc_instance->adc_peripheral; + + if(adc_instance->adc_instance_id != adc_default_instances[peripheral_index]->adc_instance_id) + return NRF_ERROR_INVALID_PARAM; + + // Check if there is an operation ongoing on this peripheral + if(adc_operations[peripheral_index] != ADC_NO_OPERATION){ + return NRF_ERROR_BUSY; + } + + // Set the operation + adc_operations[peripheral_index] = ADC_READING_OPERATION; + + // Do the ADC conversion on the configured channel + nrf_adc_start(); + while(!nrf_adc_conversion_finished()); + *raw = nrf_adc_result_get(); + nrf_adc_conversion_event_clean(); + nrf_adc_stop(); + + + // Reset the ADC Operation + adc_operations[peripheral_index] = ADC_NO_OPERATION; + + return NRF_SUCCESS; + +} + +ret_code_t adc_read_voltage(const adc_instance_t* adc_instance, float* voltage, float ref_voltage) { + int32_t raw = 0; + + ret_code_t ret = adc_read_raw(adc_instance, &raw); + + if(ret != NRF_SUCCESS) { + return ret; + } + + nrf_adc_config_scaling_t scaling = (adc_instance->nrf_adc_config).scaling; + nrf_adc_config_resolution_t resolution = (adc_instance->nrf_adc_config).resolution; + + // Compute the actual value of resolution and scaling based on the ADC-Channel setting + uint32_t resolution_as_number = (resolution == NRF_ADC_CONFIG_RES_8BIT) ? 256 : ((resolution == NRF_ADC_CONFIG_RES_9BIT)? 512 : 1024 ); + float scaling_as_number = (scaling == NRF_ADC_CONFIG_SCALING_INPUT_TWO_THIRDS || scaling == NRF_ADC_CONFIG_SCALING_SUPPLY_TWO_THIRDS) ? 0.66666666 : ((scaling == NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD|| scaling == NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD) ? 0.33333333 : 1); + + // Compute the voltage in mV + *voltage = (raw * (ref_voltage / ((float) resolution_as_number)))/scaling_as_number; + + return NRF_SUCCESS; +} + + + + + diff --git a/firmware/nRF_badge/data_collector/incl/adc_lib.h b/firmware/nRF_badge/data_collector/incl/adc_lib.h new file mode 100644 index 0000000..65c52aa --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/adc_lib.h @@ -0,0 +1,109 @@ +#ifndef __ADC_LIB_H +#define __ADC_LIB_H + + +/** @file + * + * @brief ADC abstraction library. + * + * @details It enables to read from the ADC from different contexts. + * If the selected ADC peripheral (currently only only adc_peripheral = 0 is supported) + * is currently in use, it will inform the other context by returning NRF_ERROR_BUSY. + * + */ + + +#include "sdk_common.h" // Needed for the definition of ret_code_t and the error-codes +#include "nrf_adc.h" + + +/**@example Example of adc_instance_t + * + * + * adc_instance_t adc_instance; // Create an adc_instance-struct. + * + * adc_instance.adc_peripheral = 0; // Set the adc_peripheral index to 0 (has to be enabled in sdk_config.h). + * adc_instance.nrf_adc_config.resolution = NRF_ADC_CONFIG_RES_8BIT; // Set the adc resolution. NRF_ADC_CONFIG_RES_8BIT from nrf_adc.h. + * adc_instance.nrf_adc_config.reference = NRF_ADC_CONFIG_REF_EXT_REF1; // Set the adc reference. NRF_ADC_CONFIG_REF_EXT_REF1 from nrf_adc.h. + * adc_instance.nrf_adc_config_input = NRF_ADC_CONFIG_INPUT_6; // Set the adc conversion input. NRF_ADC_CONFIG_INPUT_6 from nrf_adc.h. + * + * adc_init(&adc_instance); // Initialize the adc_instance. + * + */ + + + /**@brief ADC instance type. */ +typedef struct { + uint8_t adc_peripheral; /**< Set to the desired adc peripheral (should be 0 in case of ADC). The Peripheral has to be enabled in the sdk_config.h file */ + nrf_adc_config_t nrf_adc_config; /**< Set the adc configuration: resolution, scaling, reference (possible parameters in nrf_adc.h) */ + nrf_adc_config_input_t nrf_adc_config_input; /**< Set the adc input (possible parameters in nrf_adc.h) */ + int32_t adc_instance_id; /**< Instance index: Setted by the init-function (do not set!) */ +} adc_instance_t; + + +/**@brief Function for initializing an instance for the adc peripheral. + * + * @details This functions actually only checks the specified adc_peripheral and sets the adc_instance_id of adc_instance. + * The application must set adc_peripheral = 0 of the adc_instance (because currently only one adc peripheral is available). + * Furthermore, the application has to set nrf_adc_config, nrf_adc_config_input of adc_instance before calling this function. + * + * + * @param[in,out] adc_instance Pointer to an preconfigured adc_instance. + * @param[in] default_instance Flag if it is the default instance or not (1 or 0). + * + * @details If it is the default instance, every time the adc_read_raw (or adc_read_voltage) + * function is called, it reconfigures the adc-configuration to the default one. + * This is done to achieve very fast readings through the function adc_read_raw_default, + * where no configuration has to be done. + * + * @retval NRF_SUCCESS If the adc_instance was successfully initialized. + * @retval NRF_ERROR_INVALID_PARAM If the specified peripheral in the adc_instance is not correct. + */ +ret_code_t adc_init(adc_instance_t* adc_instance, uint8_t default_instance); + + +/**@brief Function for reading a raw ADC value on the input of the specified adc_instance in blocking mode. + * + * @details This function reads out the raw ADC value of the specified adc_instance in blocking mode. + * It tries to minimize the configuration and input-selection calls to be more efficiently. + * + * @param[in] adc_instance Pointer to an initialized adc_instance. + * @param[out] raw Pointer to memory, where the sampled ADC value, depending on the configuration of the adc_instance and the input voltage, is stored. + * + * @retval NRF_SUCCESS If the ADC read was successful. + * @retval NRF_ERROR_BUSY If the selected ADC peripheral is currently in use. + */ +ret_code_t adc_read_raw(const adc_instance_t* adc_instance, int32_t* raw); + + + +/**@brief Function for reading a raw ADC value on the input of the specified adc_instance in blocking mode (The specified adc_instance has to be the default one!). + * + * @details This function reads out the raw ADC value of the specified adc_instance in blocking mode. + * It tries to minimize the configuration and input-selection calls to be more efficiently. + * + * @param[in] adc_instance Pointer to an initialized adc_instance. + * @param[out] raw Pointer to memory, where the sampled ADC value, depending on the configuration of the adc_instance and the input voltage, is stored. + * + * @retval NRF_SUCCESS If the ADC read was successful. + * @retval NRF_ERROR_BUSY If the selected ADC peripheral is currently in use. + * @retval NRF_ERROR_INVALID_PARAM If the adc_instance is not the default one. + */ +ret_code_t adc_read_raw_default(const adc_instance_t* adc_instance, int32_t* raw); + +/**@brief Function for reading the voltage on the input of specified adc_instance in blocking mode. + * + * @details This function reads out the voltage by calling the adc_read_raw() and + * converts the raw value to a voltage in respect to the reference voltage. + * + * @param[in] adc_instance Pointer to an initialized adc_instance. + * @param[out] voltage Pointer to memory, where the voltage in mV value is stored. + * @param[in] ref_voltage The reference voltage in mV of the adc_instance. + * + * @retval NRF_SUCCESS If the ADC read was successful. + * @retval NRF_ERROR_BUSY If the selected ADC peripheral is currently in use. + */ +ret_code_t adc_read_voltage(const adc_instance_t* adc_instance, float* voltage, float ref_voltage); + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/advertiser_lib.c b/firmware/nRF_badge/data_collector/incl/advertiser_lib.c new file mode 100644 index 0000000..45fde58 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/advertiser_lib.c @@ -0,0 +1,221 @@ +#include "advertiser_lib.h" +#include "ble_lib.h" +#include "string.h" // For memset +#include "storer_lib.h" +#include "debug_lib.h" + + +#define CUSTOM_COMPANY_IDENTIFIER 0xFF00 +#define CUSTOM_ADVDATA_LEN 11 + +/**< Structure for organizing custom badge advertising data */ +typedef struct +{ + uint8_t battery; // scaled so that voltage = 1 + 0.01 * battery + uint8_t status_flags; + uint16_t ID; + uint8_t group; + uint8_t MAC[6]; +} custom_advdata_t; + + +static custom_advdata_t custom_advdata; /**< The custom advertising data structure where the current configuration is stored. */ + + +extern uint16_t crc16_compute(uint8_t const * p_data, uint32_t size, uint16_t const * p_crc); /**< In filesystem_lib.c */ + +static void advertiser_get_default_badge_assignement(BadgeAssignement* badge_assignement) { + badge_assignement->ID = crc16_compute(custom_advdata.MAC, 6, NULL);; + badge_assignement->group = ADVERTISING_DEFAULT_GROUP; +} + + + +void advertiser_init(void) { + memset(&custom_advdata, 0, sizeof(custom_advdata)); + // We need to swap the MAC address to be compatible with old code.. + uint8_t MAC[6]; + ble_get_MAC_address(MAC); + for(uint8_t i = 0; i < 6; i++) { + custom_advdata.MAC[i] = MAC[5-i]; + } + + // Try to read the badge-assignement from the filesystem/storer: + BadgeAssignement badge_assignement; + ret_code_t ret = storer_read_badge_assignement(&badge_assignement); + if(ret != NRF_SUCCESS) { + advertiser_get_default_badge_assignement(&badge_assignement); + debug_log("ADVERTISER: Could not find badge assignement in storage -> Take default: (%u, %u)\n", badge_assignement.ID, badge_assignement.group); + } else { + debug_log("ADVERTISER: Read out badge assignement from storage: (%u, %u)\n", badge_assignement.ID, badge_assignement.group); + } + custom_advdata.group = badge_assignement.group; + custom_advdata.ID = badge_assignement.ID; + + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + +ret_code_t advertiser_start_advertising(void) { + return ble_start_advertising(); +} + +void advertiser_stop_advertising(void) { + ble_stop_advertising(); +} + + +void advertiser_set_battery_voltage(float voltage) { + int32_t scaled_battery_voltage = ((int32_t)(100.0*voltage)) - 100; + scaled_battery_voltage = (scaled_battery_voltage < 0) ? 0 : scaled_battery_voltage; + scaled_battery_voltage = (scaled_battery_voltage > 255) ? 255 : scaled_battery_voltage; + custom_advdata.battery = (uint8_t) scaled_battery_voltage; + + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + + + + +ret_code_t advertiser_set_badge_assignement(BadgeAssignement badge_assignement) { + + uint8_t reset_badge_assignement = 0; + if(badge_assignement.ID == ADVERTISING_RESET_ID && badge_assignement.group == ADVERTISING_RESET_GROUP) { + advertiser_get_default_badge_assignement(&badge_assignement); + debug_log("ADVERTISER: Reset badge assignement -> Take default: (%u, %u)\n", badge_assignement.ID, badge_assignement.group); + reset_badge_assignement = 1; + } + + custom_advdata.ID = badge_assignement.ID; + custom_advdata.group = badge_assignement.group; + + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); + + + // Check if we need to reset the badge assignement, or if we should store it. + if(reset_badge_assignement) { + debug_log("ADVERTISER: Delete badge assignement from storage...\n"); + return storer_clear_badge_assignement(); + } else { // We want to store the new badge assignement + // First read if we have already the correct badge-assignement stored: + BadgeAssignement stored_badge_assignement; + ret_code_t ret = storer_read_badge_assignement(&stored_badge_assignement); + if(ret == NRF_ERROR_INVALID_STATE || ret == NRF_ERROR_INVALID_DATA || (ret == NRF_SUCCESS && (stored_badge_assignement.ID != badge_assignement.ID || stored_badge_assignement.group != badge_assignement.group))) { + // If we have not found any entry or the badge assignement mismatches --> store it. + debug_log("ADVERTISER: Badge assignement missmatch: --> setting the new badge assignement: Old (%u, %u), New (%u, %u)\n", stored_badge_assignement.ID, stored_badge_assignement.group, badge_assignement.ID, badge_assignement.group); + return storer_store_badge_assignement(&badge_assignement); + /* + if(ret == NRF_ERROR_INTERNAL) { + return NRF_ERROR_INTERNAL; + } else if (ret != NRF_SUCCESS) { // There is an error in the configuration of the badge-assignement partition + return NRF_ERROR_NOT_SUPPORTED; + } + */ + } else if(ret == NRF_ERROR_INTERNAL) { + return NRF_ERROR_INTERNAL; + } else { + debug_log("ADVERTISER: Badge assignement already up to date (%u, %u)\n", badge_assignement.ID, badge_assignement.group); + } + } + + return NRF_SUCCESS; +} + +void advertiser_set_status_flag_is_clock_synced(uint8_t is_clock_synced) { + if(is_clock_synced) + custom_advdata.status_flags |= (1 << 0); + else + custom_advdata.status_flags &= ~(1 << 0); + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + + +void advertiser_set_status_flag_microphone_enabled(uint8_t microphone_enabled) { + if(microphone_enabled) + custom_advdata.status_flags |= (1 << 1); + else + custom_advdata.status_flags &= ~(1 << 1); + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + + +void advertiser_set_status_flag_scan_enabled(uint8_t scan_enabled) { + if(scan_enabled) + custom_advdata.status_flags |= (1 << 2); + else + custom_advdata.status_flags &= ~(1 << 2); + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + + +void advertiser_set_status_flag_accelerometer_enabled(uint8_t accelerometer_enabled) { + if(accelerometer_enabled) + custom_advdata.status_flags |= (1 << 3); + else + custom_advdata.status_flags &= ~(1 << 3); + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + +void advertiser_set_status_flag_accelerometer_interrupt_enabled(uint8_t accelerometer_interrupt_enabled) { + if(accelerometer_interrupt_enabled) + custom_advdata.status_flags |= (1 << 4); + else + custom_advdata.status_flags &= ~(1 << 4); + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + +void advertiser_set_status_flag_battery_enabled(uint8_t battery_enabled) { + if(battery_enabled) + custom_advdata.status_flags |= (1 << 5); + else + custom_advdata.status_flags &= ~(1 << 5); + ble_set_advertising_custom_advdata(CUSTOM_COMPANY_IDENTIFIER, (uint8_t*) &custom_advdata, CUSTOM_ADVDATA_LEN); +} + + + + +void advertiser_get_badge_assignement(BadgeAssignement* badge_assignement) { + badge_assignement->ID = custom_advdata.ID; + badge_assignement->group = custom_advdata.group; +} + +void advertiser_get_badge_assignement_from_advdata(BadgeAssignement* badge_assignement, void* custom_advdata) { + custom_advdata_t tmp; + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp, custom_advdata, CUSTOM_ADVDATA_LEN); + + badge_assignement->ID = tmp.ID; + badge_assignement->group = tmp.group; +} + +uint8_t advertiser_get_manuf_data_len(void) { + return (uint8_t) (CUSTOM_ADVDATA_LEN + 2); // + 2 for the CUSTOM_COMPANY_IDENTIFIER +} + + +uint8_t advertiser_get_status_flag_is_clock_synced(void) { + return (custom_advdata.status_flags & (1 << 0)) ? 1 : 0; +} + + +uint8_t advertiser_get_status_flag_microphone_enabled(void) { + return (custom_advdata.status_flags & (1 << 1)) ? 1 : 0; +} + + +uint8_t advertiser_get_status_flag_scan_enabled(void) { + return (custom_advdata.status_flags & (1 << 2)) ? 1 : 0; +} + + +uint8_t advertiser_get_status_flag_accelerometer_enabled(void) { + return (custom_advdata.status_flags & (1 << 3)) ? 1 : 0; +} + +uint8_t advertiser_get_status_flag_accelerometer_interrupt_enabled(void) { + return (custom_advdata.status_flags & (1 << 4)) ? 1 : 0; +} + +uint8_t advertiser_get_status_flag_battery_enabled(void) { + return (custom_advdata.status_flags & (1 << 5)) ? 1 : 0; +} diff --git a/firmware/nRF_badge/data_collector/incl/advertiser_lib.h b/firmware/nRF_badge/data_collector/incl/advertiser_lib.h new file mode 100644 index 0000000..c1625ce --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/advertiser_lib.h @@ -0,0 +1,158 @@ +#ifndef __ADVERTISER_LIB_H +#define __ADVERTISER_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" +#include "common_messages.h" // For BadgeAssignment + + +#define ADVERTISING_DEVICE_NAME "HDBDG" /**< Name of device. Will be included in the advertising data. */ +#define ADVERTISING_INTERVAL_MS 200 /**< The advertising interval. */ +#define ADVERTISING_TIMEOUT_SECONDS 6 /**< The advertising timeout interval. */ + +#define ADVERTISING_RESET_ID 0xFFFF +#define ADVERTISING_RESET_GROUP 0xFF + +#define ADVERTISING_DEFAULT_GROUP 1 + + +/**@brief Function to initialize the advertiser. + * + * @note ble_init() has to be called before. + * @note storer_init() has to be called before. + */ +void advertiser_init(void); + +/**@brief Function to start the advertising process with the parameters: ADVERTISING_DEVICE_NAME, ADVERTISING_INTERVAL_MS, ADVERTISING_TIMEOUT_SECONDS. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +ret_code_t advertiser_start_advertising(void); + +/**@brief Function to stop the advertising process. + */ +void advertiser_stop_advertising(void); + +/**@brief Function to set the badge assignement (ID + group) of the advertising-packet. + * + * @param[in] voltage The battery voltage to set. + */ +void advertiser_set_battery_voltage(float voltage); + +/**@brief Function to set the badge assignement (ID + group) of the advertising-packet. + * + * @details This function also tries to store the badge assignement to the filesystem, if necessary. + * If the badge assignement is ADVERTISING_RESET_ID, ADVERTISING_RESET_GROUP the + * default ID and group will be taken, and the badge-assignement will be deleted from the filesystem. + * + * @param[in] badge_assignement The badge assignement to set. + * + * @retval NRF_SUCCESS If everything was successful. + * @retval NRF_ERROR_INTERNAL If an internal error occured (e.g. busy) --> retry it. + * @retval Another error code Probably of a false configuration of the filesystem partition for the badge assignement. + */ +ret_code_t advertiser_set_badge_assignement(BadgeAssignement badge_assignement); + + +/**@brief Function to set the is_clock_synced-status flag of the advertising-packet. + * + * @param[in] is_clock_synced Flag if clock is synchronized. + */ +void advertiser_set_status_flag_is_clock_synced(uint8_t is_clock_synced); + +/**@brief Function to set the microphone_enabled-status flag of the advertising-packet. + * + * @param[in] microphone_enabled Flag if microphone is enabled. + */ +void advertiser_set_status_flag_microphone_enabled(uint8_t microphone_enabled); + +/**@brief Function to set the scan_enabled-status flag of the advertising-packet. + * + * @param[in] scan_enabled Flag if scanner is enabled. + */ +void advertiser_set_status_flag_scan_enabled(uint8_t scan_enabled); + +/**@brief Function to set the accelerometer_enabled-status flag of the advertising-packet. + * + * @param[in] accelerometer_enabled Flag if accelerometer is enabled. + */ +void advertiser_set_status_flag_accelerometer_enabled(uint8_t accelerometer_enabled); + +/**@brief Function to set the accelerometer_interrupt_enabled-status flag of the advertising-packet. + * + * @param[in] accelerometer_interrupt_enabled Flag if accelerometer interrupt is enabled. + */ +void advertiser_set_status_flag_accelerometer_interrupt_enabled(uint8_t accelerometer_interrupt_enabled); + +/**@brief Function to set the battery_enabled-status flag of the advertising-packet. + * + * @param[in] battery_enabled Flag if battery is enabled. + */ +void advertiser_set_status_flag_battery_enabled(uint8_t battery_enabled); + + +/**@brief Function to retrieve the own badge-assignment. + * + * @param[out] badge_assignement Pointer where to store the badge_assignement. + */ +void advertiser_get_badge_assignement(BadgeAssignement* badge_assignement); + + +/**@brief Function to retrieve the badge assignement from a custom_advdata-packet. + * + * @param[out] badge_assignement Pointer where to store the badge_assignement. + * @param[in] custom_advdata Pointer to custom_advdata. + */ +void advertiser_get_badge_assignement_from_advdata(BadgeAssignement* badge_assignement, void* custom_advdata); + + +/**@brief Function to retrieve the length of the manufacture-data. + * + * @retval Length of the manufacture data. + */ +uint8_t advertiser_get_manuf_data_len(void); + +/**@brief Function to retrieve the is_clock_synced-status flag of the advertising-packet. + * + * @retval 0 If clock is unsynced. + * @retval 1 If clock is synced. + */ +uint8_t advertiser_get_status_flag_is_clock_synced(void); + +/**@brief Function to retrieve the microphone_enabled-status flag of the advertising-packet. + * + * @retval 0 If microphone is not enabled. + * @retval 1 If microphone is enabled. + */ +uint8_t advertiser_get_status_flag_microphone_enabled(void); + +/**@brief Function to retrieve the scan_enabled-status flag of the advertising-packet. + * + * @retval 0 If scanner is not enabled. + * @retval 1 If scanner is enabled. + */ +uint8_t advertiser_get_status_flag_scan_enabled(void); + +/**@brief Function to retrieve the accelerometer-status flag of the advertising-packet. + * + * @retval 0 If accelerometer is not enabled. + * @retval 1 If accelerometer_ is enabled. + */ +uint8_t advertiser_get_status_flag_accelerometer_enabled(void); + +/**@brief Function to retrieve the accelerometer_interrupt-status flag of the advertising-packet. + * + * @retval 0 If accelerometer_interrupt is not enabled. + * @retval 1 If accelerometer_interrupt is enabled. + */ +uint8_t advertiser_get_status_flag_accelerometer_interrupt_enabled(void); + +/**@brief Function to retrieve the battery-status flag of the advertising-packet. + * + * @retval 0 If battery is not enabled. + * @retval 1 If battery is enabled. + */ +uint8_t advertiser_get_status_flag_battery_enabled(void); + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/analog.c b/firmware/nRF_badge/data_collector/incl/analog.c deleted file mode 100644 index 0ab6cbf..0000000 --- a/firmware/nRF_badge/data_collector/incl/analog.c +++ /dev/null @@ -1,46 +0,0 @@ - -#include "analog.h" - -#define V_REF_BATTERY 3.6f -#define BATTERY_READ_RESOLUTION 1024 //Set to 2^10 because ANALOG_CONFIG_VBAT configures ADC to 10-bit resolution. - -float readVDD(void) -{ - // Configure ADC for measuring supply voltage - nrf_adc_config_t nrf_adc_config_bat = ANALOG_CONFIG_VBAT; - nrf_adc_configure(&nrf_adc_config_bat); - - // INPUT_DISABLED as we configure the CONFIG.INPSEL mux to use VDD in our struct above, so we don't need - // an input for the CONFIG.PSEL mux here. (See Figure 71 in NRF51 Reference Guide, Page 165) - int32_t reading = analogRead(NRF_ADC_CONFIG_INPUT_DISABLED); - - nrf_adc_config_t nrf_adc_config_mic = ANALOG_CONFIG_MIC; - nrf_adc_configure(&nrf_adc_config_mic); - - return reading * (V_REF_BATTERY / ((float) BATTERY_READ_RESOLUTION)); -} - -// ADC initialization. -void adc_config(void) -{ - //default: 10bit res, 1/3 prescalar, ext VCC_MIC reference - const nrf_adc_config_t nrf_adc_config = ANALOG_CONFIG_MIC; - - // Initialize and configure ADC - nrf_adc_configure( (nrf_adc_config_t *)&nrf_adc_config); - nrf_adc_input_select(NRF_ADC_CONFIG_INPUT_DISABLED); - -} - -// read an analog input -__attribute__((long_call, section(".data"))) int analogRead(nrf_adc_config_input_t input) -{ - nrf_adc_input_select(input); - nrf_adc_start(); //start conversion - while(!nrf_adc_conversion_finished()); //wait till conversion complete. - int reading = nrf_adc_result_get(); //get reading - //reset - nrf_adc_conversion_event_clean(); - nrf_adc_stop(); - return reading; -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/analog.h b/firmware/nRF_badge/data_collector/incl/analog.h deleted file mode 100644 index da13aa5..0000000 --- a/firmware/nRF_badge/data_collector/incl/analog.h +++ /dev/null @@ -1,52 +0,0 @@ -//sdfsdf - -#ifndef ANALOG_H -#define ANALOG_H - -#include "nrf_drv_config.h" -#include "nrf_soc.h" -//#include "nrf_drv_rtc.h" -//#include "app_error.h" -#include "nrf_adc.h" - -#include "ble_setup.h" -#include "debug_log.h" -#include "rtc_timing.h" - -// Should be defined in custom board files. (default values here in case, e.g., programming NRFDK board) -#if !(defined(MIC_PIN) && defined(MIC_AREF)) - #define MIC_PIN ADC_CONFIG_PSEL_AnalogInput6 //GPIO P05 - #define MIC_AREF NRF_ADC_CONFIG_REF_EXT_REF1 //GPIO P06 -#endif - - -//default: 10bit res, full scale, external VCC_MIC reference -#define ANALOG_CONFIG_MIC { NRF_ADC_CONFIG_RES_8BIT, \ - NRF_ADC_CONFIG_SCALING_INPUT_FULL_SCALE, \ - MIC_AREF } - -// for measuring supply voltage: -#define ANALOG_CONFIG_VBAT {NRF_ADC_CONFIG_RES_10BIT, \ - NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD, \ - NRF_ADC_CONFIG_REF_VBG } - -//Configure ADC to read analog input pins, with MIC_VCC AREF -void adc_config(void); - -/** - * Read an analog input - * Alters ADC configuration, reads battery voltage, then returns to default (analog input pin) configuration - */ -__attribute__((long_call, section(".data"))) int analogRead(nrf_adc_config_input_t input); - -/** - * Reads the battery voltage (VDD) using the ADC. - * This requires a special ADC configuration so it cannot be done with analogRead(). - * Note for most usages, BatteryMonitor_getBatteryVoltage is perfered, as that number is a running average and is - * much more stable. - * - * @return the battery voltage (VDD) as a float - */ -float readVDD(void); - -#endif //TIMING_H \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/app_fifo_util.c b/firmware/nRF_badge/data_collector/incl/app_fifo_util.c deleted file mode 100644 index f26d9a8..0000000 --- a/firmware/nRF_badge/data_collector/incl/app_fifo_util.c +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by Andrew Bartow on 10/16/16. -// -#include - -#include "app_fifo_util.h" - -// Rewinds the given app_fifo len bytes. -void app_fifo_rewind(app_fifo_t * app_fifo, uint16_t len) { - app_fifo->read_pos -= len; -} - -// Returns the number of items currently in the app_fifo. -uint32_t app_fifo_len(app_fifo_t * app_fifo) { - return app_fifo->write_pos - app_fifo->read_pos; -} - - -// Reads at most len bytes from app_fifo into dest. -// Returns the number of bytes that were read into dest. -uint16_t app_fifo_get_bytes(app_fifo_t * app_fifo, uint8_t * dest, uint16_t len) { - APP_ERROR_CHECK_BOOL(app_fifo != NULL); - APP_ERROR_CHECK_BOOL(dest != NULL); - - uint16_t num_bytes_read = 0; - for (int i = 0; i < len; i ++) { - if (app_fifo_get(app_fifo, &dest[i]) != NRF_SUCCESS) { - break; - } - - num_bytes_read++; - } - - return num_bytes_read; -} - -// Puts len bytes from data into app_fifo. -// Returns the number of bytes from data that were put into app_fifo. -uint16_t app_fifo_put_bytes(app_fifo_t * app_fifo, const uint8_t * data, const uint16_t len) { - APP_ERROR_CHECK_BOOL(app_fifo != NULL); - APP_ERROR_CHECK_BOOL(data != NULL); - - uint16_t num_bytes_put = 0; - for (uint16_t i = 0; i < len; i++) { - if (app_fifo_put(app_fifo, data[i]) != NRF_SUCCESS) { - break; - } - - num_bytes_put++; - } - - return num_bytes_put; -} - -// Puts the len bytes at data into the app_fifo, discarding existing data as needed. -// If len >= app_fifo_len(app_fifo), app_fifo will contain the last app_fifo_len bytes of data in order.' -// Returns NRF_SUCCESS if completed successfully. -uint32_t app_fifo_put_bytes_circular(app_fifo_t * app_fifo, const uint8_t * data, const uint16_t len) { - APP_ERROR_CHECK_BOOL(app_fifo != NULL); - APP_ERROR_CHECK_BOOL(data != NULL); - - for (uint16_t i = 0; i < len; i++) { - uint32_t err_code = app_fifo_put(app_fifo, data[i]); - - if (err_code != NRF_SUCCESS) { - if (err_code == NRF_ERROR_NO_MEM) { - // app_fifo is full, remove last byte and try again. - uint8_t byteToDiscard; - APP_ERROR_CHECK(app_fifo_get(app_fifo, &byteToDiscard)); - APP_ERROR_CHECK(app_fifo_put(app_fifo, data[i])); - } else { - APP_ERROR_CHECK(err_code); - } - } - } - - return NRF_SUCCESS; -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/app_fifo_util.h b/firmware/nRF_badge/data_collector/incl/app_fifo_util.h deleted file mode 100644 index 5d61b04..0000000 --- a/firmware/nRF_badge/data_collector/incl/app_fifo_util.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// Created by Andrew Bartow on 10/16/16. -// - -#include "app_fifo.h" - -#ifndef OPENBADGE_APP_FIFO_UTIL_H -#define OPENBADGE_APP_FIFO_UTIL_H - -// Rewinds the given app_fifo len bytes. -void app_fifo_rewind(app_fifo_t * app_fifo, uint16_t len); - -// Returns the number of items currently in the app_fifo. -uint32_t app_fifo_len(app_fifo_t * app_fifo); - -// Reads at most len bytes from app_fifo into dest. -// Returns the number of bytes that were read into dest. -uint16_t app_fifo_get_bytes(app_fifo_t * app_fifo, uint8_t * dest, uint16_t len); - -// Puts len bytes from data into app_fifo. -// Returns the number of bytes from data that were put into app_fifo. -uint16_t app_fifo_put_bytes(app_fifo_t * app_fifo, const uint8_t * data, const uint16_t len); - -// Puts the len bytes at data into the app_fifo, discarding existing data as needed. -// If len >= app_fifo_len(app_fifo), app_fifo will contain the last app_fifo_len bytes of data in order.' -// Returns NRF_SUCCESS if completed successfully. -uint32_t app_fifo_put_bytes_circular(app_fifo_t * app_fifo, const uint8_t * data, const uint16_t len); - -#endif //OPENBADGE_APP_FIFO_UTIL_H diff --git a/firmware/nRF_badge/data_collector/incl/badge_03v4.h b/firmware/nRF_badge/data_collector/incl/badge_03v4.h index 37a2b5f..b2efbbd 100644 --- a/firmware/nRF_badge/data_collector/incl/badge_03v4.h +++ b/firmware/nRF_badge/data_collector/incl/badge_03v4.h @@ -14,40 +14,31 @@ #ifndef BADGE_03V4 #define BADGE_03V4 -// LEDs definitions for badge_03v4 -#define LEDS_NUMBER 2 - -#define LED_1 8 -#define LED_2 9 #define GREEN_LED LED_1 #define RED_LED LED_2 +#define LEDS_NUMBER 2 +#define LED_1 8 +#define LED_2 9 -#define BUTTONS_NUMBER 1 - -#define BUTTON_1 2 - - +#define UART_RX_PIN 11 +#define UART_TX_PIN 10 +#define UART_CTS_PIN 0 +#define UART_RTS_PIN 0 +#define HWFC_ENABLED 0 -#define RX_PIN_NUMBER 11 -#define TX_PIN_NUMBER 10 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false -#define SPI_MASTER_0_ENABLE 1 #define SPIM0_MISO_PIN 1 // SPI MISO signal. -#define SPIM0_SS_PIN 0 // SPI CSN signal. #define SPIM0_MOSI_PIN 4 // SPI MOSI signal. #define SPIM0_SCK_PIN 3 // SPI SCK signal. -#define INT_PIN_INPUT 25 // accelerator interrupt pin. This board has no accelerator, but we set this value to make compilation simpler +#define SPIM0_ACCELEROMETER_SS_PIN 2 // SPI CSN signal for accelerometer. +#define SPIM0_EEPROM_SS_PIN 0 // SPI CSN signal for eeprom. +#define MIC_PIN ADC_CONFIG_PSEL_AnalogInput6 //GPIO P05 +#define MIC_AREF NRF_ADC_CONFIG_REF_EXT_REF1 //GPIO P06 -#define MIC_PIN ADC_CONFIG_PSEL_AnalogInput6 //GPIO P05 -#define MIC_AREF NRF_ADC_CONFIG_REF_EXT_REF1 //GPIO P06 -#define EXIST_ACCL false // board has accelerator? #endif // BADGE_03V4 diff --git a/firmware/nRF_badge/data_collector/incl/badge_03v6.h b/firmware/nRF_badge/data_collector/incl/badge_03v6.h index c542d20..5df3aa5 100644 --- a/firmware/nRF_badge/data_collector/incl/badge_03v6.h +++ b/firmware/nRF_badge/data_collector/incl/badge_03v6.h @@ -15,27 +15,31 @@ #define GREEN_LED LED_1 #define RED_LED LED_2 -#define LEDS_NUMBER 2 +#define LEDS_NUMBER 2 -#define LED_1 8 +#define LED_1 8 #define LED_2 9 -#define RX_PIN_NUMBER 11 -#define TX_PIN_NUMBER 10 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false +#define UART_RX_PIN 11 +#define UART_TX_PIN 10 +#define UART_CTS_PIN 0 +#define UART_RTS_PIN 0 +#define HWFC_ENABLED 0 + -#define SPI_MASTER_0_ENABLE 1 #define SPIM0_MISO_PIN 1 // SPI MISO signal. -#define SPIM0_SS_PIN 0 // SPI CSN signal. #define SPIM0_MOSI_PIN 4 // SPI MOSI signal. #define SPIM0_SCK_PIN 3 // SPI SCK signal. -#define INT_PIN_INPUT 25 // accelerator interrupt pin +#define SPIM0_ACCELEROMETER_SS_PIN 2 // SPI CSN signal for accelerometer. +#define SPIM0_EEPROM_SS_PIN 0 // SPI CSN signal for eeprom. + +#define ACCELEROMETER_INT_PIN 25 // accelerator interrupt pin + + +#define MIC_PIN ADC_CONFIG_PSEL_AnalogInput6 //GPIO P05 +#define MIC_AREF NRF_ADC_CONFIG_REF_EXT_REF1 //GPIO P06 -#define MIC_PIN ADC_CONFIG_PSEL_AnalogInput6 //GPIO P05 -#define MIC_AREF NRF_ADC_CONFIG_REF_EXT_REF1 //GPIO P06 -#define EXIST_ACCL true // board has accelerator? +#define ACCELEROMETER_PRESENT 1 // board has accelerator? #endif // BADGE_03V6 diff --git a/firmware/nRF_badge/data_collector/incl/battery.c b/firmware/nRF_badge/data_collector/incl/battery.c deleted file mode 100644 index fd3a0e7..0000000 --- a/firmware/nRF_badge/data_collector/incl/battery.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "app_timer.h" - -#include "analog.h" -#include "battery.h" -#include "rtc_timing.h" - -// Read the battery once every 15 seconds. -// I profiled the sample operation and determined it took ~120 microseconds to complete one read. -// So a reading every 15 seconds should pretty negligible and also produce very good data. -#define BATTERY_READ_INTERVAL_MILLIS (15 * 1000) -#define BATTERY_SAMPLES_PER_AVERAGE 5 - -static float mBatteryAverage; - -static uint32_t mBatteryMonitorTimer; - -static void on_battery_sample(void * p_context) { - float vdd = readVDD(); - mBatteryAverage -= mBatteryAverage * (1.f / (float) BATTERY_SAMPLES_PER_AVERAGE); - mBatteryAverage += vdd * (1.f / (float) BATTERY_SAMPLES_PER_AVERAGE); - - debug_log(" Read battery: %dmV (raw: %dmV).\r\n", (int) (1000.f * mBatteryAverage), (int) (1000.f * vdd)); - updateAdvData(); -} - -void BatteryMonitor_init(void) { - app_timer_create(&mBatteryMonitorTimer, APP_TIMER_MODE_REPEATED, on_battery_sample); - app_timer_start(mBatteryMonitorTimer, APP_TIMER_TICKS(BATTERY_READ_INTERVAL_MILLIS, APP_PRESCALER), NULL); - - mBatteryAverage = readVDD(); - debug_log("Battery: %dmV.\r\n", (int) (1000.0 * mBatteryAverage)); -} - -float BatteryMonitor_getBatteryVoltage(void) { - return mBatteryAverage; -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/battery.h b/firmware/nRF_badge/data_collector/incl/battery.h deleted file mode 100644 index 0904b7f..0000000 --- a/firmware/nRF_badge/data_collector/incl/battery.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Created by Andrew Bartow on 12/14/16. -// - -#ifndef OPENBADGE_BATTERY_H -#define OPENBADGE_BATTERY_H - -/* - * Initializes the battery monitoring service. - * - * Depends on ADC already being initialized. - */ -void BatteryMonitor_init(void); - -/* - * Returns the current supply voltage of the battery. Should be used in place of getBatteryVoltage in analog.c - * - * Note: this value is a running average across several battery readings. - */ -float BatteryMonitor_getBatteryVoltage(void); - -#endif //OPENBADGE_BATTERY_H diff --git a/firmware/nRF_badge/data_collector/incl/battery_lib.c b/firmware/nRF_badge/data_collector/incl/battery_lib.c new file mode 100644 index 0000000..5de4a9d --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/battery_lib.c @@ -0,0 +1,120 @@ +#include "battery_lib.h" + +#include "adc_lib.h" +#include "systick_lib.h" // Needed for battery_selftest() +#include "app_timer.h" +#include "app_scheduler.h" +#include "advertiser_lib.h" +#include "app_util_platform.h" +#include "debug_lib.h" + +#define BATTERY_PERIOD_MS 10000 +#define BATTERY_REFERENCE_VOLTAGE 1.2f /**< The internal reference voltage (NRF_ADC_CONFIG_REF_VBG) */ +#define BATTERY_SAMPLES_PER_AVERAGE 5 +#define BATTERY_SELFTEST_NUM_VOLTAGE_MEASUREMENTS 10 /**< Number of measurements for the selftest */ + +static adc_instance_t adc_instance; +static volatile float average_voltage = 0; + +APP_TIMER_DEF(internal_battery_timer); +static void internal_battery_callback(void* p_context); +static void update_advertiser_voltage(void * p_event_data, uint16_t event_size); + +ret_code_t battery_init(void) { + adc_instance.adc_peripheral = 0; + adc_instance.nrf_adc_config.resolution = NRF_ADC_CONFIG_RES_10BIT; + adc_instance.nrf_adc_config.scaling = NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD; + adc_instance.nrf_adc_config.reference = NRF_ADC_CONFIG_REF_VBG; + adc_instance.nrf_adc_config_input = NRF_ADC_CONFIG_INPUT_DISABLED; + + ret_code_t ret = adc_init(&adc_instance, 0); + if(ret != NRF_SUCCESS) return ret; + + // create a timer for periodic battery measurement + ret = app_timer_create(&internal_battery_timer, APP_TIMER_MODE_REPEATED, internal_battery_callback); + if(ret != NRF_SUCCESS) return ret; + + // Now start the timer + ret = app_timer_start(internal_battery_timer, APP_TIMER_TICKS(BATTERY_PERIOD_MS, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + +float battery_get_voltage(void) { + float tmp = 0; + CRITICAL_REGION_ENTER(); + tmp = average_voltage; + CRITICAL_REGION_EXIT(); + return tmp; +} + +/**@brief Function to read the current supply voltage in Volts. Internally the voltage is averaged. + * + * @param[out] voltage Read supply voltage in Volts. + * + * @retval NRF_SUCCESS On success. + * @retval NRF_ERROR_BUSY If the ADC-interface is busy. + */ +static ret_code_t battery_read_voltage(float* voltage) { + ret_code_t ret = adc_read_voltage(&adc_instance, voltage, BATTERY_REFERENCE_VOLTAGE); + if(ret != NRF_SUCCESS) return ret; + + + static uint8_t first_read = 1; + if(first_read) { + first_read = 0; + CRITICAL_REGION_ENTER(); + average_voltage = *voltage; + CRITICAL_REGION_EXIT(); + return NRF_SUCCESS; + } + + + CRITICAL_REGION_ENTER(); + average_voltage -= average_voltage * (1.f / (float) BATTERY_SAMPLES_PER_AVERAGE); + average_voltage += (*voltage) * (1.f / (float) BATTERY_SAMPLES_PER_AVERAGE); + *voltage = average_voltage; + CRITICAL_REGION_EXIT(); + + debug_log("BATTERY: Average battery voltage: %f\n", average_voltage); + + + return NRF_SUCCESS; +} + + +static void internal_battery_callback(void* p_context) { + float voltage = 0; + ret_code_t ret = battery_read_voltage(&voltage); + if(ret != NRF_SUCCESS) return; + + // Here we want to update the advertising data, but this should be done in main-context not in timer context. + app_sched_event_put(NULL, 0, update_advertiser_voltage); +} + +static void update_advertiser_voltage(void * p_event_data, uint16_t event_size) { + float tmp = 0; + CRITICAL_REGION_ENTER(); + tmp = average_voltage; + CRITICAL_REGION_EXIT(); + // Update the advertising data battery-voltage + advertiser_set_battery_voltage(tmp); +} + + + +bool battery_selftest(void) { + + ret_code_t ret; + for(uint32_t i = 0; i < BATTERY_SELFTEST_NUM_VOLTAGE_MEASUREMENTS; i++) { + float voltage; + ret = battery_read_voltage(&voltage); + if(ret != NRF_SUCCESS) + return 0; + if(voltage > 4.0 || voltage < 1.0) + return 0; + systick_delay_millis(10); + } + return 1; +} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/battery_lib.h b/firmware/nRF_badge/data_collector/incl/battery_lib.h new file mode 100644 index 0000000..e00b22b --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/battery_lib.h @@ -0,0 +1,36 @@ +#ifndef __BATERRY_LIB_H +#define __BATERRY_LIB_H + +#include +#include "sdk_errors.h" + +/**@brief Function to initialize the battery-module (with ADC) + * + * @retval NRF_SUCCESS If the timer was successfully created. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or + * the timer is running. + * @retval NRF_ERROR_NO_MEM If the timer operations queue was full. + * @retval NRF_ERROR_INVALID_PARAM If the specified peripheral in the adc_instance is not correct. + * + * @note App-timer has to be initialized before! + * @note App-scheduler has to be initialized before! + */ +ret_code_t battery_init(void); + +/**@brief Function to retrieve the internal averaged battery voltage. + * + * @retval The current averaged battery voltage. + */ +float battery_get_voltage(void); + + +/**@brief Function for testing the battery-module. + * + * @retval 0 If selftest failed. + * @retval 1 If selftest passed. + * + * @note systick_init() has to be called before. + */ +bool battery_selftest(void); + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/ble_lib.c b/firmware/nRF_badge/data_collector/incl/ble_lib.c new file mode 100644 index 0000000..7353e3a --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/ble_lib.c @@ -0,0 +1,479 @@ +#include "ble_lib.h" +#include "advertiser_lib.h" // For the advertising configuration + +#include "debug_lib.h" + +#include "system_event_lib.h" + +#include "ble_advertising.h" +#include "ble_gap.h" +#include "ble_nus.h" +#include "softdevice_handler.h" // Needed for softdevice_ble_evt_handler_set() +#include "ble_hci.h" // Needed for BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION in disconnect() + + + + +#define SEC_PARAM_BOND 1 /**< Perform bonding. */ +#define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */ +#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */ +#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */ +#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */ +#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */ + + + + + +static ble_uuid_t adv_uuids[] = {{BLE_UUID_NUS_SERVICE, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers of the NUS service for advertising. */ +static ble_gap_sec_params_t ble_sec_params; /**< Security requirements for this application. */ +static ble_nus_t ble_nus; /**< Struct for Nordic UART Service module. */ +static uint16_t ble_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ + +static uint8_t ble_state; /**< The current BLE-state. */ + + + +static ble_on_receive_callback_t external_ble_on_receive_callback = NULL; /**< The external on receive callback function */ +static ble_on_transmit_callback_t external_ble_on_transmit_callback = NULL; /**< The external on transmit callback function */ +static ble_on_connect_callback_t external_ble_on_connect_callback = NULL; /**< The external on connect callback function */ +static ble_on_disconnect_callback_t external_ble_on_disconnect_callback = NULL; /**< The external on disconnect callback function */ +static ble_on_scan_timeout_callback_t external_ble_on_scan_timeout_callback = NULL; /**< The external on scan timeout callback function */ +static ble_on_scan_report_callback_t external_ble_on_scan_report_callback = NULL; /**< The external on scan report callback function */ + + +static ret_code_t ble_init_services(void); +static ret_code_t ble_init_advertising(void); +static void ble_prepare_advertising_data(ble_advdata_t* advdata); + + + + +static void ble_nus_on_receive_callback(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length); +static void ble_nus_on_transmit_complete_callback(void); +static void ble_on_connect_callback(void); +static void ble_on_disconnect_callback(void); +static void ble_on_scan_report_callback(ble_gap_evt_adv_report_t* scan_report); +static void ble_on_scan_timeout_callback(void); + + +static void ble_evt_handler(ble_evt_t * p_ble_evt); + + + +ret_code_t ble_init(void) { + + // TODO: call in main.c + system_event_init(); + + + ble_state = BLE_STATE_INACTIVE; + + + ret_code_t ret; + + ble_enable_params_t ble_enable_params; + memset(&ble_enable_params, 0, sizeof(ble_enable_params)); + ret = softdevice_enable_get_default_config(0, 1, &ble_enable_params); + if(ret != NRF_SUCCESS) return ret; + ble_enable_params.gatts_enable_params.service_changed = IS_SRVC_CHANGED_CHARACT_PRESENT; + + // Enable the BLE + ret = softdevice_enable(&ble_enable_params); + //debug_log("BLE: Ret softdevice_enable %u\n", ret); + if(ret != NRF_SUCCESS) return ret; + + + // Register with the SoftDevice handler module for BLE events. + ret = softdevice_ble_evt_handler_set(ble_evt_handler); + if(ret != NRF_SUCCESS) return ret; + + + // Initializing the security params + ble_sec_params.bond = SEC_PARAM_BOND; + ble_sec_params.mitm = SEC_PARAM_MITM; + ble_sec_params.io_caps = SEC_PARAM_IO_CAPABILITIES; + ble_sec_params.oob = SEC_PARAM_OOB; + ble_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE; + ble_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE; + + + + ret = ble_init_advertising(); + //debug_log("BLE: Ret init advertising %u\n", ret); + if(ret != NRF_SUCCESS) return ret; + + + ret = ble_init_services(); + //debug_log("BLE: Ret init services %u\n", ret); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + + +/**@brief Function to initialize the Nordic Uart Service. + + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +static ret_code_t ble_init_services(void) { + + ble_nus_init_t nus_init; //Nordic UART Service - for emulating a UART over BLE + memset(&nus_init,0,sizeof(nus_init)); + + + nus_init.data_handler = ble_nus_on_receive_callback; + + + ret_code_t ret; + ret = ble_nus_init(&ble_nus, &nus_init); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + +/**@brief Function to initialize the BLE advertising-process. + + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +static ret_code_t ble_init_advertising(void) { + ret_code_t ret; + + // The sys-event handler is needed to synchronize flash and advertising processes. + ret = system_event_register_handler(ble_advertising_on_sys_evt); + if(ret != NRF_SUCCESS) return ret; + + + ble_gap_conn_sec_mode_t sec_mode; + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); //no security needed + + //set BLE name + ret = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *) ADVERTISING_DEVICE_NAME, strlen(ADVERTISING_DEVICE_NAME)); + if(ret != NRF_SUCCESS) return ret; + + //set BLE appearance + ret = sd_ble_gap_appearance_set(BLE_APPEARANCE_GENERIC_TAG); + if(ret != NRF_SUCCESS) return ret; + + + + + // Build advertising data struct to pass into @ref ble_advertising_init. + ble_advdata_t advdata; + ble_prepare_advertising_data(&advdata); + + + ble_adv_modes_config_t options = {0}; + options.ble_adv_fast_enabled = 1; + options.ble_adv_fast_interval = (uint32_t)(ADVERTISING_INTERVAL_MS/0.625f); // In units of 0.625ms + options.ble_adv_fast_timeout = ADVERTISING_TIMEOUT_SECONDS; + + + ret = ble_advertising_init(&advdata, NULL, &options, NULL /*on_adv_evt*/, NULL); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + +/**@brief Function to prepare the advertising data for advertising. + * + * @param[in] advdata Pointer to ble_advdata_t-structure that should be prepared. + */ +static void ble_prepare_advertising_data(ble_advdata_t* advdata) { + memset(advdata, 0, sizeof(ble_advdata_t)); + + advdata->name_type = BLE_ADVDATA_FULL_NAME; + advdata->include_appearance = 0; + advdata->flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + advdata->uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]); + advdata->uuids_complete.p_uuids = adv_uuids; +} + + +ret_code_t ble_set_advertising_custom_advdata(uint16_t company_identifier, uint8_t* custom_advdata, uint16_t custom_advdata_size) { + ble_advdata_manuf_data_t custom_manuf_data; + custom_manuf_data.company_identifier = company_identifier; + custom_manuf_data.data.p_data = custom_advdata; + custom_manuf_data.data.size = custom_advdata_size; + + ble_advdata_t advdata; + ble_prepare_advertising_data(&advdata); + + advdata.p_manuf_specific_data = &custom_manuf_data; + + return ble_advdata_set(&advdata, NULL); + +} + + +ret_code_t ble_start_advertising(void) { + ret_code_t ret = ble_advertising_start(BLE_ADV_MODE_FAST); + if(ret != NRF_SUCCESS) return ret; + + + ble_state |= BLE_STATE_ADVERTISING; + + return ret; +} + +void ble_stop_advertising(void) { + // Advertising can't be stopped directly, but if an advertising timeout occurs, the advertising shouldn't be started again. + ble_state &= ~BLE_STATE_ADVERTISING; +} + + + +ret_code_t ble_start_scanning(uint16_t scan_interval_ms, uint16_t scan_window_ms, uint16_t scan_duration_seconds) { + ble_gap_scan_params_t scan_params; + + scan_params.active = 0; // passive scanning, only looking for advertising packets + scan_params.selective = 0; // non-selective, don't use whitelist + scan_params.p_whitelist = NULL; // no whitelist + scan_params.interval = (uint16_t)((((uint32_t)(scan_interval_ms)) * 1000) / 625); // scan_params uses interval in units of 0.625ms + scan_params.window = (uint16_t)((((uint32_t)(scan_window_ms)) * 1000) / 625); // window also in units of 0.625ms + scan_params.timeout = scan_duration_seconds; // timeout is in s + + sd_ble_gap_scan_stop(); // stop any in-progress scans + + ret_code_t ret = sd_ble_gap_scan_start(&scan_params); // start a new scan + + if(ret != NRF_SUCCESS) return ret; + + ble_state |= BLE_STATE_SCANNING; + + return NRF_SUCCESS; +} + +void ble_stop_scanning(void) { + sd_ble_gap_scan_stop(); // stop any in-progress scans + + ble_state &= ~BLE_STATE_SCANNING; +} + + + +/**@brief The BLE-event handler that is called when there is a BLE-event generated by the BLE-Stack/Softdevice. + * + * @param[in] p_ble_evt Pointer to the BLE-event generated by the Softdevice. + */ +static void ble_evt_handler(ble_evt_t * p_ble_evt) { + + ble_nus_on_ble_evt(&ble_nus, p_ble_evt); + + + uint32_t ret; + + // security-related + static ble_gap_evt_auth_status_t m_auth_status; + bool master_id_matches; + ble_gap_sec_kdist_t * p_distributed_keys; + ble_gap_enc_info_t * p_enc_info; + ble_gap_irk_t * p_id_info; + ble_gap_sign_info_t * p_sign_info; + static ble_gap_enc_key_t m_enc_key; /**< Encryption Key (Encryption Info and Master ID). */ + static ble_gap_id_key_t m_id_key; /**< Identity Key (IRK and address). */ + static ble_gap_sign_info_t m_sign_key; /**< Signing Key (Connection Signature Resolving Key). */ + static ble_gap_sec_keyset_t m_keys = {.keys_own = {&m_enc_key, &m_id_key, &m_sign_key}}; + + + switch (p_ble_evt->header.evt_id) + { + case BLE_EVT_TX_COMPLETE: + ble_nus_on_transmit_complete_callback(); + break; + case BLE_GAP_EVT_CONNECTED: //on BLE connect event + debug_log("BLE: connection intervals: %u, %u, %u, %u\n", p_ble_evt->evt.gap_evt.params.connected.conn_params.min_conn_interval, p_ble_evt->evt.gap_evt.params.connected.conn_params.max_conn_interval, p_ble_evt->evt.gap_evt.params.connected.conn_params.slave_latency, p_ble_evt->evt.gap_evt.params.connected.conn_params.conn_sup_timeout); + ble_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + ble_on_connect_callback(); + break; + case BLE_GAP_EVT_DISCONNECTED: //on BLE disconnect event + ble_conn_handle = BLE_CONN_HANDLE_INVALID; + ble_on_disconnect_callback(); + break; + case BLE_GAP_EVT_ADV_REPORT: //On receipt of a response to an advertising request (during a scan) + ble_on_scan_report_callback(&(p_ble_evt->evt.gap_evt.params.adv_report)); + break; + case BLE_GAP_EVT_TIMEOUT: + if(p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) { + ble_on_scan_timeout_callback(); + } + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + ret = sd_ble_gap_sec_params_reply(ble_conn_handle, + BLE_GAP_SEC_STATUS_SUCCESS, + &ble_sec_params, + &m_keys); + (void) ret; + break; + case BLE_GATTS_EVT_SYS_ATTR_MISSING: + ret = sd_ble_gatts_sys_attr_set(ble_conn_handle, + NULL, + 0, + BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS); + (void) ret; + break; + case BLE_GAP_EVT_AUTH_STATUS: + m_auth_status = p_ble_evt->evt.gap_evt.params.auth_status; + break; + case BLE_GAP_EVT_SEC_INFO_REQUEST: + master_id_matches = memcmp(&p_ble_evt->evt.gap_evt.params.sec_info_request.master_id, + &m_enc_key.master_id, + sizeof(ble_gap_master_id_t)) == 0; + p_distributed_keys = &m_auth_status.kdist_own; + + p_enc_info = (p_distributed_keys->enc && master_id_matches) ? &m_enc_key.enc_info : NULL; + p_id_info = (p_distributed_keys->id && master_id_matches) ? &m_id_key.id_info : NULL; + p_sign_info = (p_distributed_keys->sign && master_id_matches) ? &m_sign_key : NULL; + + ret = sd_ble_gap_sec_info_reply(ble_conn_handle, p_enc_info, p_id_info, p_sign_info); + (void) ret; + break; + default: + break; + } + + + + // Intercept advertising timeout events to implement infinite advertising, and disconnect events to let radio-conflicting + // operations to execute after a connection closes + if( (p_ble_evt->header.evt_id == BLE_GAP_EVT_TIMEOUT // if timeout event + && p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISING) // from advertising + || p_ble_evt->header.evt_id == BLE_GAP_EVT_DISCONNECTED) { // OR if disconnect event + + + // Restart the advertiting + if(ble_state & BLE_STATE_ADVERTISING) { + ble_start_advertising(); + } + } else { + ble_advertising_on_ble_evt(p_ble_evt); + } + + + + +} + + + + +/**@brief Handler function that is called when some data were received via the Nordic Uart Service. + * + * @param[in] p_nus Pointer to the nus-identifier. + * @param[in] p_data Pointer to the received data. + * @param[in] length Length of the received data. + */ +static void ble_nus_on_receive_callback(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length) { + if(external_ble_on_receive_callback != NULL) + external_ble_on_receive_callback(p_data, length); +} + +/**@brief Handler function that is called when data were transmitted via the Nordic Uart Service. + */ +static void ble_nus_on_transmit_complete_callback(void) { + if(external_ble_on_transmit_callback != NULL) + external_ble_on_transmit_callback(); +} + +/**@brief Handler function that is called when a BLE-connection was established. + */ +static void ble_on_connect_callback(void) { + ble_state |= BLE_STATE_CONNECTED; + if(external_ble_on_connect_callback != NULL) + external_ble_on_connect_callback(); +} + +/**@brief Handler function that is called when disconnecting from an exisiting BLE-connection. + */ +static void ble_on_disconnect_callback(void) { + ble_state &= ~BLE_STATE_CONNECTED; + if(external_ble_on_disconnect_callback != NULL) + external_ble_on_disconnect_callback(); +} + +/**@brief Handler function that is called when there is an advertising report event during scanning. + * + * @param[in] scan_report Pointer to the advertsising report event. + */ +static void ble_on_scan_report_callback(ble_gap_evt_adv_report_t* scan_report) { + if(external_ble_on_scan_report_callback != NULL) + external_ble_on_scan_report_callback(scan_report); + + //debug_log("BLE: BLE on scan report callback. RSSI: %d\n", scan_report->rssi); +} + +/**@brief Handler function that is called when the scan process timed-out. + */ +static void ble_on_scan_timeout_callback(void) { + ble_state &= ~BLE_STATE_SCANNING; + if(external_ble_on_scan_timeout_callback != NULL) + external_ble_on_scan_timeout_callback(); + + //debug_log("BLE: BLE on scan timeout callback\n"); +} + + + +// BLE_GAP_ADDR_LEN = 6 from ble_gap.h +void ble_get_MAC_address(uint8_t* MAC_address) { + ble_gap_addr_t MAC; + sd_ble_gap_address_get(&MAC); + + for(uint8_t i = 0; i < BLE_GAP_ADDR_LEN; i++) { + MAC_address[i] = MAC.addr[BLE_GAP_ADDR_LEN - 1 - i]; + } +} + + + +ble_state_t ble_get_state(void) { + if(ble_state & BLE_STATE_CONNECTED) { // BLE_STATE_CONNECTED has higher priority than BLE_STATE_SCANNING and BLE_STATE_ADVERTISING! + return BLE_STATE_CONNECTED; + } else if (ble_state & BLE_STATE_SCANNING) { // BLE_STATE_SCANNING has higher priority than BLE_STATE_ADVERTISING! + return BLE_STATE_SCANNING; + } else if(ble_state & BLE_STATE_ADVERTISING) { + return BLE_STATE_ADVERTISING; + } + return BLE_STATE_INACTIVE; +} + + + +ret_code_t ble_transmit(uint8_t* data, uint16_t len) { + ret_code_t ret = ble_nus_string_send(&ble_nus, data, len); + + return ret; +} + + +void ble_disconnect(void) { + sd_ble_gap_disconnect(ble_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); +} + + + +void ble_set_on_receive_callback(ble_on_receive_callback_t ble_on_receive_callback) { + external_ble_on_receive_callback = ble_on_receive_callback; +} + +void ble_set_on_transmit_callback(ble_on_transmit_callback_t ble_on_transmit_callback) { + external_ble_on_transmit_callback = ble_on_transmit_callback; +} + +void ble_set_on_connect_callback(ble_on_connect_callback_t ble_on_connect_callback) { + external_ble_on_connect_callback = ble_on_connect_callback; +} + +void ble_set_on_disconnect_callback(ble_on_disconnect_callback_t ble_on_disconnect_callback) { + external_ble_on_disconnect_callback = ble_on_disconnect_callback; +} + +void ble_set_on_scan_timeout_callback(ble_on_scan_timeout_callback_t ble_on_scan_timeout_callback) { + external_ble_on_scan_timeout_callback = ble_on_scan_timeout_callback; +} + +void ble_set_on_scan_report_callback(ble_on_scan_report_callback_t ble_on_scan_report_callback) { + external_ble_on_scan_report_callback = ble_on_scan_report_callback; +} + diff --git a/firmware/nRF_badge/data_collector/incl/ble_lib.h b/firmware/nRF_badge/data_collector/incl/ble_lib.h new file mode 100644 index 0000000..1dcd69e --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/ble_lib.h @@ -0,0 +1,178 @@ +#ifndef __BLE_LIB_H +#define __BLE_LIB_H + +#include +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes +#include "ble_gap.h" // Needed for ble_gap_evt_adv_report_t-definition + + + + + + + +#define IS_SRVC_CHANGED_CHARACT_PRESENT 0 /**< Include or not the service_changed characteristic. if not enabled, the server's database cannot be changed for the lifetime of the device*/ + + +/**< The BLE-state type. */ +typedef enum { + BLE_STATE_INACTIVE = 0, + BLE_STATE_ADVERTISING = (1 << 0), + BLE_STATE_CONNECTED = (1 << 1), + BLE_STATE_SCANNING = (1 << 2), +} ble_state_t; + +typedef void (*ble_on_receive_callback_t) (uint8_t * p_data, uint16_t length); /**< The on receive callback function type. */ +typedef void (*ble_on_transmit_callback_t) (void); /**< The on transmit callback function type. */ +typedef void (*ble_on_connect_callback_t) (void); /**< The on connect callback function type. */ +typedef void (*ble_on_disconnect_callback_t) (void); /**< The on disconnect callback function type. */ +typedef void (*ble_on_scan_timeout_callback_t) (void); /**< The on scan timeout callback function type. */ +typedef void (*ble_on_scan_report_callback_t) (ble_gap_evt_adv_report_t* scan_report);/**< The on scan report callback function type. */ + + +/**@brief Function to initialize the BLE-Stack. + * + * @details The function initializes the advertising-process and the Nordic Uart Service (NUS) for transmitting and receiving data. + * Furthermore it sets the security parameters. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * + * @note Note that the Linker script must have the correct configuration: RAM (rwx) : ORIGIN = 0x20001fe8, LENGTH = 0x6018 for 0 central and 1 peripheral! Look at app_ram_base.h. + * Note that the Softdevice has to be initialized (by SOFTDEVICE_HANDLER_INIT) before calling this function + * Example: nrf_clock_lf_cfg_t clock_lf_cfg = {.source = NRF_CLOCK_LF_SRC_XTAL, + * .rc_ctiv = 0, + * .rc_temp_ctiv = 0, + * .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM}; + * SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL); + */ +ret_code_t ble_init(void); + + +/**@brief Function to force the BLE-Stack to disconnect from the current BLE-Connection. + */ +void ble_disconnect(void); + + +/**@brief Function to set the custom advertising data. + * + * @param[in] company_identifier A two byte unofficial company-identifier. + * @param[in] custom_advdata Pointer to memory containing the custom advertising data. + * @param[in] custom_advdata_size The size of the custom advertising data. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If the operation failed because a wrong parameter was provided internally. + * @retval NRF_ERROR_DATA_SIZE If the operation failed because not all the requested data could fit into the + * advertising packet. The maximum size of the advertisement packet + * is @ref BLE_GAP_ADV_MAX_SIZE. + * + * @note The overall maximum advertising data size is BLE_GAP_ADV_MAX_SIZE (= 31 Bytes from ble_gap.h). Therefore, the application needs to take care + * to choose the custom_advdata_size small enough that all information could be advertised + * (especially the ADVERTISING_DEVICE_NAME might be cut off, if custom_advdata_size is too big). + */ +ret_code_t ble_set_advertising_custom_advdata(uint16_t company_identifier, uint8_t* custom_advdata, uint16_t custom_advdata_size); + + +/**@brief Function to start the advertising process with the parameters: ADVERTISING_DEVICE_NAME, ADVERTISING_INTERVAL_MS, ADVERTISING_TIMEOUT_SECONDS. + * + * @details Internally the advertising is stopped by the BLE-Stack automatically. + * When this happens the advertising process is started again if it wasn't stopped by ble_stop_advertising(). + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +ret_code_t ble_start_advertising(void); + + +/**@brief Function to stop the advertising process. + */ +void ble_stop_advertising(void); + + +/**@brief Function to start a scan-operation. + * + * @param[in] scan_interval_ms The scan interval in milliseconds. + * @param[in] scan_window_ms The scan window in milliseconds. + * @param[in] scan_duration_seconds The scan duration in seconds. + * + * @retval NRF_SUCCESS Successfully initiated scanning procedure. + * @retval NRF_ERROR_INVALID_ADDR Invalid pointer supplied. + * @retval NRF_ERROR_INVALID_STATE Invalid state to perform operation. + * @retval NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied. + * @retval NRF_ERROR_BUSY The stack is busy, process pending events and retry. + * @retval NRF_ERROR_RESOURCES Not enough BLE role slots available. + * Stop one or more currently active roles (Central, Peripheral or Broadcaster) and try again + * + * @note The input-parameters of this function has to be chosen in a way that advertising is still possible. + */ +ret_code_t ble_start_scanning(uint16_t scan_interval_ms, uint16_t scan_window_ms, uint16_t scan_duration_seconds); + + +/**@brief Function for stopping any ongoing scan-operation. + */ +void ble_stop_scanning(void); + + +/**@brief Function to retrieve the MAC-address. + * + * @param[out] MAC_address Pointer to array where to store the MAC-address. + * + * @note The MAC_address-array has to have a size of at least BLE_GAP_ADDR_LEN (= 6 from ble_gap.h) bytes. + * The ble_init()-function has to be called, before calling this function. + */ +void ble_get_MAC_address(uint8_t* MAC_address); + + +/**@brief Function for retrieve the current BLE-state. + * + * @retval The current BLE-State. + */ +ble_state_t ble_get_state(void); + + +/**@brief Function to transmit data via the established BLE-connection. + * + * @retval NRF_SUCCESS If the data were sent successfully. Otherwise, an error code is returned. + * + * @note A BLE-connection has to be established before calling this function to work. + */ +ret_code_t ble_transmit(uint8_t* data, uint16_t len); + + + +/**@brief Function to set the on receive callback function. + * + * @param [in] ble_on_receive_callback The callback function that should be called. + */ +void ble_set_on_receive_callback(ble_on_receive_callback_t ble_on_receive_callback); + +/**@brief Function to set the on transmit callback function. + * + * @param [in] ble_on_transmit_callback The callback function that should be called. + */ +void ble_set_on_transmit_callback(ble_on_transmit_callback_t ble_on_transmit_callback); + +/**@brief Function to set the on connect callback function. + * + * @param [in] ble_on_connect_callback The callback function that should be called. + */ +void ble_set_on_connect_callback(ble_on_connect_callback_t ble_on_connect_callback); + +/**@brief Function to set the on disconnect callback function. + * + * @param [in] ble_on_disconnect_callback The callback function that should be called. + */ +void ble_set_on_disconnect_callback(ble_on_disconnect_callback_t ble_on_disconnect_callback); + +/**@brief Function to set the on scan timeout callback function. + * + * @param [in] ble_on_scan_timeout_callback The callback function that should be called. + */ +void ble_set_on_scan_timeout_callback(ble_on_scan_timeout_callback_t ble_on_scan_timeout_callback); + +/**@brief Function to set the on scan report callback function. + * + * @param [in] ble_on_scan_report_callback The callback function that should be called. + */ +void ble_set_on_scan_report_callback(ble_on_scan_report_callback_t ble_on_scan_report_callback); + + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/ble_setup.c b/firmware/nRF_badge/data_collector/incl/ble_setup.c deleted file mode 100644 index be5fccb..0000000 --- a/firmware/nRF_badge/data_collector/incl/ble_setup.c +++ /dev/null @@ -1,570 +0,0 @@ -#include - -#include "app_fifo_util.h" -#include "ble_setup.h" - -#include "battery.h" - -// Size of BLE FIFO, must be power of two. -#define BLE_FIFO_SIZE 512 - -#define BLE_ADV_PAUSE_CALLBACK_QUEUE_SIZE 3 - -static ble_gap_sec_params_t m_sec_params; /**< Security requirements for this application. */ -static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ - -//static ble_bas_t m_bas; //Struct for Battery Service module -static ble_nus_t m_nus; //Struct for Nordic UART Service module - - -// Buffer used to allow for sending of long messages (up to 512 bytes) via ble_write_buffered(). -static app_fifo_t m_ble_fifo; -static uint8_t m_ble_fifo_buffer[BLE_FIFO_SIZE]; - -static adv_paused_callback_t m_adv_pause_callback_queue[BLE_ADV_PAUSE_CALLBACK_QUEUE_SIZE] = {0}; - -volatile bool isConnected = false; -volatile bool isAdvertising = false; -//volatile bool isScanning = false; - -//ble_uuid_t m_adv_uuids[] = {{BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}, // Universally unique service identifiers. -// {BLE_UUID_NUS_SERVICE, BLE_UUID_TYPE_BLE}}; -ble_uuid_t m_adv_uuids[] = {{BLE_UUID_NUS_SERVICE, BLE_UUID_TYPE_BLE}}; -// ^^^ With both service UUIDs, ID, and group #, adv payload is too big. Quick fix above to make it fit. -// may also be fixable by specifying size of custom data manually, instead of sizeof (which includes padding) - - -void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) -{ - debug_log("ERR: SoftDevice assert, line %d in file %s . Halting...\r\n",(int)line_num,p_file_name); - while(1); -} - - -static void ble_error_handler(uint32_t error_code, uint32_t line_num) -{ - debug_log("ERR: BLE, error code %d, line %d.\r\n",(int)error_code,(int)line_num); - while(1); -} - - -void ble_queue_adv_paused_callback(adv_paused_callback_t callback) { - for (int i = 0; i < BLE_ADV_PAUSE_CALLBACK_QUEUE_SIZE; i++) { - if (m_adv_pause_callback_queue[i] == NULL || m_adv_pause_callback_queue[i] == callback) { - m_adv_pause_callback_queue[i] = callback; - return; - } - } - // No room for this callback. - APP_ERROR_CHECK(NRF_ERROR_NO_MEM); -} - -static void ble_adv_paused_call_callbacks(void) { - for (int i = 0; i < BLE_ADV_PAUSE_CALLBACK_QUEUE_SIZE; i++) { - if (m_adv_pause_callback_queue[i] != NULL) { - m_adv_pause_callback_queue[i](); - m_adv_pause_callback_queue[i] = NULL; - } - } -} - -static void gap_params_init(void) -{ - uint32_t err_code; - ble_gap_conn_sec_mode_t sec_mode; - - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); //no security needed - - //set BLE name - err_code = sd_ble_gap_device_name_set(&sec_mode,(const uint8_t *)DEVICE_NAME,strlen(DEVICE_NAME)); - APP_ERROR_CHECK(err_code); - - //set BLE appearance - err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_GENERIC_TAG); - APP_ERROR_CHECK(err_code); -} - - -static void services_init(void) -{ - uint32_t err_code; - ble_nus_init_t nus_init; //Nordic UART Service - for emulating a UART over BLE - memset(&nus_init,0,sizeof(nus_init)); - - nus_init.data_handler = BLEonReceive; - - err_code = ble_nus_init(&m_nus, &nus_init); - APP_ERROR_CHECK(err_code); - - - /* - ble_bas_init_t bas_init; //Battery service (part of BLE standard) - memset(&bas_init,0,sizeof(bas_init)); - - bas_init.evt_handler = NULL; - bas_init.support_notification = false; - bas_init.p_report_ref = NULL; - bas_init.initial_batt_level = 100; - - err_code = ble_bas_init(&m_bas, &bas_init); - BLE_ERROR_CHECK(err_code); - */ - -} - - -static void sec_params_init(void) -{ - m_sec_params.bond = SEC_PARAM_BOND; - m_sec_params.mitm = SEC_PARAM_MITM; - m_sec_params.io_caps = SEC_PARAM_IO_CAPABILITIES; - m_sec_params.oob = SEC_PARAM_OOB; - m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE; - m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE; -} - -// Sends all buffered bytes that can currently be sent. -static void send_buffered_bytes(void) { - bool tx_buffers_available = true; - while (app_fifo_len(&m_ble_fifo) > 0 && tx_buffers_available) { - uint8_t nus_packet[BLE_NUS_MAX_DATA_LEN]; - uint16_t packet_len = app_fifo_get_bytes(&m_ble_fifo, nus_packet, BLE_NUS_MAX_DATA_LEN); - uint32_t err_code = ble_nus_string_send(&m_nus, nus_packet, packet_len); - - switch (err_code) { - case NRF_SUCCESS: - // Yay! We could queue the packet with the SoftDevice. - break; - case BLE_ERROR_NO_TX_BUFFERS: - // Expected failure. The SoftDevice is out of buffers to take our packet with. - // Recover by rewinding our buffer packet_len bytes so we send it next time. - tx_buffers_available = false; - app_fifo_rewind(&m_ble_fifo, packet_len); - break; - default: - // Some unexpected error occurred. Reset! - APP_ERROR_CHECK(err_code); - } - } -} - -static void on_ble_evt(ble_evt_t * p_ble_evt) -{ - uint32_t err_code; - - // security-related - static ble_gap_evt_auth_status_t m_auth_status; - bool master_id_matches; - ble_gap_sec_kdist_t * p_distributed_keys; - ble_gap_enc_info_t * p_enc_info; - ble_gap_irk_t * p_id_info; - ble_gap_sign_info_t * p_sign_info; - static ble_gap_enc_key_t m_enc_key; /**< Encryption Key (Encryption Info and Master ID). */ - static ble_gap_id_key_t m_id_key; /**< Identity Key (IRK and address). */ - static ble_gap_sign_info_t m_sign_key; /**< Signing Key (Connection Signature Resolving Key). */ - static ble_gap_sec_keyset_t m_keys = {.keys_periph = {&m_enc_key, &m_id_key, &m_sign_key}}; - - - switch (p_ble_evt->header.evt_id) - { - case BLE_EVT_TX_COMPLETE: - send_buffered_bytes(); - break; - case BLE_GAP_EVT_CONNECTED: //on BLE connect event - m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; - BLEonConnect(); - break; - case BLE_GAP_EVT_DISCONNECTED: //on BLE disconnect event - m_conn_handle = BLE_CONN_HANDLE_INVALID; - //debug_log("Disconnect reason: %d\r\n",(int)p_ble_evt->evt.gap_evt.params.disconnected.reason); - BLEonDisconnect(); - break; - case BLE_GAP_EVT_ADV_REPORT: //On receipt of a response to an advertising request (during a scan) - BLEonAdvReport(&(p_ble_evt->evt.gap_evt.params.adv_report)); - break; - - case BLE_GAP_EVT_TIMEOUT: - if(p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) { - //debug_log("Scan ended\r\n"); - BLEonScanTimeout(); - } - /*else { - debug_log("Timeout. src=%d\r\n", p_ble_evt->evt.gap_evt.params.timeout.src); - }*/ - break; - - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: - err_code = sd_ble_gap_sec_params_reply(m_conn_handle, - BLE_GAP_SEC_STATUS_SUCCESS, - &m_sec_params, - &m_keys); - APP_ERROR_CHECK(err_code); - break; - case BLE_GATTS_EVT_SYS_ATTR_MISSING: - err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, - NULL, - 0, - BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS); - APP_ERROR_CHECK(err_code); - break; - case BLE_GAP_EVT_AUTH_STATUS: - m_auth_status = p_ble_evt->evt.gap_evt.params.auth_status; - break; - case BLE_GAP_EVT_SEC_INFO_REQUEST: - master_id_matches = memcmp(&p_ble_evt->evt.gap_evt.params.sec_info_request.master_id, - &m_enc_key.master_id, - sizeof(ble_gap_master_id_t)) == 0; - p_distributed_keys = &m_auth_status.kdist_periph; - - p_enc_info = (p_distributed_keys->enc && master_id_matches) ? &m_enc_key.enc_info : NULL; - p_id_info = (p_distributed_keys->id && master_id_matches) ? &m_id_key.id_info : NULL; - p_sign_info = (p_distributed_keys->sign && master_id_matches) ? &m_sign_key : NULL; - - err_code = sd_ble_gap_sec_info_reply(m_conn_handle, p_enc_info, p_id_info, p_sign_info); - APP_ERROR_CHECK(err_code); - break; - - default: - break; - } -} - - -static void ble_evt_dispatch(ble_evt_t * p_ble_evt) -{ - on_ble_evt(p_ble_evt); - - // Intercept advertising timeout events to implement infinite advertising, and disconnect events to let radio-conflicting - // operations to execute after a connection closes - if( (p_ble_evt->header.evt_id == BLE_GAP_EVT_TIMEOUT // if timeout event - && p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISING) // from advertising - || p_ble_evt->header.evt_id == BLE_GAP_EVT_DISCONNECTED) // OR if disconnect event - { - // Count number of pending pause requests - int pauseReqSrc = PAUSE_REQ_NONE; - for(int src=0; srcdefault ID: 0x%hX\r\n",defaultID); - badgeAssignment.ID = defaultID; - badgeAssignment.group = NO_GROUP; - - // Copy MAC address into custom advertising data struct. - for(int i = 0; i <= 5; i++) { - customAdvData.MAC[i] = MAC.addr[i]; - } - - app_fifo_init(&m_ble_fifo, m_ble_fifo_buffer, sizeof(m_ble_fifo_buffer)); - - //advertising_init(); - //uint32_t err_code = ble_advertising_start(BLE_ADV_MODE_FAST); - //BLE_ERROR_CHECK(err_code); -} - -void BLEstartAdvertising() -{ - if((!isConnected) && (!isAdvertising)) { - uint32_t err_code = ble_advertising_start(BLE_ADV_MODE_FAST); - if(err_code == NRF_SUCCESS) { - debug_log("ADV: advertising started\r\n"); - isAdvertising = true; - return; - } - APP_ERROR_CHECK(err_code); - } -} - - -void BLEdisable() -{ - uint32_t err_code = softdevice_handler_sd_disable(); - APP_ERROR_CHECK(err_code); -} - - -bool BLEpause(ble_pauseReq_src source) -{ - pauseRequest[source] = true; - bool isScanning = (getScanState() == SCANNER_SCANNING); - //debug_log("%d %d %d\r\n", isConnected, isAdvertising, isScanning); - if(isConnected || isAdvertising || isScanning) return false; // return false if BLE active. - else return true; -} - -void BLEresume(ble_pauseReq_src source) -{ - pauseRequest[source] = false; - for(int src=0; srcchunk_num = chunk_num; + chunk_fifo->chunk_size = chunk_size; + chunk_fifo->additional_info_size = additional_info_size; + chunk_fifo->p_chunk_fifo_buf = p_chunk_fifo_buf; + chunk_fifo->chunk_read_pos = 0; + chunk_fifo->chunk_write_pos = 0; + chunk_fifo->chunk_open_read = 0; + chunk_fifo->chunk_open_write = 0; + + return NRF_SUCCESS; +} + + +ret_code_t chunk_fifo_read_open(chunk_fifo_t* chunk_fifo, void** p_chunk, void** p_additional_info) { + + ret_code_t ret; + if(chunk_fifo->chunk_read_pos != chunk_fifo->chunk_write_pos) { + // New chunks are available + *p_chunk = &(chunk_fifo->p_chunk_fifo_buf[(chunk_fifo->chunk_read_pos)*(chunk_fifo->chunk_size + chunk_fifo->additional_info_size)]); + if(p_additional_info != NULL) + *p_additional_info = &(chunk_fifo->p_chunk_fifo_buf[chunk_fifo->chunk_size + (chunk_fifo->chunk_read_pos)*(chunk_fifo->chunk_size + chunk_fifo->additional_info_size)]); + + chunk_fifo->chunk_open_read = 1; + ret = NRF_SUCCESS; + } else { + ret = NRF_ERROR_NOT_FOUND; + } + return ret; +} + +void chunk_fifo_read_close(chunk_fifo_t* chunk_fifo) { + + if(!chunk_fifo->chunk_open_read) + return; + + chunk_fifo->chunk_open_read = 0; + + if(chunk_fifo->chunk_read_pos == chunk_fifo->chunk_write_pos) // This one should actually not happen + return; + + + chunk_fifo->chunk_read_pos = (chunk_fifo->chunk_read_pos + 1) % (chunk_fifo->chunk_num + 1); + + return; + +} + +void chunk_fifo_write_open(chunk_fifo_t* chunk_fifo, void** p_chunk, void** p_additional_info) { + + chunk_fifo->chunk_open_write = 1; + + *p_chunk = &(chunk_fifo->p_chunk_fifo_buf[(chunk_fifo->chunk_write_pos)*(chunk_fifo->chunk_size + chunk_fifo->additional_info_size)]); + if(p_additional_info != NULL) + *p_additional_info = &(chunk_fifo->p_chunk_fifo_buf[chunk_fifo->chunk_size + (chunk_fifo->chunk_write_pos)*(chunk_fifo->chunk_size + chunk_fifo->additional_info_size)]); + +} + +void chunk_fifo_write_close(chunk_fifo_t* chunk_fifo) { + + // Only increment the write-pos if there was a write-open operation before + + if(!chunk_fifo->chunk_open_write) { + return; + } + + chunk_fifo->chunk_open_write = 0; + + // Only increment the write-pos, if the old chunks (that have not been read yet) were not overwritten + if((chunk_fifo->chunk_write_pos + 1) % (chunk_fifo->chunk_num + 1) == chunk_fifo->chunk_read_pos) { + return; + } + + chunk_fifo->chunk_write_pos = (chunk_fifo->chunk_write_pos + 1) % (chunk_fifo->chunk_num + 1); + + return; +} + +uint8_t chunk_fifo_get_number_of_chunks(chunk_fifo_t* chunk_fifo) { + uint8_t num = 0; + if(chunk_fifo->chunk_write_pos >= chunk_fifo->chunk_read_pos) { + num = chunk_fifo->chunk_write_pos - chunk_fifo->chunk_read_pos; + } else { + num = (chunk_fifo->chunk_num + 1) - (chunk_fifo->chunk_read_pos - chunk_fifo->chunk_write_pos); + } + return num; +} diff --git a/firmware/nRF_badge/data_collector/incl/chunk_fifo_lib.h b/firmware/nRF_badge/data_collector/incl/chunk_fifo_lib.h new file mode 100644 index 0000000..8d9d91d --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/chunk_fifo_lib.h @@ -0,0 +1,123 @@ +/**@file + * @details This module provides a chunk-FIFO to store whole chunks with additional infos in a FIFO with a given size. + * One application of a chunk-FIFO is for example when different modules need to exchange data in a safe way. + * Safe means that while a chunk is not read by the application it couldn't be overwritten. + * In difference to other FIFO implementations with a put()- and a get()-function, this implementation + * provides two functions for opening and closing an operation like reading or writing. + * The open functions directly provide pointers that could be casted to any desired structure to work on. + * This has the benefit that no memcpy or sth like this has to be done. Important when used in ISRs. + */ + +#ifndef __CHUNK_FIFO_LIB_H +#define __CHUNK_FIFO_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +/**@brief A chunk FIFO instance structure. + * @details Keeps track of which bytes to read and write next. + * Also, it keeps the information about which memory is allocated for the buffer + * and its size. This structure must be initialized by chunk_fifo_init() before use. + */ +typedef struct +{ + uint8_t chunk_num; /**< Number of chunks in chunk-fifo. */ + uint32_t chunk_size; /**< Size of one chunk. */ + uint32_t additional_info_size; /**< Size of one additional info. */ + uint8_t * p_chunk_fifo_buf; /**< Pointer to FIFO buffer memory. */ + volatile uint8_t chunk_read_pos; /**< Next read position in the chunk-fifo buffer. */ + volatile uint8_t chunk_write_pos; /**< Next write position in the chunk-fifo buffer. */ + volatile uint8_t chunk_open_read; /**< Flag if currently there is a chunk read operation in progress. */ + volatile uint8_t chunk_open_write; /**< Flag if currently there is a chunk write operation in progress. */ +} chunk_fifo_t; + + +/**@brief Macro to initialize a chunk-fifo identifier and statically allocate memory for the chunk-fifo. + * + * @param[in] ret Name of the return-value variable that will be set through chunk_fifo_init(). + * @param[in] chunk_fifo Name (not pointer!) of the chunk-fifo identifier variable that will be used to control the chunk-fifo. + * @param[in] chunk_num Maximum number of chunks in FIFO. + * @param[in] chunk_size Size of one chunk. + * @param[in] additional_info_size Size of one associated additional-info. + */ +#define CHUNK_FIFO_INIT(ret, chunk_fifo, chunk_num, chunk_size, additional_info_size) { \ + static uint8_t chunk_fifo##_fifo_buf[(chunk_num+1)*(chunk_size + additional_info_size)]; \ + ret = chunk_fifo_init(&chunk_fifo, chunk_num, chunk_size, additional_info_size, chunk_fifo##_fifo_buf); \ + (void) ret; \ +} + +/**@brief Function to initialize a chunk-fifo identifier. + * + * @param[in] chunk_fifo Pointer to chunk-fifo identifier variable. + * @param[in] chunk_num Maximum number of chunks in FIFO. + * @param[in] chunk_size Size of one chunk. + * @param[in] additional_info_size Size of one associated additional-info. + * @param[in] p_chunk_fifo_buf Pointer to buffer memory where the chunks and additional-infos should be stored (Minimal size: (chunk_num + 1)*(chunk_size + additional_info_size)). + * + * @retval NRF_SUCCESS If the intialization was successful. + * @retval NRF_ERROR_INVALID_PARAM If p_chunk_fifo_buf == NULL or chunk_num == 0. + */ +ret_code_t chunk_fifo_init(chunk_fifo_t* chunk_fifo, uint8_t chunk_num, uint32_t chunk_size, uint32_t additional_info_size, uint8_t* p_chunk_fifo_buf); + + +/**@brief Function to open a read operation of the next chunk in the chunk-FIFO. + * + * @details This functions opens a read operation of the next chunk. + * To work with the data (and additional-info) the application needs to provide the address + * of a pointer-variable. The data can the be accessed via this pointer-variable. + * This pointer-variable could be a pointer to a struct. + * + * @param[in] chunk_fifo Pointer to chunk-fifo identifier variable. + * @param[out] p_chunk Pointer to a pointer variable, where the chunk-data could be retrieved from. Could also be NULL. + * @param[out] p_additional_info Pointer to a pointer variable, where the chunk-additional-info could be retrieved from. + * + * @retval NRF_SUCCESS If there is a chunk in the FIFO. + * @retval NRF_ERROR_NOT_FOUND If there is no chunk in the FIFO. + */ +ret_code_t chunk_fifo_read_open(chunk_fifo_t* chunk_fifo, void** p_chunk, void** p_additional_info); + +/**@brief Function to close/finish a read operation of the currently opened read chunk. + * + * @details This functions closes/finishes the read operation of the currently opened read chunk. + * This is equal to the consummation of the chunk (like a normal get()-function) by incrementing the chunk_read_pos. + * The closing of the chunk and incrementing of chunk_read_pos only takes place if there was a read-opening operation (by chunk_fifo_read_open()) before. + * + * @param[in] chunk_fifo Pointer to chunk-fifo identifier variable. + */ +void chunk_fifo_read_close(chunk_fifo_t* chunk_fifo); + +/**@brief Function to open a write operation of a chunk. + * + * @details This functions opens a write operation of a chunk. + * To write data (and additional-info) to the FIFO chunk the application needs to provide the address + * of a pointer-variable. The data can the be accessed(written via this pointer-variable. + * This pointer-variable could be a pointer to a struct. + * If all the available chunks are already filled, this functions will always set the pointer variable +* to the same address until a read-operation was perforemed so the other chunks won't be overwritten. + * + * @param[in] chunk_fifo Pointer to chunk-fifo identifier variable. + * @param[out] p_chunk Pointer to a pointer variable, where the chunk-data could be written to. + * @param[out] p_additional_info Pointer to a pointer variable, where the chunk-additional-info could be written to. Could also be NULL. + */ +void chunk_fifo_write_open(chunk_fifo_t* chunk_fifo, void** p_chunk, void** p_additional_info); + +/**@brief Function to close/finish a write operation of the currently opened write chunk. + * + * @details This functions closes/finishes the write operation of the currently opened write chunk. + * This is equal to the put()-function of a normal FIFO by incrementing the chunk_write_pos, + * except it would reach the chunk_read_pos or there was no write-opening operation (by chunk_fifo_write_open()) before. + * + * @param[in] chunk_fifo Pointer to chunk-fifo identifier variable. + */ +void chunk_fifo_write_close(chunk_fifo_t* chunk_fifo); + +/**@brief Function to retrieve the current number of finished chunks in the chunk-FIFO. + * + * @param[in] chunk_fifo Pointer to chunk-fifo identifier variable. + * + * @retval Number of finished chunks. + */ +uint8_t chunk_fifo_get_number_of_chunks(chunk_fifo_t* chunk_fifo); + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/chunk_messages.c b/firmware/nRF_badge/data_collector/incl/chunk_messages.c new file mode 100644 index 0000000..e177af8 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/chunk_messages.c @@ -0,0 +1,39 @@ +#include "tinybuf.h" +#include "chunk_messages.h" + +const tb_field_t BatteryChunk_fields[3] = { + {513, tb_offsetof(BatteryChunk, timestamp), 0, 0, tb_membersize(BatteryChunk, timestamp), 0, 0, 0, &Timestamp_fields}, + {513, tb_offsetof(BatteryChunk, battery_data), 0, 0, tb_membersize(BatteryChunk, battery_data), 0, 0, 0, &BatteryData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneChunk_fields[4] = { + {513, tb_offsetof(MicrophoneChunk, timestamp), 0, 0, tb_membersize(MicrophoneChunk, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(MicrophoneChunk, sample_period_ms), 0, 0, tb_membersize(MicrophoneChunk, sample_period_ms), 0, 0, 0, NULL}, + {516, tb_offsetof(MicrophoneChunk, microphone_data), tb_delta(MicrophoneChunk, microphone_data_count, microphone_data), 1, tb_membersize(MicrophoneChunk, microphone_data[0]), tb_membersize(MicrophoneChunk, microphone_data)/tb_membersize(MicrophoneChunk, microphone_data[0]), 0, 0, &MicrophoneData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanSamplingChunk_fields[3] = { + {513, tb_offsetof(ScanSamplingChunk, timestamp), 0, 0, tb_membersize(ScanSamplingChunk, timestamp), 0, 0, 0, &Timestamp_fields}, + {516, tb_offsetof(ScanSamplingChunk, scan_result_data), tb_delta(ScanSamplingChunk, scan_result_data_count, scan_result_data), 1, tb_membersize(ScanSamplingChunk, scan_result_data[0]), tb_membersize(ScanSamplingChunk, scan_result_data)/tb_membersize(ScanSamplingChunk, scan_result_data[0]), 0, 0, &ScanResultData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanChunk_fields[3] = { + {513, tb_offsetof(ScanChunk, timestamp), 0, 0, tb_membersize(ScanChunk, timestamp), 0, 0, 0, &Timestamp_fields}, + {516, tb_offsetof(ScanChunk, scan_result_data), tb_delta(ScanChunk, scan_result_data_count, scan_result_data), 1, tb_membersize(ScanChunk, scan_result_data[0]), tb_membersize(ScanChunk, scan_result_data)/tb_membersize(ScanChunk, scan_result_data[0]), 0, 0, &ScanResultData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerChunk_fields[3] = { + {513, tb_offsetof(AccelerometerChunk, timestamp), 0, 0, tb_membersize(AccelerometerChunk, timestamp), 0, 0, 0, &Timestamp_fields}, + {516, tb_offsetof(AccelerometerChunk, accelerometer_data), tb_delta(AccelerometerChunk, accelerometer_data_count, accelerometer_data), 1, tb_membersize(AccelerometerChunk, accelerometer_data[0]), tb_membersize(AccelerometerChunk, accelerometer_data)/tb_membersize(AccelerometerChunk, accelerometer_data[0]), 0, 0, &AccelerometerData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerInterruptChunk_fields[2] = { + {513, tb_offsetof(AccelerometerInterruptChunk, timestamp), 0, 0, tb_membersize(AccelerometerInterruptChunk, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + diff --git a/firmware/nRF_badge/data_collector/incl/chunk_messages.h b/firmware/nRF_badge/data_collector/incl/chunk_messages.h new file mode 100644 index 0000000..0439368 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/chunk_messages.h @@ -0,0 +1,57 @@ +#ifndef __CHUNK_MESSAGES_H +#define __CHUNK_MESSAGES_H + +#include +#include "tinybuf.h" +#include "common_messages.h" + +#define MICROPHONE_CHUNK_DATA_SIZE 114 +#define ACCELEROMETER_CHUNK_DATA_SIZE 100 +#define SCAN_CHUNK_DATA_SIZE 29 +#define SCAN_SAMPLING_CHUNK_DATA_SIZE 255 +#define SCAN_CHUNK_AGGREGATE_TYPE_MAX 0 +#define SCAN_CHUNK_AGGREGATE_TYPE_MEAN 1 + + +typedef struct { + Timestamp timestamp; + BatteryData battery_data; +} BatteryChunk; + +typedef struct { + Timestamp timestamp; + uint16_t sample_period_ms; + uint8_t microphone_data_count; + MicrophoneData microphone_data[114]; +} MicrophoneChunk; + +typedef struct { + Timestamp timestamp; + uint8_t scan_result_data_count; + ScanResultData scan_result_data[255]; +} ScanSamplingChunk; + +typedef struct { + Timestamp timestamp; + uint8_t scan_result_data_count; + ScanResultData scan_result_data[29]; +} ScanChunk; + +typedef struct { + Timestamp timestamp; + uint8_t accelerometer_data_count; + AccelerometerData accelerometer_data[100]; +} AccelerometerChunk; + +typedef struct { + Timestamp timestamp; +} AccelerometerInterruptChunk; + +extern const tb_field_t BatteryChunk_fields[3]; +extern const tb_field_t MicrophoneChunk_fields[4]; +extern const tb_field_t ScanSamplingChunk_fields[3]; +extern const tb_field_t ScanChunk_fields[3]; +extern const tb_field_t AccelerometerChunk_fields[3]; +extern const tb_field_t AccelerometerInterruptChunk_fields[2]; + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/chunk_messages.tb b/firmware/nRF_badge/data_collector/incl/chunk_messages.tb new file mode 100644 index 0000000..f0a1f29 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/chunk_messages.tb @@ -0,0 +1,56 @@ +import common_messages + +extern Timestamp; +extern BatteryData; +extern MicrophoneData; +extern ScanResultData; +extern AccelerometerData; + + +define { + MICROPHONE_CHUNK_DATA_SIZE = 114; +} + +define { + ACCELEROMETER_CHUNK_DATA_SIZE = 100; +} + +define { + SCAN_CHUNK_DATA_SIZE = 29; + SCAN_SAMPLING_CHUNK_DATA_SIZE = 255; + SCAN_CHUNK_AGGREGATE_TYPE_MAX = 0; + SCAN_CHUNK_AGGREGATE_TYPE_MEAN = 1; +} + + +message BatteryChunk { + required Timestamp timestamp; + required BatteryData battery_data; +} + +message MicrophoneChunk { + required Timestamp timestamp; + required uint16 sample_period_ms; + repeated MicrophoneData microphone_data[MICROPHONE_CHUNK_DATA_SIZE]; +} + +message ScanSamplingChunk { + required Timestamp timestamp; + repeated ScanResultData scan_result_data[SCAN_SAMPLING_CHUNK_DATA_SIZE]; +} + +message ScanChunk { + required Timestamp timestamp; + repeated ScanResultData scan_result_data[SCAN_CHUNK_DATA_SIZE]; +} + +message AccelerometerChunk { + required Timestamp timestamp; + repeated AccelerometerData accelerometer_data[ACCELEROMETER_CHUNK_DATA_SIZE]; +} + + +message AccelerometerInterruptChunk { + required Timestamp timestamp; +} + diff --git a/firmware/nRF_badge/data_collector/incl/circular_fifo_lib.c b/firmware/nRF_badge/data_collector/incl/circular_fifo_lib.c new file mode 100644 index 0000000..2a75b7e --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/circular_fifo_lib.c @@ -0,0 +1,83 @@ +#include "circular_fifo_lib.h" + +#include "stdlib.h" // Needed for NULL definition + + + +ret_code_t circular_fifo_init(circular_fifo_t * p_fifo, uint8_t * p_buf, uint16_t buf_size) { + if(p_buf == NULL) + return NRF_ERROR_NULL; + + + p_fifo->p_buf = p_buf; + p_fifo->buf_size = buf_size; + p_fifo->read_pos = 0; + p_fifo->write_pos = 0; + p_fifo->read_flag = 0; + + return NRF_SUCCESS; +} + + +void circular_fifo_flush(circular_fifo_t * p_fifo) { + p_fifo->read_pos = p_fifo->write_pos; + +} + +ret_code_t circular_fifo_get(circular_fifo_t * p_fifo, uint8_t* byte) { + ret_code_t ret = NRF_ERROR_NOT_FOUND; + + if(p_fifo->read_pos != p_fifo->write_pos) { + *byte = p_fifo->p_buf[p_fifo->read_pos]; + p_fifo->read_pos = (p_fifo->read_pos + 1) % (p_fifo->buf_size + 1); + ret = NRF_SUCCESS; + } + return ret; +} + +void circular_fifo_put(circular_fifo_t * p_fifo, uint8_t byte) { + + uint32_t incremented_write_pos = (p_fifo->write_pos + 1) % (p_fifo->buf_size + 1); + if(incremented_write_pos == p_fifo->read_pos) { + if(p_fifo->read_flag) // If we try to read the current element, we are not allowed to overwrite it. + return; + // If we could overwrite it, also increment the read-pos + p_fifo->read_pos = (p_fifo->read_pos + 1) % (p_fifo->buf_size + 1); + } + + p_fifo->p_buf[p_fifo->write_pos] = byte; + p_fifo->write_pos = incremented_write_pos; + +} + +void circular_fifo_read(circular_fifo_t * p_fifo, uint8_t * p_byte_array, uint32_t * p_size) { + uint32_t index = 0; + p_fifo->read_flag = 1; + while(index < *p_size) { + if(circular_fifo_get(p_fifo, &(p_byte_array[index])) != NRF_SUCCESS) { + break; + } + index++; + } + p_fifo->read_flag = 0; + *p_size = index; +} + +void circular_fifo_write(circular_fifo_t * p_fifo, uint8_t const * p_byte_array, uint32_t size) { + uint32_t index = 0; + while(index < size) { + circular_fifo_put(p_fifo, p_byte_array[index]); + index++; + } +} + + +uint32_t circular_fifo_get_size(circular_fifo_t * p_fifo) { + uint32_t available_len = 0; + if(p_fifo->write_pos >= p_fifo->read_pos) { + available_len = p_fifo->write_pos - p_fifo->read_pos; + } else { + available_len = p_fifo->buf_size + 1 - p_fifo->read_pos + p_fifo->write_pos; + } + return available_len; +} diff --git a/firmware/nRF_badge/data_collector/incl/circular_fifo_lib.h b/firmware/nRF_badge/data_collector/incl/circular_fifo_lib.h new file mode 100644 index 0000000..82347c0 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/circular_fifo_lib.h @@ -0,0 +1,111 @@ +/**@file + */ + +#ifndef __CIRCULAR_FIFO_LIB_H +#define __CIRCULAR_FIFO_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +/**@brief A FIFO instance structure. + * @details Keeps track of which bytes to read and write next. + * Also, it keeps the information about which memory is allocated for the buffer + * and its size. This structure must be initialized by circular_fifo_init() before use. + */ +typedef struct +{ + uint8_t * p_buf; /**< Pointer to FIFO buffer memory. */ + uint16_t buf_size; /**< Size of the FIFO. */ + volatile uint32_t read_pos; /**< Next read position in the FIFO buffer. */ + volatile uint32_t write_pos; /**< Next write position in the FIFO buffer. */ + volatile uint8_t read_flag; /**< Flag if currently reading, to synchronize with write. */ +} circular_fifo_t; + + +/**@brief Macro to initialize a circular-fifo identifier and statically allocate memory for the circular-fifo. + * + * @param[in] ret Name of the return-value variable that will be set through circular_fifo_init(). + * @param[in] circular_fifo Name (not pointer!) of the circular-fifo identifier variable that will be used to control the circular-fifo. + * @param[in] buf_size Number of elements storable in the circular fifo. + */ +#define CIRCULAR_FIFO_INIT(ret, circular_fifo, buf_size) { \ + static uint8_t circular_fifo##_fifo_buf[buf_size + 1]; \ + ret = circular_fifo_init(&circular_fifo, circular_fifo##_fifo_buf, buf_size); \ + (void) ret; \ +} + +/**@brief Function for initializing the FIFO. + * + * @param[out] p_fifo FIFO object. + * @param[in] p_buf FIFO buffer for storing data. The buffer size must be a power of two. + * @param[in] buf_size Size of the FIFO buffer provided. This size must be a power of two. + * + * @retval NRF_SUCCESS If initialization was successful. + * @retval NRF_ERROR_NULL If a NULL pointer is provided as buffer. + * + * @note The buffer (p_buf) has to have a size of at least (buf_size + 1) bytes. + * That's why it's recommended to use the CIRCULAR_FIFO_INIT-macro to initzialize it. + */ +ret_code_t circular_fifo_init(circular_fifo_t * p_fifo, uint8_t * p_buf, uint16_t buf_size); + + +/**@brief Function for flushing the FIFO. + * + * @param[in] p_fifo Pointer to the FIFO. + */ +void circular_fifo_flush(circular_fifo_t * p_fifo); + + + +/**@brief Function for adding an element to the FIFO. + * + * @param[in] p_fifo Pointer to the FIFO. + * @param[in] byte Data byte to add to the FIFO. + */ +void circular_fifo_put(circular_fifo_t * p_fifo, uint8_t byte); + + +/**@brief Function for getting the next element from the FIFO. + * + * @param[in] p_fifo Pointer to the FIFO. + * @param[out] byte Byte fetched from the FIFO. + * + * @retval NRF_SUCCESS If an element was returned. + * @retval NRF_ERROR_NOT_FOUND If there are no more elements in the queue. + */ +ret_code_t circular_fifo_get(circular_fifo_t * p_fifo, uint8_t* byte); + + +/**@brief Function for reading bytes from the FIFO. + * + * @param[in] p_fifo Pointer to the FIFO. Must not be NULL. + * @param[out] p_byte_array Memory pointer where the read bytes are fetched from the FIFO. + * This field must not be NULL. + * @param[inout] p_size Address to memory indicating the maximum number of bytes to be read. + * The provided memory is overwritten with the actual number of bytes + * read. This field must not be NULL. + */ +void circular_fifo_read(circular_fifo_t * p_fifo, uint8_t * p_byte_array, uint32_t * p_size); + +/**@brief Function for writing bytes to the FIFO. + * + * @details If size is larger than the number of available bytes in FIFO, the data is overwritten circulary. + * + * @param[in] p_fifo Pointer to the FIFO. Must not be NULL. + * @param[in] p_byte_array Memory pointer containing the bytes to be written to the FIFO. + * This field must not be NULL. + * @param[in] size Number of bytes to be written. + */ +void circular_fifo_write(circular_fifo_t * p_fifo, uint8_t const * p_byte_array, uint32_t size); + + +/**@brief Function for retrieving the number of available bytes in the FIFO. + * + * @param[in] p_fifo Pointer to the FIFO. + * + * @retval Number of available bytes in FIFO. + */ +uint32_t circular_fifo_get_size(circular_fifo_t * p_fifo); + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/collector.c b/firmware/nRF_badge/data_collector/incl/collector.c deleted file mode 100644 index c7519fa..0000000 --- a/firmware/nRF_badge/data_collector/incl/collector.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Classes for handling storage to flash, and retreival of data from flash for sending - * FlashHandler simplifies writing to flash. The program may always use storeWord() to - * store data; update(), if called repeatedly, handles the actual storage and organization - * in flash, including avoiding conflict with BLE activity. - * RetrieveHandler simplifies sending data over BLE - */ - - - -#include - -#include "battery.h" -#include "collector.h" - -static uint32_t mCollectorSampleTaskTimer; -static uint32_t mCollectorCollectTaskTimer; - -static void collector_sample_task(void * p_context) { - if (isCollecting) { - uint32_t collection_start = timer_comparison_ticks_now(); - while (timer_comparison_ticks_since_start(collection_start) < APP_TIMER_TICKS(READING_WINDOW_MS, APP_PRESCALER)) { - takeMicReading(); - } - } -} - -static void collector_collect_task(void * p_context) { - if (isCollecting && readingsCount > 0) { - collectSample(); - } -} - -void collector_init() -{ - debug_log("Reading window: %lu %lu", (uint32_t) (1000*READING_WINDOW_MS), (uint32_t) (1000*READING_PERIOD_MS)); - - // Set sampling timing parameters to defaults - samplePeriod = SAMPLE_PERIOD; - - collect.to = 0; - collect.loc = 0; - - // initialize sampling things - takingReadings = false; - readingsCount = 0; - readingsSum = 0; - sampleStart = 0; - sampleStartms = 0; - - isCollecting = false; - - app_timer_create(&mCollectorSampleTaskTimer, APP_TIMER_MODE_REPEATED, collector_sample_task); - app_timer_create(&mCollectorCollectTaskTimer, APP_TIMER_MODE_REPEATED, collector_collect_task); -} - -/** - * Take a reading from the mic (and add to total for averaging later) - */ -void takeMicReading() -{ - if(takingReadings == false) { // if we aren't currently taking readings for a sample, start new sample - readingsCount = 0; // reset number of readings - readingsSum = 0; // reset readings total - sampleStart = now(); // get timestamp for first reading in sample (also recalculates fractional part) - sampleStartms = nowFractional(); // get fractional part of timestamp - takingReadings = true; // we're starting a reading - } - int sample = analogRead(MIC_PIN); - readingsSum += abs(sample - MIC_ZERO); - readingsCount++; -} - -static void setupChunk(int chunk, unsigned long timestamp, unsigned long msTimestamp) -{ - if(chunk > LAST_RAM_CHUNK || chunk < 0) { - debug_log("ERR: Invalid collector chunk\r\n"); - return; - } - - memset(micBuffer[chunk].samples, INVALID_SAMPLE, sizeof(micBuffer[chunk].samples)); // reset sample array - micBuffer[chunk].battery = BatteryMonitor_getBatteryVoltage(); - micBuffer[chunk].timestamp = timestamp; // record timestamp for chunk - micBuffer[chunk].msTimestamp = msTimestamp; // record fractional part of timestamp - micBuffer[chunk].check = CHECK_INCOMPLETE; // denote that chunk is incomplete -} - -void collectSample() -{ - unsigned int micValue = readingsSum / (readingsCount/2); - unsigned char reading = micValue <= MAX_MIC_SAMPLE ? micValue : MAX_MIC_SAMPLE; //clip sample - //debug_log("r%d n%d\r\n",(int)reading,(int)readingsCount); - if(readingsCount < 10) { - debug_log("hold up that's not enough samples man wtf\r\n"); - } - //debug_log("readingsCount: %d\r\n",(int)readingsCount); - readingsCount = 0; - readingsSum = 0; - - if(collect.loc == 0) { // are we at start of a new chunk - setupChunk(collect.to,sampleStart,sampleStartms); - debug_log("COLLECTOR: Started RAM chunk %d. %lu %lu\r\n",collect.to, sampleStart, sampleStartms); - //printCollectorChunk(collect.to); - } - - micBuffer[collect.to].samples[collect.loc] = reading; // add reading - collect.loc++; // move to next location in sample array - - if(collect.loc >= SAMPLES_PER_CHUNK) { // did we reach the end of the chunk - micBuffer[collect.to].check = micBuffer[collect.to].timestamp; // mark chunk as complete - collect.to = (collect.to < LAST_RAM_CHUNK) ? collect.to+1 : 0; - collect.loc = 0; - - Storer_ScheduleBufferedDataStorage(); - } - - takingReadings = false; // we finished taking readings for that sample -} - -void startCollector() -{ - if(!isCollecting) { - isCollecting = true; - debug_log(" Collector started\r\n"); - app_timer_start(mCollectorSampleTaskTimer, APP_TIMER_TICKS(READING_PERIOD_MS, APP_PRESCALER), NULL); - app_timer_start(mCollectorCollectTaskTimer, APP_TIMER_TICKS(SAMPLE_PERIOD, APP_PRESCALER), NULL); - updateAdvData(); - } -} - -void stopCollector() -{ - if(isCollecting) { - // Reset internal collector variables - takingReadings = false; - readingsSum = 0; - readingsCount = 0; - - // Current chunk may be incomplete, but if collecting restarts, it should resume from a new chunk in RAM buffer. - - micBuffer[collect.to].check = CHECK_TRUNC; // mark chunk as truncated; it's not full, but we're done writing in it - micBuffer[collect.to].samples[SAMPLES_PER_CHUNK-1] = collect.loc; // store the number of samples in this chunk - - debug_log(" Collector stopped. Truncated RAM chunk %d.\r\n",collect.to); - - // Advance to next chunk in buffer (will resume collecting from a new chunk) - collect.to = (collect.to < LAST_RAM_CHUNK) ? collect.to+1 : 0; - collect.loc = 0; - - isCollecting = false; // Disable collector - app_timer_stop(mCollectorSampleTaskTimer); - app_timer_stop(mCollectorCollectTaskTimer); - Storer_ScheduleBufferedDataStorage(); - updateAdvData(); - } -} - - -void printCollectorChunk(int chunk) -{ - if(chunk > LAST_RAM_CHUNK || chunk < 0) { - debug_log("ERR: Invalid collector chunk to print\r\n"); - return; - } - debug_log("RAM chunk %d:\r\n",chunk); - debug_log("ts: 0x%lX - ms: %hd - ba: %d -- ch: 0x%lX",micBuffer[chunk].timestamp,micBuffer[chunk].msTimestamp, - (int)(micBuffer[chunk].battery*1000),micBuffer[chunk].check); - nrf_delay_ms(3); - for(int i = 0; i < SAMPLES_PER_CHUNK; i++) { - if(i%10 == 0) { - debug_log("\r\n "); - } - unsigned char sample = micBuffer[chunk].samples[i]; - if(sample != INVALID_SAMPLE) { - debug_log("%3u, ",(int)sample); - } - else { - debug_log("- , "); - } - nrf_delay_ms(2); - } - debug_log("\r\n---\r\n"); - nrf_delay_ms(10); -} diff --git a/firmware/nRF_badge/data_collector/incl/collector.h b/firmware/nRF_badge/data_collector/incl/collector.h deleted file mode 100644 index a5a3281..0000000 --- a/firmware/nRF_badge/data_collector/incl/collector.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Methods to simplifies writing to flash: The program may always use storeWord() to - * store data; update(), if called repeatedly, handles the actual storage and organization - * in flash, including avoiding conflict with BLE activity. - * Methods to automate sending data over BLE from flash - */ - -#ifndef COLLECTOR_H -#define COLLECTOR_H - -#include -#include - -#include "debug_log.h" -#include "nrf_gpio.h" //abstraction for dealing with gpio -#include "nrf_adc.h" //abstraction for dealing with adc -//#include "ble_flash.h" //for accessing flash -#include "nrf_delay.h" - -#include "nrf_drv_config.h" -#include "boards.h" - -#define PANIC_LED LED_2 - -#include "ble_setup.h" // uses updateAdvData() when collector started -#include "rtc_timing.h" // uses now() to get chunk timestamp -#include "analog.h" // uses analogRead() to get mic data; readBattery() to get battery voltage -//#include "internal_flash.h" // uses some #defined constants related to memory usage -#include "storer.h" - -// Setup expected "zero" for the mic value. -// analog ref is mic VCC, mic is 1/2 VCC biased, input is 1/3 scaled, so zero value is approx. (1023/2)/3 ~= 170 -//#define MIC_ZERO 166 -#define MIC_ZERO 125 // for 8bit conversion, full scale - -#define MAX_MIC_SAMPLE 254 // mic samples will be clipped to this value -#define INVALID_SAMPLE 255 // dummy byte reserved for unused/invalid samples in chunk sample array - -#define CHECK_INCOMPLETE 0xFFFFFFFFUL // chunk check value for incomplete chunk -#define CHECK_TRUNC 0x7FFFFFFFUL // chunk check value for truncated chunk - collector was stopped before complete chunk - // Last byte of sample array stores the number of samples in the chunk. - - -// --------- Sampling timing parameters ---------- -unsigned long sampleWindow; // how long we should sample -unsigned long samplePeriod; // time between samples - must exceed SAMPLE_WINDOW -// vvv Default values -//#define SAMPLE_PERIOD 250UL -//#define SAMPLE_WINDOW 100UL -#define SAMPLE_PERIOD 50UL -#define SAMPLE_SLEEP_RATIO 0.075 -#define READING_PERIODS_PER_SECOND 700.0 -#define READING_PERIOD_MS (1000.0 / READING_PERIODS_PER_SECOND) -#define READING_WINDOW_MS (READING_PERIOD_MS * SAMPLE_SLEEP_RATIO) - - - -bool isCollecting; - -bool takingReadings; // whether we're currently taking readings for a sample -unsigned long sampleStart; // timestamp of first reading for current sample -unsigned long sampleStartms; // timestamp of first reading for current sample -unsigned int readingsCount; // number of mic readings taken for current sample -unsigned long readingsSum; // sum of all mic readings taken for current sample - - - - -#define SAMPLES_PER_CHUNK 114 // 128-(4+2+4+4) --- see chunk structure below -#define MIC_BUFFER_SIZE 20 // number of chunks in mic RAM buffer -#define LAST_RAM_CHUNK (MIC_BUFFER_SIZE - 1) // index of last chunk in RAM buffer - -typedef union -{ - struct - { - unsigned long timestamp; // Timestamp of first sample in chunk 4byte - float battery; // Battery voltage 4byte - unsigned short msTimestamp; // Fractional part of chunk timestamp (0-999) 2byte - unsigned char samples[SAMPLES_PER_CHUNK]; // Sound data samples 114byte - unsigned long check; // Copy of timestamp, to validate chunk 4byte - }; // 128byte total - unsigned long wordBuf[WORDS_PER_CHUNK]; // 128byte chunks = 32words (128/4) -} mic_chunk_t; - - - -mic_chunk_t micBuffer[MIC_BUFFER_SIZE]; // RAM buffer for mic data - memory structure identical to data stored to flash - -struct -{ - int to; // which chunk in RAM buffer we're currently storing to - int loc; // next index in sample array to be written -} collect; // Struct for keeping track of storing mic data to RAM - - -void collector_init(); - -/** - * Take a reading from the mic (and add to total for averaging later) - */ -void takeMicReading(); - -/** - * Take average of mic readings, put into mic RAM buffer - */ -void collectSample(); - -/** - * Start (or restart) collecting. - */ -void startCollector(); - -/** - * Halt collecting; current chunk is likely incomplete, filled with some INVALID_READING samples. - * Next call to addMicReading will start from next chunk in RAM buffer - */ -void stopCollector(); - -/** - * Get battery voltage - * Returns the current chunk's voltage if collecting is enabled; otherwise, returns an analogRead of VCC - */ -//float getBatteryVoltage(); - -/** - * Print chunk contents from the RAM buffer to the debug log - */ -void printCollectorChunk(int chunk); - - -#endif //#ifndef MIC_H - diff --git a/firmware/nRF_badge/data_collector/incl/common_messages.c b/firmware/nRF_badge/data_collector/incl/common_messages.c new file mode 100644 index 0000000..c4ae5ba --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/common_messages.c @@ -0,0 +1,47 @@ +#include "tinybuf.h" +#include "common_messages.h" + +const tb_field_t Timestamp_fields[3] = { + {65, tb_offsetof(Timestamp, seconds), 0, 0, tb_membersize(Timestamp, seconds), 0, 0, 0, NULL}, + {65, tb_offsetof(Timestamp, ms), 0, 0, tb_membersize(Timestamp, ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t BadgeAssignement_fields[3] = { + {65, tb_offsetof(BadgeAssignement, ID), 0, 0, tb_membersize(BadgeAssignement, ID), 0, 0, 0, NULL}, + {65, tb_offsetof(BadgeAssignement, group), 0, 0, tb_membersize(BadgeAssignement, group), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t BatteryData_fields[2] = { + {129, tb_offsetof(BatteryData, voltage), 0, 0, tb_membersize(BatteryData, voltage), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneData_fields[2] = { + {65, tb_offsetof(MicrophoneData, value), 0, 0, tb_membersize(MicrophoneData, value), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanDevice_fields[3] = { + {65, tb_offsetof(ScanDevice, ID), 0, 0, tb_membersize(ScanDevice, ID), 0, 0, 0, NULL}, + {33, tb_offsetof(ScanDevice, rssi), 0, 0, tb_membersize(ScanDevice, rssi), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanResultData_fields[3] = { + {513, tb_offsetof(ScanResultData, scan_device), 0, 0, tb_membersize(ScanResultData, scan_device), 0, 0, 0, &ScanDevice_fields}, + {65, tb_offsetof(ScanResultData, count), 0, 0, tb_membersize(ScanResultData, count), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerData_fields[2] = { + {65, tb_offsetof(AccelerometerData, acceleration), 0, 0, tb_membersize(AccelerometerData, acceleration), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerRawData_fields[2] = { + {40, tb_offsetof(AccelerometerRawData, raw_acceleration), 0, 0, tb_membersize(AccelerometerRawData, raw_acceleration[0]), tb_membersize(AccelerometerRawData, raw_acceleration)/tb_membersize(AccelerometerRawData, raw_acceleration[0]), 0, 0, NULL}, + TB_LAST_FIELD, +}; + diff --git a/firmware/nRF_badge/data_collector/incl/common_messages.h b/firmware/nRF_badge/data_collector/incl/common_messages.h new file mode 100644 index 0000000..d1cfd22 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/common_messages.h @@ -0,0 +1,54 @@ +#ifndef __COMMON_MESSAGES_H +#define __COMMON_MESSAGES_H + +#include +#include "tinybuf.h" + + + +typedef struct { + uint32_t seconds; + uint16_t ms; +} Timestamp; + +typedef struct { + uint16_t ID; + uint8_t group; +} BadgeAssignement; + +typedef struct { + float voltage; +} BatteryData; + +typedef struct { + uint8_t value; +} MicrophoneData; + +typedef struct { + uint16_t ID; + int8_t rssi; +} ScanDevice; + +typedef struct { + ScanDevice scan_device; + uint8_t count; +} ScanResultData; + +typedef struct { + uint16_t acceleration; +} AccelerometerData; + +typedef struct { + int16_t raw_acceleration[3]; +} AccelerometerRawData; + +extern const tb_field_t Timestamp_fields[3]; +extern const tb_field_t BadgeAssignement_fields[3]; +extern const tb_field_t BatteryData_fields[2]; +extern const tb_field_t MicrophoneData_fields[2]; +extern const tb_field_t ScanDevice_fields[3]; +extern const tb_field_t ScanResultData_fields[3]; +extern const tb_field_t AccelerometerData_fields[2]; +extern const tb_field_t AccelerometerRawData_fields[2]; + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/common_messages.tb b/firmware/nRF_badge/data_collector/incl/common_messages.tb new file mode 100644 index 0000000..dc9df1a --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/common_messages.tb @@ -0,0 +1,37 @@ +message Timestamp { + required uint32 seconds; + required uint16 ms; +} + +message BadgeAssignement { + required uint16 ID; + required uint8 group; +} + +message BatteryData { + required float voltage; +} + +message MicrophoneData { + required uint8 value; +} + + +message ScanDevice { + required uint16 ID; + required int8 rssi; +} + +message ScanResultData { + required ScanDevice scan_device; + required uint8 count; +} + +message AccelerometerData { + required uint16 acceleration; +} + +message AccelerometerRawData { + fixed_repeated int16 raw_acceleration[3]; +} + diff --git a/firmware/nRF_badge/data_collector/incl/debug_lib.c b/firmware/nRF_badge/data_collector/incl/debug_lib.c new file mode 100644 index 0000000..b36ead2 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/debug_lib.c @@ -0,0 +1,132 @@ +#include "debug_lib.h" + + + +#ifdef DEBUG_LOG_ENABLE +#include "app_util_platform.h" +#include "uart_lib.h" +#include // Needed for the printf-function +#include // Needed for the printf-function +#include // Needed for the vsnprintf-function + +#include "app_fifo.h" + +#include "custom_board.h" + +#define DEBUG_PRINTF_BUFFER_SIZE 200 /**< Buffer Size for one printf decoding */ +#define DEBUG_FIFO_BUFFER_SIZE 2048 /**< Has to be a power of two */ +#define UART_TRANSMIT_BUFFER_SIZE 100 /**< Size of the buffer of one UART transmit operation */ + +static uint8_t debug_fifo_buf[DEBUG_FIFO_BUFFER_SIZE]; /**< The buffer for the FIFO */ +static uint8_t uart_transmit_buf[UART_TRANSMIT_BUFFER_SIZE]; /**< The buffer that is used to actually call the uart-transmit-function. Because the FIFO can't do that */ +static volatile uint8_t uart_transmit_flag = 0; /**< Flag that shows if there is already an ongoing transmit operation */ + +static app_fifo_t debug_fifo; /**< The FIFO for the messages to transmit */ + +static uart_instance_t uart_instance; /**< The private uart_instance used for the uart_printf operations */ + +void debug_init(void) +{ + + uart_instance.uart_peripheral = 0; + uart_instance.nrf_drv_uart_config.baudrate = (nrf_uart_baudrate_t) NRF_UART_BAUDRATE_115200; + uart_instance.nrf_drv_uart_config.hwfc = HWFC_ENABLED ? NRF_UART_HWFC_ENABLED : NRF_UART_HWFC_DISABLED; + uart_instance.nrf_drv_uart_config.interrupt_priority = APP_IRQ_PRIORITY_MID; + uart_instance.nrf_drv_uart_config.parity = NRF_UART_PARITY_EXCLUDED; + uart_instance.nrf_drv_uart_config.pselcts = UART_CTS_PIN; + uart_instance.nrf_drv_uart_config.pselrts = UART_RTS_PIN; + uart_instance.nrf_drv_uart_config.pselrxd = UART_RX_PIN; + uart_instance.nrf_drv_uart_config.pseltxd = UART_TX_PIN; + + ret_code_t ret; + UART_INIT(&uart_instance, &ret); + + app_fifo_init(&debug_fifo, debug_fifo_buf, sizeof(debug_fifo_buf)); + + uart_transmit_flag = 0; + +} + +static void uart_handler(uart_evt_t const * p_event) { + if(p_event->type == UART_TRANSMIT_DONE) { + uint32_t len = sizeof(uart_transmit_buf); + app_fifo_read(&debug_fifo, uart_transmit_buf, &len); + // Check if we need to transmit again + if(len > 0) { + uart_transmit_bkgnd(&uart_instance, uart_handler, uart_transmit_buf, len); + } else { + uart_transmit_flag = 0; + } + + } else if(p_event->type == UART_ERROR) { + uart_transmit_flag = 0; + } +} + + +void debug_log(const char* format, ...) { + + + // This is done locally in the function (because if more contextes tries to call this function at the same time..) + uint8_t printf_buf[DEBUG_PRINTF_BUFFER_SIZE]; + + va_list args; + va_start(args, format); + + int ret_vsnprintf = vsnprintf((char*) printf_buf, sizeof(printf_buf), format, args); + va_end(args); + + // ret_vsnprintf == length or error code + + // Check if the output of vsnprintf is correct + if(ret_vsnprintf < 0 || ret_vsnprintf >= sizeof(printf_buf)) { + return; + } + + // Flag if we need to start the UART transmit operation + uint8_t start_transmit = 0; + + // Put on the FIFO: + CRITICAL_REGION_ENTER(); + // Check if there is enough space in the fifo + uint32_t available_size = 0; + app_fifo_write(&debug_fifo, NULL, &available_size); + + if(ret_vsnprintf <= available_size) { + uint32_t len = (uint32_t) ret_vsnprintf; + + app_fifo_write(&debug_fifo, printf_buf, &len); + + // Check if we need to start the uart operation + + if(!uart_transmit_flag) { + uart_transmit_flag = 1; + start_transmit = 1; + } + } + CRITICAL_REGION_EXIT(); + + // Check if we need to start transmitting, otherwise there should alredy be an transmit operation that queues the currently inserted one. + if(start_transmit) { + uint32_t len = sizeof(uart_transmit_buf); + app_fifo_read(&debug_fifo, uart_transmit_buf, &len); + if(len > 0) { + uart_transmit_bkgnd(&uart_instance, uart_handler, uart_transmit_buf, len); + } else { + uart_transmit_flag = 0; + } + + } +} + + +void debug_log_dump(uint8_t * p_buffer, uint32_t len) +{ + debug_log("\r\n"); + for (uint32_t index = 0; index < len; index++) { + debug_log("0x%02X ", p_buffer[index]); + } + debug_log("\r\n"); +} + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/debug_lib.h b/firmware/nRF_badge/data_collector/incl/debug_lib.h new file mode 100644 index 0000000..bbca578 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/debug_lib.h @@ -0,0 +1,61 @@ +#ifndef __DEBUG_LIB_H +#define __DEBUG_LIB_H + + + + +/** @file + * + * @brief Debug abstraction library. + * + * @details This module can be used to print formatted debug data to the UART-module. + * Internally the module uses the uart_lib-module to format and print stuff via UART. + * If debug is not enabled, the function calls are replaced by empty functions, causing the compiler to remove them. + */ + + + +#ifdef DEBUG_LOG_ENABLE + +#include "stdint.h" + + +/** + * @brief Function for initializing the debug module. + * + * @details Initializes the module to use UART as output. + * This function configures the UART parameters. + */ +void debug_init(void); + +/** + * @brief Function for logging debug messages. + * + * @details This function logs messages over UART. The module must be initialized before using this function. + * The function uses an internal FIFO to transmit the bytes in the background. + * + * @param[in] format The format string that should be printed. + * @param[in] ... Variable arguments that should be inserted into the format string. + */ +void debug_log( const char* format, ...); + +/** + * @brief Dump auxiliary byte buffer to the debug trace. + * + * @details This Function logs messages over UART. The module must be initialized before using this function. + * + * @param[in] p_buffer Buffer to be dumped on the debug trace. + * @param[in] len Size of the buffer. + */ +void debug_log_dump(uint8_t * p_buffer, uint32_t len); + +#else + +#define debug_init(...) +#define debug_log(...) +#define debug_log_dump(...) + +#endif + + +#endif //__DEBUG_LIB_H diff --git a/firmware/nRF_badge/data_collector/incl/debug_log.c b/firmware/nRF_badge/data_collector/incl/debug_log.c deleted file mode 100644 index 18a28ed..0000000 --- a/firmware/nRF_badge/data_collector/incl/debug_log.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Modified from app_trace from NRF51 SDK - * original copyright information: - * - * Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * - */ - -#include -#include -#include -#include -#include - -#ifdef DEBUG_LOG_ENABLE -#include "app_uart.h" -#include "nordic_common.h" -#include "boards.h" -#include "debug_log.h" -#include "app_error.h" -#include "uart_commands.h" - -#ifndef UART_TX_BUF_SIZE - #define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */ -#endif -#ifndef UART_RX_BUF_SIZE - #define UART_RX_BUF_SIZE 32 /**< UART RX buffer size. */ -#endif - -void uart_error_handle(app_uart_evt_t * p_event) -{ - /*if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR) - { - APP_ERROR_HANDLER(p_event->data.error_communication); - } - else if (p_event->evt_type == APP_UART_FIFO_ERROR) - { - APP_ERROR_HANDLER(p_event->data.error_code); - }*/ -} - -void uart_event_handle(app_uart_evt_t * p_event) { - if (p_event->evt_type == APP_UART_DATA_READY) { - uint8_t rx_byte; - while (app_uart_get(&rx_byte) == NRF_SUCCESS) { - UARTCommands_ProcessChar(rx_byte); - } - } else { - uart_error_handle(p_event); - } -} - -void debug_log_init(void) -{ - uint32_t err_code = NRF_SUCCESS; - const app_uart_comm_params_t comm_params = { - RX_PIN_NUMBER, - TX_PIN_NUMBER, - RTS_PIN_NUMBER, - CTS_PIN_NUMBER, - APP_UART_FLOW_CONTROL_DISABLED, - false, - UART_BAUDRATE_BAUDRATE_Baud115200 - }; - - APP_UART_FIFO_INIT(&comm_params, - UART_RX_BUF_SIZE, - UART_TX_BUF_SIZE, - uart_event_handle, - APP_IRQ_PRIORITY_LOW, - err_code); - - UNUSED_VARIABLE(err_code); -} - -void debug_log_dump(uint8_t * p_buffer, uint32_t len) -{ - debug_log("\r\n"); - for (uint32_t index = 0; index < len; index++) { - debug_log("0x%02X ", p_buffer[index]); - } - debug_log("\r\n"); -} - -#endif // DEBUG_LOG_ENABLE - -/** - *@} - **/ - diff --git a/firmware/nRF_badge/data_collector/incl/debug_log.h b/firmware/nRF_badge/data_collector/incl/debug_log.h deleted file mode 100644 index 7585974..0000000 --- a/firmware/nRF_badge/data_collector/incl/debug_log.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Derived from app_trace from NRF51 SDK - * for enabling debug logging in exclusively application-specific code. - * (i.e. to ignore debug traces of Nordic libraries) - */ - -#ifndef DEBUG_LOG_H -#define DEBUG_LOG_H - -#include -#include - -/** - * @defgroup app_trace Debug Logger - * @ingroup app_common - * @{ - * @brief Enables debug logs/ trace over UART. - * @details Enables debug logs/ trace over UART. Tracing is enabled only if - * ENABLE_DEBUG_LOG_SUPPORT is defined in the project. - */ -#ifdef DEBUG_LOG_ENABLE -/** - * @brief Module Initialization. - * - * @details Initializes the module to use UART as trace output. - * - * @warning This function will configure UART using default board configuration. - * Do not call this function if UART is configured from a higher level in the application. - */ -void debug_log_init(void); - -/** - * @brief Log debug messages. - * - * @details This API logs messages over UART. The module must be initialized before using this API. - * - * @note Though this is currently a macro, it should be used used and treated as function. - */ -#define debug_log printf - -/** - * @brief Dump auxiliary byte buffer to the debug trace. - * - * @details This API logs messages over UART. The module must be initialized before using this API. - * - * @param[in] p_buffer Buffer to be dumped on the debug trace. - * @param[in] len Size of the buffer. - */ -void debug_log_dump(uint8_t * p_buffer, uint32_t len); - -#else // DEBUG_LOG_ENABLE - -#define debug_log_init(...) -#define debug_log(...) -#define debug_log_dump(...) - -#endif // DEBUG_LOG_ENABLE - -/** @} */ - -#endif //DEBUG_LOG_H diff --git a/firmware/nRF_badge/data_collector/incl/eeprom_lib.c b/firmware/nRF_badge/data_collector/incl/eeprom_lib.c new file mode 100644 index 0000000..138d8b0 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/eeprom_lib.c @@ -0,0 +1,445 @@ +#include "eeprom_lib.h" + +#include "spi_lib.h" +#include "nrf_drv_common.h" +#include "custom_board.h" + +#include "app_util_platform.h" + +#include "debug_lib.h" + +#include "systick_lib.h" // Needed for the timeout-checks + + + +#define CMD_WREN 0b00000110 /**< Write enable command code */ +#define CMD_RDSR 0b00000101 /**< Read status register command code */ +#define CMD_WRSR 0b00000001 /**< Write status register command code */ +#define CMD_WRITE 0b00000010 /**< Write to memory array command code */ +#define CMD_READ 0b00000011 /**< Read from memory array command code */ + +#define EEPROM_OPERATION_TIMEOUT_MS 500 /**< The time in milliseconds to wait for an operation to finish. */ +#define EEPROM_PAGE_SIZE 128 /**< We need to align the bytes to write and read to the EEPROM boundaries (because of unknown reasons it couldn't be 256..) */ + + + +static ret_code_t eeprom_read_status(uint8_t* eeprom_status); +static bool eeprom_is_busy(void); +static ret_code_t eeprom_write_enable(void); +static ret_code_t eeprom_global_unprotect(void); + + +static uint8_t eeprom_buf[EEPROM_PAGE_SIZE + 4]; /**< Buffer for EEPROM read/store operations (+ 4 for header) */ + + +typedef struct { + uint8_t first_data[4]; + uint8_t* p_data; +} eeprom_finish_operation_t; + + + +static spi_instance_t spi_instance; /**< The spi instance used to communicate with the EEPROM-IC via SPI */ + +static volatile eeprom_operation_t eeprom_operation = EEPROM_NO_OPERATION; /**< The current EEPROM operation */ + +static volatile eeprom_finish_operation_t eeprom_finish_operation; + + + + +// It is a little bit more complicate than flash, because there is no callback when the actual write operation is done in EEPROM! +void spi_event_handler(spi_evt_t const * p_event) { + if(p_event->type == SPI_TRANSFER_DONE) { + // Just copy the replaced 4 bytes at the beginning back to the data-array (rx_data or tx_data) + memcpy((uint8_t*) eeprom_finish_operation.p_data, (uint8_t*) eeprom_finish_operation.first_data, 4); + } +} + + + + + +ret_code_t eeprom_init(void) { + + spi_instance.spi_peripheral = 0; + spi_instance.nrf_drv_spi_config.frequency = NRF_DRV_SPI_FREQ_8M; + spi_instance.nrf_drv_spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST; + spi_instance.nrf_drv_spi_config.mode = NRF_DRV_SPI_MODE_3; + spi_instance.nrf_drv_spi_config.orc = 0; + spi_instance.nrf_drv_spi_config.irq_priority = APP_IRQ_PRIORITY_MID; + spi_instance.nrf_drv_spi_config.ss_pin = SPIM0_EEPROM_SS_PIN; + spi_instance.nrf_drv_spi_config.miso_pin = SPIM0_MISO_PIN; + spi_instance.nrf_drv_spi_config.mosi_pin = SPIM0_MOSI_PIN; + spi_instance.nrf_drv_spi_config.sck_pin = SPIM0_SCK_PIN; + + ret_code_t ret = spi_init(&spi_instance); + + // ret could be NRF_SUCCESS or NRF_ERROR_INVALID_PARAM + if(ret != NRF_SUCCESS) + return NRF_ERROR_INTERNAL; + + // Directly unprotect the EEPROM in the initialization step + ret = eeprom_global_unprotect(); + + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY or NRF_ERROR_TIMEOUT + + return ret; +} + + + + + +/**@brief Function for reading the status of the EEPROM. + * + * @param[in,out] eeprom_status Pointer to memory where to save current eeprom status. + * + * @retval NRF_SUCCESS If the spi operation was succesful. + * @retval NRF_ERROR_BUSY If there is already an SPI operation ongoing on the same spi peripheral. + */ +static ret_code_t eeprom_read_status(uint8_t* p_eeprom_status) { + uint8_t tx_buf[1] = {CMD_RDSR}; + uint8_t rx_buf[2] = {0, 0x01}; + + ret_code_t ret = spi_transmit_receive(&spi_instance, tx_buf, 1, rx_buf, 2); + + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INVALID_ADDR (the last one can't happen here) + if(ret != NRF_SUCCESS) + ret = NRF_ERROR_BUSY; + + + *p_eeprom_status = rx_buf[1]; + + return ret; +} + +/**@brief Function for checking if the EEPROM is busy. + * + * @retval 0 If the EEPROM and the spi interface is not busy. + * @retval 1 If the EEPROM or the spi interface is busy. + */ +static bool eeprom_is_busy(void) { + uint8_t eeprom_status = 0x01; + ret_code_t ret = eeprom_read_status(&eeprom_status); + // Check if the SPI is busy + if(ret != NRF_SUCCESS) + return 1; + + // Check if busy flag of the EEPROM status is set + if(eeprom_status & 0x01) { + return 1; + } + return 0; +} + + + +/**@brief Function for enabling a write operation to the EEPROM. + * + * @retval NRF_SUCCESS If the spi transmit operation was succesful. + * @retval NRF_ERROR_BUSY If there is already an SPI operation ongoing on the same spi peripheral. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +static ret_code_t eeprom_write_enable(void) { + // Generating one-byte buffer for writing the "write enable"-command + uint8_t tx_buf[1] = {CMD_WREN}; + + // SPI transmit in blocking mode + ret_code_t ret = spi_transmit(&spi_instance, tx_buf, 1); + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INVALID_ADDR (the last one can't happen here) + if(ret != NRF_SUCCESS) { + return NRF_ERROR_BUSY; + } + + + uint64_t end_ms = systick_get_continuous_millis() + EEPROM_OPERATION_TIMEOUT_MS; + while(eeprom_get_operation() != EEPROM_NO_OPERATION && systick_get_continuous_millis() < end_ms); + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + // Reset the eeprom operation + eeprom_operation = EEPROM_NO_OPERATION; + return NRF_ERROR_TIMEOUT; + } + + return NRF_SUCCESS; +} + +/**@brief Function for unprotecting all EEPROM blocks for writing to them. + * + * @retval NRF_SUCCESS If the spi transmit operation was succesful. + * @retval NRF_ERROR_BUSY If there is already an SPI operation ongoing on the same spi peripheral. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +static ret_code_t eeprom_global_unprotect(void) { + + // Enable writing + ret_code_t ret = eeprom_write_enable(); + if(ret != NRF_SUCCESS) + return ret; + + // Generating two-byte buffer for writing the "write status register"-command and the new value of the status register + // Status register format (Datasheet p.20, Table 6) | SRWD | 0 | 0 | 0 | BP1 | BP0 | WEL | WIP | + // Only the Bits SRWD (Status Register Write Protect), BP1 and BP0 (Block Protect) can be manipulated + uint8_t tx_buf[2] = {CMD_WRSR, 0x00}; + + // SPI transmit in blocking mode + ret = spi_transmit(&spi_instance, tx_buf, 2); + + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INVALID_ADDR (the last one can't happen here) + if(ret != NRF_SUCCESS) + ret = NRF_ERROR_BUSY; + + uint64_t end_ms = systick_get_continuous_millis() + EEPROM_OPERATION_TIMEOUT_MS; + while(eeprom_get_operation() != EEPROM_NO_OPERATION && systick_get_continuous_millis() < end_ms); + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + // Reset the eeprom operation + eeprom_operation = EEPROM_NO_OPERATION; + return NRF_ERROR_TIMEOUT; + } + + + return ret; +} + + + +ret_code_t eeprom_store(uint32_t address, const uint8_t* tx_data, uint32_t length_tx_data) { + + // Check if the specified address is valid. The EEPROM has 256kByte of memory. + // Furthermore check if the data pointer is in RAM, not in read only-Memory + if((address + length_tx_data > (EEPROM_SIZE)) || !nrf_drv_is_in_RAM(tx_data)) + return NRF_ERROR_INVALID_PARAM; + + + if(tx_data == NULL) + return NRF_ERROR_INVALID_PARAM; + + // Check if the EEPROM has already an ongoing operation. + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + return NRF_ERROR_BUSY; + } + + uint32_t address_offset = 0; + while(length_tx_data > 0) { + // Set the eeprom operation to store + eeprom_operation = EEPROM_STORE_OPERATION; + + // Enable writing + ret_code_t ret = eeprom_write_enable(); + // ret could be NRF_SUCCESS or NRF_ERROR_BUSY + if(ret != NRF_SUCCESS) { + // Reset the eeprom operation + eeprom_operation = EEPROM_NO_OPERATION; + return ret; + } + + uint32_t tmp_address = address + address_offset; + // The header for writing {CMD_WRITE, address24_16, address15_8, address7_0} + uint8_t tx_header[4] = {CMD_WRITE, ((tmp_address>>16)&0xFF), ((tmp_address>>8)&0xFF), ((tmp_address>>0)&0xFF)}; + + // Round up to next Page-address + uint32_t next_page_address = ((tmp_address/EEPROM_PAGE_SIZE) + 1) * EEPROM_PAGE_SIZE; + uint32_t step_len = next_page_address - tmp_address; + step_len = (length_tx_data > step_len) ? step_len : length_tx_data; + + memcpy(eeprom_buf, tx_header, 4); + memcpy(&eeprom_buf[4], &tx_data[address_offset], step_len); + + // Start a blocking spi transmission + ret = spi_transmit(&spi_instance, eeprom_buf, 4 + step_len); + + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INVALID_ADDR + if(ret == NRF_ERROR_INVALID_ADDR) ret = NRF_ERROR_INVALID_PARAM; + if(ret != NRF_SUCCESS) { + // Reset the eeprom operation + eeprom_operation = EEPROM_NO_OPERATION; + return ret; + } + + // Wait until the first 4 data bytes are stored into the EEPROM (important to not just check if they have been transmitted via spi, because the EEPROM needs time to store it internally) + uint64_t end_ms = systick_get_continuous_millis() + EEPROM_OPERATION_TIMEOUT_MS; + while(eeprom_get_operation() != EEPROM_NO_OPERATION && systick_get_continuous_millis() < end_ms); + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + // Reset the eeprom operation + eeprom_operation = EEPROM_NO_OPERATION; + return NRF_ERROR_TIMEOUT; + } + + + length_tx_data -= step_len; + address_offset += step_len; + } + + return NRF_SUCCESS; +} + + + + + +ret_code_t eeprom_read(uint32_t address, uint8_t* rx_data, uint32_t length_rx_data) { + // Check if the specified address is valid. The EEPROM has 256kByte of memory. + // Furthermore check if the data pointer is in RAM, not in read only-Memory + if((address + length_rx_data > (EEPROM_SIZE) ) || !nrf_drv_is_in_RAM(rx_data)) + return NRF_ERROR_INVALID_PARAM; + + if(rx_data == NULL) + return NRF_ERROR_INVALID_PARAM; + + // Check if the EEPROM has already an ongoing operation. + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + return NRF_ERROR_BUSY; + } + + + uint32_t address_offset = 0; + while(length_rx_data > 0) { + // Set the eeprom operation to read + eeprom_operation = EEPROM_READ_OPERATION; + + uint32_t tmp_address = address + address_offset; + uint8_t tx_header[4] = {CMD_READ, ((tmp_address>>16)&0xFF), ((tmp_address>>8)&0xFF), ((tmp_address>>0)&0xFF)}; + + // Round up to next Page-address + uint32_t next_page_address = ((tmp_address/EEPROM_PAGE_SIZE) + 1) * EEPROM_PAGE_SIZE; + uint32_t step_len = next_page_address - tmp_address; + step_len = (length_rx_data > step_len) ? step_len : length_rx_data; + memset(eeprom_buf, 0, sizeof(eeprom_buf)); + + // Do a blocking transmit receive operation + ret_code_t ret = spi_transmit_receive(&spi_instance, tx_header, 4, eeprom_buf, 4 + step_len); + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INVALID_ADDR + if(ret == NRF_ERROR_INVALID_ADDR) ret = NRF_ERROR_INVALID_PARAM; + if(ret != NRF_SUCCESS) { + // Reset the eeprom operation + eeprom_operation = EEPROM_NO_OPERATION; + return ret; + } + + // Wait until the first 4 data bytes are stored into the EEPROM (important to not just check if they have been transmitted via spi, because the EEPROM needs time to store it internally) + uint64_t end_ms = systick_get_continuous_millis() + EEPROM_OPERATION_TIMEOUT_MS; + while(eeprom_get_operation() != EEPROM_NO_OPERATION && systick_get_continuous_millis() < end_ms); + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + // Reset the eeprom operation + eeprom_operation = EEPROM_NO_OPERATION; + return NRF_ERROR_TIMEOUT; + } + memcpy(&rx_data[address_offset], &eeprom_buf[4], step_len); + + length_rx_data -= step_len; + address_offset += step_len; + } + + + return NRF_SUCCESS; +} + + + + +eeprom_operation_t eeprom_get_operation(void) { + + // Reset the eeprom operation if the EEPROM and the spi interface is not busy (this could not be done in an interrupt handler, + // because there is no interrupt from the EEPROM only from the SPI, but this is not representative for internal EEPROM operation) + if(eeprom_is_busy()) { + // If no operation is set, but the EEPROM seems to be busy, there should be an store operation ongoing (e.g. unprotect-function) + if(eeprom_operation == EEPROM_NO_OPERATION) + eeprom_operation = EEPROM_STORE_OPERATION; + } else { + eeprom_operation = EEPROM_NO_OPERATION; + + } + + return eeprom_operation; + +} + + + +uint32_t eeprom_get_size(void) { + return EEPROM_SIZE; +} + +bool eeprom_selftest(void) { + + #define EEPROM_TEST_DATA_LEN 500 + #define EEPROM_TEST_ADDRESS 1234 + + uint8_t data[EEPROM_TEST_DATA_LEN + 1]; + uint8_t rx_data[EEPROM_TEST_DATA_LEN + 1]; + + for(uint32_t i = 0; i < EEPROM_TEST_DATA_LEN; i++) { + data[i] = (i%52)+65; + rx_data[i] = 0; + } + + + + debug_log("EEPROM: Started EEPROM selftest...\n\r"); + + + +//********************** Read and write Tests *********************** + ret_code_t ret = eeprom_store(EEPROM_TEST_ADDRESS, (uint8_t*) data, EEPROM_TEST_DATA_LEN); + debug_log("EEPROM: Test store, Ret: %d\n\r", ret); + if(ret != NRF_SUCCESS) { + debug_log("EEPROM: Store failed!\n\r"); + return 0; + } + + + + ret = eeprom_read(EEPROM_TEST_ADDRESS, rx_data, EEPROM_TEST_DATA_LEN); + rx_data[EEPROM_TEST_DATA_LEN] = 0; + data[EEPROM_TEST_DATA_LEN] = 0; + + debug_log("EEPROM: Test read, Ret: %d\n", ret); + if(ret != NRF_SUCCESS) { + debug_log("EEPROM: Read failed!\n\r"); + return 0; + } + + + if(memcmp(data, rx_data, EEPROM_TEST_DATA_LEN) != 0) { + debug_log("EEPROM: Read data don't match written data!"); + return 0; + } + +//********************* No RAM memory tests ************************* + char* test = "HELLO"; + ret = eeprom_store(EEPROM_TEST_ADDRESS, (uint8_t*) test, 5); + + debug_log("EEPROM: Test store non RAM data, Ret: %d\n\r", ret); + if(ret != NRF_ERROR_INVALID_PARAM) { + debug_log("EEPROM: Test store non RAM data failed!\n\r"); + return 0; + } + + + ret = eeprom_read(EEPROM_TEST_ADDRESS, (uint8_t*) test, 5); + + debug_log("EEPROM: Test read to non RAM data, Ret: %d\n\r", ret); + if(ret != NRF_ERROR_INVALID_PARAM) { + debug_log("EEPROM: Test read to non RAM data failed!\n\r"); + return 0; + } + +//******************* False address test ************************** + ret = eeprom_store(EEPROM_SIZE - 2, (uint8_t*) data, EEPROM_TEST_DATA_LEN); + + debug_log("EEPROM: Test invalid address, Ret: %d\n\r", ret); + if(ret != NRF_ERROR_INVALID_PARAM) { + debug_log("EEPROM: Test invalid address failed!\n\r"); + return 0; + } + + debug_log("EEPROM: EEPROM test successful!!\n\r"); + + + return 1; +} + + + + + diff --git a/firmware/nRF_badge/data_collector/incl/eeprom_lib.h b/firmware/nRF_badge/data_collector/incl/eeprom_lib.h new file mode 100644 index 0000000..1376f2a --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/eeprom_lib.h @@ -0,0 +1,110 @@ +#ifndef __EEPROM_LIB_H +#define __EEPROM_LIB_H + + +/** @file + * + * @brief EEPROM-IC abstraction library. + * + * @details It enables to store and read data to and from the EEPROM via SPI in blocking or in non-blocking mode. + * It uses a cool mechanism to not allocate big internal buffers for the SPI transfers. + * For the SPI operations the spi_lib-module is used. + * + * @note It is important that the tx_data-buffer (for store operations) is in the Data RAM section (not in read-only section). + */ + +#include +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +#define EEPROM_SIZE (1024*256) /**< Size of the external EEPROM in bytes */ + +/**@brief The different EEPROM operations. These operations will be used to set the peripheral busy or not. */ + typedef enum { + EEPROM_NO_OPERATION = 0, /**< Currently no operation ongoing. */ + EEPROM_STORE_OPERATION = (1 << 0), /**< Currently there is an ongoing store operation. */ + EEPROM_READ_OPERATION = (1 << 1), /**< Currently there is an ongoing read operation. */ +} eeprom_operation_t; + + +/**@brief Function for initializing the eeprom module. + * + * @details This functions initializes the underlying spi-module. + * The spi peripheral has be enabled in the config-file: sdk_config.h. + * Furthermore, it unprotects the EEPROM (to be able to write to any EEPROM block). + * + * + * @retval NRF_SUCCESS If the module was successfully initialized. + * @retval NRF_ERROR_INTERNAL If there was an error while initializing the spi-module (e.g. bad configuration) + * @retval NRF_ERROR_BUSY If the unprotecting operation failed (because of an ongoing spi operation). + * @retval NRF_ERROR_TIMEOUT If the unprotecting operation takes too long. + */ +ret_code_t eeprom_init(void); + + + +/**@brief Function for storing data in blocking mode in EEPROM via SPI. + * + * @details This function waits until the operation (spi transfer and EEPROM internal write) has terminated. + * + * + * @param[in] address Address in EEPROM where to store the data. + * @param[in] tx_data Pointer to the data to store. This is not const because it is internally modified. It must be in Data RAM region. + * @param[in] length_tx_data Length of the data to store. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If the SPI interface or the EEPROM is busy. + * @retval NRF_ERROR_INVALID_PARAM If the address is to big or the provided tx_data-buffer is not placed in the Data RAM region. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t eeprom_store(uint32_t address, const uint8_t* tx_data, uint32_t length_tx_data); + + + +/**@brief Function for reading data in blocking mode from EEPROM via SPI. + * + * @details This function waits until the operation (spi transfer) has terminated. + * + * + * @param[in] address Address of the data to be read from EEPROM. + * @param[in] rx_data Pointer to the buffer where to store the read data. It must be in Data RAM region. + * @param[in] length_rx_data Length of the data to read. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If the SPI interface or the EEPROM is busy. + * @retval NRF_ERROR_INVALID_PARAM If the address is to big or the provided rx_data-buffer is not placed in the Data RAM region. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t eeprom_read(uint32_t address, uint8_t* rx_data, uint32_t length_rx_data); + + +/**@brief Function for retrieving the current operation. + * + * @details This function actually only return the current eeprom operation. + * Furthermore, it checks if the SPI or EEPROM is busy. If this is the case and if the current + * operation is EEPROM_NO_OPERATION, it sets the operation to EEPROM_STORE_OPERATION. + * If the SPI and EEPROM is not busy, it resets the current operation to EEPROM_NO_OPERATION. + * + * + * @retval EEPROM_NO_OPERATION If there is currently no EEPROM (and SPI) operation is ongoing. + * @retval EEPROM_STORE_OPERATION If there is currently a store-operation ongoing. + * @retval EEPROM_READ_OPERATION If there is currently a read-operation ongoing. + */ +eeprom_operation_t eeprom_get_operation(void); + + +/**@brief Function for reading the number of bytes in the EEPROM. + * + * @retval Number of bytes in the EEPROM. + */ +uint32_t eeprom_get_size(void); + +/**@brief Function for testing the eeprom module. + * + * @retval 0 If selftest failed. + * @retval 1 If selftest passed. + */ +bool eeprom_selftest(void); + + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/ext_eeprom.c b/firmware/nRF_badge/data_collector/incl/ext_eeprom.c deleted file mode 100644 index fd3352c..0000000 --- a/firmware/nRF_badge/data_collector/incl/ext_eeprom.c +++ /dev/null @@ -1,190 +0,0 @@ -#include "ext_eeprom.h" - - - -void spi_evt_handler(spi_master_evt_t spi_master_evt) -{ - if (spi_master_evt.evt_type == SPI_MASTER_EVT_TRANSFER_COMPLETED) { - switch (extEEPROMstate) { - case EXT_EEPROM_COMMAND: //completed sending a one-off command - //debug_log("Sent EEPROM command\r\n"); - extEEPROMstate = EXT_EEPROM_SPI_IDLE; - break; - case EXT_EEPROM_READ: //completed reading values into rx buffer - //debug_log("Finished read\r\n"); - extEEPROMstate = EXT_EEPROM_SPI_IDLE; - break; - case EXT_EEPROM_WRITE: //completed writing values from tx buffer - //debug_log("Finished write\r\n"); - extEEPROMstate = EXT_EEPROM_SPI_IDLE; - break; - default: - break; - } - } -} - -void spi_init() -{ - uint32_t err_code = NRF_SUCCESS; - - //manually handle SS pin - nrf_gpio_pin_set(SPIM0_SS_PIN); - nrf_gpio_cfg_output(SPIM0_SS_PIN); - - // Configure SPI master. - spi_master_config_t spi_config = { - SPI_FREQUENCY_FREQUENCY_M8, // Serial clock frequency 1 Mbps. - SPIM0_SCK_PIN, // Defined in badge_03.h - SPIM0_MISO_PIN, - SPIM0_MOSI_PIN, - SPIM0_SS_PIN, // // SS, we'll handle that manually - APP_IRQ_PRIORITY_LOW, // Interrupt priority LOW. - SPI_CONFIG_ORDER_MsbFirst, // Bits order LSB. - SPI_CONFIG_CPOL_ActiveLow, // Serial clock polarity ACTIVELOW. - SPI_CONFIG_CPHA_Trailing, // Serial clock phase TRAILING. - 0 // Don't disable all IRQs. - }; - - err_code = spi_master_open(SPI_MASTER_0, &spi_config); - if (err_code != NRF_SUCCESS) { - debug_log("Err\r\n"); - } - - // Register event handler for SPI master. - spi_master_evt_handler_reg(SPI_MASTER_0, spi_evt_handler); -} - -void spi_end() { - spi_master_close(SPI_MASTER_0); -} - -bool spi_busy() { - return (spi_master_get_state(SPI_MASTER_0) == SPI_MASTER_STATE_BUSY); -} - -ext_eeprom_status_t ext_eeprom_get_status() { - if (extEEPROMstate != EXT_EEPROM_SPI_IDLE) { - return extEEPROMstate; // currently reading/writing on SPI - } - else { - unsigned char status = ext_eeprom_read_status(); - if (status & EXT_EEPROM_SREG_BUSY) { - return EXT_EEPROM_IC_BUSY; // EEPROM chip is busy writing/erasing - } - return EXT_EEPROM_ALL_IDLE; // EEPROM chip is free, no SPI transfers pending - } -} - -bool ext_eeprom_busy() { - return (ext_eeprom_get_status() != EXT_EEPROM_ALL_IDLE); -} - -void ext_eeprom_wait() { - while (ext_eeprom_busy()); -} - - -unsigned char dummyRxBuf[1]; - - -//enable writes to EEPROM chip, needed before any writing command (status reg write, page write, etc) -static void ext_eeprom_WEL() -{ - uint8_t txBuf[1] = {WEL_OPCODE}; //enable EEPROM write - spi_master_send_recv(SPI_MASTER_0,txBuf,sizeof(txBuf),dummyRxBuf,0); - while(spi_busy()); -} - -uint32_t ext_eeprom_global_unprotect() -{ - if (extEEPROMstate != EXT_EEPROM_SPI_IDLE) { - return EXT_EEPROM_ERR_BUSY; - } - ext_eeprom_WEL(); - // opcode global unprotect - uint8_t txBuf[2] = {WRITESREG_OPCODE, 0x00}; //Write to status register - extEEPROMstate = EXT_EEPROM_COMMAND; - spi_master_send_recv(SPI_MASTER_0,txBuf,sizeof(txBuf),dummyRxBuf,0); - while (spi_busy()); - return EXT_EEPROM_SUCCESS; -} - -uint8_t ext_eeprom_read_status() -{ - if (extEEPROMstate != EXT_EEPROM_SPI_IDLE) { - return EXT_EEPROM_ERR_BUSY; - } - // opcode - uint8_t txBuf[1] = {READSREG_OPCODE}; - uint8_t rxBuf[2] = {0,0}; - spi_master_send_recv(SPI_MASTER_0,txBuf,sizeof(txBuf),rxBuf,2); - while (spi_busy()); - return rxBuf[1]; -} - - -uint32_t ext_eeprom_read(unsigned int address, uint8_t* rx, unsigned int numBytes) -{ - if(extEEPROMstate != EXT_EEPROM_SPI_IDLE) { - return EXT_EEPROM_ERR_BUSY; - } - // opcode address - uint8_t txBuf[4] = {READ_OPCODE, (address>>16)&0xff,(address>>8)&0xff,address&0xff}; - extEEPROMstate = EXT_EEPROM_READ; - spi_master_send_recv(SPI_MASTER_0,txBuf,sizeof(txBuf),rx,numBytes); - return EXT_EEPROM_SUCCESS; -} - - -uint32_t ext_eeprom_write(unsigned int address, uint8_t* txRaw, unsigned int numBytes) -{ - if(extEEPROMstate != EXT_EEPROM_SPI_IDLE) { - return EXT_EEPROM_ERR_BUSY; - } - ext_eeprom_WEL(); //enable writes - txRaw[0] = WRITE_OPCODE; //opcode - //address bytes - txRaw[1] = (address>>16)&0xff; - txRaw[2] = (address>>8)&0xff; - txRaw[3] = address&0xff; - //data - extEEPROMstate = EXT_EEPROM_WRITE; - spi_master_send_recv(SPI_MASTER_0,txRaw,numBytes,dummyRxBuf,0); - return EXT_EEPROM_SUCCESS; -} - - -bool testExternalEEPROM(void) -{ - - // unlock EEPROM - uint32_t stat = ext_eeprom_global_unprotect(); - if (stat != EXT_EEPROM_SUCCESS) { - return false; - } - ext_eeprom_wait(); // wait for unlock to finish - - // write to the first block and test the result - unsigned char value[8] = {0,0,0,0,1,2,3,4}; // includes padding bytes - stat = ext_eeprom_write(0,value,sizeof(value)); - if (stat != EXT_EEPROM_SUCCESS) { - return false; - } - - ext_eeprom_wait(); // wait for write to finish - - /* not getting what I expected */ - unsigned char buf[8]; // includes padding bytes - stat = ext_eeprom_read(0,buf,sizeof(buf)); - if (stat != EXT_EEPROM_SUCCESS) { - return false; - } - - if (buf[4] != 1) { - debug_log("Error - got : %d\r\n", buf[0]); - return false; - } - - return true; -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/ext_eeprom.h b/firmware/nRF_badge/data_collector/incl/ext_eeprom.h deleted file mode 100644 index 609ff87..0000000 --- a/firmware/nRF_badge/data_collector/incl/ext_eeprom.h +++ /dev/null @@ -1,191 +0,0 @@ -/* Methods to simplify access to an external Atmel AT25M02 (or equivalent) EEPROM IC - * - */ - - -#ifndef EXT_EEPROM_H -#define EXT_EEPROM_H - - -#include -#include - -#include "nordic_common.h" -#include "nrf.h" -#include "nrf51_bitfields.h" -#include "app_util_platform.h" - - -#include "app_error.h" //error handling -#include "nrf_delay.h" //includes blocking delay functions -#include "nrf_gpio.h" //abstraction for dealing with gpio -#include "spi_master.h" //abstraction for dealing with SPI - -#include "debug_log.h" //UART debugging logger -//requires app_fifo, app_uart_fifo.c and retarget.c for printf to work properly - -#include "nrf_drv_config.h" -#include "boards.h" - - -#define WEL_OPCODE 0x06 // enable write -#define WRITESREG_OPCODE 0x01 // write to status register -#define READSREG_OPCODE 0x05 // read status register -#define WRITE_OPCODE 0x02 // write to main memory -#define READ_OPCODE 0x03 // read from main memory - - - -#define EXT_EEPROM_SREG_BUSY 0x1 // indicates whether device is busy -#define EXT_EEPROM_SREG_WEL 0x2 // write enable bit -#define EXT_EEPROM_SREG_PR0 0x04 // block protect bit 0 -#define EXT_EEPROM_SREG_PR1 0x08 // block protect bit 1 -#define EXT_EEPROM_SREG_WPEN 0x80 // write protect enable - - -typedef enum -{ - EXT_EEPROM_SPI_IDLE = 0, // no pending SPI operations. EEPROM IC may still be busy - EXT_EEPROM_COMMAND, // sending a one-off command over SPI, e.g. write enable - EXT_EEPROM_READ, // sending a read command over SPI - EXT_EEPROM_WRITE, // sending a write command over SPI - EXT_EEPROM_PENDING, // IC may be busy with a write operation - // The following are only used in returns from ext_eeprom_get_state() - // because they require polling the state of the EEPROM IC, which isn't done continuously - EXT_EEPROM_IC_BUSY, // SPI is not active, but EEPROM IC is still busy - EXT_EEPROM_ALL_IDLE // SPI is not active, and EEPROM IC not busy -} ext_eeprom_status_t; - -typedef enum ext_eeprom_result_t -{ - EXT_EEPROM_SUCCESS = 0, - EXT_EEPROM_ERR_BUSY = 1 -} ext_eeprom_result_t; - -#define EXT_EEPROM_PADDING 4 //size of padding at beginning of rx buffer, for tx bytes - - - - -/** - * Macro for creating a buffer appropriate for use with the ext_eeprom_write function. - * Buffer needs to have 4 extra bytes at start, for external EEPROM opcode+address - * The following macro sets up a union that accomplishes this, while allowing the actual data - * to be manipulated (via the .data member) as a normal array without the 4-byte padding. - * Example usage: - * CREATE_TX_BUFFER(buffer,1); - * buffer.data[0] = 42; - * ext_eeprom_write(address,buffer.whole,sizeof(buffer.whole)); - */ - -#define CREATE_TX_BUFFER(__BUF_NAME__,__BUF_LEN__) \ - union \ - { \ - struct \ - { \ - unsigned char dummy[4]; \ - unsigned char data[__BUF_LEN__]; \ - }; \ - unsigned char whole[__BUF_LEN__+4]; \ - } __BUF_NAME__ - - -volatile int extEEPROMstate; - -/* - * Called on SPI event interrupt - */ -void spi_evt_handler(spi_master_evt_t spi_master_evt); - -/* - * Initialize SPI module (master 0) - */ -void spi_init(); - -/* - * Disable SPI module - */ -void spi_end(); - -/* - * Returns true if SPI module is busy with transfer - */ -bool spi_busy(); - -/* - * Returns EEPROM access status, including checking whether the external EEPROM chip itself is busy - */ -ext_eeprom_status_t ext_eeprom_get_status(); - -/* - * Returns true if external eeprom access operations are active - */ -bool ext_eeprom_busy(); - -/* - * Waits until external eeprom access operations are done - */ -void ext_eeprom_wait(); - - -/* - * Enable writing to external EEPROM - */ -uint32_t ext_eeprom_write_enable(); - -/* - * Globally unprotect sectors on EEPROM (must call ext_eeprom_write_enable first) - */ -uint32_t ext_eeprom_global_unprotect(); - -/* - * Read status register of external EEPROM chip - */ -uint8_t ext_eeprom_read_status(); - -/* - * Read manufacturer and device ID information - */ -uint32_t ext_eeprom_read_MFID(); - -/* - * Read from the OTP security register - * (useful to verify SPI communication is functional) - * Parameters: initial address, receiving buffer, number of bytes to read - * Returns EXT_EEPROM_ERR_BUSY if there's currently another pending external EEPROM operation - */ -uint32_t ext_eeprom_read_OTP(unsigned int address, uint8_t* rx, unsigned int numBytes); - -/* - * Read from EEPROM - * Parameters: initial address, receiving buffer, number of bytes to read - ** NOTE: rx buffer should contain 4 dummy bytes at beginning, for transmitting opcode, address - * Returns EXT_EEPROM_ERR_BUSY if there's currently another pending external EEPROM operation - */ -uint32_t ext_eeprom_read(unsigned int address, uint8_t* rx, unsigned int numBytes); - - -/* - * Write to EEPROM - * Parameters: initial address, transmitting buffer, number of bytes to write - ** NOTE: tx buffer must contain 4 dummy bytes at beginning. ext_eeprom_write will fill them - * with opcode and address bytes. - * For convenience, see CREATE_TX_BUFFER macro above - * Returns EXT_EEPROM_ERR_BUSY if there's currently another pending external EEPROM operation - */ -uint32_t ext_eeprom_write(unsigned int address, uint8_t* tx, unsigned int numBytes); - - - -// Tests external EEPROM. Returns true on success -bool testExternalEEPROM(void); - - - -/* - * Manually clock out a bit on MOSI and SCK - */ -//void clock(unsigned char bit); - - -#endif //#ifndef EXTERNAL_EEPROM_H \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/filesystem_lib.c b/firmware/nRF_badge/data_collector/incl/filesystem_lib.c new file mode 100644 index 0000000..1afc2f5 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/filesystem_lib.c @@ -0,0 +1,1402 @@ +#include "filesystem_lib.h" +#include "storage_lib.h" + + +#include "stdio.h" +#include "string.h" // For memcpy + +#define ITERATOR_VALID_NUMBER 0xA5 + +uint16_t number_of_partitions = 0; /**< Number of registered partitions */ +partition_t partitions [MAX_NUMBER_OF_PARTITIONS]; /**< Array of partitions (index is referenced through "partition_id & 0x3FFF") */ +partition_iterator_t partition_iterators [MAX_NUMBER_OF_PARTITIONS]; /**< Array of partition-iterators (index is referenced through "partition_id & 0x3FFF") */ + + + +uint32_t next_free_address = 0; /**< The next free address for a new partition */ + +ret_code_t filesystem_check_iterator_conflict(uint16_t partition_id, uint32_t element_address, uint16_t element_len); + +/**@brief Function for calculating CRC-16 (CRC-16 CCITT) in blocks. + * + * Feed each consecutive data block into this function, along with the current value of p_crc as + * returned by the previous call of this function. The first call of this function should pass NULL + * as the initial value of the crc in p_crc. + * + * @param[in] p_data The input data block for computation. + * @param[in] size The size of the input data block in bytes. + * @param[in] p_crc The previous calculated CRC-16 value or NULL if first call. + * + * @retval The updated CRC-16 value, based on the input supplied. + */ +uint16_t crc16_compute(uint8_t const * p_data, uint32_t size, uint16_t const * p_crc) { + uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc; + + for (uint32_t i = 0; i < size; i++) + { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= p_data[i]; + crc ^= (uint8_t)(crc & 0xFF) >> 4; + crc ^= (crc << 8) << 4; + crc ^= ((crc & 0xFF) << 4) << 1; + } + + return crc; +} + + + +/** @brief Function for incrementing the record_id. + * + * @details This function increments the record id and take some special values into consideration. + * The values 0xFFFF and 0 are special values, because the default value in flash/EEPROM is 0xFFFF or 0 + * and a search for the next/previous valid record_id could behave incorrect. + * + * @param[in] record_id The record_id that should be incremented. + * + * @retval Incremented record_id. + */ +uint16_t increment_record_id(uint16_t record_id) { + + return (record_id >= 0xFFFE) ? (1) : (record_id + 1); +} + + + +/** @brief Function for decrementing the record_id. + * + * @details This function decrements the record id and take some special values into consideration. + * The values 0xFFFF and 0 are special values, because the default value in flash/EEPROM is 0xFFFF or 0 + * and a search for the next/previous valid record_id could behave incorrect. + * + * @param[in] record_id The record_id that should be decremented. + * + * @retval Decremented record_id. + */ +uint16_t decrement_record_id(uint16_t record_id) { + + return (record_id <= 1) ? (0xFFFE) : (record_id - 1); +} + + + + + + + +/** @brief Function to compute the number of available bytes in storage. + * + * @details This function computes the number of available bytes in storage when the partition begins at partition_start_address. + * The available bytes that are returned, could be smaller (if less bytes are available), bigger (if the end address is rounded to the next unit boundary) or equal to the required bytes. + * + * + * @param[in] partition_start_address The start address of the partition + * @param[in] required_size The number of required bytes for the partition. + * @param[out] available_size Pointer to memory where the number of available bytes is stored. + * + * @retval NRF_SUCCSS If operation was successful (available_size is smaller, bigger or equal required_size). + * @retval NRF_ERROR_INVALID_PARAM If the partition_start_address >= storage-size, the required_size == 0 or partition_start_address is not unit-aligned. + * @retval NRF_ERROR_INTERNAL If there was an internal error while computing (should not happen). + */ +ret_code_t filesystem_compute_available_size(uint32_t partition_start_address, uint32_t required_size, uint32_t* available_size) { + if(partition_start_address >= storage_get_size() || required_size == 0) + return NRF_ERROR_INVALID_PARAM; + + + *available_size = storage_get_size() - partition_start_address; + if(*available_size > required_size) { + *available_size = required_size; + } + + uint32_t start_unit_address, end_unit_address; + ret_code_t ret = storage_get_unit_address_limits(partition_start_address, *available_size, &start_unit_address, &end_unit_address); + if(ret != NRF_SUCCESS) // Should actually not happen + return NRF_ERROR_INTERNAL; + + + // Check if the partition_start_address == start_unit_address. Should actually always be true, because we try always to place partition_start_address to the beginning of a unit. + if(partition_start_address != start_unit_address) + return NRF_ERROR_INVALID_PARAM; + + // Now set require_size to the unit_address boundaries + *available_size = end_unit_address - start_unit_address + 1; + + return NRF_SUCCESS; +} + + +/** @brief Function to retrieve the corresponding first_element_header-address in the swap page. + * + * @param[in] partition_id The identifier of the partition. + * + * @retval Address of the first_element_header-address of the specified partition in the swap page. + */ +uint32_t filesystem_get_swap_page_address_of_partition(uint16_t partition_id) { + uint32_t address = 0; + // Clear all the MSBs + uint16_t index = partition_id & (0x3FFF); + + address = index * (PARTITION_METADATA_SIZE + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE + PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE); + + return address; +} + + +/** @brief Function to backup a first element-header in the swap-page. + * + * @details This function backups the first-element header in the swap-page. To minimze the number of store operations, + * the header is only stored if it differs from the currently header in the swap-page. + * The function also checks if the store operation was successful by reading and comparing the stored data. + * + * @param[in] first_element_address_swap_page The address where to store the first element-header in the swap page. + * @param[in] serialized_first_element_header The serialized bytes of the first element-header. + * @param[in] first_element_header_len The number of bytes of the first element-header. + * + * @retval NRF_SUCCESS If the backup-operation was successfully. + * @retval NRF_ERROR_INVALID_PARAM If the specified address is outside the swap-page. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be stored because of busy, or the data weren't stored correctly). + */ +ret_code_t filesystem_backup_first_element_header(uint32_t first_element_address_swap_page, uint8_t* serialized_first_element_header, uint16_t first_element_header_len) { + + ret_code_t ret; + + uint8_t tmp[first_element_header_len]; + + // Check if address is in swap page + if(first_element_address_swap_page + first_element_header_len > SWAP_PAGE_SIZE) + return NRF_ERROR_INVALID_PARAM; + + + // Read metadata and element-header from swap page + ret = storage_read(first_element_address_swap_page, &tmp[0], first_element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + + // Compare + if(memcmp(serialized_first_element_header, tmp, first_element_header_len) != 0) { + // The entries in swap page and storage mismatch --> backup + + // Check which unit size is in the swap page (to determine if swap page is in flash or eeprom) + uint32_t first_element_swap_page_start_unit_address, first_element_swap_page_end_unit_address; + ret = storage_get_unit_address_limits(first_element_address_swap_page, first_element_header_len, &first_element_swap_page_start_unit_address, &first_element_swap_page_end_unit_address); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Compute the unit size + uint32_t unit_size = first_element_swap_page_end_unit_address + 1 - first_element_swap_page_start_unit_address; + + uint8_t tmp_swap_page[unit_size]; + + // Read the unit from the swap page + ret = storage_read(first_element_swap_page_start_unit_address, &tmp_swap_page[0], unit_size); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Replace the entry + memcpy(&tmp_swap_page[first_element_address_swap_page - first_element_swap_page_start_unit_address], serialized_first_element_header, first_element_header_len); + + // Store the unit to the swap page again + ret = storage_store(first_element_swap_page_start_unit_address, tmp_swap_page, unit_size); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Check if write was successful + uint8_t tmp_read[sizeof(tmp_swap_page)]; + ret = storage_read(first_element_swap_page_start_unit_address, &tmp_read[0], sizeof(tmp_swap_page)); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + if(memcmp(tmp_swap_page, tmp_read, sizeof(tmp_swap_page)) != 0) return NRF_ERROR_INTERNAL; + + } + return NRF_SUCCESS; +} + + +/** @brief Function to retrieve the element-header length. + * + * @details The length of the element-header depends on whether the partition is dynamic or static + * and whether CRC should be used or not. + * + * @param[in] partition_id The identifier of the partition. + * + * @retval The length of the element-header. + */ +uint16_t filesystem_get_element_header_len(uint16_t partition_id) { + + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + uint8_t has_element_crc = (partition_id & 0x4000) ? 1 : 0; + + + + uint16_t element_header_len = PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE; + + if(is_dynamic) + element_header_len += PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE; + + if(has_element_crc) + element_header_len += PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE; + + return element_header_len; +} + + + + +/** @brief Function to efficiently serialize a metadata-struct into bytes. + * + * @warning The output buffer "serialized" must have a size of at least PARTITION_METADATA_SIZE bytes. + * + * @param[in] metadata Pointer to the metadata that should be serialized. + * @param[out] serialized Pointer to buffer where the serialized metadata should be stored to. + */ +void filesystem_serialize_metadata(const partition_metadata_t* metadata, uint8_t* serialized) { + uint8_t tmp[PARTITION_METADATA_SIZE]; + tmp[0] = (metadata->header_crc >> 8) & 0xFF; + tmp[1] = (metadata->header_crc) & 0xFF; + tmp[2] = (metadata->partition_id >> 8) & 0xFF; + tmp[3] = (metadata->partition_id) & 0xFF; + tmp[4] = (metadata->partition_size >> 24) & 0xFF; + tmp[5] = (metadata->partition_size >> 16) & 0xFF; + tmp[6] = (metadata->partition_size >> 8) & 0xFF; + tmp[7] = (metadata->partition_size) & 0xFF; + tmp[8] = (metadata->first_element_len >> 8) & 0xFF; + tmp[9] = (metadata->first_element_len) & 0xFF; + tmp[10] = (metadata->last_element_address >> 24) & 0xFF; + tmp[11] = (metadata->last_element_address >> 16) & 0xFF; + tmp[12] = (metadata->last_element_address >> 8) & 0xFF; + tmp[13] = (metadata->last_element_address) & 0xFF; + memcpy(serialized, (uint8_t*) tmp, sizeof(tmp)); +} + +/** @brief Function to deserialize bytes into a metadata-struct. + * + * @param[in] serialized Pointer to buffer that contains the serialized metadata. + * @param[out] metadata Pointer to a metadata-struct that should be filled. + */ +void filesystem_deserialize_metadata(const uint8_t* serialized, partition_metadata_t* metadata) { + uint8_t tmp[PARTITION_METADATA_SIZE]; + memcpy((uint8_t*) tmp, serialized, sizeof(tmp)); + + metadata->header_crc = (((uint16_t)tmp[0]) << 8) | tmp[1]; + metadata->partition_id = (((uint16_t)tmp[2]) << 8) | tmp[3]; + metadata->partition_size = (((uint32_t)tmp[4]) << 24) | (((uint32_t)tmp[5]) << 16) | (((uint32_t)tmp[6]) << 8) | tmp[7]; + metadata->first_element_len = (((uint16_t)tmp[8]) << 8) | tmp[9]; + metadata->last_element_address = (((uint32_t)tmp[10]) << 24) | (((uint32_t)tmp[11]) << 16) | (((uint32_t)tmp[12]) << 8) | tmp[13]; +} + + +/** @brief Function to efficiently serialize an element-header-struct into bytes. + * + * @warning The output buffer "serialized" must have a size of at least "filesystem_get_element_header_len(partition_id)" bytes. + * + * @param[in] element_header Pointer to the metadata that should be serialized. + * @param[out] serialized Pointer to buffer where the serialized metadata should be stored to. + */ +void filesystem_serialize_element_header(uint16_t partition_id, const partition_element_header_t* element_header, uint8_t* serialized) { + uint16_t header_len = filesystem_get_element_header_len(partition_id); + + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + uint8_t has_element_crc = (partition_id & 0x4000) ? 1 : 0; + + uint8_t tmp[header_len]; + + tmp[0] = (element_header->record_id >> 8) & 0xFF; + tmp[1] = (element_header->record_id) & 0xFF; + + if(is_dynamic) { + tmp[2] = (element_header->previous_len_XOR_cur_len >> 8) & 0xFF; + tmp[3] = (element_header->previous_len_XOR_cur_len) & 0xFF; + } + + if(has_element_crc) { + tmp[2 + is_dynamic*2] = (element_header->element_crc >> 8) & 0xFF; + tmp[3 + is_dynamic*2] = (element_header->element_crc) & 0xFF; + } + + memcpy(serialized, (uint8_t*) tmp, sizeof(tmp)); +} + +/** @brief Function to deserialize bytes into a element-header-struct. + * + * @param[in] serialized Pointer to buffer that contains the serialized element-header. + * @param[out] element_header Pointer to a element-header-struct that should be filled. + */ +void filesystem_deserialize_element_header(uint16_t partition_id, const uint8_t* serialized, partition_element_header_t* element_header) { + uint16_t header_len = filesystem_get_element_header_len(partition_id); + + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + uint8_t has_element_crc = (partition_id & 0x4000) ? 1 : 0; + + uint8_t tmp[header_len]; + + memcpy((uint8_t*) tmp, serialized, sizeof(tmp)); + element_header->record_id = ((uint16_t)tmp[0]) << 8 | tmp[1]; + + if(is_dynamic) { + element_header->previous_len_XOR_cur_len = ((uint16_t)tmp[2]) << 8 | tmp[3]; + } else { + element_header->previous_len_XOR_cur_len = 0; + } + + if(has_element_crc) { + element_header->element_crc = ((uint16_t)tmp[2 + is_dynamic*2]) << 8 | tmp[3 + is_dynamic*2]; + } else { + element_header->element_crc = 0; + } +} + + + +/** @brief Function to read a element-header from storage. + * + * @details The function reads a element-header (record_id, element_crc, previous_len_XOR_cur_len) from storage at a given address. + * If the header at the first address of the partition should be read, the function tries to read the header from the first address. + * If the metadata + element-header is not valid (checked by header-crc), the function tries to read the first element-header from the swap-page. + * If the entry in the swap-page is valid, it is copied to the storage (to the first address of the partition). + * Otherwise if the entry in the swap-page is not valid, NRF_ERROR_NOT_FOUND is returned. + * + * + * @param[in] partition_id The identifier of the partition. + * @param[in] element_address The address of the element-header. + * @param[out] record_id The record-id of the element. + * @param[out] element_crc The CRC of the element-data. + * @param[out] previous_len_XOR_cur_len The XOR of the previous- and the current-element length. + * + * @retval NRF_SUCCESS If the element-header read operation was successfully. + * @retval NRF_ERROR_NOT_FOUND If the function couldn't find any valid first-element-header. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_read_element_header(uint16_t partition_id, uint32_t element_address, uint16_t* record_id, uint16_t* element_crc, uint16_t* previous_len_XOR_cur_len) { + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + + ret_code_t ret; + + + uint32_t partition_start_address = partitions[index].first_element_address; + + partition_element_header_t element_header; + uint16_t element_header_len = filesystem_get_element_header_len(partition_id); + + // Check if the application tries to read the element-header of the first element or a normal element-header + if(element_address == partition_start_address) { + + partition_metadata_t metadata; + + + uint8_t tmp[PARTITION_METADATA_SIZE + element_header_len]; + + // Read metadata + ret = storage_read(element_address, &tmp[0], PARTITION_METADATA_SIZE); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + filesystem_deserialize_metadata(&tmp[0], &metadata); + + // Read element header + ret = storage_read(element_address + PARTITION_METADATA_SIZE, &tmp[PARTITION_METADATA_SIZE], element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + filesystem_deserialize_element_header(partition_id, &tmp[PARTITION_METADATA_SIZE], &element_header); + + // Check the header crc + uint16_t header_crc = crc16_compute(&tmp[2], PARTITION_METADATA_SIZE + element_header_len - 2, NULL); + if(header_crc == metadata.header_crc) { + // Check if the metadata are consistent with the application metadata for the partition (if static --> element-length must be equal) + if(metadata.partition_id == partitions[index].metadata.partition_id && metadata.partition_size == partitions[index].metadata.partition_size && (is_dynamic || metadata.first_element_len == partitions[index].metadata.first_element_len)) { + partitions[index].metadata = metadata; + *record_id = element_header.record_id; + *element_crc = element_header.element_crc; + *previous_len_XOR_cur_len = element_header.previous_len_XOR_cur_len; + return NRF_SUCCESS; + } + return NRF_ERROR_NOT_FOUND; + } + + // The header/metadata in storage is not correct --> check in swap page + uint32_t first_element_address_swap_page = filesystem_get_swap_page_address_of_partition(partition_id); + + // Read metadata from swap page + ret = storage_read(first_element_address_swap_page, &tmp[0], PARTITION_METADATA_SIZE); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + filesystem_deserialize_metadata(&tmp[0], &metadata); + + // Read element header from swap page + ret = storage_read(first_element_address_swap_page + PARTITION_METADATA_SIZE, &tmp[PARTITION_METADATA_SIZE], element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + filesystem_deserialize_element_header(partition_id, &tmp[PARTITION_METADATA_SIZE], &element_header); + + // Check the header crc of swap page + header_crc = crc16_compute(&tmp[2], PARTITION_METADATA_SIZE + element_header_len - 2, NULL); + if(header_crc == metadata.header_crc) { + // Check if the metadata are consistent with the application metadata for the partition + if(metadata.partition_id == partitions[index].metadata.partition_id && metadata.partition_size == partitions[index].metadata.partition_size && (is_dynamic || metadata.first_element_len == partitions[index].metadata.first_element_len)) { + // Copy the metadata and element header to the storage + ret = storage_store(element_address, &tmp[0], PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Check if the metadata + header was written successful + uint8_t tmp_read[sizeof(tmp)]; + ret = storage_read(element_address, &tmp_read[0], PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + if(memcmp(tmp, tmp_read, sizeof(tmp)) != 0) return NRF_ERROR_INTERNAL; + + partitions[index].metadata = metadata; + *record_id = element_header.record_id; + *element_crc = element_header.element_crc; + *previous_len_XOR_cur_len = element_header.previous_len_XOR_cur_len; + return NRF_SUCCESS; + } + return NRF_ERROR_NOT_FOUND; + } + return NRF_ERROR_NOT_FOUND; + } else { + + // It is a normal element + uint8_t tmp[element_header_len]; + + // Read element header + ret = storage_read(element_address, &tmp[0], element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + filesystem_deserialize_element_header(partition_id, &tmp[0], &element_header); + + *record_id = element_header.record_id; + *element_crc = element_header.element_crc; + *previous_len_XOR_cur_len = element_header.previous_len_XOR_cur_len; + return NRF_SUCCESS; + } +} + +/** @brief Function to read the next element-header from storage. + * + * @details The function computes the address of the next element and tries to read the element-header at this address. + * If the record-id of the next element-header is not consistent with the record-id of the current element-header, NRF_ERROR_NOT_FOUND is returned. + * + * + * @param[in] partition_id The identifier of the partition. + * @param[in] cur_element_address The address of the current element-header. + * @param[in] cur_element_record_id The record-id of the current element. + * @param[out] next_element_address The address of the next element-header. + * @param[out] next_element_record_id The record-id of the next element. + * @param[out] next_element_crc The CRC of the next element-data. + * @param[out] next_element_previous_len_XOR_cur_len The XOR of next previous- and the current-element length. + * + * @retval NRF_SUCCESS If the element-header read operation was successfully. + * @retval NRF_ERROR_NOT_FOUND If the function couldn't find a valid next element-header. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_get_next_element_header(uint16_t partition_id, uint32_t cur_element_address, uint16_t cur_element_record_id, uint16_t cur_element_len, uint32_t* next_element_address, uint16_t* next_element_record_id, uint16_t* next_element_crc, uint16_t* next_element_previous_len_XOR_cur_len) { + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + + + ret_code_t ret; + + + uint32_t partition_start_address = partitions[index].first_element_address; + uint32_t partition_size = partitions[index].metadata.partition_size; + + + uint16_t element_header_len = filesystem_get_element_header_len(partition_id); + + + uint32_t cur_header_len = (cur_element_address == partition_start_address) ? (PARTITION_METADATA_SIZE + element_header_len) : (element_header_len); + + + *next_element_address = cur_element_address + cur_header_len + cur_element_len; + + uint16_t record_id, element_crc, previous_len_XOR_cur_len; + + + ret = filesystem_read_element_header(partition_id, *next_element_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret != NRF_SUCCESS) + return ret; + + + // Check the record id of the "next element" + if(record_id == increment_record_id(cur_element_record_id)) { + + *next_element_record_id = record_id; + *next_element_crc = element_crc; + *next_element_previous_len_XOR_cur_len = previous_len_XOR_cur_len; + + uint32_t next_element_len = 0; + if(is_dynamic) + next_element_len = cur_element_len ^ previous_len_XOR_cur_len; // Compute the next element length by XOR + else + next_element_len = partitions[index].metadata.first_element_len; + + // Check if the next element is in the partition boundaries + if(*next_element_address + element_header_len + next_element_len <= partition_start_address + partition_size) { + return NRF_SUCCESS; + } + + } + + // Otherwise check if the first element is the next element: + *next_element_address = partition_start_address; + + ret = filesystem_read_element_header(partition_id, *next_element_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret != NRF_SUCCESS) + return ret; + + // Check the record id --> if only one element in partition, this won't be true + if(record_id == increment_record_id(cur_element_record_id)) { + + *next_element_record_id = record_id; + *next_element_crc = element_crc; + *next_element_previous_len_XOR_cur_len = previous_len_XOR_cur_len; + + return NRF_SUCCESS; + } + return NRF_ERROR_NOT_FOUND; +} + + + +/** @brief Function to read the previous element-header from storage. + * + * @details The function computes the address of the previous element and tries to read the element-header at this address. + * If the record-id of the previous element-header is not consistent with the record-id of the current element-header, NRF_ERROR_NOT_FOUND is returned. + * + * + * @param[in] partition_id The identifier of the partition. + * @param[in] cur_element_address The address of the current element-header. + * @param[in] cur_element_record_id The record-id of the current element. + * @param[out] previous_element_address The address of the previous element-header. + * @param[out] previous_element_record_id The record-id of the previous element. + * @param[out] previous_element_crc The CRC of the previous element-data. + * @param[out] previous_element_previous_len_XOR_cur_len The XOR of previous previous- and the current-element length. + * + * @retval NRF_SUCCESS If the element-header read operation was successfully. + * @retval NRF_ERROR_NOT_FOUND If the function couldn't find a valid previous element-header. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_get_previous_element_header(uint16_t partition_id, uint32_t cur_element_address, uint16_t cur_element_record_id, uint16_t cur_element_len, uint32_t* previous_element_address, uint16_t* previous_element_record_id, uint16_t* previous_element_crc, uint16_t* previous_element_previous_len_XOR_cur_len) { + uint16_t index = partition_id & 0x3FFF; + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + + ret_code_t ret; + + + uint32_t partition_start_address = partitions[index].first_element_address; + uint32_t last_element_address = partitions[index].metadata.last_element_address; + + + uint16_t element_header_len = filesystem_get_element_header_len(partition_id); + + + uint16_t record_id, element_crc, previous_len_XOR_cur_len; + + // Check if the cur-element is the first element + if(cur_element_address <= partition_start_address) { + // Check if there is a valid last element address + if(last_element_address <= partition_start_address) { + return NRF_ERROR_NOT_FOUND; + } + + *previous_element_address = last_element_address; + + // Read the previous (here: last) element + ret = filesystem_read_element_header(partition_id, *previous_element_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret != NRF_SUCCESS) + return ret; + + // Check the record id + if(record_id == decrement_record_id(cur_element_record_id)) { + *previous_element_record_id = record_id; + *previous_element_crc = element_crc; + *previous_element_previous_len_XOR_cur_len = previous_len_XOR_cur_len; + return NRF_SUCCESS; + } + return NRF_ERROR_NOT_FOUND; + } + + // Otherwise the previous element is not the last element + + + // Read the current element to compute the previous length + ret = filesystem_read_element_header(partition_id, cur_element_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret != NRF_SUCCESS) + return ret; + + uint32_t previous_element_len = 0; + if(is_dynamic) + previous_element_len = cur_element_len ^ previous_len_XOR_cur_len; // Compute the previous element length by XOR + else + previous_element_len = partitions[index].metadata.first_element_len; + + // Compute the previous_element_address + if(cur_element_address <= partition_start_address + (PARTITION_METADATA_SIZE + element_header_len) + previous_element_len) { + *previous_element_address = partition_start_address; + } else { // It is a normal element (not first element) + *previous_element_address = cur_element_address - previous_element_len - element_header_len; + } + + // Read the previous_element_address + ret = filesystem_read_element_header(partition_id, *previous_element_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret != NRF_SUCCESS) + return ret; + + + // Check the record id + if(record_id == decrement_record_id(cur_element_record_id)) { + + *previous_element_record_id = record_id; + *previous_element_crc = element_crc; + *previous_element_previous_len_XOR_cur_len = previous_len_XOR_cur_len; + + return NRF_SUCCESS; + } + + return NRF_ERROR_NOT_FOUND; +} + +/** @brief Function to find the address of the latest element of a partition. + * + * @details The function tries to find the latest stored element by stepping through the next element-header and checking the assigned record-ids. + * + * + * @param[in] partition_id The identifier of the partition. + * @param[out] latest_element_address The address of the latest element-header. + * @param[out] latest_element_record_id The record-id of the latest element-header. + * @param[out] latest_element_len The length of the latest element. + * + * @retval NRF_SUCCESS If the latest element find operation was successfully. + * @retval NRF_ERROR_NOT_FOUND If the function couldn't find any valid element-header. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_find_latest_element_address(uint16_t partition_id, uint32_t* latest_element_address, uint16_t* latest_element_record_id, uint16_t* latest_element_len) { + + uint16_t index = partition_id & 0x3FFF; + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + + + ret_code_t ret; + + uint32_t partition_start_address = partitions[index].first_element_address; + uint32_t first_element_len = partitions[index].metadata.first_element_len; + + *latest_element_address = partition_start_address; + + + uint16_t record_id, element_crc, previous_len_XOR_cur_len; + uint32_t cur_element_address = partition_start_address; + // Read the first element, for the initial record_id + ret = filesystem_read_element_header(partition_id, cur_element_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret != NRF_SUCCESS) // ret could be NRF_SUCCESS, NRF_ERROR_NOT_FOUND, NRF_ERROR_INTERNAL + return ret; + + + uint32_t cur_element_len = first_element_len; + + + + uint32_t next_element_address; + uint16_t next_element_record_id; + ret = filesystem_get_next_element_header(partition_id, cur_element_address, record_id, cur_element_len, &next_element_address, &next_element_record_id, &element_crc, &previous_len_XOR_cur_len); + + // Search until there is no element + while(ret == NRF_SUCCESS) { + if(next_element_address <= cur_element_address) + break; + + if(is_dynamic) + cur_element_len = cur_element_len ^ previous_len_XOR_cur_len; + else + cur_element_len = first_element_len; + + cur_element_address = next_element_address; + record_id = next_element_record_id; + + + ret = filesystem_get_next_element_header(partition_id, cur_element_address, record_id, cur_element_len, &next_element_address, &next_element_record_id, &element_crc, &previous_len_XOR_cur_len); + } + *latest_element_address = cur_element_address; + *latest_element_record_id = record_id; + *latest_element_len = cur_element_len; + + if(ret != NRF_SUCCESS && ret != NRF_ERROR_NOT_FOUND) + return ret; + + return NRF_SUCCESS; +} + + + + + + + +ret_code_t filesystem_init(void) { + ret_code_t ret = storage_init(); + + if(ret != NRF_SUCCESS) + return ret; + + ret = filesystem_reset(); + + + return ret; +} + +ret_code_t filesystem_reset(void) { + number_of_partitions = 0; + // Compute the address area for the swap page + uint32_t start_unit_address, end_unit_address; + ret_code_t ret = storage_get_unit_address_limits(0, SWAP_PAGE_SIZE, &start_unit_address, &end_unit_address); + if(ret != NRF_SUCCESS) // Should actually not happen + return NRF_ERROR_INTERNAL; + + // Set the next free address to the address after the swap page + next_free_address = end_unit_address + 1; + + return NRF_SUCCESS; +} + + +ret_code_t filesystem_clear(void) { + + uint32_t length = storage_get_size(); + ret_code_t ret = storage_clear(0, length); + if(ret != NRF_SUCCESS) return ret; + + ret = filesystem_reset(); + return ret; +} + +uint32_t filesystem_get_available_size(void) { + uint32_t storage_size = storage_get_size(); + if(storage_size >= next_free_address) { + return storage_size - next_free_address; + } + return 0; +} + + + +ret_code_t filesystem_register_partition(uint16_t* partition_id, uint32_t* required_size, uint8_t is_dynamic, uint8_t enable_crc, uint16_t element_len) { + if(number_of_partitions >= MAX_NUMBER_OF_PARTITIONS) + return NRF_ERROR_NO_MEM; + + // Check if we have element length > 0 in static partitions + if(!is_dynamic && element_len == 0) + return NRF_ERROR_INVALID_PARAM; + + if(is_dynamic) + element_len = 0; + + uint32_t partition_start_address = next_free_address; + + // Compute the size that is available in storage + uint32_t available_size = 0; + ret_code_t ret = filesystem_compute_available_size(partition_start_address, *required_size, &available_size); + if(ret != NRF_SUCCESS) return NRF_ERROR_NO_MEM; + + *partition_id = number_of_partitions; + if(is_dynamic) + *partition_id |= 0x8000; + if(enable_crc) + *partition_id |= 0x4000; + + + partitions[number_of_partitions].has_first_element = 0; + partitions[number_of_partitions].first_element_address = partition_start_address; + partitions[number_of_partitions].latest_element_address = partition_start_address; + partitions[number_of_partitions].latest_element_record_id = 1; + partitions[number_of_partitions].latest_element_len = 0; + + partitions[number_of_partitions].metadata.partition_id = *partition_id; + partitions[number_of_partitions].metadata.partition_size = available_size; + partitions[number_of_partitions].metadata.first_element_len = element_len; + partitions[number_of_partitions].metadata.last_element_address = partition_start_address; + + + // Set require_size to the available bytes + *required_size = available_size; + + uint16_t element_header_len = filesystem_get_element_header_len(*partition_id); + + + + // Check if we have enough space for at least the first element header and data (in dynamic case, this check will only check if we can store the first-element header). + if(available_size < (uint32_t)(PARTITION_METADATA_SIZE + element_header_len + element_len) ) + return NRF_ERROR_NO_MEM; + + + uint16_t record_id, element_crc, previous_len_XOR_cur_len; + ret = filesystem_read_element_header(*partition_id, partition_start_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret != NRF_SUCCESS && ret != NRF_ERROR_NOT_FOUND) + return NRF_ERROR_INTERNAL; // --> retry + + + if(ret == NRF_ERROR_NOT_FOUND) { // No first element header found in storage and swap page --> create one the next time a store operation is performed + + partitions[number_of_partitions].has_first_element = 0; + + } else if(ret == NRF_SUCCESS) { // Find the latest element! + + ret = filesystem_find_latest_element_address(*partition_id, &(partitions[number_of_partitions].latest_element_address), &(partitions[number_of_partitions].latest_element_record_id), &(partitions[number_of_partitions].latest_element_len)); + if(ret != NRF_SUCCESS) return ret; + + partitions[number_of_partitions].has_first_element = 1; + } + + + + next_free_address = partition_start_address + *required_size; + number_of_partitions ++; + + return NRF_SUCCESS; +} + + +ret_code_t filesystem_clear_partition(uint16_t partition_id) { + // We just need to clear the first element-header and the header in the SWAP-PAGE to clear a whole partition. + + uint16_t index = partition_id & 0x3FFF; + + + ret_code_t ret; + + + uint32_t partition_start_address = partitions[index].first_element_address; + + uint16_t element_header_len = filesystem_get_element_header_len(partition_id); + uint16_t record_id, element_crc, previous_len_XOR_cur_len; + + uint16_t new_record_id = 1; // The new record-id for the new first element header + + ret = filesystem_read_element_header(partition_id, partition_start_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret == NRF_SUCCESS) { // We found a first element header + // We will use its record_id to update the new_record_id + new_record_id = increment_record_id(record_id); + } else if(ret == NRF_ERROR_NOT_FOUND) { + new_record_id = 1; + } else { // ret == NRF_ERROR_INTERNAL + return ret; + } + + // Clear the backup page: + uint32_t first_element_address_swap_page = filesystem_get_swap_page_address_of_partition(partition_id); + uint8_t tmp[PARTITION_METADATA_SIZE + element_header_len]; + memset(tmp, 0xFF, sizeof(tmp)); // Set to an bad metadata + first_element_header + ret = filesystem_backup_first_element_header(first_element_address_swap_page, tmp, PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // CLear the first element-header: + memset(tmp, 0xFF, sizeof(tmp)); // Set to an bad metadata + first_element_header + // Write metadata + header to storage + ret = storage_store(partition_start_address, &tmp[0], PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + // Check if the metadata + header was written successfully + uint8_t tmp_read[sizeof(tmp)]; + ret = storage_read(partition_start_address, &tmp_read[0], PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + if(memcmp(tmp, tmp_read, sizeof(tmp)) != 0) return NRF_ERROR_INTERNAL; + + + // Now reset the state of the partition: + partitions[index].has_first_element = 0; + partitions[index].first_element_address = partition_start_address; + partitions[index].latest_element_address = partition_start_address; + partitions[index].latest_element_record_id = new_record_id; + partitions[index].latest_element_len = 0; + + return NRF_SUCCESS; +} + + + + + +ret_code_t filesystem_store_element(uint16_t partition_id, uint8_t* element_data, uint16_t element_len) { + uint16_t index = partition_id & 0x3FFF; + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + + // If partition is static, we check the element length for correctness (except element_len == 0, then we will set it to the initialized value) + if(!is_dynamic) { + if(element_len == 0) + element_len = partitions[index].metadata.first_element_len; + else if(element_len != partitions[index].metadata.first_element_len) + return NRF_ERROR_INVALID_PARAM; + // Else the element_len == partitions[index].metadata.first_element_len! + } + + ret_code_t ret; + + + uint32_t partition_start_address = partitions[index].first_element_address; + uint32_t partition_size = partitions[index].metadata.partition_size; + + uint16_t element_header_len = filesystem_get_element_header_len(partition_id); + + + + // Compute the record id and the address where to store the element + uint32_t element_address; + uint16_t record_id, previous_len_XOR_cur_len; + if(partitions[index].has_first_element == 0) { + + element_address = partition_start_address; + record_id = partitions[index].latest_element_record_id; + previous_len_XOR_cur_len = 0; + } else { + uint32_t latest_element_address = partitions[index].latest_element_address; + uint16_t latest_element_record_id = partitions[index].latest_element_record_id; + uint16_t latest_element_len = partitions[index].latest_element_len; + + // Compute the (next) element address + uint32_t latest_header_len = (latest_element_address == partition_start_address) ? (PARTITION_METADATA_SIZE + element_header_len) : (element_header_len); + uint32_t next_element_address = latest_element_address + latest_header_len + latest_element_len; + + // Check if the next element would be in partition boundaries + if(next_element_address + element_header_len + element_len <= partition_start_address + partition_size) { + element_address = next_element_address; + } else { + element_address = partition_start_address; + } + + record_id = increment_record_id(latest_element_record_id); + previous_len_XOR_cur_len = latest_element_len ^ element_len; + } + + + // Compute the length of the element header + uint16_t header_len = (element_address == partition_start_address) ? (PARTITION_METADATA_SIZE + element_header_len) : (element_header_len); + + + // Check if there is enough space in partition to store the element + if(element_address + header_len + element_len > partition_start_address + partition_size) + return NRF_ERROR_NO_MEM; + + // Check for conflict with iterator + if(filesystem_check_iterator_conflict(partition_id, element_address, element_len) != NRF_SUCCESS) { + return NRF_ERROR_INTERNAL; + } + + + + + /* Check if the next-element header has an record id that would match the next-record id. + * If this is the case, we need to clear it, because when reregistering the partition we could assume that this is the next element, although it is from a former store-operation. + * We need to do it before storing the new data (and header), because: + * - When the supply voltage drops before this clear operation took place, and we have already done the data-storage, we have the same problem again. + * - When flash: we first clear it. Probably we erase the page again, when writing the data, but this is ok. It should not happen very often. + * When we wouldn't do it before writing the data, the second erase operation could corrupt the already written data. + * - When in EEPROM: it doesn't matter at all. + */ + + // Therefore, we need to get the next-element header and address: + uint32_t next_element_address; + uint16_t next_element_record_id, next_element_crc, next_element_previous_len_XOR_cur_len; + ret = filesystem_get_next_element_header(partition_id, element_address, record_id, element_len, &next_element_address, &next_element_record_id, &next_element_crc, &next_element_previous_len_XOR_cur_len); + if(ret == NRF_SUCCESS) { + // Yes there is a problematic candidate that we have to clear + // We only need to clear the header of the next element, if the next element is not the first element. So actually we only need to clear if the next_element_address > element_address. + if(next_element_address > element_address) { + ret = storage_clear(next_element_address, element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + } + + } else if(ret == NRF_ERROR_NOT_FOUND) { + // No there is no problematic candidate that we have to clear + } else { // ret == NRF_ERROR_INTERNAL + return ret; + } + + + + + + + + + + // Compute the crc of the element + uint16_t element_crc = crc16_compute(element_data, element_len, NULL); + + partition_element_header_t element_header; + element_header.record_id = record_id; + element_header.element_crc = element_crc; + element_header.previous_len_XOR_cur_len = previous_len_XOR_cur_len; + + + + if(element_address == partition_start_address) { + uint8_t tmp[PARTITION_METADATA_SIZE + element_header_len]; + + + // Write the new metadata and element-header to storage + partition_metadata_t metadata; + + metadata.header_crc = 0;// Init with 0 + metadata.partition_id = partition_id; + metadata.partition_size = partitions[index].metadata.partition_size; + metadata.first_element_len = element_len; + + + // Set the last element address and length + if(element_address + PARTITION_METADATA_SIZE + element_header_len + element_len >= partitions[index].latest_element_address) { + // The new element would overwrite the former last element --> replace the last element with the new element + metadata.last_element_address = element_address; + } else { + // The former last element won't be overwritten by the new element --> set to latest element + metadata.last_element_address = partitions[index].latest_element_address; + } + + + filesystem_serialize_metadata(&metadata, &tmp[0]); + filesystem_serialize_element_header(partition_id, &element_header, &tmp[PARTITION_METADATA_SIZE]); + + // Compute the metadata header-crc + metadata.header_crc = crc16_compute(&tmp[2], PARTITION_METADATA_SIZE + element_header_len - 2, NULL); + + + // Update the metadata and first_element_header in RAM + partitions[index].metadata = metadata; + partitions[index].first_element_header = element_header; + } + + + + + + + + + // Before storing the new first-element-header to storage, store it in the backup page!! (Not the old header, because if first partition address + // is erased, the old header doesn't use anything. So we need to backup the current every time we write on the same unit as first element + // --> therefore we need the partitions[index].first_element_header... + + + // Check if the element_address is on the same unit as the first-element, if yes backup the first element header in swap page + uint32_t element_start_unit_address, element_end_unit_address; + // It doesn't matter if element_header_len or element_header_len+PARTITION_METADATA_SIZE, because we only need element_start_unit_address + ret = storage_get_unit_address_limits(element_address, element_header_len + element_len, &element_start_unit_address, &element_end_unit_address); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + if(element_start_unit_address == partition_start_address) { + // Backup the first element header, if necessary + uint32_t first_element_address_swap_page = filesystem_get_swap_page_address_of_partition(partition_id); + + uint8_t tmp[PARTITION_METADATA_SIZE + element_header_len]; + + filesystem_serialize_metadata(&partitions[index].metadata, &tmp[0]); + + filesystem_serialize_element_header(partition_id, &partitions[index].first_element_header, &tmp[PARTITION_METADATA_SIZE]); + + + ret = filesystem_backup_first_element_header(first_element_address_swap_page, tmp, PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + } + + + + + + + // Now write to the actual storage. + if(element_address == partition_start_address) { + + uint8_t tmp[PARTITION_METADATA_SIZE + element_header_len]; + filesystem_serialize_metadata(&partitions[index].metadata, &tmp[0]); + filesystem_serialize_element_header(partition_id, &element_header, &tmp[PARTITION_METADATA_SIZE]); + + // Write metadata + header to storage + ret = storage_store(element_address, &tmp[0], PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Check if the metadata + header was written successfully + uint8_t tmp_read[sizeof(tmp)]; + ret = storage_read(element_address, &tmp_read[0], PARTITION_METADATA_SIZE + element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + if(memcmp(tmp, tmp_read, sizeof(tmp)) != 0) return NRF_ERROR_INTERNAL; + + // Write the data + ret = storage_store(element_address + PARTITION_METADATA_SIZE + element_header_len, element_data, element_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + + + } else { + uint8_t tmp[element_header_len]; + filesystem_serialize_element_header(partition_id, &element_header, &tmp[0]); + + // Write header to storage + ret = storage_store(element_address, &tmp[0], element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Check if the header was written successful + uint8_t tmp_read[sizeof(tmp)]; + ret = storage_read(element_address, &tmp_read[0], element_header_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + if(memcmp(tmp, tmp_read, sizeof(tmp)) != 0) return NRF_ERROR_INTERNAL; + + // Write the data + ret = storage_store(element_address + element_header_len, element_data, element_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + } + + + // Set the latest element address + partitions[index].latest_element_address = element_address; + partitions[index].latest_element_record_id = record_id; + partitions[index].latest_element_len = element_len; + + partitions[index].has_first_element = 1; + + + + + + + return NRF_SUCCESS; + + +} + + + + + + + + +/** @brief Function to check the validity of a iterator. + * + * @details The function checks the validity of a iterator by reading the current element-header again and comparing if it has changed. + * If the element-header has changed the iterator is invalidated. + * + * + * @param[in] partition_id The identifier of the partition. + * + * @retval NRF_SUCCESS If the iterator is valid. + * @retval NRF_ERROR_INVALID_STATE If the iterator is invalid. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_iterator_check_validity(uint16_t partition_id) { + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + uint8_t has_element_crc = (partition_id & 0x4000) ? 1 : 0; + + + if(partition_iterators[index].iterator_valid != ITERATOR_VALID_NUMBER) { + return NRF_ERROR_INVALID_STATE; + } + + + if(!partitions[index].has_first_element) { + partition_iterators[index].iterator_valid = 0; + return NRF_ERROR_INVALID_STATE; + } + + uint32_t cur_element_address = partition_iterators[index].cur_element_address; + partition_element_header_t cur_element_header = partition_iterators[index].cur_element_header; + + uint16_t record_id, element_crc, previous_len_XOR_cur_len; + ret_code_t ret = filesystem_read_element_header(partition_id, cur_element_address, &record_id, &element_crc, &previous_len_XOR_cur_len); + if(ret == NRF_ERROR_NOT_FOUND) { // Only if read of the first element header failed (because deleted in storage and swap page..) + partition_iterators[index].iterator_valid = 0; + return NRF_ERROR_INVALID_STATE; + } + + if(ret != NRF_SUCCESS) // ret could be NRF_SUCCESS or NRF_ERROR_INTERNAL + return ret; + + // Check the validity (equality of record_id's, element_crc's and previous_len_XOR_cur_len) + if(record_id != cur_element_header.record_id || (has_element_crc && element_crc != cur_element_header.element_crc) || (is_dynamic && previous_len_XOR_cur_len != cur_element_header.previous_len_XOR_cur_len)) { + partition_iterators[index].iterator_valid = 0; + return NRF_ERROR_INVALID_STATE; + } + + return NRF_SUCCESS; +} + +/** @brief Function to check for a conflict between iterator and a storing operation. + * + * @details The function checks if the iterator is valid/enabled. + * If yes it checks for conflicts between the current iterator-element and the element to store. + * + * + * @param[in] partition_id The identifier of the partition. + * @param[in] element_address The address where the new element should be stored to. + * @param[in] element_len The length of the new element. + * + * @retval NRF_SUCCESS If there is no conflict between the iterator and the storer. + * @retval NRF_ERROR_FORBIDDEN If there is a conflict between the iterator and the storer. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_check_iterator_conflict(uint16_t partition_id, uint32_t element_address, uint16_t element_len) { + uint16_t index = partition_id & 0x3FFF; + + // Check iterator validity? + ret_code_t ret = filesystem_iterator_check_validity(partition_id); + if(ret == NRF_ERROR_INVALID_STATE) { + return NRF_SUCCESS; + } else if(ret == NRF_ERROR_INTERNAL) { + return NRF_ERROR_INTERNAL; + } + // Here ret should be NRF_SUCCESS --> we need to check for conflicts + + + uint32_t partition_start_address = partitions[index].first_element_address; + uint16_t element_header_len = filesystem_get_element_header_len(partition_id); + + uint32_t element_start_address = element_address; + uint32_t element_end_address = element_start_address + element_len + ((element_start_address == partition_start_address) ? (PARTITION_METADATA_SIZE + element_header_len) : (element_header_len)); + + uint32_t iterator_start_address = partition_iterators[index].cur_element_address; + uint32_t iterator_element_len = partition_iterators[index].cur_element_len + ((iterator_start_address == partition_start_address) ? (PARTITION_METADATA_SIZE + element_header_len) : (element_header_len)); + uint32_t iterator_end_address = iterator_start_address + iterator_element_len; + uint32_t dummy; + ret = storage_get_unit_address_limits(iterator_start_address, iterator_element_len, &iterator_start_address, &dummy); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Check for conflicts: + if(element_start_address >= iterator_start_address && element_start_address < iterator_end_address) { + return NRF_ERROR_FORBIDDEN; + } else if(element_end_address > iterator_start_address && element_end_address <= iterator_end_address) { + return NRF_ERROR_FORBIDDEN; + } else if(element_start_address <= iterator_start_address && element_end_address >= iterator_end_address ) { + return NRF_ERROR_FORBIDDEN; + } else { + return NRF_SUCCESS; + } + +} + + +ret_code_t filesystem_iterator_init(uint16_t partition_id) { + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + + + partition_iterators[index].iterator_valid = 0; + + if(!partitions[index].has_first_element) { + return NRF_ERROR_INVALID_STATE; + } + + // Set the current element address and length of the iterator to the latest element of the partition + partition_iterators[index].cur_element_address = partitions[index].latest_element_address; + partition_iterators[index].cur_element_len = partitions[index].latest_element_len; + + + // Read the current element header (record_id, element_crc and previous_len_XOR_cur_len) + ret_code_t ret = filesystem_read_element_header(partition_id, partition_iterators[index].cur_element_address, &(partition_iterators[index].cur_element_header.record_id), &(partition_iterators[index].cur_element_header.element_crc), &(partition_iterators[index].cur_element_header.previous_len_XOR_cur_len)); + if(ret == NRF_ERROR_NOT_FOUND) { // Should actually not happen, because we should have a first element + return NRF_ERROR_INVALID_STATE; + } + + if(ret != NRF_SUCCESS) + return ret; + + + // Set iterator to valid (unlikely bitpattern 10100101 = 0xA5) + partition_iterators[index].iterator_valid = ITERATOR_VALID_NUMBER; + + return NRF_SUCCESS; +} + +void filesystem_iterator_invalidate(uint16_t partition_id) { + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + partition_iterators[index].iterator_valid = 0; +} + + +ret_code_t filesystem_iterator_next(uint16_t partition_id) { + ret_code_t ret = filesystem_iterator_check_validity(partition_id); + if(ret != NRF_SUCCESS) + return ret; + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + + + uint32_t cur_element_address = partition_iterators[index].cur_element_address; + uint16_t cur_element_len = partition_iterators[index].cur_element_len; + partition_element_header_t cur_element_header = partition_iterators[index].cur_element_header; + + + uint32_t next_element_address; + uint16_t next_element_len; + partition_element_header_t next_element_header; + + + ret = filesystem_get_next_element_header(partition_id, cur_element_address, cur_element_header.record_id, cur_element_len, &next_element_address, &(next_element_header.record_id), &(next_element_header.element_crc), &(next_element_header.previous_len_XOR_cur_len)); + if(ret != NRF_SUCCESS) + return ret; + + if(is_dynamic) + next_element_len = cur_element_len ^ next_element_header.previous_len_XOR_cur_len; + else + next_element_len = partitions[index].metadata.first_element_len; + + + partition_iterators[index].cur_element_address = next_element_address; + partition_iterators[index].cur_element_len = next_element_len; + partition_iterators[index].cur_element_header = next_element_header; + + return NRF_SUCCESS; +} + +ret_code_t filesystem_iterator_previous(uint16_t partition_id) { + ret_code_t ret = filesystem_iterator_check_validity(partition_id); + if(ret != NRF_SUCCESS) + return ret; + + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + uint8_t is_dynamic = (partition_id & 0x8000) ? 1 : 0; + + uint32_t cur_element_address = partition_iterators[index].cur_element_address; + uint16_t cur_element_len = partition_iterators[index].cur_element_len; + partition_element_header_t cur_element_header = partition_iterators[index].cur_element_header; + + + uint32_t previous_element_address; + uint16_t previous_element_len; + partition_element_header_t previous_element_header; + + ret = filesystem_get_previous_element_header(partition_id, cur_element_address, cur_element_header.record_id, cur_element_len, &previous_element_address, &(previous_element_header.record_id), &(previous_element_header.element_crc), &(previous_element_header.previous_len_XOR_cur_len)); + if(ret != NRF_SUCCESS) + return ret; + if(is_dynamic) + previous_element_len = cur_element_len ^ cur_element_header.previous_len_XOR_cur_len; + else + previous_element_len = partitions[index].metadata.first_element_len; + + // Just for safety reasons (when last-element-address points to some data with correct record-id, but some invalid data-length) + if(previous_element_address + filesystem_get_element_header_len(partition_id) + previous_element_len > partitions[index].first_element_address + partitions[index].metadata.partition_size) + return NRF_ERROR_NOT_FOUND; + + partition_iterators[index].cur_element_address = previous_element_address; + partition_iterators[index].cur_element_len = previous_element_len; + partition_iterators[index].cur_element_header = previous_element_header; + + return NRF_SUCCESS; +} + +ret_code_t filesystem_iterator_read_element(uint16_t partition_id, uint8_t* element_data, uint16_t* element_len, uint16_t* record_id) { + ret_code_t ret = filesystem_iterator_check_validity(partition_id); + if(ret != NRF_SUCCESS) + return ret; + + uint16_t index = partition_id & 0x3FFF; // Clear the MSBs + uint8_t has_element_crc = (partition_id & 0x4000) ? 1 : 0; + + + uint32_t cur_element_address = partition_iterators[index].cur_element_address; + *element_len = partition_iterators[index].cur_element_len; + partition_element_header_t cur_element_header = partition_iterators[index].cur_element_header; + + + uint32_t header_len = (cur_element_address == partitions[index].first_element_address) ? (PARTITION_METADATA_SIZE + filesystem_get_element_header_len(partition_id)) : (filesystem_get_element_header_len(partition_id)); + + ret = storage_read(cur_element_address + header_len, element_data, *element_len); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + if(has_element_crc) { + // Check the element-crc + uint16_t element_crc = crc16_compute(element_data, *element_len, NULL); + if(element_crc != cur_element_header.element_crc) { + return NRF_ERROR_INVALID_DATA; + } + } + + *record_id = cur_element_header.record_id; + + return NRF_SUCCESS; +} + diff --git a/firmware/nRF_badge/data_collector/incl/filesystem_lib.h b/firmware/nRF_badge/data_collector/incl/filesystem_lib.h new file mode 100644 index 0000000..8ad0bc1 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/filesystem_lib.h @@ -0,0 +1,279 @@ +/** @file + * + * + * @brief Filesystem optimized for different partitions of sequential store/read operations. + * + * @details The application can register partitions for storing and reading records/elements. + * Each record in a partition contains a record/element-header followed by the actual data bytes. + * The element-header has a 2-Byte record-id that is incremented for each sequential record. + * Optionally a 2-Byte CRC-value can be stored in the header to detect corrupted data. + * The element-header at the first address in each partition contains additional metadata about the partition. + * So the structure of a partition looks like this: (The number in the brackets represents the number of bytes needed.) + * | metadata (14) | element-header (2-6) | --- data --- | element-header (2-6) | --- data --- | element-header (2-6) | --- data --- | ... + * + * @details There are two different types of partitions: static and dynamic. + * A static partition can be used for storing/reading a constant number of bytes for each entry/record. + * A dynamic partition can be used for storing/reading a variable number of bytes for each entry/record. + * The dynamic partition is organized as a XORed doubly linked list. Therefore another entry in the element-header + * is done to retrieve the address of the previous and next element/record. + * + * @details Because flash store operations can lead to inconsistent data if the power supply breaks down, + * some safety countermeasurements are incorporated. A swap page for the first element-headers for each partition + * is implemented to backup the first element-header to prevent data loss. The swap-page is placed at the beginning + * of the storage. It is advantageous to have the swap-page in a storage with byte-units like EEPROM. + * + * @details For reading the sequential records/elements of a partition, an iterator should be used. + * It could happen that the element the iterator is currently pointing to is overwritten by new data. + * In this case the iterator is invalidated, and the application needs to reinitialize the iterator. + */ + +#ifndef __FILESYSTEM_LIB_H +#define __FILESYSTEM_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +#define MAX_NUMBER_OF_PARTITIONS 20 /**< Maximal number of registerable partitions. */ + + +#define PARTITION_METADATA_SIZE 14 /**< Number of bytes of the metadata. */ +#define PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE 2 /**< Number of bytes of the record-id. */ +#define PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE 2 /**< Number of bytes of previous_len_XOR_cur_len. */ +#define PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE 2 /**< Number of bytes of the CRC-value. */ + + +#define SWAP_PAGE_SIZE (MAX_NUMBER_OF_PARTITIONS*(PARTITION_METADATA_SIZE + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE + PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE)) /**< Size of the swap-page */ + + + + + + +typedef struct { + uint16_t header_crc; /**< The CRC of the metadata and element-header of first element. Needed to validate the first element */ + uint16_t partition_id; /**< The partition identifier. Highest bit --> dynamic(1) or static(0), second highest bit --> CRC enabled (1) or disabled (0) */ + uint32_t partition_size; /**< Size of the partition. */ + uint16_t first_element_len; /**< Length of first element. Static partition: length of every element. Dynamic partition: length of the first element. */ + uint32_t last_element_address; /**< Element of last address in partition (only needed in dynamic partition). */ +} partition_metadata_t; /**< Metadata-struct that is stored in the storage (14 Bytes) */ + +typedef struct { + uint16_t record_id; /**< The record-id of the element/record. */ + uint16_t previous_len_XOR_cur_len; /**< In dynamic partition, needed for XORed doubly linked list, to reference to next and previous element. */ + uint16_t element_crc; /**< The CRC-value of the element-data. */ +} partition_element_header_t; /**< Element-header-struct that is stored in the storage (max. 6 Bytes, depends on configuration). */ + + +typedef struct { + partition_metadata_t metadata; /**< Copy of the current metadata that must be backupt continiously. */ + + uint32_t first_element_address; /**< The address of the first element/The partition start address. */ + uint8_t has_first_element; /**< Flag if the partition has a first element or not. */ + partition_element_header_t first_element_header; /**< Copy of the current first element header that must be backupt continiously. */ + + uint32_t latest_element_address; /**< The address of the latest element to compute the next "free" address. */ + uint16_t latest_element_record_id; /**< The record-id of the latest element to compute the next element record-id address. */ + uint16_t latest_element_len; /**< The length of the latest element to compute the next element address. */ + +} partition_t; /**< Partition-struct to manage a partition. */ + + +typedef struct { + uint32_t cur_element_address; /**< The address of the current element. */ + uint16_t cur_element_len; /**< The length of the current element. */ + partition_element_header_t cur_element_header; /**< Copy of the current element header. */ + uint8_t iterator_valid; /**< Specific numbers that represents if the iterator is valid or not. */ +} partition_iterator_t; /**< Iterator-struct to manage a partition-iterator. */ + + + + + + + + + +/** @brief Function for initializing the filesystem. + * + * @details It initializes the underlying storage module and resets the filesystem by calling filesystem_reset(). + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_INTERNAL If the underlying storage-module could not correctly initialized or there went something wrong while resetting the filesystem. + * @retval NRF_ERROR_BUSY If the operation could not be performed currently, because the underlying storage module is busy or it depends on another module that is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t filesystem_init(void); + + +/** @brief Function for resetting the filesystem. + * + * @details The function resets the filesystem by setting the internal number_of_partitions to zero, and recomputing the next_free_address. + * But it doesn't reinitialize the underlying storage module. After the reset, the partitions need to be registered again. + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_INTERNAL If there was an invalid param to storage_get_unit_address_limits (e.g. the SWAP_PAGE_SIZE is larger than the storage) --> should actually not happen. + */ +ret_code_t filesystem_reset(void); + +/** @brief Function for clearing the storage of the filesystem. + * + * @details The function cleares the complete storage and resets the filesystem afterwords via filesystem_reset(). + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_BUSY If the underlying storage-module is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t filesystem_clear(void); + +/** @brief Function for retrieving the available number of bytes in the filesystem. + * + * @retval Available bytes in the filesystem. + */ +uint32_t filesystem_get_available_size(void); + +/** @brief Function for registering a partition. + * + * @details The function registers a dynamic or static partition. It searches for an already existing partition in storage at the same address the + * new partition would be. If there is already a partition (with the same settings: partition_id, partition_size), the latest element address + * is searched, and new store operations will start beginning from this address. + * + * @param[out] partition_id Pointer to the identifier of the partition. + * @param[in/out] required_size The required size of the partition. Could be smaller, equal or greater after registration. + * @param[in] is_dynamic Flag if the partition should be dynamic (1) or static (0). + * @param[in] enable_crc Flag if the crc for element data should be enabled (1) or not (0). + * @param[in] element_len If the partition is static, this parameter represents the element length. + * If the partition is dynamic, this parameter could be arbitrary (e.g. 0). + * + * @retval NRF_SUCCESS If the partition-registraton was successful. + * @retval NRF_ERROR_INVALID_PARAM If the required-size == 0, or element_len == 0 in a static partition. + * @retval NRF_ERROR_NO_MEM If there were already more than MAX_NUMBER_OF_PARTITIONS partition-registrations, or if the available size for the partition is too small. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. data couldn't be read because of busy). + */ +ret_code_t filesystem_register_partition(uint16_t* partition_id, uint32_t* required_size, uint8_t is_dynamic, uint8_t enable_crc, uint16_t element_len); + +/** @brief Function for clearing a partition. + * + * @details The function clears a partition. It removes the first-element-header at the beginning of the partition and in the swap-page. + * Furthermore, it resets the state of the partition to "zero" (the default values after registering a partition). + * After clearing a partition no more elements could be found. The application needn't to register the partition again, but can just use it. + * If there was already a first-element-header in the partition, it reads out its record_id and increments it, so that the record-id of the + * new first-element-header is different. This is done, to not clear the (already existing) next element-headers all the time while storing. + * + * + * @param[in] partition_id The identifier of the partition. + * + * @retval NRF_SUCCESS If the store operation was succesful. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. data couldn't be stored/read because of busy). + */ +ret_code_t filesystem_clear_partition(uint16_t partition_id); + +/** @brief Function for storing an element (some bytes) to a partition. + * + * @details The function computes the address where to store the next element and stores the element-data together with an element-header. + * Each time an element-header is stored, the function checks the correctness of the store operation by reading and compring the element-header. + * + * If the element is stored to the first address in the partition, the procedure is the following: + * Before the first-element-header in storage is overwritten by the new first-element-header, the new first-element-header is backuped in the swap-page. + * This is done, because if the store operation fails (because of a breaked power supply), we would loose the reference to the last element (in a dynamic partition), + * and we wouldn't find it anymore if the whole first unit (in case of flash) is erased/corrupted. + * Furthermore, if there are repeated store operations in the first unit, + * this backup is needed to keep a valid partition even if the first-element-header is corrupted during these store-operations. + * + * Furthermore, before storing the data to storage, it is checked that the next-element header has not a consecutive record-id, to don't get confused. + * If it has a consecutive record-id the next-element header is cleared before writing the new data. + * + * Another addition is, that it checks for a conflict with the iterator before storing the data (A conflict means, that the iterator is valid and the storer + * wants to overwrite the element the iterator is currently pointing to. This is not allowed.). When a conflict is present the function returns NRF_ERROR_INTERNAL. + * So it is very important to invalidate the iterator if it is not used anymore by filesystem_iterator_invalidate(). + * + * @note It is advantageous to keep the swap-page in a storage part with a unit size of one byte, because if different partitions alternately store in their first unit, + * the first-element-header must be backupt each time. If the addresses of the backupt first-element-headers are on the same unit (e.g. in flash), + * it could happen that they delete each other backupt first-element-header. In this case the first-element-header has to be rewritten more often, + * in worst case "#partitions x #store operations on first unit" times. + * + * @param[in] partition_id The identifier of the partition. + * @param[in] element_data Pointer to the data that should be stored. + * @param[in] element_len The length of the data to store (if the partition is static, this parameter could be 0 or the registered element_len). + * + * @retval NRF_SUCCESS If the store operation was succesful. + * @retval NRF_ERROR_INVALID_PARAM If the partition is static and the element_len != 0 && element_len != registered element_len. + * @retval NRF_ERROR_NO_MEM If the element is too big, to be stored in the partition. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. data couldn't be stored/read because of busy). + * Or there is a conflict between storing and the iterator. + */ +ret_code_t filesystem_store_element(uint16_t partition_id, uint8_t* element_data, uint16_t element_len); + + + +/** @brief Function for initializing the iterator for a partition. + * + * @details The function initializes and validates the iterator to point to the latest stored element of a partition. + * + * @note When an iterator was initialized, it needs to be invalidated (via filesystem_iterator_invalidate()) if it is not used anymore. + * Otherwise the store-function could not overwrite the element where the iterator is currently pointing to. + * + * @param[in] partition_id The identifier of the partition. + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_INVALID_STATE If the partition has no first element-header. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_iterator_init(uint16_t partition_id); + +/** @brief Function for invalidating the iterator of a partition. + * + * @details The function invalidates the iterator of a partition, so that the store-function could overwrite the element + * where the iterator was pointing to. + * + * @param[in] partition_id The identifier of the partition. + */ +void filesystem_iterator_invalidate(uint16_t partition_id); + +/** @brief Function to set the iterator pointing to the next element in a partition. + * + * @details The iterator steps to the next element address and reads the record-id if it is consistent with the current record-id. + * + * @param[in] partition_id The identifier of the partition. + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_NOT_FOUND If there is no next element in the partition. + * @retval NRF_ERROR_INVALID_STATE If the iterator was invalidated. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_iterator_next(uint16_t partition_id); + +/** @brief Function to set the iterator pointing to the previous element in a partition. + * + * @details The iterator steps to the previous element address and reads the record-id if it is consistent with the current record-id. + * + * @param[in] partition_id The identifier of the partition. + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_NOT_FOUND If there is no previous element in the partition. + * @retval NRF_ERROR_INVALID_STATE If the iterator was invalidated. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_iterator_previous(uint16_t partition_id); + + +/** @brief Function to read the element the iterator is currently pointing to. + * + * @details The function reads the element data from the storage and if CRC is enabled, the data is checked for integrity. + * + * @param[in] partition_id The identifier of the partition. + * @param[out] element_data Pointer to buffer where the data should be stored to. + * @param[out] element_len Pointer to memory where the data length should stored to. + * @param[out] record_id Pointer to memory where the record-id should stored to. + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_INVALID_DATA If CRC is enabled and the data are corrupted. + * @retval NRF_ERROR_INVALID_STATE If the iterator was invalidated. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. the data couldn't be read because of busy). + */ +ret_code_t filesystem_iterator_read_element(uint16_t partition_id, uint8_t* element_data, uint16_t* element_len, uint16_t* record_id); + + + + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/flash_lib.c b/firmware/nRF_badge/data_collector/incl/flash_lib.c new file mode 100644 index 0000000..9882cb1 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/flash_lib.c @@ -0,0 +1,539 @@ +#include "flash_lib.h" + + +#include "fstorage.h" + + +#include "nrf_drv_common.h" // Needed for data in RAM check + +#include "system_event_lib.h" // Needed to register an system event handler! +#include "systick_lib.h" // Needed for the timeout-checks + +#include "debug_lib.h" + + +#define FLASH_OPERATION_TIMEOUT_MS 1000 /**< The time in milliseconds to wait for an operation to finish. */ + + +static volatile flash_operation_t flash_operation = FLASH_NO_OPERATION; + +static uint32_t const * address_of_page(uint16_t page_num); +static uint32_t const * address_of_word(uint32_t word_num); + + + +volatile fs_ret_t handler_result = 0; + +/** + *@brief Function for handling fstorage events. + */ +static void fs_evt_handler(fs_evt_t const * const evt, fs_ret_t result) +{ handler_result = result; + + if (result == FS_SUCCESS) { // Operation was successful! + if(flash_get_operation() & FLASH_ERASE_OPERATION) { + flash_operation &= ~FLASH_ERASE_OPERATION; + } + + if(flash_get_operation() & FLASH_STORE_OPERATION) { + flash_operation &= ~FLASH_STORE_OPERATION; + } + } else { // An error occurred during the operation! + + // Set the Error of the specific operation + if(flash_get_operation() & FLASH_ERASE_OPERATION) { + flash_operation &= ~FLASH_ERASE_OPERATION; + flash_operation |= FLASH_ERASE_ERROR; + } + + if(flash_get_operation() & FLASH_STORE_OPERATION) { + flash_operation &= ~FLASH_STORE_OPERATION; + flash_operation |= FLASH_STORE_ERROR; + } + } +} + + + + + +FS_REGISTER_CFG(fs_config_t fs_config) = +{ + .callback = fs_evt_handler, /**< Function for event callbacks. */ + .num_pages = FLASH_NUM_PAGES, /**< Number of physical flash pages required. */ + .priority = 0x0F /**< Priority for flash usage. */ +}; + +ret_code_t flash_init(void) { + + // TODO: remove and put in main.c + system_event_init(); + + + fs_ret_t status_init = fs_init(); + + + + if(status_init != FS_SUCCESS) { + return NRF_ERROR_INTERNAL; + } + + // Register the system event handler for flash, but only if we successfully initialized the fstorage module first. + system_event_register_handler(fs_sys_event_handler); + + + return NRF_SUCCESS; +} + + +/** @brief Retrieve the address of a page. + * + * @param[in] The page number for which the address should be retrieved. + * + * @retval Address of the specified page. + */ +static uint32_t const * address_of_page(uint16_t page_num) +{ + return fs_config.p_start_addr + (page_num * FLASH_PAGE_SIZE_WORDS); +} + + +/**@brief Retrieve the address of a word. + * + * @param[in] The word number for which the address should be retrieved. + * + * @retval Address of the specified word. + */ +static uint32_t const * address_of_word(uint32_t word_num) +{ + return fs_config.p_start_addr + (word_num); +} + + + + + + + +ret_code_t flash_erase_bkgnd(uint32_t page_num, uint16_t num_pages) { + + // Check if we have already an ongoing operation + if((flash_get_operation() & FLASH_STORE_OPERATION) || (flash_get_operation() & FLASH_ERASE_OPERATION)) { + return NRF_ERROR_BUSY; + } + + flash_operation |= FLASH_ERASE_OPERATION; + // Reset the Error flag (if it exists) + flash_operation &= ~FLASH_ERASE_ERROR; + + // Call the fstorage library for the actual erase operation. + fs_ret_t status_erase = fs_erase(&fs_config, address_of_page(page_num), num_pages, NULL); + + + // Prepare the return value: + ret_code_t ret = NRF_SUCCESS; + if(status_erase != FS_SUCCESS) { + + // Reset the erase operation + flash_operation &= ~FLASH_ERASE_OPERATION; + + if(status_erase == FS_ERR_QUEUE_FULL) { + ret = NRF_ERROR_BUSY; + } else if(status_erase == FS_ERR_NOT_INITIALIZED || status_erase == FS_ERR_INVALID_CFG){ + ret = NRF_ERROR_INTERNAL; + } else { // FS_ERR_NULL_ARG || FS_ERR_INVALID_ARG || FS_ERR_INVALID_ADDR || FS_ERR_UNALIGNED_ADDR + ret = NRF_ERROR_INVALID_PARAM; + } + } + + return ret; +} + +ret_code_t flash_erase(uint32_t page_num, uint16_t num_pages) { + ret_code_t ret = flash_erase_bkgnd(page_num, num_pages); + + // Return if erase operation was not started successfully + if(ret != NRF_SUCCESS) { + return ret; + } + + // Wait for the erase operation to terminate with timeout check. + uint64_t end_ms = systick_get_continuous_millis() + FLASH_OPERATION_TIMEOUT_MS; + while(flash_get_operation() & FLASH_ERASE_OPERATION && systick_get_continuous_millis() < end_ms); + + if(flash_get_operation() & FLASH_ERASE_OPERATION) { + // Reset the erase operation + + flash_operation &= ~FLASH_ERASE_OPERATION; + return NRF_ERROR_TIMEOUT; + } + + // Return an error if the erase operation was not successful. + if(flash_get_operation() & FLASH_ERASE_ERROR) { + return NRF_ERROR_TIMEOUT; + } + + return NRF_SUCCESS; + +} + + + +ret_code_t flash_store_bkgnd(uint32_t word_num, const uint32_t* p_words, uint16_t length_words) { + + // Check if we have already an ongoing operation + if((flash_get_operation() & FLASH_STORE_OPERATION) || (flash_get_operation() & FLASH_ERASE_OPERATION)) { + return NRF_ERROR_BUSY; + } + + if(p_words == NULL) + return NRF_ERROR_INVALID_PARAM; + + + flash_operation |= FLASH_STORE_OPERATION; + // Reset the Error flag (if it exists) + flash_operation &= ~FLASH_STORE_ERROR; + + // Call the fstorage library for the actual storage operation. + fs_ret_t status_store = fs_store(&fs_config, address_of_word(word_num), p_words, length_words, NULL); + + + + // Prepare the return value: + ret_code_t ret = NRF_SUCCESS; + if(status_store != FS_SUCCESS) { + + // Reset the store operation + flash_operation &= ~FLASH_STORE_OPERATION; + + if(status_store == FS_ERR_QUEUE_FULL) { + ret = NRF_ERROR_BUSY; + } else if(status_store == FS_ERR_NOT_INITIALIZED || status_store == FS_ERR_INVALID_CFG){ + ret = NRF_ERROR_INTERNAL; + } else { // FS_ERR_NULL_ARG || FS_ERR_INVALID_ARG || FS_ERR_INVALID_ADDR || FS_ERR_UNALIGNED_ADDR + ret = NRF_ERROR_INVALID_PARAM; + } + } + + return ret; +} + +ret_code_t flash_store(uint32_t word_num, const uint32_t* p_words, uint16_t length_words) { + // Call the non-blocking flash store function. + ret_code_t ret = flash_store_bkgnd(word_num, p_words, length_words); + + // Return if store operation was not started successfully + if(ret != NRF_SUCCESS) { + return ret; + } + + // Wait for the store operation to terminate with timeout check. + uint64_t end_ms = systick_get_continuous_millis() + FLASH_OPERATION_TIMEOUT_MS; + while(flash_get_operation() & FLASH_STORE_OPERATION && systick_get_continuous_millis() < end_ms); + + if(flash_get_operation() & FLASH_STORE_OPERATION) { + // Reset the store operation + flash_operation &= ~FLASH_STORE_OPERATION; + return NRF_ERROR_TIMEOUT; + } + + // Return an error if the store operation was not successful. + if(flash_get_operation() & FLASH_STORE_ERROR) { + return NRF_ERROR_TIMEOUT; + } + + return NRF_SUCCESS; + +} + +flash_operation_t flash_get_operation(void) { + return flash_operation; +} + + +ret_code_t flash_read(uint32_t word_num, uint32_t* p_words, uint16_t length_words) { + + + if(!nrf_drv_is_in_RAM(p_words)) + return NRF_ERROR_INVALID_PARAM; + + if(p_words == NULL) + return NRF_ERROR_INVALID_PARAM; + + if(word_num + length_words > (FLASH_PAGE_SIZE_WORDS*FLASH_NUM_PAGES)) + return NRF_ERROR_INVALID_PARAM; + + for(uint32_t i = 0; i < length_words; i++) { + p_words[i] = *(address_of_word(word_num + i)); + } + return NRF_SUCCESS; +} + + +uint32_t flash_get_page_size_words(void) { + + return FLASH_PAGE_SIZE_WORDS; +} + +uint32_t flash_get_page_number(void) { + return FLASH_NUM_PAGES; +} + + + + +bool flash_selftest(void) { + + debug_log("FLASH: Started flash selftest...\n\r"); + + debug_log("FLASH: Flash page addresses: From %p to %p\n\r", address_of_page(0), address_of_page(FLASH_NUM_PAGES-1)); + + debug_log("FLASH: Flash word addresses: From %p to %p\n\r", address_of_word(0), address_of_word(flash_get_page_size_words()*flash_get_page_number()-1)); + + debug_log("FLASH: Page size words: %u, Number of pages: %u\n\r", flash_get_page_size_words(), flash_get_page_number()); + + + // Just a dummy write to test erasing.. + uint32_t tmp = 0xABCD1234; + flash_store_bkgnd(0, &tmp, 1); + + // Wait for the store operation to terminate with timeout check. + uint64_t end_ms = systick_get_continuous_millis() + FLASH_OPERATION_TIMEOUT_MS; + while(flash_get_operation() & FLASH_STORE_OPERATION && systick_get_continuous_millis() < end_ms); + if(flash_get_operation() & FLASH_STORE_OPERATION) { + // Reset the store operation + flash_operation &= ~FLASH_STORE_OPERATION; + debug_log("FLASH: Flash store operation timed out!\n\r"); + return 0; + } + + +//******************** Test erasing 2 pages ************************* + ret_code_t ret = flash_erase_bkgnd(0, 2); + debug_log("FLASH: Started erasing: Ret %d\n\r", ret); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Start erasing failed!\n\r"); + return 0; + } + + flash_operation_t erase_operation = flash_get_operation(); + + // Wait for the erase operation to terminate with timeout check. + end_ms = systick_get_continuous_millis() + FLASH_OPERATION_TIMEOUT_MS; + while(erase_operation & FLASH_ERASE_OPERATION && systick_get_continuous_millis() < end_ms) { + erase_operation = flash_get_operation(); + } + if(erase_operation & FLASH_ERASE_OPERATION) { + // Reset the erase operation + flash_operation &= ~FLASH_ERASE_OPERATION; + debug_log("FLASH: Flash erase operation timed out!\n\r"); + return 0; + } + + if(erase_operation & FLASH_ERASE_ERROR) { + debug_log("FLASH: Erasing error!\n\r"); + return 0; + } + debug_log("FLASH: Erasing success!\n\r"); + + + +//******************** Test write a word ************************* + #define FLASH_TEST_ADDRESS 0 //(flash_get_page_size_words()*flash_get_page_number()) + uint32_t write_word = 0x1234ABCD; // Should be != 0xFFFFFFFF --> next test will assume this! + + ret = flash_store_bkgnd(FLASH_TEST_ADDRESS, &write_word, 1); + debug_log("FLASH: Started storing to flash at word 0: 0x%X, Ret: %d\n\r", write_word, ret); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Start storing word failed!\n\r"); + return 0; + } + + flash_operation_t store_operation = flash_get_operation(); + + // Wait for the store operation to terminate with timeout check. + end_ms = systick_get_continuous_millis() + FLASH_OPERATION_TIMEOUT_MS; + while(store_operation & FLASH_STORE_OPERATION && systick_get_continuous_millis() < end_ms) { + store_operation = flash_get_operation(); + } + if(store_operation & FLASH_STORE_OPERATION) { + // Reset the erase operation + flash_operation &= ~FLASH_STORE_OPERATION; + debug_log("FLASH: Flash store operation timed out!\n\r"); + return 0; + } + + if(store_operation & FLASH_STORE_ERROR) { + debug_log("FLASH: Storing error!\n\r"); + return 0; + } + + + // Read the word again: + uint32_t read_word; + // Check if the stored word is the correct word! + ret = flash_read(FLASH_TEST_ADDRESS, &read_word, 1); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Read failed!\n\r"); + return 0; + } + + if(read_word != write_word) { + debug_log("FLASH: Stored word is not the right word!\n\r"); + return 0; + } + + + debug_log("FLASH: Storing success!\n\r"); + +//******************** Test writing to the same position another value ************************* + write_word = 0xFFFFFFFF; + + + + + ret = flash_store(FLASH_TEST_ADDRESS + 100, &write_word, 1); + //ret = flash_store_bkgnd(0, &write_word, 1); + debug_log("FLASH: Storing to flash at word 0: 0x%X, Ret: %d\n\r", write_word, ret); + + if(ret != NRF_SUCCESS) { + if(ret == NRF_ERROR_TIMEOUT) { + debug_log("FLASH: Storing word timed out!\n\r"); + } else { + debug_log("FLASH: Storing word failed!\n\r"); + } + return 0; + } + + // Check if the stored word is the correct word! + ret = flash_read(FLASH_TEST_ADDRESS, &read_word, 1); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Read failed!\n\r"); + return 0; + } + + if(read_word == write_word) { + debug_log("FLASH: Store word should actually fail!! Written: 0x%X, Read: 0x%X\n\r", write_word, read_word); + return 0; + } + + + + debug_log("FLASH: Store different word at same position behaves as expected!\n\r"); + + +//******************** Test a overflowing write operation over 2 pages ************************* + #define WORD_NUMBER 5 + uint32_t write_address = (flash_get_page_size_words())-2; + uint32_t write_words[WORD_NUMBER]; + ret = flash_store_bkgnd(write_address, write_words, WORD_NUMBER); + debug_log("FLASH: Started storing words at address: %u, Ret: %d\n\r",write_address, ret); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Start storing words failed!\n\r"); + return 0; + } + + store_operation = flash_get_operation(); + + // Wait for the store operation to terminate with timeout check. + end_ms = systick_get_continuous_millis() + FLASH_OPERATION_TIMEOUT_MS; + while(store_operation & FLASH_STORE_OPERATION && systick_get_continuous_millis() < end_ms) { + store_operation = flash_get_operation(); + } + if(store_operation & FLASH_STORE_OPERATION) { + // Reset the erase operation + flash_operation &= ~FLASH_STORE_OPERATION; + debug_log("FLASH: Flash store operation timed out!\n\r"); + return 0; + } + + + if(store_operation & FLASH_STORE_ERROR) { + debug_log("FLASH: Storing words error!\n\r"); + return 0; + } + // Check if the stored words are correct! + uint32_t read_words[WORD_NUMBER]; + ret = flash_read(write_address, read_words, WORD_NUMBER); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Read failed!\n\r"); + return 0; + } + + if(memcmp((uint8_t*)&write_words[0], (uint8_t*)&read_words[0], sizeof(uint32_t)*WORD_NUMBER) != 0) { + debug_log("FLASH: Stored words are not the right words!\n\r"); + return 0; + } + + debug_log("FLASH: Storing words success!\n\r"); + +//******************* Test read to non RAM memory ************************************** + + + char* non_ram = "ABCD"; + uint32_t* p_non_ram = (uint32_t*) non_ram; + + ret = flash_read(0, p_non_ram, 1); + if(ret == NRF_SUCCESS) { + debug_log("FLASH: No RAM memory test failed!\n\r"); + return 0; + } + + +//******************* False address test ************************** + + ret = flash_store(flash_get_page_size_words()*flash_get_page_number()-2, write_words, WORD_NUMBER); + debug_log("FLASH: Test invalid address store, Ret: %d\n\r", ret); + if(ret != NRF_ERROR_INVALID_PARAM) { + debug_log("FLASH: Test invalid address store failed!\n\r"); + return 0; + } + + ret = flash_read(flash_get_page_size_words()*flash_get_page_number()-2, write_words, WORD_NUMBER); + debug_log("FLASH: Test invalid address read, Ret: %d\n\r", ret); + if(ret != NRF_ERROR_INVALID_PARAM) { + debug_log("FLASH: Test invalid address read failed!\n\r"); + return 0; + } + +//****************** Large data write **************************** + #define LARGE_WORD_NUMBER 100 + uint32_t large_words_address = 1234; + uint32_t large_write_words[LARGE_WORD_NUMBER]; + uint32_t large_read_words[LARGE_WORD_NUMBER]; + for(uint32_t i = 0; i < LARGE_WORD_NUMBER; i++) { + large_write_words[i] = i; + large_read_words[i] = 0; + } + + ret = flash_erase(0, 15); + debug_log("FLASH: Started erasing: Ret %d\n\r", ret); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Start erasing failed! @ %u\n\r", __LINE__); + return 0; + } + + ret = flash_store(large_words_address, large_write_words, LARGE_WORD_NUMBER); + debug_log("FLASH: Test large words store, Ret: %d\n\r", ret); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Test large words store failed! @ %u\n\r", __LINE__); + return 0; + } + + ret = flash_read(large_words_address, large_read_words, LARGE_WORD_NUMBER); + debug_log("FLASH: Test large words read, Ret: %d\n\r", ret); + if(ret != NRF_SUCCESS) { + debug_log("FLASH: Test large words read failed! @ %u\n\r", __LINE__); + return 0; + } + + if(memcmp((uint8_t*)&large_write_words[0], (uint8_t*)&large_read_words[0], sizeof(uint32_t)*LARGE_WORD_NUMBER) != 0) { + debug_log("FLASH: Stored words are not the right words! @ %u\n\r", __LINE__); + return 0; + } + + debug_log("FLASH: Flash test successful!!\n\r"); + + + return 1; +} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/flash_lib.h b/firmware/nRF_badge/data_collector/incl/flash_lib.h new file mode 100644 index 0000000..0d18e8e --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/flash_lib.h @@ -0,0 +1,222 @@ +#ifndef __FLASH_LIB_H +#define __FLASH_LIB_H + + +// https://devzone.nordicsemi.com/f/nordic-q-a/18083/fds---pstorage---fstorage---which-to-use +// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v12.0.0%2Flib_fstorage.html&cp=4_0_0_3_32 + + +/** @file + * + * @brief Internal flash abstraction library. + * + * @details It enables to erase pages, store and read word data into the flash memory by using the fstorage-library. + * The erase and store operations are asynchronous/non-blocking functions. To check the status of the operation, + * just call flash_get_operation(). + * This underlying fstorage module uses the softdevice, so the application has to initialize it before using this module. + * Furthermore, for retrieving system events (needed by fstorage) the system_event_lib-module is used. + * + * @note It is important to erase a flash page before storing data into it. This library doesn't report an error, if the data haven't been stored correctly. + * In case the flash page was erased before, the data should actually be stored correctly, but to be on the safe side, read out the data again and check. + * + */ + + +#include +//#include "sdk_common.h" // Needed for the definition of ret_code_t and the error-codes +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +// TODO: define this by the linker script with enough space for new program code! +#ifdef UNIT_TEST +#define FLASH_NUM_PAGES 30 +#else +#ifdef DEBUG_LOG_ENABLE +#define FLASH_NUM_PAGES 50 +#else +#define FLASH_NUM_PAGES 70 +#endif +#endif + +#if defined (NRF51) +#define FLASH_PAGE_SIZE_WORDS 256 /**< Number of words in one page (nrf51: 256) */ +#elif (defined (NRF52) || defined(NRF52840_XXAA)) +#define FLASH_PAGE_SIZE_WORDS 1024 /**< Number of words in one page (nrf52: 1024) */ +#endif + + +/**@brief The different EEPROM operations. These operations will be used to set the peripheral busy or not. + * + * @note When retrieving the current operation via flash_get_operation(). There could be more than one operation set at the same time. + * E.g. there can be FLASH_STORE_OPERATION and FLASH_ERASE_ERROR set at the same time (if the + * former scheduled erase operation failed and we scheduled a store operation after that). + */ +typedef enum { + FLASH_NO_OPERATION = 0, /**< Currently no store operation ongoing. */ + FLASH_STORE_OPERATION = (1 << 0), /**< Currently there is an ongoing store operation. */ + FLASH_ERASE_OPERATION = (1 << 1), /**< Currently there is an ongoing erase operation. */ + FLASH_STORE_ERROR = (1 << 2), /**< There was an error while storing the former data. */ + FLASH_ERASE_ERROR = (1 << 3), /**< There was an error while erasing the former pages. */ +} flash_operation_t; + + + + + + +/**@brief Function for initializing the flash module. + * + * @details This functions initializes the underlying fstorage-module with the specified parameters. + * The parameters for the fstorage-module are specified in the config-file: sdk_config.h. + * + * + * @retval NRF_SUCCESS If the module was successfully initialized. + * @retval NRF_ERROR_INTERNAL If there was an error while initializing the fstorage-module. + */ +ret_code_t flash_init(void); + + + + + +/**@brief Function for erasing pages in flash in asynchronous/non-blocking/background mode. + * + * @details Function uses the fstorage-library to erase pages in flash memory. + * This is a non-blocking function. So you can just check the status of the ongoing erase operation + * by calling flash_get_erase_operation(). + * Normally you should always check flash_get_erase_operation() before calling + * this function. Because if the erasing operation failed, the mentioned function + * will inform you by returning the flash_erase_operation_t FLASH_ERASE_ERROR. + * + * @param[in] page_num The index of the first page to erase. + * @param[in] num_pages Number of pages to erase. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (erase or store). + * @retval NRF_ERROR_INTERNAL If the module is not initialized or has a false configuration. + * @retval NRF_ERROR_INVALID_PARAM If the specified input parameters are bad. + */ +ret_code_t flash_erase_bkgnd(uint32_t page_num, uint16_t num_pages); + + +/**@brief Function for erasing pages in flash in blocking mode. + * + * @details Function uses internally flash_erase_bkgnd() for erasing the data + * and the flash_get_erase_operation() to wait until the operation has terminated. + * + * @param[in] page_num The index of the first page to erase. + * @param[in] num_pages Number of pages to erase. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (erase or store). + * @retval NRF_ERROR_INTERNAL If the module is not initialized or has a false configuration. + * @retval NRF_ERROR_INVALID_PARAM If the specified input parameters are bad. + * @retval NRF_ERROR_TIMEOUT If the store operation timed out or the operation takes too long. + */ +ret_code_t flash_erase(uint32_t page_num, uint16_t num_pages); + + + + + +/**@brief Function for storing data in flash in asynchronous/non-blocking/background mode. + * + * @details Function uses the fstorage-library to store data into flash memory. + * The fstorage library possibly splits the data into smaller chunks, + * to have more chances to get stored between BLE events. + * This is a non-blocking function. So you can just check the status of the ongoing store operation + * by calling flash_get_store_operation(). + * Normally you should always check flash_get_store_operation() before calling + * this function. Because if the store operation failed, the mentioned function + * will inform you by returning the flash_store_operation_t FLASH_STORE_ERROR. + * + * @warning The data to be written to flash has to be kept in memory until the operation has terminated. + * + * @param[in] word_num The 32bit address where to store the data. Starts at 0 . + * @param[in] p_words Pointer to the data to store in flash (32 bit words). + * @param[in] length_words Length of the data to store, in words. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (erase or store). + * @retval NRF_ERROR_INTERNAL If the module is not initialized or has a false configuration. + * @retval NRF_ERROR_INVALID_PARAM If the specified input parameters are bad. + */ +ret_code_t flash_store_bkgnd(uint32_t word_num, const uint32_t* p_words, uint16_t length_words); + + +/**@brief Function for storing data in flash in blocking mode. + * + * @details Function uses internally flash_store_bkgnd() for storing the data + * and the flash_get_store_operation() to wait until the operation has terminated. + * + * + * @param[in] word_num The 32bit address where to store the data. Starts at 0 . + * @param[in] p_words Pointer to the data to store in flash (32 bit words). + * @param[in] length_words Length of the data to store, in words. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (erase or store). + * @retval NRF_ERROR_INTERNAL If the module is not initialized or has a false configuration. + * @retval NRF_ERROR_INVALID_PARAM If the specified input parameters are bad. + * @retval NRF_ERROR_TIMEOUT If the store operation timed out or the operation takes too long. + */ +ret_code_t flash_store(uint32_t word_num, const uint32_t* p_words, uint16_t length_words); + + +/**@brief Function for retrieving the current store status/operation. + * + * @details This function returns the current flash_operation_t (and combinations of them). + * E.g. there can be FLASH_STORE_OPERATION and FLASH_ERASE_ERROR set at the same time (if the + * former scheduled erase operation failed and we scheduled a store operation after that). + * The application can check the status through this function, + * to decide whether the operation is done, or to reschedule + * the operation of the former data/pages because of an error while storing/erasing. + * + * + * @retval FLASH_STORE_NO_OPERATION If there is currently no store operation in process. + * @retval FLASH_STORE_OPERATION If there is currently a store operation in process. + * @retval FLASH_ERASE_OPERATION If there is currently a store operation in process. + * @retval FLASH_STORE_ERROR If an error occured while storing the former data. So the application can reschedule the storage of the former data. + * @retval FLASH_ERASE_ERROR If an error occured while erasing the former pages. So the application can reschedule the erase of the former pages. + */ +flash_operation_t flash_get_operation(void); + + + +/**@brief Function for reading words from flash. + * + * + * @warning The application must ensure that p_words has space for least length_words. + * + * @param[in] word_num The address of the first word to read. + * @param[in] p_words Pointer to memory where the words should be stored to. + * @param[in] length_words Number of words to read. + * + * @retval NRF_SUCCESS If the operation was successfully. + * @retval NRF_ERROR_INVALID_PARAM If the p_words pointer is not in RAM section, the word_num (address) is invalid or p_word is NULL. + */ +ret_code_t flash_read(uint32_t word_num, uint32_t* p_words, uint16_t length_words); + + +/**@brief Function for reading number of words in one page. + * + * @retval Number of words in one page. + */ +uint32_t flash_get_page_size_words(void); + +/**@brief Function for reading available number of pages. + * + * @retval Number of available pages. + */ +uint32_t flash_get_page_number(void); + + +/**@brief Function for testing the flash module. + * + * @retval 0 If selftest failed. + * @retval 1 If selftest passed. + */ +bool flash_selftest(void); + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/microphone_lib.c b/firmware/nRF_badge/data_collector/incl/microphone_lib.c new file mode 100644 index 0000000..6b25cc7 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/microphone_lib.c @@ -0,0 +1,175 @@ +#include "microphone_lib.h" + +#include "custom_board.h" // Needed for the Pin configuration +#include "adc_lib.h" +#include "debug_lib.h" +#include "systick_lib.h" // Needed for microphone_selftest() +#include "nrf_gpio.h" + +#define ABS(x) (((x) >= 0)? (x) : -(x)) +#define MICROPHONE_ZERO_OFFSET 125 /**< The zero noise mmicrophone offset to compensate */ +#define MICROPHONE_SELFTEST_TIME_FOR_NOISE_GENERATION_MS 10000 /**< The time to wait for noise generation, for selftest */ +#define MICROPHONE_SELFTEST_THRESHOLD 20 /**< The threshold that has to be exceeded to pass the selftest */ +#define MICROPHONE_SELFTEST_SECTION_SIZE_MS 200 /**< The section size in ms */ +#define MICROPHONE_SELFTEST_THRESHOLD_PERCENTAGE 60 /**< If this percentage of reads is above the threshold in one section, there is noise. + The same for no noise. Should be > 50 percentage */ + +static adc_instance_t adc_instance; + +void microphone_init(void) { + + adc_instance.adc_peripheral = 0; + adc_instance.nrf_adc_config.resolution = NRF_ADC_CONFIG_RES_8BIT; + adc_instance.nrf_adc_config.scaling = NRF_ADC_CONFIG_SCALING_INPUT_FULL_SCALE; + adc_instance.nrf_adc_config.reference = MIC_AREF; // NRF_ADC_CONFIG_REF_EXT_REF1; + adc_instance.nrf_adc_config_input = MIC_PIN; //NRF_ADC_CONFIG_INPUT_6; //ADC_CONFIG_PSEL_AnalogInput6; + + + adc_init(&adc_instance, 1); +} + +ret_code_t microphone_read(uint8_t* value) { + int32_t raw; + ret_code_t ret = adc_read_raw(&adc_instance, &raw); + *value = (uint8_t) ABS((raw - MICROPHONE_ZERO_OFFSET)); + return ret; +} + + +/**@brief Function that returns the average micorphone value over ~50ms. + * + * @retval The average microphone value. + */ + +static uint8_t get_avg_value(void) { + uint32_t avg_value = 0; + uint32_t avg_counter = 0; + for(uint8_t i = 0; i < 50; i++) { + systick_delay_millis(1); + uint8_t value = 0; + ret_code_t ret = microphone_read(&value); + if(ret != NRF_SUCCESS) + return 0; + avg_value += value; + avg_counter++; + } + avg_value /= (avg_counter/2); + avg_value = avg_value > 255 ? 255 : avg_value; + return (uint8_t) avg_value; +} + + + +/**@brief Function that checks the sections for the sections_pattern. + * + * @param[in] sections The array of sections. + * @param[in] size Number of elements in sections array. + * @param[in] sections_pattern The pattern of sections. + * @param[in] pattern_size Number of elements in sections pattern array. + * + * @retval 1 if section pattern was found. + * @retval 0 otherwise. + */ +static uint8_t check_sections_pattern(const int8_t* sections, uint32_t size, const int8_t* sections_pattern, uint32_t pattern_size) { + + if(pattern_size > size) + return 0; + + + uint32_t pattern_index = 0; + + for(uint32_t i = 0; i < size; i++) { + if(sections[i] == sections_pattern[pattern_index]) { + pattern_index++; + if(pattern_index >= pattern_size) + return 1; + } + } + return 0; +} + +/**@brief Function that checks one section (if there is noise or not). + * + * @param[in] above_threshold_counter Number of reads above the threshold. + * @param[in] below_threshold_counter Number of reads below the threshold. + * + * @retval 1 section has noise. + * @retval 0 section has no noise. + * @retval -1 section is undefined. + */ +static int8_t check_section(uint32_t above_threshold_counter, uint32_t below_threshold_counter) { + int8_t ret = -1; + uint32_t counter_sum = above_threshold_counter + below_threshold_counter; + if(counter_sum == 0) + return -1; + + if(((above_threshold_counter * 100) / counter_sum) >= MICROPHONE_SELFTEST_THRESHOLD_PERCENTAGE) { + ret = 1; + } else if(((below_threshold_counter * 100) / counter_sum) >= MICROPHONE_SELFTEST_THRESHOLD_PERCENTAGE) { + ret = 0; + } + return ret; +} + +bool microphone_selftest(void) { + + // The pattern to search for. + int8_t sections_pattern[4] = {0, 1, 0, 1}; + + + //systick_delay_millis(500); + debug_log("MICROPHONE: Waiting for noise pattern for: %u ms.\n", MICROPHONE_SELFTEST_TIME_FOR_NOISE_GENERATION_MS); + uint64_t end_ms = systick_get_continuous_millis() + MICROPHONE_SELFTEST_TIME_FOR_NOISE_GENERATION_MS; + + + uint32_t number_of_sections = MICROPHONE_SELFTEST_TIME_FOR_NOISE_GENERATION_MS/MICROPHONE_SELFTEST_SECTION_SIZE_MS; + + uint64_t section_end_ms = systick_get_continuous_millis() + MICROPHONE_SELFTEST_SECTION_SIZE_MS; + int8_t sections[number_of_sections]; + uint32_t section_above_threshold_counter = 0; + uint32_t section_below_threshold_counter = 0; + + + uint32_t current_section = 0; + + while(systick_get_continuous_millis() < end_ms) { + // Check if we need to close the current section? + if(systick_get_continuous_millis() >= section_end_ms) { + + sections[current_section] = check_section(section_above_threshold_counter, section_below_threshold_counter); + + // Check the sections: + if(check_sections_pattern(sections, current_section + 1, sections_pattern, sizeof(sections_pattern))) { + debug_log("MICROPHONE: Section check successful!\n"); + return 1; + } + + // Increment the sections + current_section = (current_section + 1) < number_of_sections ? (current_section + 1) : current_section; + sections[current_section] = 0; + section_above_threshold_counter = 0; + section_below_threshold_counter = 0; + + section_end_ms = systick_get_continuous_millis() + MICROPHONE_SELFTEST_SECTION_SIZE_MS; + } + + uint8_t cur_value = get_avg_value(); + + // Display the microphone value + if(cur_value >= MICROPHONE_SELFTEST_THRESHOLD) { + //nrf_gpio_pin_write(GREEN_LED, LED_ON); + nrf_gpio_pin_write(RED_LED, LED_OFF); + nrf_gpio_pin_write(GREEN_LED, LED_OFF); + section_above_threshold_counter++; + } else { + //nrf_gpio_pin_write(GREEN_LED, LED_OFF); + nrf_gpio_pin_write(RED_LED, LED_ON); + nrf_gpio_pin_write(GREEN_LED, LED_ON); + section_below_threshold_counter++; + } + + } + + return 0; + +} diff --git a/firmware/nRF_badge/data_collector/incl/microphone_lib.h b/firmware/nRF_badge/data_collector/incl/microphone_lib.h new file mode 100644 index 0000000..851acfc --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/microphone_lib.h @@ -0,0 +1,37 @@ +#ifndef __MICROPHONE_LIB_H +#define __MICROPHONE_LIB_H + +#include +#include "sdk_errors.h" + + +/**@brief Function to initialize the microphone-module (with ADC) + */ +void microphone_init(void); + + +/**@brief Function to read the current microphone value. + * + * @param[out] value Read microphone value. + * + * @retval NRF_SUCCESS On success. + * @retval NRF_ERROR_BUSY If the ADC-interface is busy. + */ +ret_code_t microphone_read(uint8_t* value) ; + + +/**@brief Function for testing the microphone module. + * + * @details When the function records average microphone values over ~50ms. + * It searches for a specific pattern {no noise, noise, no noise, noise} + * of so called "sections". + * During the tests: When no noise -> both LEDs on, if noise -> both LEDs off. + * + * @retval 0 If selftest failed. + * @retval 1 If selftest passed. + * + * @note systick_init() has to be called before. + */ +bool microphone_selftest(void); + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/processing_lib.c b/firmware/nRF_badge/data_collector/incl/processing_lib.c new file mode 100644 index 0000000..156129f --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/processing_lib.c @@ -0,0 +1,200 @@ +#include "processing_lib.h" +#include // For qsort +#include "app_scheduler.h" + +#include "sampling_lib.h" +#include "chunk_fifo_lib.h" +#include "storer_lib.h" +#include "systick_lib.h" + +#include "chunk_messages.h" + +#include "debug_lib.h" + + +static ScanChunk scan_chunk; /**< A Scan-chunk structure, needed to convert from ScanSamplingChunk to ScanChunk */ + +/************************** ACCELEROMETER ***********************/ +void processing_process_accelerometer_chunk(void * p_event_data, uint16_t event_size) { + //debug_log("PROCESSING: processing_process_accelerometer_chunk...\n"); + AccelerometerChunk* accelerometer_chunk; + while(chunk_fifo_read_open(&accelerometer_chunk_fifo, (void**) &accelerometer_chunk, NULL) == NRF_SUCCESS) { + + ret_code_t ret = storer_store_accelerometer_chunk(accelerometer_chunk); + debug_log("PROCESSING: Try to store accelerometer chunk: Ret %d\n", ret); + if(ret == NRF_ERROR_INTERNAL) { // E.g. if busy --> reschedule + app_sched_event_put(NULL, 0, processing_process_accelerometer_chunk); + break; + } else { + chunk_fifo_read_close(&accelerometer_chunk_fifo); + } + } +} + +/********************* ACCELEROMTER INTERRUPT **********************/ +void processing_process_accelerometer_interrupt_chunk(void * p_event_data, uint16_t event_size) { + //debug_log("PROCESSING: processing_process_accelerometer_interrupt_chunk...\n"); + AccelerometerInterruptChunk* accelerometer_interrupt_chunk; + while(chunk_fifo_read_open(&accelerometer_interrupt_chunk_fifo, (void**) &accelerometer_interrupt_chunk, NULL) == NRF_SUCCESS) { + + ret_code_t ret = storer_store_accelerometer_interrupt_chunk(accelerometer_interrupt_chunk); + debug_log("PROCESSING: Try to store accelerometer interrupt chunk: Ret %d\n", ret); + if(ret == NRF_ERROR_INTERNAL) { // E.g. if busy --> reschedule + app_sched_event_put(NULL, 0, processing_process_accelerometer_interrupt_chunk); + break; + } else { + chunk_fifo_read_close(&accelerometer_interrupt_chunk_fifo); + } + } +} + +/******************************** BATTERY *********************************/ +void processing_process_battery_chunk(void * p_event_data, uint16_t event_size) { + //debug_log("PROCESSING: processing_process_battery_chunk...\n"); + BatteryChunk* battery_chunk; + while(chunk_fifo_read_open(&battery_chunk_fifo, (void**) &battery_chunk, NULL) == NRF_SUCCESS) { + // Store the battery chunk + ret_code_t ret = storer_store_battery_chunk(battery_chunk); + debug_log("PROCESSING: Try to store battery chunk: Ret %d\n", ret); + if(ret == NRF_ERROR_INTERNAL) { // E.g. if busy --> reschedule + app_sched_event_put(NULL, 0, processing_process_battery_chunk); + break; + } else { + chunk_fifo_read_close(&battery_chunk_fifo); + } + } + +} + + +/******************************* MICROPHONE *********************************/ +void processing_process_microphone_chunk(void * p_event_data, uint16_t event_size) { + //debug_log("PROCESSING: processing_process_microphone_chunk...\n"); + MicrophoneChunk* microphone_chunk; + while(chunk_fifo_read_open(µphone_chunk_fifo, (void**) µphone_chunk, NULL) == NRF_SUCCESS) { + ret_code_t ret = storer_store_microphone_chunk(microphone_chunk); + debug_log("PROCESSING: Try to store microphone chunk: Ret %d\n", ret); + if(ret == NRF_ERROR_INTERNAL) { // E.g. if busy --> reschedule + app_sched_event_put(NULL, 0, processing_process_microphone_chunk); + break; + } else { + chunk_fifo_read_close(µphone_chunk_fifo); + } + } +} + + +/******************************* SCAN *********************************/ + +/**@brief Function that checks whether a scan-result is from a beacon or not. + * + * @param[in] scan_result_data Pointer to scan result data. + * + * @retval 0 If it is no beacon. + * @retval 1 If it is a beacon. + */ +static uint8_t is_beacon(ScanResultData* scan_result_data) { + if(scan_result_data->scan_device.ID >= SCAN_BEACON_ID_THRESHOLD) { + return 1; + } + return 0; +} + +/**@brief Function that compares two scan-results by beacon. + * + * @param[in] a Pointer to scan result data of device A. + * @param[in] b Pointer to scan result data of device B. + * + * @retval -1 If device A is a beacon and device B is no beacon --> A should be in front of B. + * @retval 0 If device A is a beacon and device B is a beacon. Or if A is no beacon and B is no beacon --> Nothing has to be done here. + * @retval 1 If device B is a beacon and device A is no beacon --> B should be in front of A. + */ +static int compare_by_beacon(const void* a, const void* b) { + uint8_t is_beacon_a = is_beacon((ScanResultData*) a); + uint8_t is_beacon_b = is_beacon((ScanResultData*) b); + if(is_beacon_a > is_beacon_b) + return -1; // We want A in front of B in the sorted list + else if(is_beacon_a < is_beacon_b) + return 1; // We want B in front of A in the sorted list + else + return 0; +} + +/**@brief Function that compares two scan-results by RSSI value. + * + * @param[in] a Pointer to scan result data of device A. + * @param[in] b Pointer to scan result data of device B. + * + * @retval -1 If device A has a greater RSSI value than device B --> A should be in front of B. + * @retval 0 If device A has the same RSSI value than device B --> Nothing has to be done here. + * @retval 1 If device B has a greater RSSI value than device A --> B should be in front of A. + */ +static int compare_by_RSSI(const void* a, const void* b) { + int8_t rssi_a = ((ScanResultData*) a)->scan_device.rssi; + int8_t rssi_b = ((ScanResultData*) b)->scan_device.rssi; + + if(rssi_a > rssi_b) + return -1; // We want A in front of B in the sorted list + else if(rssi_a < rssi_b) + return 1; // We want B in front of A in the sorted list + else + return 0; +} + +/**@brief Function that sorts the seen devices in the scan_sampling_chunk-structure. + * + * @details As first step the beacons are brought to the begin of the structure and sorted by RSSI-value, + * to get at least SCAN_PRIORITIZED_BEACONS (or less if there are not that much beacons available) in the scan. + * After that all remaining seen devices are sorted by RSSI-value. + * + * + * @param[in] scan_sampling_chunk Pointer to the ScanSamplingChunk to be sorted. + */ +static void sort_scan(ScanSamplingChunk* scan_sampling_chunk) { + // First get the number of beacons: + uint32_t num_beacons = 0; + for(uint32_t i = 0; i < scan_sampling_chunk->scan_result_data_count; i++) { + if(is_beacon(&(scan_sampling_chunk->scan_result_data[i]))) { + num_beacons++; + } + } + // Then split Beacons and Badges + qsort(scan_sampling_chunk->scan_result_data, scan_sampling_chunk->scan_result_data_count, sizeof(ScanResultData), compare_by_beacon); + + // Sort the beacons' RSSI values + qsort(scan_sampling_chunk->scan_result_data, num_beacons, sizeof(ScanResultData), compare_by_RSSI); + + // Calculate the number of prioritized beacons + uint32_t prioritized_beacons = (num_beacons > SCAN_PRIORITIZED_BEACONS) ? SCAN_PRIORITIZED_BEACONS : num_beacons; + + // Finally sort all devices, except the prioritized beacons + qsort(&((scan_sampling_chunk->scan_result_data)[prioritized_beacons]), scan_sampling_chunk->scan_result_data_count - num_beacons, sizeof(ScanResultData), compare_by_RSSI); +} + +void processing_process_scan_sampling_chunk(void * p_event_data, uint16_t event_size) { + //debug_log("PROCESSING: processing_process_scan_sampling_chunk...\n"); + ScanSamplingChunk* scan_sampling_chunk; + + while(chunk_fifo_read_open(&scan_sampling_chunk_fifo, (void**) &scan_sampling_chunk, NULL) == NRF_SUCCESS) { + // if there are more devices than we can store --> sort the scan chunk to store the "important" devices + if(scan_sampling_chunk->scan_result_data_count > SCAN_CHUNK_DATA_SIZE) { + sort_scan(scan_sampling_chunk); + scan_sampling_chunk->scan_result_data_count = SCAN_CHUNK_DATA_SIZE; + } + + scan_chunk.timestamp = scan_sampling_chunk->timestamp; + scan_chunk.scan_result_data_count = scan_sampling_chunk->scan_result_data_count; + for(uint32_t i = 0; i < scan_chunk.scan_result_data_count; i++) { + scan_chunk.scan_result_data[i] = scan_sampling_chunk->scan_result_data[i]; + } + + ret_code_t ret = storer_store_scan_chunk(&scan_chunk); + debug_log("PROCESSING: Try to store scan chunk: Ret %d\n", ret); + if(ret == NRF_ERROR_INTERNAL) { // E.g. if busy --> reschedule + app_sched_event_put(NULL, 0, processing_process_scan_sampling_chunk); + break; + } else { + chunk_fifo_read_close(&scan_sampling_chunk_fifo); + } + } +} diff --git a/firmware/nRF_badge/data_collector/incl/processing_lib.h b/firmware/nRF_badge/data_collector/incl/processing_lib.h new file mode 100644 index 0000000..685fbfe --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/processing_lib.h @@ -0,0 +1,68 @@ +/**@file + * @details This module provides the processing of the sampled data-chunks. + * It uses the chunk-fifos that are declared in sampling_lib.h for to get the data chunks that should be processed. + * All the processing functions are scheduled via the scheduler (and reschedule themself when an error occured). + */ + +#ifndef __PROCESSING_LIB_H +#define __PROCESSING_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + +/**< Processing parameters for the scan chunks */ +#define SCAN_BEACON_ID_THRESHOLD 16000 +#define SCAN_PRIORITIZED_BEACONS 4 + +/**@brief Function that processes the accelerometer chunks. + * + * @details It checks for available chunks in the chunk-fifo and tries to store the chunk as it is in the filesystem via the storer-module. + * + * @param[in] p_event_data Pointer to event data (actually always == NULL). + * @param[in] event_size Event data size (actually always == 0). + */ +void processing_process_accelerometer_chunk(void * p_event_data, uint16_t event_size); + +/**@brief Function that processes the accelerometer interrupt chunks. + * + * @details It checks for available chunks in the chunk-fifo and tries to store the chunk as it is in the filesystem via the storer-module. + * + * @param[in] p_event_data Pointer to event data (actually always == NULL). + * @param[in] event_size Event data size (actually always == 0). + */ +void processing_process_accelerometer_interrupt_chunk(void * p_event_data, uint16_t event_size); + +/**@brief Function that processes the battery chunks. + * + * @details It checks for available chunks in the chunk-fifo and tries to store the chunk as it is in the filesystem via the storer-module. + * + * @param[in] p_event_data Pointer to event data (actually always == NULL). + * @param[in] event_size Event data size (actually always == 0). + */ +void processing_process_battery_chunk(void * p_event_data, uint16_t event_size); + +/**@brief Function that processes the microphone chunks. + * + * @details It checks for available chunks in the chunk-fifo and tries to store the chunk as it is in the filesystem via the storer-module. + * + * @param[in] p_event_data Pointer to event data (actually always == NULL). + * @param[in] event_size Event data size (actually always == 0). + */ +void processing_process_microphone_chunk(void * p_event_data, uint16_t event_size); + +/**@brief Function that processes the scanning chunks. + * + * @details It checks for available chunks in the chunk-fifo. In this case not the ScanSamplingChunk-structure is stored but the ScanChunk-structure. + * The ScanSamplingChunk-structure can hold much more devices than the ScanChunk-structure that is used for storing. + * To get only the relevant devices a sorting mechanism is performed that sortes for strong RSSI-values and prioritzes beacons. + * After the sorting, the ScanChunk is stored in the filesystem via the storer-module. + * + * @param[in] p_event_data Pointer to event data (actually always == NULL). + * @param[in] event_size Event data size (actually always == 0). + */ +void processing_process_scan_sampling_chunk(void * p_event_data, uint16_t event_size); + + + + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.c b/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.c new file mode 100644 index 0000000..c371c72 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.c @@ -0,0 +1,138 @@ +#include "tinybuf.h" +#include "protocol_messages_01v1.h" + +//#define PROTOCOL_01v1 +#ifdef PROTOCOL_01v1 + +const tb_field_t StatusRequest_fields[2] = { + {513, tb_offsetof(StatusRequest, timestamp), 0, 0, tb_membersize(StatusRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StatusAssignRequest_fields[3] = { + {513, tb_offsetof(StatusAssignRequest, timestamp), 0, 0, tb_membersize(StatusAssignRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {513, tb_offsetof(StatusAssignRequest, badge_assignement), 0, 0, tb_membersize(StatusAssignRequest, badge_assignement), 0, 0, 0, &BadgeAssignement_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartMicrophoneRequest_fields[3] = { + {513, tb_offsetof(StartMicrophoneRequest, timestamp), 0, 0, tb_membersize(StartMicrophoneRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartMicrophoneRequest, timeout), 0, 0, tb_membersize(StartMicrophoneRequest, timeout), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopMicrophoneRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartScanRequest_fields[7] = { + {513, tb_offsetof(StartScanRequest, timestamp), 0, 0, tb_membersize(StartScanRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartScanRequest, timeout), 0, 0, tb_membersize(StartScanRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, window), 0, 0, tb_membersize(StartScanRequest, window), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, interval), 0, 0, tb_membersize(StartScanRequest, interval), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, duration), 0, 0, tb_membersize(StartScanRequest, duration), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, period), 0, 0, tb_membersize(StartScanRequest, period), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopScanRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneDataRequest_fields[2] = { + {513, tb_offsetof(MicrophoneDataRequest, timestamp), 0, 0, tb_membersize(MicrophoneDataRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanDataRequest_fields[2] = { + {65, tb_offsetof(ScanDataRequest, seconds), 0, 0, tb_membersize(ScanDataRequest, seconds), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t IdentifyRequest_fields[2] = { + {65, tb_offsetof(IdentifyRequest, timeout), 0, 0, tb_membersize(IdentifyRequest, timeout), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t Request_fields[10] = { + {528, tb_offsetof(Request, type.status_request), tb_delta(Request, which_type, type.status_request), 1, tb_membersize(Request, type.status_request), 0, 115, 1, &StatusRequest_fields}, + {528, tb_offsetof(Request, type.status_assign_request), tb_delta(Request, which_type, type.status_assign_request), 1, tb_membersize(Request, type.status_assign_request), 0, 83, 0, &StatusAssignRequest_fields}, + {528, tb_offsetof(Request, type.start_microphone_request), tb_delta(Request, which_type, type.start_microphone_request), 1, tb_membersize(Request, type.start_microphone_request), 0, 49, 0, &StartMicrophoneRequest_fields}, + {528, tb_offsetof(Request, type.stop_microphone_request), tb_delta(Request, which_type, type.stop_microphone_request), 1, tb_membersize(Request, type.stop_microphone_request), 0, 48, 0, &StopMicrophoneRequest_fields}, + {528, tb_offsetof(Request, type.start_scan_request), tb_delta(Request, which_type, type.start_scan_request), 1, tb_membersize(Request, type.start_scan_request), 0, 112, 0, &StartScanRequest_fields}, + {528, tb_offsetof(Request, type.stop_scan_request), tb_delta(Request, which_type, type.stop_scan_request), 1, tb_membersize(Request, type.stop_scan_request), 0, 113, 0, &StopScanRequest_fields}, + {528, tb_offsetof(Request, type.microphone_data_request), tb_delta(Request, which_type, type.microphone_data_request), 1, tb_membersize(Request, type.microphone_data_request), 0, 114, 0, &MicrophoneDataRequest_fields}, + {528, tb_offsetof(Request, type.scan_data_request), tb_delta(Request, which_type, type.scan_data_request), 1, tb_membersize(Request, type.scan_data_request), 0, 98, 0, &ScanDataRequest_fields}, + {528, tb_offsetof(Request, type.identify_request), tb_delta(Request, which_type, type.identify_request), 1, tb_membersize(Request, type.identify_request), 0, 105, 0, &IdentifyRequest_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StatusResponse_fields[6] = { + {65, tb_offsetof(StatusResponse, clock_status), 0, 0, tb_membersize(StatusResponse, clock_status), 0, 0, 0, NULL}, + {65, tb_offsetof(StatusResponse, scan_status), 0, 0, tb_membersize(StatusResponse, scan_status), 0, 0, 0, NULL}, + {65, tb_offsetof(StatusResponse, collector_status), 0, 0, tb_membersize(StatusResponse, collector_status), 0, 0, 0, NULL}, + {513, tb_offsetof(StatusResponse, timestamp), 0, 0, tb_membersize(StatusResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + {513, tb_offsetof(StatusResponse, battery_data), 0, 0, tb_membersize(StatusResponse, battery_data), 0, 0, 0, &BatteryData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartMicrophoneResponse_fields[2] = { + {513, tb_offsetof(StartMicrophoneResponse, timestamp), 0, 0, tb_membersize(StartMicrophoneResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StopMicrophoneResponse_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartScanResponse_fields[2] = { + {513, tb_offsetof(StartScanResponse, timestamp), 0, 0, tb_membersize(StartScanResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StopScanResponse_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneDataResponseHeader_fields[4] = { + {513, tb_offsetof(MicrophoneDataResponseHeader, timestamp), 0, 0, tb_membersize(MicrophoneDataResponseHeader, timestamp), 0, 0, 0, &Timestamp_fields}, + {513, tb_offsetof(MicrophoneDataResponseHeader, battery_data), 0, 0, tb_membersize(MicrophoneDataResponseHeader, battery_data), 0, 0, 0, &BatteryData_fields}, + {65, tb_offsetof(MicrophoneDataResponseHeader, sample_period_ms), 0, 0, tb_membersize(MicrophoneDataResponseHeader, sample_period_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneDataResponse_fields[3] = { + {513, tb_offsetof(MicrophoneDataResponse, microphone_data_response_header), 0, 0, tb_membersize(MicrophoneDataResponse, microphone_data_response_header), 0, 0, 0, &MicrophoneDataResponseHeader_fields}, + {516, tb_offsetof(MicrophoneDataResponse, microphone_data), tb_delta(MicrophoneDataResponse, microphone_data_count, microphone_data), 1, tb_membersize(MicrophoneDataResponse, microphone_data[0]), tb_membersize(MicrophoneDataResponse, microphone_data)/tb_membersize(MicrophoneDataResponse, microphone_data[0]), 0, 0, &MicrophoneData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanDataResponseHeader_fields[3] = { + {65, tb_offsetof(ScanDataResponseHeader, timestamp_seconds), 0, 0, tb_membersize(ScanDataResponseHeader, timestamp_seconds), 0, 0, 0, NULL}, + {513, tb_offsetof(ScanDataResponseHeader, battery_data), 0, 0, tb_membersize(ScanDataResponseHeader, battery_data), 0, 0, 0, &BatteryData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanDataResponse_fields[3] = { + {513, tb_offsetof(ScanDataResponse, scan_data_response_header), 0, 0, tb_membersize(ScanDataResponse, scan_data_response_header), 0, 0, 0, &ScanDataResponseHeader_fields}, + {516, tb_offsetof(ScanDataResponse, scan_result_data), tb_delta(ScanDataResponse, scan_result_data_count, scan_result_data), 1, tb_membersize(ScanDataResponse, scan_result_data[0]), tb_membersize(ScanDataResponse, scan_result_data)/tb_membersize(ScanDataResponse, scan_result_data[0]), 0, 0, &ScanResultData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t IdentifyResponse_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t Response_fields[9] = { + {528, tb_offsetof(Response, type.status_response), tb_delta(Response, which_type, type.status_response), 1, tb_membersize(Response, type.status_response), 0, 1, 1, &StatusResponse_fields}, + {528, tb_offsetof(Response, type.start_microphone_response), tb_delta(Response, which_type, type.start_microphone_response), 1, tb_membersize(Response, type.start_microphone_response), 0, 2, 0, &StartMicrophoneResponse_fields}, + {528, tb_offsetof(Response, type.stop_microphone_response), tb_delta(Response, which_type, type.stop_microphone_response), 1, tb_membersize(Response, type.stop_microphone_response), 0, 3, 0, &StopMicrophoneResponse_fields}, + {528, tb_offsetof(Response, type.start_scan_response), tb_delta(Response, which_type, type.start_scan_response), 1, tb_membersize(Response, type.start_scan_response), 0, 4, 0, &StartScanResponse_fields}, + {528, tb_offsetof(Response, type.stop_scan_response), tb_delta(Response, which_type, type.stop_scan_response), 1, tb_membersize(Response, type.stop_scan_response), 0, 5, 0, &StopScanResponse_fields}, + {528, tb_offsetof(Response, type.microphone_data_response), tb_delta(Response, which_type, type.microphone_data_response), 1, tb_membersize(Response, type.microphone_data_response), 0, 6, 0, &MicrophoneDataResponse_fields}, + {528, tb_offsetof(Response, type.scan_data_response), tb_delta(Response, which_type, type.scan_data_response), 1, tb_membersize(Response, type.scan_data_response), 0, 7, 0, &ScanDataResponse_fields}, + {528, tb_offsetof(Response, type.identify_response), tb_delta(Response, which_type, type.identify_response), 1, tb_membersize(Response, type.identify_response), 0, 8, 0, &IdentifyResponse_fields}, + TB_LAST_FIELD, +}; + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.h b/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.h new file mode 100644 index 0000000..25d2027 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.h @@ -0,0 +1,169 @@ +#ifndef __PROTOCOL_MESSAGES_01V1_H +#define __PROTOCOL_MESSAGES_01V1_H + +#include +#include "tinybuf.h" +#include "common_messages.h" + +#define PROTOCOL_MICROPHONE_DATA_SIZE 114 +#define PROTOCOL_SCAN_DATA_SIZE 29 + +#define Request_status_request_tag 115 +#define Request_status_assign_request_tag 83 +#define Request_start_microphone_request_tag 49 +#define Request_stop_microphone_request_tag 48 +#define Request_start_scan_request_tag 112 +#define Request_stop_scan_request_tag 113 +#define Request_microphone_data_request_tag 114 +#define Request_scan_data_request_tag 98 +#define Request_identify_request_tag 105 +#define Response_status_response_tag 1 +#define Response_start_microphone_response_tag 2 +#define Response_stop_microphone_response_tag 3 +#define Response_start_scan_response_tag 4 +#define Response_stop_scan_response_tag 5 +#define Response_microphone_data_response_tag 6 +#define Response_scan_data_response_tag 7 +#define Response_identify_response_tag 8 + +typedef struct { + Timestamp timestamp; +} StatusRequest; + +typedef struct { + Timestamp timestamp; + BadgeAssignement badge_assignement; +} StatusAssignRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; +} StartMicrophoneRequest; + +typedef struct { +} StopMicrophoneRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint16_t window; + uint16_t interval; + uint16_t duration; + uint16_t period; +} StartScanRequest; + +typedef struct { +} StopScanRequest; + +typedef struct { + Timestamp timestamp; +} MicrophoneDataRequest; + +typedef struct { + uint32_t seconds; +} ScanDataRequest; + +typedef struct { + uint16_t timeout; +} IdentifyRequest; + +typedef struct { + uint8_t which_type; + union { + StatusRequest status_request; + StatusAssignRequest status_assign_request; + StartMicrophoneRequest start_microphone_request; + StopMicrophoneRequest stop_microphone_request; + StartScanRequest start_scan_request; + StopScanRequest stop_scan_request; + MicrophoneDataRequest microphone_data_request; + ScanDataRequest scan_data_request; + IdentifyRequest identify_request; + } type; +} Request; + +typedef struct { + uint8_t clock_status; + uint8_t scan_status; + uint8_t collector_status; + Timestamp timestamp; + BatteryData battery_data; +} StatusResponse; + +typedef struct { + Timestamp timestamp; +} StartMicrophoneResponse; + +typedef struct { +} StopMicrophoneResponse; + +typedef struct { + Timestamp timestamp; +} StartScanResponse; + +typedef struct { +} StopScanResponse; + +typedef struct { + Timestamp timestamp; + BatteryData battery_data; + uint16_t sample_period_ms; +} MicrophoneDataResponseHeader; + +typedef struct { + MicrophoneDataResponseHeader microphone_data_response_header; + uint8_t microphone_data_count; + MicrophoneData microphone_data[114]; +} MicrophoneDataResponse; + +typedef struct { + uint32_t timestamp_seconds; + BatteryData battery_data; +} ScanDataResponseHeader; + +typedef struct { + ScanDataResponseHeader scan_data_response_header; + uint8_t scan_result_data_count; + ScanResultData scan_result_data[29]; +} ScanDataResponse; + +typedef struct { +} IdentifyResponse; + +typedef struct { + uint8_t which_type; + union { + StatusResponse status_response; + StartMicrophoneResponse start_microphone_response; + StopMicrophoneResponse stop_microphone_response; + StartScanResponse start_scan_response; + StopScanResponse stop_scan_response; + MicrophoneDataResponse microphone_data_response; + ScanDataResponse scan_data_response; + IdentifyResponse identify_response; + } type; +} Response; + +extern const tb_field_t StatusRequest_fields[2]; +extern const tb_field_t StatusAssignRequest_fields[3]; +extern const tb_field_t StartMicrophoneRequest_fields[3]; +extern const tb_field_t StopMicrophoneRequest_fields[1]; +extern const tb_field_t StartScanRequest_fields[7]; +extern const tb_field_t StopScanRequest_fields[1]; +extern const tb_field_t MicrophoneDataRequest_fields[2]; +extern const tb_field_t ScanDataRequest_fields[2]; +extern const tb_field_t IdentifyRequest_fields[2]; +extern const tb_field_t Request_fields[10]; +extern const tb_field_t StatusResponse_fields[6]; +extern const tb_field_t StartMicrophoneResponse_fields[2]; +extern const tb_field_t StopMicrophoneResponse_fields[1]; +extern const tb_field_t StartScanResponse_fields[2]; +extern const tb_field_t StopScanResponse_fields[1]; +extern const tb_field_t MicrophoneDataResponseHeader_fields[4]; +extern const tb_field_t MicrophoneDataResponse_fields[3]; +extern const tb_field_t ScanDataResponseHeader_fields[3]; +extern const tb_field_t ScanDataResponse_fields[3]; +extern const tb_field_t IdentifyResponse_fields[1]; +extern const tb_field_t Response_fields[9]; + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.tb b/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.tb new file mode 100644 index 0000000..912232d --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/protocol_messages_01v1.tb @@ -0,0 +1,135 @@ +import common_messages + +extern Timestamp; +extern BadgeAssignement; +extern BatteryData; +extern MicrophoneData; +extern ScanResultData; + + +define { + PROTOCOL_MICROPHONE_DATA_SIZE = 114; + PROTOCOL_SCAN_DATA_SIZE = 29; +} + + +message StatusRequest { + required Timestamp timestamp; +} + +message StatusAssignRequest { + required Timestamp timestamp; + required BadgeAssignement badge_assignement; +} + +message StartMicrophoneRequest { + required Timestamp timestamp; + required uint16 timeout; +} + +message StopMicrophoneRequest { + +} + +message StartScanRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 window; + required uint16 interval; + required uint16 duration; + required uint16 period; +} + +message StopScanRequest { +} + +message MicrophoneDataRequest { + required Timestamp timestamp; +} + +message ScanDataRequest { + required uint32 seconds; +} + +message IdentifyRequest { + required uint16 timeout; +} + + + +message Request { + oneof type { + StatusRequest status_request (115); + StatusAssignRequest status_assign_request (83); + StartMicrophoneRequest start_microphone_request (49); + StopMicrophoneRequest stop_microphone_request (48); + StartScanRequest start_scan_request (112); + StopScanRequest stop_scan_request (113); + MicrophoneDataRequest microphone_data_request (114); + ScanDataRequest scan_data_request (98); + IdentifyRequest identify_request (105); + } +} + + + + +message StatusResponse { + required uint8 clock_status; + required uint8 scan_status; + required uint8 collector_status; + required Timestamp timestamp; + required BatteryData battery_data; +} + +message StartMicrophoneResponse { + required Timestamp timestamp; +} + +message StopMicrophoneResponse { +} + +message StartScanResponse { + required Timestamp timestamp; +} + +message StopScanResponse { +} + +message MicrophoneDataResponseHeader { + required Timestamp timestamp; + required BatteryData battery_data; + required uint16 sample_period_ms; +} + +message MicrophoneDataResponse { + required MicrophoneDataResponseHeader microphone_data_response_header; + repeated MicrophoneData microphone_data [PROTOCOL_MICROPHONE_DATA_SIZE]; +} + +message ScanDataResponseHeader { + required uint32 timestamp_seconds; + required BatteryData battery_data; +} + + +message ScanDataResponse { + required ScanDataResponseHeader scan_data_response_header; + repeated ScanResultData scan_result_data[PROTOCOL_SCAN_DATA_SIZE]; +} + +message IdentifyResponse { +} + +message Response { + oneof type { + StatusResponse status_response (1); + StartMicrophoneResponse start_microphone_response (2); + StopMicrophoneResponse stop_microphone_response (3); + StartScanResponse start_scan_response (4); + StopScanResponse stop_scan_response (5); + MicrophoneDataResponse microphone_data_response (6); + ScanDataResponse scan_data_response (7); + IdentifyResponse identify_response (8); + } +} diff --git a/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.c b/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.c new file mode 100644 index 0000000..0569520 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.c @@ -0,0 +1,315 @@ +#include "tinybuf.h" +#include "protocol_messages_02v1.h" + +//#define PROTOCOL_02v1 +#ifdef PROTOCOL_02v1 + +const tb_field_t StatusRequest_fields[3] = { + {513, tb_offsetof(StatusRequest, timestamp), 0, 0, tb_membersize(StatusRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {514, tb_offsetof(StatusRequest, badge_assignement), tb_delta(StatusRequest, has_badge_assignement, badge_assignement), 1, tb_membersize(StatusRequest, badge_assignement), 0, 0, 0, &BadgeAssignement_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartMicrophoneRequest_fields[4] = { + {513, tb_offsetof(StartMicrophoneRequest, timestamp), 0, 0, tb_membersize(StartMicrophoneRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartMicrophoneRequest, timeout), 0, 0, tb_membersize(StartMicrophoneRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartMicrophoneRequest, period_ms), 0, 0, tb_membersize(StartMicrophoneRequest, period_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopMicrophoneRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartScanRequest_fields[8] = { + {513, tb_offsetof(StartScanRequest, timestamp), 0, 0, tb_membersize(StartScanRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartScanRequest, timeout), 0, 0, tb_membersize(StartScanRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, window), 0, 0, tb_membersize(StartScanRequest, window), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, interval), 0, 0, tb_membersize(StartScanRequest, interval), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, duration), 0, 0, tb_membersize(StartScanRequest, duration), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, period), 0, 0, tb_membersize(StartScanRequest, period), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanRequest, aggregation_type), 0, 0, tb_membersize(StartScanRequest, aggregation_type), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopScanRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartAccelerometerRequest_fields[7] = { + {513, tb_offsetof(StartAccelerometerRequest, timestamp), 0, 0, tb_membersize(StartAccelerometerRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartAccelerometerRequest, timeout), 0, 0, tb_membersize(StartAccelerometerRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerRequest, operating_mode), 0, 0, tb_membersize(StartAccelerometerRequest, operating_mode), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerRequest, full_scale), 0, 0, tb_membersize(StartAccelerometerRequest, full_scale), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerRequest, datarate), 0, 0, tb_membersize(StartAccelerometerRequest, datarate), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerRequest, fifo_sampling_period_ms), 0, 0, tb_membersize(StartAccelerometerRequest, fifo_sampling_period_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopAccelerometerRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartAccelerometerInterruptRequest_fields[6] = { + {513, tb_offsetof(StartAccelerometerInterruptRequest, timestamp), 0, 0, tb_membersize(StartAccelerometerInterruptRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartAccelerometerInterruptRequest, timeout), 0, 0, tb_membersize(StartAccelerometerInterruptRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerInterruptRequest, threshold_mg), 0, 0, tb_membersize(StartAccelerometerInterruptRequest, threshold_mg), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerInterruptRequest, minimal_duration_ms), 0, 0, tb_membersize(StartAccelerometerInterruptRequest, minimal_duration_ms), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerInterruptRequest, ignore_duration_ms), 0, 0, tb_membersize(StartAccelerometerInterruptRequest, ignore_duration_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopAccelerometerInterruptRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartBatteryRequest_fields[4] = { + {513, tb_offsetof(StartBatteryRequest, timestamp), 0, 0, tb_membersize(StartBatteryRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartBatteryRequest, timeout), 0, 0, tb_membersize(StartBatteryRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartBatteryRequest, period_ms), 0, 0, tb_membersize(StartBatteryRequest, period_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopBatteryRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneDataRequest_fields[2] = { + {513, tb_offsetof(MicrophoneDataRequest, timestamp), 0, 0, tb_membersize(MicrophoneDataRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanDataRequest_fields[2] = { + {513, tb_offsetof(ScanDataRequest, timestamp), 0, 0, tb_membersize(ScanDataRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerDataRequest_fields[2] = { + {513, tb_offsetof(AccelerometerDataRequest, timestamp), 0, 0, tb_membersize(AccelerometerDataRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerInterruptDataRequest_fields[2] = { + {513, tb_offsetof(AccelerometerInterruptDataRequest, timestamp), 0, 0, tb_membersize(AccelerometerInterruptDataRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t BatteryDataRequest_fields[2] = { + {513, tb_offsetof(BatteryDataRequest, timestamp), 0, 0, tb_membersize(BatteryDataRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartMicrophoneStreamRequest_fields[4] = { + {513, tb_offsetof(StartMicrophoneStreamRequest, timestamp), 0, 0, tb_membersize(StartMicrophoneStreamRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartMicrophoneStreamRequest, timeout), 0, 0, tb_membersize(StartMicrophoneStreamRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartMicrophoneStreamRequest, period_ms), 0, 0, tb_membersize(StartMicrophoneStreamRequest, period_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopMicrophoneStreamRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartScanStreamRequest_fields[8] = { + {513, tb_offsetof(StartScanStreamRequest, timestamp), 0, 0, tb_membersize(StartScanStreamRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartScanStreamRequest, timeout), 0, 0, tb_membersize(StartScanStreamRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanStreamRequest, window), 0, 0, tb_membersize(StartScanStreamRequest, window), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanStreamRequest, interval), 0, 0, tb_membersize(StartScanStreamRequest, interval), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanStreamRequest, duration), 0, 0, tb_membersize(StartScanStreamRequest, duration), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanStreamRequest, period), 0, 0, tb_membersize(StartScanStreamRequest, period), 0, 0, 0, NULL}, + {65, tb_offsetof(StartScanStreamRequest, aggregation_type), 0, 0, tb_membersize(StartScanStreamRequest, aggregation_type), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopScanStreamRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartAccelerometerStreamRequest_fields[7] = { + {513, tb_offsetof(StartAccelerometerStreamRequest, timestamp), 0, 0, tb_membersize(StartAccelerometerStreamRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartAccelerometerStreamRequest, timeout), 0, 0, tb_membersize(StartAccelerometerStreamRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerStreamRequest, operating_mode), 0, 0, tb_membersize(StartAccelerometerStreamRequest, operating_mode), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerStreamRequest, full_scale), 0, 0, tb_membersize(StartAccelerometerStreamRequest, full_scale), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerStreamRequest, datarate), 0, 0, tb_membersize(StartAccelerometerStreamRequest, datarate), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerStreamRequest, fifo_sampling_period_ms), 0, 0, tb_membersize(StartAccelerometerStreamRequest, fifo_sampling_period_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopAccelerometerStreamRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartAccelerometerInterruptStreamRequest_fields[6] = { + {513, tb_offsetof(StartAccelerometerInterruptStreamRequest, timestamp), 0, 0, tb_membersize(StartAccelerometerInterruptStreamRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartAccelerometerInterruptStreamRequest, timeout), 0, 0, tb_membersize(StartAccelerometerInterruptStreamRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerInterruptStreamRequest, threshold_mg), 0, 0, tb_membersize(StartAccelerometerInterruptStreamRequest, threshold_mg), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerInterruptStreamRequest, minimal_duration_ms), 0, 0, tb_membersize(StartAccelerometerInterruptStreamRequest, minimal_duration_ms), 0, 0, 0, NULL}, + {65, tb_offsetof(StartAccelerometerInterruptStreamRequest, ignore_duration_ms), 0, 0, tb_membersize(StartAccelerometerInterruptStreamRequest, ignore_duration_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopAccelerometerInterruptStreamRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t StartBatteryStreamRequest_fields[4] = { + {513, tb_offsetof(StartBatteryStreamRequest, timestamp), 0, 0, tb_membersize(StartBatteryStreamRequest, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(StartBatteryStreamRequest, timeout), 0, 0, tb_membersize(StartBatteryStreamRequest, timeout), 0, 0, 0, NULL}, + {65, tb_offsetof(StartBatteryStreamRequest, period_ms), 0, 0, tb_membersize(StartBatteryStreamRequest, period_ms), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t StopBatteryStreamRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t IdentifyRequest_fields[2] = { + {65, tb_offsetof(IdentifyRequest, timeout), 0, 0, tb_membersize(IdentifyRequest, timeout), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t TestRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t RestartRequest_fields[1] = { + TB_LAST_FIELD, +}; + +const tb_field_t Request_fields[30] = { + {528, tb_offsetof(Request, type.status_request), tb_delta(Request, which_type, type.status_request), 1, tb_membersize(Request, type.status_request), 0, 1, 1, &StatusRequest_fields}, + {528, tb_offsetof(Request, type.start_microphone_request), tb_delta(Request, which_type, type.start_microphone_request), 1, tb_membersize(Request, type.start_microphone_request), 0, 2, 0, &StartMicrophoneRequest_fields}, + {528, tb_offsetof(Request, type.stop_microphone_request), tb_delta(Request, which_type, type.stop_microphone_request), 1, tb_membersize(Request, type.stop_microphone_request), 0, 3, 0, &StopMicrophoneRequest_fields}, + {528, tb_offsetof(Request, type.start_scan_request), tb_delta(Request, which_type, type.start_scan_request), 1, tb_membersize(Request, type.start_scan_request), 0, 4, 0, &StartScanRequest_fields}, + {528, tb_offsetof(Request, type.stop_scan_request), tb_delta(Request, which_type, type.stop_scan_request), 1, tb_membersize(Request, type.stop_scan_request), 0, 5, 0, &StopScanRequest_fields}, + {528, tb_offsetof(Request, type.start_accelerometer_request), tb_delta(Request, which_type, type.start_accelerometer_request), 1, tb_membersize(Request, type.start_accelerometer_request), 0, 6, 0, &StartAccelerometerRequest_fields}, + {528, tb_offsetof(Request, type.stop_accelerometer_request), tb_delta(Request, which_type, type.stop_accelerometer_request), 1, tb_membersize(Request, type.stop_accelerometer_request), 0, 7, 0, &StopAccelerometerRequest_fields}, + {528, tb_offsetof(Request, type.start_accelerometer_interrupt_request), tb_delta(Request, which_type, type.start_accelerometer_interrupt_request), 1, tb_membersize(Request, type.start_accelerometer_interrupt_request), 0, 8, 0, &StartAccelerometerInterruptRequest_fields}, + {528, tb_offsetof(Request, type.stop_accelerometer_interrupt_request), tb_delta(Request, which_type, type.stop_accelerometer_interrupt_request), 1, tb_membersize(Request, type.stop_accelerometer_interrupt_request), 0, 9, 0, &StopAccelerometerInterruptRequest_fields}, + {528, tb_offsetof(Request, type.start_battery_request), tb_delta(Request, which_type, type.start_battery_request), 1, tb_membersize(Request, type.start_battery_request), 0, 10, 0, &StartBatteryRequest_fields}, + {528, tb_offsetof(Request, type.stop_battery_request), tb_delta(Request, which_type, type.stop_battery_request), 1, tb_membersize(Request, type.stop_battery_request), 0, 11, 0, &StopBatteryRequest_fields}, + {528, tb_offsetof(Request, type.microphone_data_request), tb_delta(Request, which_type, type.microphone_data_request), 1, tb_membersize(Request, type.microphone_data_request), 0, 12, 0, &MicrophoneDataRequest_fields}, + {528, tb_offsetof(Request, type.scan_data_request), tb_delta(Request, which_type, type.scan_data_request), 1, tb_membersize(Request, type.scan_data_request), 0, 13, 0, &ScanDataRequest_fields}, + {528, tb_offsetof(Request, type.accelerometer_data_request), tb_delta(Request, which_type, type.accelerometer_data_request), 1, tb_membersize(Request, type.accelerometer_data_request), 0, 14, 0, &AccelerometerDataRequest_fields}, + {528, tb_offsetof(Request, type.accelerometer_interrupt_data_request), tb_delta(Request, which_type, type.accelerometer_interrupt_data_request), 1, tb_membersize(Request, type.accelerometer_interrupt_data_request), 0, 15, 0, &AccelerometerInterruptDataRequest_fields}, + {528, tb_offsetof(Request, type.battery_data_request), tb_delta(Request, which_type, type.battery_data_request), 1, tb_membersize(Request, type.battery_data_request), 0, 16, 0, &BatteryDataRequest_fields}, + {528, tb_offsetof(Request, type.start_microphone_stream_request), tb_delta(Request, which_type, type.start_microphone_stream_request), 1, tb_membersize(Request, type.start_microphone_stream_request), 0, 17, 0, &StartMicrophoneStreamRequest_fields}, + {528, tb_offsetof(Request, type.stop_microphone_stream_request), tb_delta(Request, which_type, type.stop_microphone_stream_request), 1, tb_membersize(Request, type.stop_microphone_stream_request), 0, 18, 0, &StopMicrophoneStreamRequest_fields}, + {528, tb_offsetof(Request, type.start_scan_stream_request), tb_delta(Request, which_type, type.start_scan_stream_request), 1, tb_membersize(Request, type.start_scan_stream_request), 0, 19, 0, &StartScanStreamRequest_fields}, + {528, tb_offsetof(Request, type.stop_scan_stream_request), tb_delta(Request, which_type, type.stop_scan_stream_request), 1, tb_membersize(Request, type.stop_scan_stream_request), 0, 20, 0, &StopScanStreamRequest_fields}, + {528, tb_offsetof(Request, type.start_accelerometer_stream_request), tb_delta(Request, which_type, type.start_accelerometer_stream_request), 1, tb_membersize(Request, type.start_accelerometer_stream_request), 0, 21, 0, &StartAccelerometerStreamRequest_fields}, + {528, tb_offsetof(Request, type.stop_accelerometer_stream_request), tb_delta(Request, which_type, type.stop_accelerometer_stream_request), 1, tb_membersize(Request, type.stop_accelerometer_stream_request), 0, 22, 0, &StopAccelerometerStreamRequest_fields}, + {528, tb_offsetof(Request, type.start_accelerometer_interrupt_stream_request), tb_delta(Request, which_type, type.start_accelerometer_interrupt_stream_request), 1, tb_membersize(Request, type.start_accelerometer_interrupt_stream_request), 0, 23, 0, &StartAccelerometerInterruptStreamRequest_fields}, + {528, tb_offsetof(Request, type.stop_accelerometer_interrupt_stream_request), tb_delta(Request, which_type, type.stop_accelerometer_interrupt_stream_request), 1, tb_membersize(Request, type.stop_accelerometer_interrupt_stream_request), 0, 24, 0, &StopAccelerometerInterruptStreamRequest_fields}, + {528, tb_offsetof(Request, type.start_battery_stream_request), tb_delta(Request, which_type, type.start_battery_stream_request), 1, tb_membersize(Request, type.start_battery_stream_request), 0, 25, 0, &StartBatteryStreamRequest_fields}, + {528, tb_offsetof(Request, type.stop_battery_stream_request), tb_delta(Request, which_type, type.stop_battery_stream_request), 1, tb_membersize(Request, type.stop_battery_stream_request), 0, 26, 0, &StopBatteryStreamRequest_fields}, + {528, tb_offsetof(Request, type.identify_request), tb_delta(Request, which_type, type.identify_request), 1, tb_membersize(Request, type.identify_request), 0, 27, 0, &IdentifyRequest_fields}, + {528, tb_offsetof(Request, type.test_request), tb_delta(Request, which_type, type.test_request), 1, tb_membersize(Request, type.test_request), 0, 28, 0, &TestRequest_fields}, + {528, tb_offsetof(Request, type.restart_request), tb_delta(Request, which_type, type.restart_request), 1, tb_membersize(Request, type.restart_request), 0, 29, 0, &RestartRequest_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StatusResponse_fields[9] = { + {65, tb_offsetof(StatusResponse, clock_status), 0, 0, tb_membersize(StatusResponse, clock_status), 0, 0, 0, NULL}, + {65, tb_offsetof(StatusResponse, microphone_status), 0, 0, tb_membersize(StatusResponse, microphone_status), 0, 0, 0, NULL}, + {65, tb_offsetof(StatusResponse, scan_status), 0, 0, tb_membersize(StatusResponse, scan_status), 0, 0, 0, NULL}, + {65, tb_offsetof(StatusResponse, accelerometer_status), 0, 0, tb_membersize(StatusResponse, accelerometer_status), 0, 0, 0, NULL}, + {65, tb_offsetof(StatusResponse, accelerometer_interrupt_status), 0, 0, tb_membersize(StatusResponse, accelerometer_interrupt_status), 0, 0, 0, NULL}, + {65, tb_offsetof(StatusResponse, battery_status), 0, 0, tb_membersize(StatusResponse, battery_status), 0, 0, 0, NULL}, + {513, tb_offsetof(StatusResponse, timestamp), 0, 0, tb_membersize(StatusResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + {513, tb_offsetof(StatusResponse, battery_data), 0, 0, tb_membersize(StatusResponse, battery_data), 0, 0, 0, &BatteryData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartMicrophoneResponse_fields[2] = { + {513, tb_offsetof(StartMicrophoneResponse, timestamp), 0, 0, tb_membersize(StartMicrophoneResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartScanResponse_fields[2] = { + {513, tb_offsetof(StartScanResponse, timestamp), 0, 0, tb_membersize(StartScanResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartAccelerometerResponse_fields[2] = { + {513, tb_offsetof(StartAccelerometerResponse, timestamp), 0, 0, tb_membersize(StartAccelerometerResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartAccelerometerInterruptResponse_fields[2] = { + {513, tb_offsetof(StartAccelerometerInterruptResponse, timestamp), 0, 0, tb_membersize(StartAccelerometerInterruptResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StartBatteryResponse_fields[2] = { + {513, tb_offsetof(StartBatteryResponse, timestamp), 0, 0, tb_membersize(StartBatteryResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneDataResponse_fields[5] = { + {65, tb_offsetof(MicrophoneDataResponse, last_response), 0, 0, tb_membersize(MicrophoneDataResponse, last_response), 0, 0, 0, NULL}, + {513, tb_offsetof(MicrophoneDataResponse, timestamp), 0, 0, tb_membersize(MicrophoneDataResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + {65, tb_offsetof(MicrophoneDataResponse, sample_period_ms), 0, 0, tb_membersize(MicrophoneDataResponse, sample_period_ms), 0, 0, 0, NULL}, + {516, tb_offsetof(MicrophoneDataResponse, microphone_data), tb_delta(MicrophoneDataResponse, microphone_data_count, microphone_data), 1, tb_membersize(MicrophoneDataResponse, microphone_data[0]), tb_membersize(MicrophoneDataResponse, microphone_data)/tb_membersize(MicrophoneDataResponse, microphone_data[0]), 0, 0, &MicrophoneData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanDataResponse_fields[4] = { + {65, tb_offsetof(ScanDataResponse, last_response), 0, 0, tb_membersize(ScanDataResponse, last_response), 0, 0, 0, NULL}, + {513, tb_offsetof(ScanDataResponse, timestamp), 0, 0, tb_membersize(ScanDataResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + {516, tb_offsetof(ScanDataResponse, scan_result_data), tb_delta(ScanDataResponse, scan_result_data_count, scan_result_data), 1, tb_membersize(ScanDataResponse, scan_result_data[0]), tb_membersize(ScanDataResponse, scan_result_data)/tb_membersize(ScanDataResponse, scan_result_data[0]), 0, 0, &ScanResultData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerDataResponse_fields[4] = { + {65, tb_offsetof(AccelerometerDataResponse, last_response), 0, 0, tb_membersize(AccelerometerDataResponse, last_response), 0, 0, 0, NULL}, + {513, tb_offsetof(AccelerometerDataResponse, timestamp), 0, 0, tb_membersize(AccelerometerDataResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + {516, tb_offsetof(AccelerometerDataResponse, accelerometer_data), tb_delta(AccelerometerDataResponse, accelerometer_data_count, accelerometer_data), 1, tb_membersize(AccelerometerDataResponse, accelerometer_data[0]), tb_membersize(AccelerometerDataResponse, accelerometer_data)/tb_membersize(AccelerometerDataResponse, accelerometer_data[0]), 0, 0, &AccelerometerData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerInterruptDataResponse_fields[3] = { + {65, tb_offsetof(AccelerometerInterruptDataResponse, last_response), 0, 0, tb_membersize(AccelerometerInterruptDataResponse, last_response), 0, 0, 0, NULL}, + {513, tb_offsetof(AccelerometerInterruptDataResponse, timestamp), 0, 0, tb_membersize(AccelerometerInterruptDataResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t BatteryDataResponse_fields[4] = { + {65, tb_offsetof(BatteryDataResponse, last_response), 0, 0, tb_membersize(BatteryDataResponse, last_response), 0, 0, 0, NULL}, + {513, tb_offsetof(BatteryDataResponse, timestamp), 0, 0, tb_membersize(BatteryDataResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + {513, tb_offsetof(BatteryDataResponse, battery_data), 0, 0, tb_membersize(BatteryDataResponse, battery_data), 0, 0, 0, &BatteryData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t StreamResponse_fields[7] = { + {513, tb_offsetof(StreamResponse, timestamp), 0, 0, tb_membersize(StreamResponse, timestamp), 0, 0, 0, &Timestamp_fields}, + {516, tb_offsetof(StreamResponse, battery_stream), tb_delta(StreamResponse, battery_stream_count, battery_stream), 1, tb_membersize(StreamResponse, battery_stream[0]), tb_membersize(StreamResponse, battery_stream)/tb_membersize(StreamResponse, battery_stream[0]), 0, 0, &BatteryStream_fields}, + {516, tb_offsetof(StreamResponse, microphone_stream), tb_delta(StreamResponse, microphone_stream_count, microphone_stream), 1, tb_membersize(StreamResponse, microphone_stream[0]), tb_membersize(StreamResponse, microphone_stream)/tb_membersize(StreamResponse, microphone_stream[0]), 0, 0, &MicrophoneStream_fields}, + {516, tb_offsetof(StreamResponse, scan_stream), tb_delta(StreamResponse, scan_stream_count, scan_stream), 1, tb_membersize(StreamResponse, scan_stream[0]), tb_membersize(StreamResponse, scan_stream)/tb_membersize(StreamResponse, scan_stream[0]), 0, 0, &ScanStream_fields}, + {516, tb_offsetof(StreamResponse, accelerometer_stream), tb_delta(StreamResponse, accelerometer_stream_count, accelerometer_stream), 1, tb_membersize(StreamResponse, accelerometer_stream[0]), tb_membersize(StreamResponse, accelerometer_stream)/tb_membersize(StreamResponse, accelerometer_stream[0]), 0, 0, &AccelerometerStream_fields}, + {516, tb_offsetof(StreamResponse, accelerometer_interrupt_stream), tb_delta(StreamResponse, accelerometer_interrupt_stream_count, accelerometer_interrupt_stream), 1, tb_membersize(StreamResponse, accelerometer_interrupt_stream[0]), tb_membersize(StreamResponse, accelerometer_interrupt_stream)/tb_membersize(StreamResponse, accelerometer_interrupt_stream[0]), 0, 0, &AccelerometerInterruptStream_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t TestResponse_fields[2] = { + {65, tb_offsetof(TestResponse, test_failed), 0, 0, tb_membersize(TestResponse, test_failed), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t Response_fields[14] = { + {528, tb_offsetof(Response, type.status_response), tb_delta(Response, which_type, type.status_response), 1, tb_membersize(Response, type.status_response), 0, 1, 1, &StatusResponse_fields}, + {528, tb_offsetof(Response, type.start_microphone_response), tb_delta(Response, which_type, type.start_microphone_response), 1, tb_membersize(Response, type.start_microphone_response), 0, 2, 0, &StartMicrophoneResponse_fields}, + {528, tb_offsetof(Response, type.start_scan_response), tb_delta(Response, which_type, type.start_scan_response), 1, tb_membersize(Response, type.start_scan_response), 0, 3, 0, &StartScanResponse_fields}, + {528, tb_offsetof(Response, type.start_accelerometer_response), tb_delta(Response, which_type, type.start_accelerometer_response), 1, tb_membersize(Response, type.start_accelerometer_response), 0, 4, 0, &StartAccelerometerResponse_fields}, + {528, tb_offsetof(Response, type.start_accelerometer_interrupt_response), tb_delta(Response, which_type, type.start_accelerometer_interrupt_response), 1, tb_membersize(Response, type.start_accelerometer_interrupt_response), 0, 5, 0, &StartAccelerometerInterruptResponse_fields}, + {528, tb_offsetof(Response, type.start_battery_response), tb_delta(Response, which_type, type.start_battery_response), 1, tb_membersize(Response, type.start_battery_response), 0, 6, 0, &StartBatteryResponse_fields}, + {528, tb_offsetof(Response, type.microphone_data_response), tb_delta(Response, which_type, type.microphone_data_response), 1, tb_membersize(Response, type.microphone_data_response), 0, 7, 0, &MicrophoneDataResponse_fields}, + {528, tb_offsetof(Response, type.scan_data_response), tb_delta(Response, which_type, type.scan_data_response), 1, tb_membersize(Response, type.scan_data_response), 0, 8, 0, &ScanDataResponse_fields}, + {528, tb_offsetof(Response, type.accelerometer_data_response), tb_delta(Response, which_type, type.accelerometer_data_response), 1, tb_membersize(Response, type.accelerometer_data_response), 0, 9, 0, &AccelerometerDataResponse_fields}, + {528, tb_offsetof(Response, type.accelerometer_interrupt_data_response), tb_delta(Response, which_type, type.accelerometer_interrupt_data_response), 1, tb_membersize(Response, type.accelerometer_interrupt_data_response), 0, 10, 0, &AccelerometerInterruptDataResponse_fields}, + {528, tb_offsetof(Response, type.battery_data_response), tb_delta(Response, which_type, type.battery_data_response), 1, tb_membersize(Response, type.battery_data_response), 0, 11, 0, &BatteryDataResponse_fields}, + {528, tb_offsetof(Response, type.stream_response), tb_delta(Response, which_type, type.stream_response), 1, tb_membersize(Response, type.stream_response), 0, 12, 0, &StreamResponse_fields}, + {528, tb_offsetof(Response, type.test_response), tb_delta(Response, which_type, type.test_response), 1, tb_membersize(Response, type.test_response), 0, 13, 0, &TestResponse_fields}, + TB_LAST_FIELD, +}; +#endif diff --git a/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.h b/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.h new file mode 100644 index 0000000..a2b4704 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.h @@ -0,0 +1,386 @@ +#ifndef __PROTOCOL_MESSAGES_02V1_H +#define __PROTOCOL_MESSAGES_02V1_H + +#include +#include "tinybuf.h" +#include "common_messages.h" +#include "stream_messages.h" + +#define PROTOCOL_MICROPHONE_DATA_SIZE 114 +#define PROTOCOL_SCAN_DATA_SIZE 29 +#define PROTOCOL_ACCELEROMETER_DATA_SIZE 100 +#define PROTOCOL_MICROPHONE_STREAM_SIZE 10 +#define PROTOCOL_SCAN_STREAM_SIZE 10 +#define PROTOCOL_ACCELEROMETER_STREAM_SIZE 10 +#define PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE 10 +#define PROTOCOL_BATTERY_STREAM_SIZE 10 + +#define Request_status_request_tag 1 +#define Request_start_microphone_request_tag 2 +#define Request_stop_microphone_request_tag 3 +#define Request_start_scan_request_tag 4 +#define Request_stop_scan_request_tag 5 +#define Request_start_accelerometer_request_tag 6 +#define Request_stop_accelerometer_request_tag 7 +#define Request_start_accelerometer_interrupt_request_tag 8 +#define Request_stop_accelerometer_interrupt_request_tag 9 +#define Request_start_battery_request_tag 10 +#define Request_stop_battery_request_tag 11 +#define Request_microphone_data_request_tag 12 +#define Request_scan_data_request_tag 13 +#define Request_accelerometer_data_request_tag 14 +#define Request_accelerometer_interrupt_data_request_tag 15 +#define Request_battery_data_request_tag 16 +#define Request_start_microphone_stream_request_tag 17 +#define Request_stop_microphone_stream_request_tag 18 +#define Request_start_scan_stream_request_tag 19 +#define Request_stop_scan_stream_request_tag 20 +#define Request_start_accelerometer_stream_request_tag 21 +#define Request_stop_accelerometer_stream_request_tag 22 +#define Request_start_accelerometer_interrupt_stream_request_tag 23 +#define Request_stop_accelerometer_interrupt_stream_request_tag 24 +#define Request_start_battery_stream_request_tag 25 +#define Request_stop_battery_stream_request_tag 26 +#define Request_identify_request_tag 27 +#define Request_test_request_tag 28 +#define Request_restart_request_tag 29 +#define Response_status_response_tag 1 +#define Response_start_microphone_response_tag 2 +#define Response_start_scan_response_tag 3 +#define Response_start_accelerometer_response_tag 4 +#define Response_start_accelerometer_interrupt_response_tag 5 +#define Response_start_battery_response_tag 6 +#define Response_microphone_data_response_tag 7 +#define Response_scan_data_response_tag 8 +#define Response_accelerometer_data_response_tag 9 +#define Response_accelerometer_interrupt_data_response_tag 10 +#define Response_battery_data_response_tag 11 +#define Response_stream_response_tag 12 +#define Response_test_response_tag 13 + +typedef struct { + Timestamp timestamp; + uint8_t has_badge_assignement; + BadgeAssignement badge_assignement; +} StatusRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint16_t period_ms; +} StartMicrophoneRequest; + +typedef struct { +} StopMicrophoneRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint16_t window; + uint16_t interval; + uint16_t duration; + uint16_t period; + uint8_t aggregation_type; +} StartScanRequest; + +typedef struct { +} StopScanRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint8_t operating_mode; + uint8_t full_scale; + uint16_t datarate; + uint16_t fifo_sampling_period_ms; +} StartAccelerometerRequest; + +typedef struct { +} StopAccelerometerRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint16_t threshold_mg; + uint16_t minimal_duration_ms; + uint32_t ignore_duration_ms; +} StartAccelerometerInterruptRequest; + +typedef struct { +} StopAccelerometerInterruptRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint32_t period_ms; +} StartBatteryRequest; + +typedef struct { +} StopBatteryRequest; + +typedef struct { + Timestamp timestamp; +} MicrophoneDataRequest; + +typedef struct { + Timestamp timestamp; +} ScanDataRequest; + +typedef struct { + Timestamp timestamp; +} AccelerometerDataRequest; + +typedef struct { + Timestamp timestamp; +} AccelerometerInterruptDataRequest; + +typedef struct { + Timestamp timestamp; +} BatteryDataRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint16_t period_ms; +} StartMicrophoneStreamRequest; + +typedef struct { +} StopMicrophoneStreamRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint16_t window; + uint16_t interval; + uint16_t duration; + uint16_t period; + uint8_t aggregation_type; +} StartScanStreamRequest; + +typedef struct { +} StopScanStreamRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint8_t operating_mode; + uint8_t full_scale; + uint16_t datarate; + uint16_t fifo_sampling_period_ms; +} StartAccelerometerStreamRequest; + +typedef struct { +} StopAccelerometerStreamRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint16_t threshold_mg; + uint16_t minimal_duration_ms; + uint32_t ignore_duration_ms; +} StartAccelerometerInterruptStreamRequest; + +typedef struct { +} StopAccelerometerInterruptStreamRequest; + +typedef struct { + Timestamp timestamp; + uint16_t timeout; + uint32_t period_ms; +} StartBatteryStreamRequest; + +typedef struct { +} StopBatteryStreamRequest; + +typedef struct { + uint16_t timeout; +} IdentifyRequest; + +typedef struct { +} TestRequest; + +typedef struct { +} RestartRequest; + +typedef struct { + uint8_t which_type; + union { + StatusRequest status_request; + StartMicrophoneRequest start_microphone_request; + StopMicrophoneRequest stop_microphone_request; + StartScanRequest start_scan_request; + StopScanRequest stop_scan_request; + StartAccelerometerRequest start_accelerometer_request; + StopAccelerometerRequest stop_accelerometer_request; + StartAccelerometerInterruptRequest start_accelerometer_interrupt_request; + StopAccelerometerInterruptRequest stop_accelerometer_interrupt_request; + StartBatteryRequest start_battery_request; + StopBatteryRequest stop_battery_request; + MicrophoneDataRequest microphone_data_request; + ScanDataRequest scan_data_request; + AccelerometerDataRequest accelerometer_data_request; + AccelerometerInterruptDataRequest accelerometer_interrupt_data_request; + BatteryDataRequest battery_data_request; + StartMicrophoneStreamRequest start_microphone_stream_request; + StopMicrophoneStreamRequest stop_microphone_stream_request; + StartScanStreamRequest start_scan_stream_request; + StopScanStreamRequest stop_scan_stream_request; + StartAccelerometerStreamRequest start_accelerometer_stream_request; + StopAccelerometerStreamRequest stop_accelerometer_stream_request; + StartAccelerometerInterruptStreamRequest start_accelerometer_interrupt_stream_request; + StopAccelerometerInterruptStreamRequest stop_accelerometer_interrupt_stream_request; + StartBatteryStreamRequest start_battery_stream_request; + StopBatteryStreamRequest stop_battery_stream_request; + IdentifyRequest identify_request; + TestRequest test_request; + RestartRequest restart_request; + } type; +} Request; + +typedef struct { + uint8_t clock_status; + uint8_t microphone_status; + uint8_t scan_status; + uint8_t accelerometer_status; + uint8_t accelerometer_interrupt_status; + uint8_t battery_status; + Timestamp timestamp; + BatteryData battery_data; +} StatusResponse; + +typedef struct { + Timestamp timestamp; +} StartMicrophoneResponse; + +typedef struct { + Timestamp timestamp; +} StartScanResponse; + +typedef struct { + Timestamp timestamp; +} StartAccelerometerResponse; + +typedef struct { + Timestamp timestamp; +} StartAccelerometerInterruptResponse; + +typedef struct { + Timestamp timestamp; +} StartBatteryResponse; + +typedef struct { + uint8_t last_response; + Timestamp timestamp; + uint16_t sample_period_ms; + uint8_t microphone_data_count; + MicrophoneData microphone_data[114]; +} MicrophoneDataResponse; + +typedef struct { + uint8_t last_response; + Timestamp timestamp; + uint8_t scan_result_data_count; + ScanResultData scan_result_data[29]; +} ScanDataResponse; + +typedef struct { + uint8_t last_response; + Timestamp timestamp; + uint8_t accelerometer_data_count; + AccelerometerData accelerometer_data[100]; +} AccelerometerDataResponse; + +typedef struct { + uint8_t last_response; + Timestamp timestamp; +} AccelerometerInterruptDataResponse; + +typedef struct { + uint8_t last_response; + Timestamp timestamp; + BatteryData battery_data; +} BatteryDataResponse; + +typedef struct { + Timestamp timestamp; + uint8_t battery_stream_count; + BatteryStream battery_stream[10]; + uint8_t microphone_stream_count; + MicrophoneStream microphone_stream[10]; + uint8_t scan_stream_count; + ScanStream scan_stream[10]; + uint8_t accelerometer_stream_count; + AccelerometerStream accelerometer_stream[10]; + uint8_t accelerometer_interrupt_stream_count; + AccelerometerInterruptStream accelerometer_interrupt_stream[10]; +} StreamResponse; + +typedef struct { + uint8_t test_failed; +} TestResponse; + +typedef struct { + uint8_t which_type; + union { + StatusResponse status_response; + StartMicrophoneResponse start_microphone_response; + StartScanResponse start_scan_response; + StartAccelerometerResponse start_accelerometer_response; + StartAccelerometerInterruptResponse start_accelerometer_interrupt_response; + StartBatteryResponse start_battery_response; + MicrophoneDataResponse microphone_data_response; + ScanDataResponse scan_data_response; + AccelerometerDataResponse accelerometer_data_response; + AccelerometerInterruptDataResponse accelerometer_interrupt_data_response; + BatteryDataResponse battery_data_response; + StreamResponse stream_response; + TestResponse test_response; + } type; +} Response; + +extern const tb_field_t StatusRequest_fields[3]; +extern const tb_field_t StartMicrophoneRequest_fields[4]; +extern const tb_field_t StopMicrophoneRequest_fields[1]; +extern const tb_field_t StartScanRequest_fields[8]; +extern const tb_field_t StopScanRequest_fields[1]; +extern const tb_field_t StartAccelerometerRequest_fields[7]; +extern const tb_field_t StopAccelerometerRequest_fields[1]; +extern const tb_field_t StartAccelerometerInterruptRequest_fields[6]; +extern const tb_field_t StopAccelerometerInterruptRequest_fields[1]; +extern const tb_field_t StartBatteryRequest_fields[4]; +extern const tb_field_t StopBatteryRequest_fields[1]; +extern const tb_field_t MicrophoneDataRequest_fields[2]; +extern const tb_field_t ScanDataRequest_fields[2]; +extern const tb_field_t AccelerometerDataRequest_fields[2]; +extern const tb_field_t AccelerometerInterruptDataRequest_fields[2]; +extern const tb_field_t BatteryDataRequest_fields[2]; +extern const tb_field_t StartMicrophoneStreamRequest_fields[4]; +extern const tb_field_t StopMicrophoneStreamRequest_fields[1]; +extern const tb_field_t StartScanStreamRequest_fields[8]; +extern const tb_field_t StopScanStreamRequest_fields[1]; +extern const tb_field_t StartAccelerometerStreamRequest_fields[7]; +extern const tb_field_t StopAccelerometerStreamRequest_fields[1]; +extern const tb_field_t StartAccelerometerInterruptStreamRequest_fields[6]; +extern const tb_field_t StopAccelerometerInterruptStreamRequest_fields[1]; +extern const tb_field_t StartBatteryStreamRequest_fields[4]; +extern const tb_field_t StopBatteryStreamRequest_fields[1]; +extern const tb_field_t IdentifyRequest_fields[2]; +extern const tb_field_t TestRequest_fields[1]; +extern const tb_field_t RestartRequest_fields[1]; +extern const tb_field_t Request_fields[30]; +extern const tb_field_t StatusResponse_fields[9]; +extern const tb_field_t StartMicrophoneResponse_fields[2]; +extern const tb_field_t StartScanResponse_fields[2]; +extern const tb_field_t StartAccelerometerResponse_fields[2]; +extern const tb_field_t StartAccelerometerInterruptResponse_fields[2]; +extern const tb_field_t StartBatteryResponse_fields[2]; +extern const tb_field_t MicrophoneDataResponse_fields[5]; +extern const tb_field_t ScanDataResponse_fields[4]; +extern const tb_field_t AccelerometerDataResponse_fields[4]; +extern const tb_field_t AccelerometerInterruptDataResponse_fields[3]; +extern const tb_field_t BatteryDataResponse_fields[4]; +extern const tb_field_t StreamResponse_fields[7]; +extern const tb_field_t TestResponse_fields[2]; +extern const tb_field_t Response_fields[14]; + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.tb b/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.tb new file mode 100644 index 0000000..d2b114b --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/protocol_messages_02v1.tb @@ -0,0 +1,324 @@ +import common_messages +import stream_messages + +extern Timestamp; +extern BadgeAssignement; +extern BatteryData; +extern MicrophoneData; +extern ScanResultData; +extern AccelerometerData; +extern BatteryStream; +extern MicrophoneStream; +extern ScanStream; +extern AccelerometerStream; +extern AccelerometerInterruptStream; + +define { + PROTOCOL_MICROPHONE_DATA_SIZE = 114; + PROTOCOL_SCAN_DATA_SIZE = 29; + PROTOCOL_ACCELEROMETER_DATA_SIZE = 100; +} + +define { + PROTOCOL_MICROPHONE_STREAM_SIZE = 10; + PROTOCOL_SCAN_STREAM_SIZE = 10; + PROTOCOL_ACCELEROMETER_STREAM_SIZE = 10; + PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE = 10; + PROTOCOL_BATTERY_STREAM_SIZE = 10; +} + + +message StatusRequest { + required Timestamp timestamp; + optional BadgeAssignement badge_assignement; +} + +message StartMicrophoneRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 period_ms; +} + +message StopMicrophoneRequest { +} + +message StartScanRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 window; + required uint16 interval; + required uint16 duration; + required uint16 period; + required uint8 aggregation_type; +} + +message StopScanRequest { +} + +message StartAccelerometerRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint8 operating_mode; + required uint8 full_scale; + required uint16 datarate; + required uint16 fifo_sampling_period_ms; +} + +message StopAccelerometerRequest { +} + + +message StartAccelerometerInterruptRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 threshold_mg; + required uint16 minimal_duration_ms; + required uint32 ignore_duration_ms; +} + +message StopAccelerometerInterruptRequest { +} + +message StartBatteryRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint32 period_ms; +} + +message StopBatteryRequest { +} + + + +message MicrophoneDataRequest { + required Timestamp timestamp; +} + +message ScanDataRequest { + required Timestamp timestamp; +} + +message AccelerometerDataRequest { + required Timestamp timestamp; +} + +message AccelerometerInterruptDataRequest { + required Timestamp timestamp; +} + +message BatteryDataRequest { + required Timestamp timestamp; +} + + + +message StartMicrophoneStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 period_ms; +} + +message StopMicrophoneStreamRequest { +} + +message StartScanStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 window; + required uint16 interval; + required uint16 duration; + required uint16 period; + required uint8 aggregation_type; +} + +message StopScanStreamRequest { +} + +message StartAccelerometerStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint8 operating_mode; + required uint8 full_scale; + required uint16 datarate; + required uint16 fifo_sampling_period_ms; +} + +message StopAccelerometerStreamRequest { +} + + +message StartAccelerometerInterruptStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint16 threshold_mg; + required uint16 minimal_duration_ms; + required uint32 ignore_duration_ms; +} + +message StopAccelerometerInterruptStreamRequest { +} + +message StartBatteryStreamRequest { + required Timestamp timestamp; + required uint16 timeout; + required uint32 period_ms; +} + +message StopBatteryStreamRequest { +} + + +message IdentifyRequest { + required uint16 timeout; +} + +message TestRequest { +} + +message RestartRequest { +} + +message Request { + oneof type { + StatusRequest status_request (1); + StartMicrophoneRequest start_microphone_request (2); + StopMicrophoneRequest stop_microphone_request (3); + StartScanRequest start_scan_request (4); + StopScanRequest stop_scan_request (5); + StartAccelerometerRequest start_accelerometer_request (6); + StopAccelerometerRequest stop_accelerometer_request (7); + StartAccelerometerInterruptRequest start_accelerometer_interrupt_request (8); + StopAccelerometerInterruptRequest stop_accelerometer_interrupt_request (9); + StartBatteryRequest start_battery_request (10); + StopBatteryRequest stop_battery_request (11); + MicrophoneDataRequest microphone_data_request (12); + ScanDataRequest scan_data_request (13); + AccelerometerDataRequest accelerometer_data_request (14); + AccelerometerInterruptDataRequest accelerometer_interrupt_data_request (15); + BatteryDataRequest battery_data_request (16); + StartMicrophoneStreamRequest start_microphone_stream_request (17); + StopMicrophoneStreamRequest stop_microphone_stream_request (18); + StartScanStreamRequest start_scan_stream_request (19); + StopScanStreamRequest stop_scan_stream_request (20); + StartAccelerometerStreamRequest start_accelerometer_stream_request (21); + StopAccelerometerStreamRequest stop_accelerometer_stream_request (22); + StartAccelerometerInterruptStreamRequest start_accelerometer_interrupt_stream_request (23); + StopAccelerometerInterruptStreamRequest stop_accelerometer_interrupt_stream_request (24); + StartBatteryStreamRequest start_battery_stream_request (25); + StopBatteryStreamRequest stop_battery_stream_request (26); + IdentifyRequest identify_request (27); + TestRequest test_request (28); + RestartRequest restart_request (29); + } +} + + + + + + + +message StatusResponse { + required uint8 clock_status; + required uint8 microphone_status; + required uint8 scan_status; + required uint8 accelerometer_status; + required uint8 accelerometer_interrupt_status; + required uint8 battery_status; + required Timestamp timestamp; + required BatteryData battery_data; +} + +message StartMicrophoneResponse { + required Timestamp timestamp; +} + + +message StartScanResponse { + required Timestamp timestamp; +} + + +message StartAccelerometerResponse { + required Timestamp timestamp; +} + + +message StartAccelerometerInterruptResponse { + required Timestamp timestamp; +} + + +message StartBatteryResponse { + required Timestamp timestamp; +} + + + + +message MicrophoneDataResponse { + required uint8 last_response; + required Timestamp timestamp; + required uint16 sample_period_ms; + repeated MicrophoneData microphone_data [PROTOCOL_MICROPHONE_DATA_SIZE]; +} + +message ScanDataResponse { + required uint8 last_response; + required Timestamp timestamp; + repeated ScanResultData scan_result_data[PROTOCOL_SCAN_DATA_SIZE]; +} + +message AccelerometerDataResponse { + required uint8 last_response; + required Timestamp timestamp; + repeated AccelerometerData accelerometer_data[PROTOCOL_ACCELEROMETER_DATA_SIZE]; +} + +message AccelerometerInterruptDataResponse { + required uint8 last_response; + required Timestamp timestamp; +} + +message BatteryDataResponse { + required uint8 last_response; + required Timestamp timestamp; + required BatteryData battery_data; +} + + + +message StreamResponse { + required Timestamp timestamp; + repeated BatteryStream battery_stream[PROTOCOL_BATTERY_STREAM_SIZE]; + repeated MicrophoneStream microphone_stream[PROTOCOL_MICROPHONE_STREAM_SIZE]; + repeated ScanStream scan_stream[PROTOCOL_SCAN_STREAM_SIZE]; + repeated AccelerometerStream accelerometer_stream[PROTOCOL_ACCELEROMETER_STREAM_SIZE]; + repeated AccelerometerInterruptStream accelerometer_interrupt_stream[PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE]; +} + + + +message TestResponse { + required uint8 test_failed; +} + + + +message Response { + oneof type { + StatusResponse status_response (1); + StartMicrophoneResponse start_microphone_response (2); + StartScanResponse start_scan_response (3); + StartAccelerometerResponse start_accelerometer_response (4); + StartAccelerometerInterruptResponse start_accelerometer_interrupt_response (5); + StartBatteryResponse start_battery_response (6); + MicrophoneDataResponse microphone_data_response (7); + ScanDataResponse scan_data_response (8); + AccelerometerDataResponse accelerometer_data_response (9); + AccelerometerInterruptDataResponse accelerometer_interrupt_data_response (10); + BatteryDataResponse battery_data_response (11); + StreamResponse stream_response (12); + TestResponse test_response (13); + } +} diff --git a/firmware/nRF_badge/data_collector/incl/request_handler_lib_01v1.c b/firmware/nRF_badge/data_collector/incl/request_handler_lib_01v1.c new file mode 100644 index 0000000..0b74116 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/request_handler_lib_01v1.c @@ -0,0 +1,666 @@ +#include "request_handler_lib_01v1.h" + +//#define PROTOCOL_01v1 + +#ifdef PROTOCOL_01v1 + + +#include +#include "app_fifo.h" +#include "app_scheduler.h" +#include "sender_lib.h" +#include "systick_lib.h" +#include "storer_lib.h" +#include "sampling_lib.h" +#include "advertiser_lib.h" // To retrieve the current badge-assignement and set the clock-sync status +#include "battery_lib.h" + +#include "debug_lib.h" + +#include "tinybuf.h" +#include "protocol_messages_01v1.h" +#include "chunk_messages.h" + +#ifndef UNIT_TEST +#include "custom_board.h" +#include "nrf_gpio.h" +#endif + + +#define RECEIVE_NOTIFICATION_FIFO_SIZE 256 /**< Buffer size for the receive-notification FIFO. Has to be a power of two */ +#define AWAIT_DATA_TIMEOUT_MS 1000 +#define TRANSMIT_DATA_TIMEOUT_MS 100 +#define REQUEST_HANDLER_SERIALIZED_BUFFER_SIZE 512 +#define RESPONSE_MAX_TRANSMIT_RETRIES 50 + + +#define MICROPHONE_SAMPLING_PERIOD_MS 50 +#define SCAN_AGGREGATION_TYPE SCAN_CHUNK_AGGREGATE_TYPE_MAX + +#define MICROPHONE_DATA_RESPONSE_HEADER_SIZE 13 /** Needed for the old protocol */ +#define SCAN_DATA_RESPONSE_HEADER_SIZE 9 /** Needed for the old protocol */ + + +typedef struct { + uint64_t request_timepoint_ticks; + Request request; +} request_event_t; + +typedef struct { + uint32_t response_retries; + app_sched_event_handler_t response_success_handler; /**< Scheduler function that should be called, after the reponse was transmitted successfully, to queue some other reponses */ + app_sched_event_handler_t response_fail_handler; /**< Scheduler function that should be called, if the response could not be transmitted */ + Response response; +} response_event_t; + + +//typedef void (* request_handler_t)(request_event_t* request_event); +typedef void (* request_handler_t)(void * p_event_data, uint16_t event_size); + +typedef struct { + uint8_t type; + request_handler_t handler; +} request_handler_for_type_t; + + + + +static request_event_t request_event; /**< Needed a reponse event, to put the timepoint ticks into the structure */ +static response_event_t response_event; /**< Needed a reponse event, to put the reties and the function to call after success into the structure */ +static Timestamp response_timestamp; /**< Needed for the status-, and start-requests */ +static uint8_t response_clock_status; /**< Needed for the status-, and start-requests */ + + +static app_fifo_t receive_notification_fifo; +static uint8_t receive_notification_buf[RECEIVE_NOTIFICATION_FIFO_SIZE]; +static volatile uint8_t processing_receive_notification = 0; /**< Flag that represents if the processing of a receive notification is still running. */ + +static uint8_t serialized_buf[REQUEST_HANDLER_SERIALIZED_BUFFER_SIZE]; + +/**< Store-messages to represent how the data should be stored (global because needed more than one) */ +static MicrophoneChunk microphone_chunk; +static ScanChunk scan_chunk; + + + +static void receive_notification_handler(receive_notification_t receive_notification); +static void process_receive_notification(void * p_event_data, uint16_t event_size); + +// TODO: add respone handler function declaration here +static void status_request_handler(void * p_event_data, uint16_t event_size); +static void status_assign_request_handler(void * p_event_data, uint16_t event_size); +static void start_microphone_request_handler(void * p_event_data, uint16_t event_size); +static void stop_microphone_request_handler(void * p_event_data, uint16_t event_size); +static void start_scan_request_handler(void * p_event_data, uint16_t event_size); +static void stop_scan_request_handler(void * p_event_data, uint16_t event_size); +static void microphone_data_request_handler(void * p_event_data, uint16_t event_size); +static void scan_data_request_handler(void * p_event_data, uint16_t event_size); +static void identify_request_handler(void * p_event_data, uint16_t event_size); + + +static request_handler_for_type_t request_handlers[] = { + { + .type = Request_status_request_tag, + .handler = status_request_handler, + }, + { + .type = Request_status_assign_request_tag, + .handler = status_assign_request_handler, + }, + { + .type = Request_start_microphone_request_tag, + .handler = start_microphone_request_handler, + }, + { + .type = Request_stop_microphone_request_tag, + .handler = stop_microphone_request_handler, + }, + { + .type = Request_start_scan_request_tag, + .handler = start_scan_request_handler, + }, + { + .type = Request_stop_scan_request_tag, + .handler = stop_scan_request_handler, + }, + { + .type = Request_microphone_data_request_tag, + .handler = microphone_data_request_handler, + }, + { + .type = Request_scan_data_request_tag, + .handler = scan_data_request_handler, + }, + { + .type = Request_identify_request_tag, + .handler = identify_request_handler, + } +}; + + + +// App-scheduler has to be initialized before! +// Sender has to be initialized before! +// Sampling has to be initialized before! +ret_code_t request_handler_init(void) { + ret_code_t ret = sender_init(); + if(ret != NRF_SUCCESS) return ret; + + sender_set_receive_notification_handler(receive_notification_handler); + + ret = app_fifo_init(&receive_notification_fifo, receive_notification_buf, sizeof(receive_notification_buf)); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + + +static void receive_notification_handler(receive_notification_t receive_notification) { + // Put the receive_notification into the fifo + uint32_t available_len = 0; + uint32_t notification_size = sizeof(receive_notification); + app_fifo_write(&receive_notification_fifo, NULL, &available_len); + if(available_len < notification_size) { + debug_log("REQUEST_HANDLER: Not enough bytes in Notification FIFO: %u < %u\n", available_len, notification_size); + return; + } + + app_fifo_write(&receive_notification_fifo, (uint8_t*) &receive_notification, ¬ification_size); + + + + // Start the processing of the receive notification (but only if it was not started before/is still running). So it ignores the new notifications until the current + // processed notification is ready. + if(!processing_receive_notification) { + processing_receive_notification = 1; + app_sched_event_put(NULL, 0, process_receive_notification); + } +} + +static void finish_request(void) { + processing_receive_notification = 0; + app_sched_event_put(NULL, 0, process_receive_notification); +} + +// Called when await data failed, or decoding the notification failed, or request does not exist, or transmitting the response failed (because disconnected or something else) +static void finish_request_error(void) { + app_fifo_flush(&receive_notification_fifo); + debug_log("REQUEST_HANDLER: Error while processing request/response --> Disconnect!!!\n"); + sender_disconnect(); // To clear the RX- and TX-FIFO + processing_receive_notification = 0; + storer_invalidate_iterators(); +} + +static void process_receive_notification(void * p_event_data, uint16_t event_size) { + + receive_notification_t receive_notification; + uint32_t notification_size = sizeof(receive_notification); + // Check size of FIFO + uint32_t available_size; + app_fifo_read(&receive_notification_fifo, NULL, &available_size); + if(available_size < notification_size) { + app_fifo_flush(&receive_notification_fifo); + processing_receive_notification = 0; + return; + } + + processing_receive_notification = 1; + + + + + + app_fifo_read(&receive_notification_fifo, (uint8_t*) &receive_notification, ¬ification_size); + + // Get the timestamp and clock-sync status before processing the request! + response_timestamp.seconds = receive_notification.timepoint_seconds; + response_timestamp.ms = receive_notification.timepoint_milliseconds; + response_clock_status = systick_is_synced(); + + // Read out the received data of the notification + uint32_t serialized_len = receive_notification.notification_len; + uint64_t timepoint_ticks = receive_notification.timepoint_ticks; + ret_code_t ret = sender_await_data(serialized_buf, serialized_len, AWAIT_DATA_TIMEOUT_MS); + if(ret != NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: sender_await_data() error\n"); + finish_request_error(); + return; + } + + // Now decode the serialized notification + memset(&(request_event.request), 0, sizeof(request_event.request)); + tb_istream_t istream = tb_istream_from_buffer(serialized_buf, serialized_len); + uint8_t decode_status = tb_decode(&istream, Request_fields, &(request_event.request), TB_LITTLE_ENDIAN); + if(decode_status == 0) { + debug_log("REQUEST_HANDLER: Error decoding\n"); + finish_request_error(); + return; + } + debug_log("REQUEST_HANDLER: Decoded successfully\n"); + + if(istream.bytes_read < serialized_len) { + debug_log("REQUEST_HANDLER: Warning decoding: %u bytes have not been read.\n", serialized_len - istream.bytes_read); + } + // We need to handle the StatusRequestAssignment message manually, because the old protocol sucks a little bit.. + if(request_event.request.which_type == Request_status_request_tag) { + istream = tb_istream_from_buffer(&serialized_buf[1], serialized_len - 1); // Without the which-field + StatusAssignRequest tmp; + decode_status = tb_decode(&istream, StatusAssignRequest_fields, &tmp, TB_LITTLE_ENDIAN); + if(decode_status != 0) { // If decoding was successful --> we assume that we received a StatusRequestAssign-message and not a StatusRequest-message + request_event.request.which_type = Request_status_assign_request_tag; + request_event.request.type.status_assign_request = tmp; + } + } + + request_event.request_timepoint_ticks = timepoint_ticks; + + + + debug_log("REQUEST_HANDLER: Which request type: %u, Ticks: %u\n", request_event.request.which_type, request_event.request_timepoint_ticks); + + request_handler_t request_handler = NULL; + for(uint8_t i = 0; i < sizeof(request_handlers)/sizeof(request_handler_for_type_t); i++) { + if(request_event.request.which_type == request_handlers[i].type) { + request_handler = request_handlers[i].handler; + + + break; + } + + } + if(request_handler != NULL) { + + // If we have received a request, we know that the hub is still active --> reset all timeouts of the sampling + sampling_reset_timeouts(); + + request_handler(NULL, 0); + } else { + // Should actually not happen, but to be sure... + debug_log("REQUEST_HANDLER: Have not found a corresponding request handler for which_type: %u\n", request_event.request.which_type); + finish_request_error(); + } +} + + +static void send_response(void * p_event_data, uint16_t event_size) { + // On transmit failure, reschedule itself + response_event.response_fail_handler = send_response; + + // Check if we already had too much retries + if(response_event.response_retries > RESPONSE_MAX_TRANSMIT_RETRIES) { + finish_request_error(); + return; + } + + response_event.response_retries++; + + // Encoding has not to be done every time, but to don't have an extra function for that, we just do it here.. + tb_ostream_t ostream = tb_ostream_from_buffer(serialized_buf, sizeof(serialized_buf)); + uint8_t encode_status = tb_encode(&ostream, Response_fields, &(response_event.response), TB_LITTLE_ENDIAN); + uint32_t len = ostream.bytes_written; + + + + + if(encode_status == 0) { + debug_log("REQUEST_HANDLER: Error encoding response!\n"); + finish_request_error(); + return; + } + + + + ret_code_t ret = NRF_SUCCESS; + // This is a fix to be compatible with the old protocol-implementation. + // We first check that the transmit-FIFO is empty. If not --> reschedule. + // If it is empty we check if we need to transmit microphone-data or scan-data, + // because these two types have to be handled separately (first a header has to be sent) + // and after that the actual data have to be sent. + + uint32_t transmit_fifo_size = sender_get_transmit_fifo_size(); + uint64_t end_ms = systick_get_continuous_millis() + TRANSMIT_DATA_TIMEOUT_MS; + while(transmit_fifo_size != 0 && systick_get_continuous_millis() < end_ms) { + transmit_fifo_size = sender_get_transmit_fifo_size(); + } + + + if(transmit_fifo_size != 0) { + ret = NRF_ERROR_NO_MEM; + } else { + // In the old Protocol there should be no which_field indicating the response-type, so we ignore the first byte + uint32_t transmit_len = len-1; + uint8_t* transmit_buf = &serialized_buf[1]; + // Split Headers: + uint32_t header_len = 0; + // Mic response --> 13 Byte Header + // Scan response --> 9 Byte Header + if(response_event.response.which_type == Response_microphone_data_response_tag) { + header_len = (transmit_len < MICROPHONE_DATA_RESPONSE_HEADER_SIZE) ? transmit_len : MICROPHONE_DATA_RESPONSE_HEADER_SIZE; + } else if(response_event.response.which_type == Response_scan_data_response_tag) { + header_len = (transmit_len < SCAN_DATA_RESPONSE_HEADER_SIZE) ? transmit_len : SCAN_DATA_RESPONSE_HEADER_SIZE; + } + + ret = sender_transmit(transmit_buf, header_len, TRANSMIT_DATA_TIMEOUT_MS); + if(ret == NRF_SUCCESS) { // Wait until the header has been transmitted: + do { + transmit_fifo_size = sender_get_transmit_fifo_size(); + // If the connection drops, the FIFO will be cleared automatically --> leaving this loop + } while(transmit_fifo_size != 0); + } + ret = sender_transmit(&transmit_buf[header_len], transmit_len-header_len, TRANSMIT_DATA_TIMEOUT_MS); + } + + + debug_log("REQUEST_HANDLER: Transmit status %u!\n", ret); + + + if(ret == NRF_SUCCESS) { + if(response_event.response_success_handler != NULL) { + app_sched_event_put(NULL, 0, response_event.response_success_handler); + } else { + // If we don't need to call a success_handler-function, we are done with the request + finish_request(); + } + } else { + if(ret == NRF_ERROR_NO_MEM) // Only reschedule a fail handler, if we could not transmit because of memory problems + if(response_event.response_fail_handler != NULL) { // This function should actually always be set.. + app_sched_event_put(NULL, 0, response_event.response_fail_handler); + return; + } + // Some BLE-problems occured (e.g. disconnected...) + finish_request_error(); + } +} + + +/** + Every request-handler has an response-handler! +*/ + + + + + +/**< These are the response handlers, that are called by the request-handlers. */ + +static void status_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_status_response_tag; + response_event.response.type.status_response.clock_status = response_clock_status; + response_event.response.type.status_response.collector_status = (sampling_get_sampling_configuration() & SAMPLING_MICROPHONE) ? 1 : 0; + response_event.response.type.status_response.scan_status = (sampling_get_sampling_configuration() & SAMPLING_SCAN) ? 1 : 0; + response_event.response.type.status_response.timestamp = response_timestamp; + response_event.response.type.status_response.battery_data.voltage = battery_get_voltage(); + + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + debug_log("REQUEST_HANDLER: Status response: %u, %u\n", (uint32_t)response_event.response.type.status_response.timestamp.seconds, response_event.response.type.status_response.timestamp.ms); + + send_response(NULL, 0); +} + +static void start_microphone_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_start_microphone_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + response_event.response.type.start_microphone_response.timestamp = response_timestamp; + + + send_response(NULL, 0); +} + + +static void stop_microphone_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_stop_microphone_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + send_response(NULL, 0); + +} + + +static void start_scan_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_start_scan_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + response_event.response.type.start_scan_response.timestamp = response_timestamp; + + + send_response(NULL, 0); +} + + +static void stop_scan_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_stop_scan_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + send_response(NULL, 0); + +} + +static void microphone_data_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_microphone_data_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = microphone_data_response_handler; + + + ret_code_t ret = storer_get_next_microphone_chunk(µphone_chunk); + if(ret == NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: Found microphone data: %u, %u\n", microphone_chunk.timestamp.seconds, microphone_chunk.timestamp.ms); + // Send microphone data + response_event.response.type.microphone_data_response.microphone_data_response_header.timestamp = microphone_chunk.timestamp; + response_event.response.type.microphone_data_response.microphone_data_response_header.battery_data.voltage = 0; + response_event.response.type.microphone_data_response.microphone_data_response_header.sample_period_ms = microphone_chunk.sample_period_ms; + uint32_t microphone_data_count = (microphone_chunk.microphone_data_count > PROTOCOL_MICROPHONE_DATA_SIZE) ? PROTOCOL_MICROPHONE_DATA_SIZE : microphone_chunk.microphone_data_count; + response_event.response.type.microphone_data_response.microphone_data_count = microphone_data_count; + memcpy(response_event.response.type.microphone_data_response.microphone_data, microphone_chunk.microphone_data, microphone_data_count*sizeof(MicrophoneData)); + + send_response(NULL, 0); + } else if(ret == NRF_ERROR_NOT_FOUND || ret == NRF_ERROR_INVALID_STATE) { + debug_log("REQUEST_HANDLER: Could not fetch Mic-data. Sending end Header..\n"); + memset(&(response_event.response.type.microphone_data_response.microphone_data_response_header), 0, sizeof(response_event.response.type.microphone_data_response.microphone_data_response_header)); + response_event.response.type.microphone_data_response.microphone_data_count = 0; + + // Send end-header + response_event.response_success_handler = NULL; + send_response(NULL, 0); + } else { + app_sched_event_put(NULL, 0, microphone_data_response_handler); + } +} + +static void scan_data_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_scan_data_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = scan_data_response_handler; + + + + ret_code_t ret = storer_get_next_scan_chunk(&scan_chunk); + if(ret == NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: Found scan data..\n"); + // Send scan data + response_event.response.type.scan_data_response.scan_data_response_header.timestamp_seconds = scan_chunk.timestamp.seconds; + response_event.response.type.scan_data_response.scan_data_response_header.battery_data.voltage = 0; + uint32_t scan_result_data_count = (scan_chunk.scan_result_data_count > PROTOCOL_SCAN_DATA_SIZE) ? PROTOCOL_SCAN_DATA_SIZE : scan_chunk.scan_result_data_count; + response_event.response.type.scan_data_response.scan_result_data_count = scan_result_data_count; + memcpy(response_event.response.type.scan_data_response.scan_result_data, scan_chunk.scan_result_data, scan_result_data_count*sizeof(ScanResultData)); + + + send_response(NULL, 0); + } else if(ret == NRF_ERROR_NOT_FOUND || ret == NRF_ERROR_INVALID_STATE) { + debug_log("REQUEST_HANDLER: Could not fetch Scan-data. Sending end Header..\n"); + memset(&(response_event.response.type.scan_data_response.scan_data_response_header), 0, sizeof(response_event.response.type.scan_data_response.scan_data_response_header)); + response_event.response.type.scan_data_response.scan_result_data_count = 0; + + // Send end-header + response_event.response_success_handler = NULL; + send_response(NULL, 0); + } else { + app_sched_event_put(NULL, 0, scan_data_response_handler); + } +} + + +static void identify_response_handler(void * p_event_data, uint16_t event_size) { + response_event.response.which_type = Response_identify_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + send_response(NULL, 0); + +} + + + + + +/**< These are the request handlers that actually call the response-handlers via the scheduler */ + +static void status_request_handler(void * p_event_data, uint16_t event_size) { + + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.status_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + app_sched_event_put(NULL, 0, status_response_handler); +} + +static void status_assign_request_handler(void * p_event_data, uint16_t event_size) { + + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.status_assign_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + + BadgeAssignement badge_assignement; + badge_assignement = request_event.request.type.status_assign_request.badge_assignement; + + ret_code_t ret = advertiser_set_badge_assignement(badge_assignement); + if(ret == NRF_ERROR_INTERNAL) { + app_sched_event_put(NULL, 0, status_assign_request_handler); + } else if(ret != NRF_SUCCESS) { + finish_request_error(); + } else { // ret should be NRF_SUCCESS here + app_sched_event_put(NULL, 0, status_response_handler); + } +} + +static void start_microphone_request_handler(void * p_event_data, uint16_t event_size) { + + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_microphone_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_microphone_request.timeout; + + debug_log("REQUEST_HANDLER: Start microphone with timeout: %u \n", timeout); + + ret_code_t ret = sampling_start_microphone(timeout*60*1000, MICROPHONE_SAMPLING_PERIOD_MS, 0); + debug_log("REQUEST_HANDLER: Ret sampling_start_microphone: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + app_sched_event_put(NULL, 0, start_microphone_response_handler); + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_microphone_request_handler); + } +} + +static void stop_microphone_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_microphone(0); + + debug_log("REQUEST_HANDLER: Stop microphone\n"); + app_sched_event_put(NULL, 0, stop_microphone_response_handler); +} + +static void start_scan_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_scan_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_scan_request.timeout; + uint16_t window = (request_event.request).type.start_scan_request.window; + uint16_t interval = (request_event.request).type.start_scan_request.interval; + uint16_t duration = (request_event.request).type.start_scan_request.duration; + uint16_t period = (request_event.request).type.start_scan_request.period; + + if(duration > period) + period = duration; + + debug_log("REQUEST_HANDLER: Start scanning with timeout: %u, window: %u, interval: %u, duration: %u, period: %u\n", timeout, window, interval, duration, period); + BadgeAssignement badge_assignement; + advertiser_get_badge_assignement(&badge_assignement); + ret_code_t ret = sampling_start_scan(timeout*60*1000, period, interval, window, duration, badge_assignement.group, SCAN_AGGREGATION_TYPE, 0); + debug_log("REQUEST_HANDLER: Ret sampling_start_scan: %d\n\r", ret); + + + + if(ret == NRF_SUCCESS) { + app_sched_event_put(NULL, 0, start_scan_response_handler); + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_scan_request_handler); + } +} + +static void stop_scan_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_scan(0); + + debug_log("REQUEST_HANDLER: Stop scan\n"); + app_sched_event_put(NULL, 0, stop_scan_response_handler); +} + + +static void microphone_data_request_handler(void * p_event_data, uint16_t event_size) { + Timestamp timestamp = request_event.request.type.microphone_data_request.timestamp; + debug_log("REQUEST_HANDLER: Pull microphone data since: %u s, %u ms\n", timestamp.seconds, timestamp.ms); + + ret_code_t ret = storer_find_microphone_chunk_from_timestamp(timestamp, µphone_chunk); + if(ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) { + app_sched_event_put(NULL, 0, microphone_data_response_handler); + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, microphone_data_request_handler); + } +} + + +static void scan_data_request_handler(void * p_event_data, uint16_t event_size) { + + Timestamp timestamp; + timestamp.seconds = request_event.request.type.scan_data_request.seconds; + timestamp.ms = 0; + debug_log("REQUEST_HANDLER: Pull scan data since: %u s, %u ms\n", timestamp.seconds, timestamp.ms); + + ret_code_t ret = storer_find_scan_chunk_from_timestamp(timestamp, &scan_chunk); + if(ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) { + app_sched_event_put(NULL, 0, scan_data_response_handler); + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, scan_data_request_handler); + } + +} + + +static void identify_request_handler(void * p_event_data, uint16_t event_size) { + uint16_t timeout = (request_event.request).type.identify_request.timeout; + (void) timeout; + debug_log("REQUEST_HANDLER: Identify request with timeout: %u\n", timeout); + #ifndef UNIT_TEST + nrf_gpio_pin_write(RED_LED, LED_ON); + systick_delay_millis(timeout*1000); + nrf_gpio_pin_write(RED_LED, LED_OFF); + #endif + app_sched_event_put(NULL, 0, identify_response_handler); +} + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/request_handler_lib_01v1.h b/firmware/nRF_badge/data_collector/incl/request_handler_lib_01v1.h new file mode 100644 index 0000000..b275a8a --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/request_handler_lib_01v1.h @@ -0,0 +1,29 @@ +#ifndef __REQUEST_HANDLER_LIB_H +#define __REQUEST_HANDLER_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +/**@brief Function to initialize the request-handler mechanism. + * + * @details When sth is received via the ble-interface, the notification handler is called. + * The notification is processed (and the ticks at the receive timepoint is saved to set the internal clock correctly). + * After the processing of the notification a corresponding request-handler is called, that interprets and processes + * the received data. Optionally the request-handler calls a response handler that generates a response and sends + * it via the ble-interface. + * + * + * @note app-scheduler has to be initialized before! + * @note sender_lib has to be initialized before! + * @note advertiser_lib has to be initialized before! + * @note storer_lib has to be initialized before! + * @note sampling_lib has to be initialized before! + * @note systick_lib has to be initialized before! + */ +ret_code_t request_handler_init(void); + + + + +#endif + diff --git a/firmware/nRF_badge/data_collector/incl/request_handler_lib_02v1.c b/firmware/nRF_badge/data_collector/incl/request_handler_lib_02v1.c new file mode 100644 index 0000000..3155d55 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/request_handler_lib_02v1.c @@ -0,0 +1,1461 @@ +#include "request_handler_lib_02v1.h" + +#ifdef PROTOCOL_02v1 + + +#include +#include "app_fifo.h" +#include "app_scheduler.h" +#include "sender_lib.h" +#include "systick_lib.h" +#include "storer_lib.h" +#include "sampling_lib.h" +#include "advertiser_lib.h" // To retrieve the current badge-assignement and set the clock-sync status +#include "battery_lib.h" +#include "accel_lib.h" // For ACCELEROMETER_PRESENT + +#include "debug_lib.h" + +#include "tinybuf.h" +#include "protocol_messages_02v1.h" +#include "chunk_messages.h" +#include "selftest_lib.h" + +#ifndef UNIT_TEST +#include "custom_board.h" +#include "nrf_gpio.h" +#endif + +#define RECEIVE_NOTIFICATION_FIFO_SIZE 256 /**< Buffer size for the receive-notification FIFO. Has to be a power of two */ +#define AWAIT_DATA_TIMEOUT_MS 1000 +#define TRANSMIT_DATA_TIMEOUT_MS 100 +#define REQUEST_HANDLER_SERIALIZED_BUFFER_SIZE 512 +#define RESPONSE_MAX_TRANSMIT_RETRIES 50 + + + +typedef struct { + uint64_t request_timepoint_ticks; + Request request; +} request_event_t; + +typedef struct { + uint32_t response_retries; + app_sched_event_handler_t response_success_handler; /**< Scheduler function that should be called, after the reponse was transmitted successfully, to queue some other reponses */ + app_sched_event_handler_t response_fail_handler; /**< Scheduler function that should be called, if the response could not be transmitted */ + Response response; +} response_event_t; + + +//typedef void (* request_handler_t)(request_event_t* request_event); +typedef void (* request_handler_t)(void * p_event_data, uint16_t event_size); + +typedef struct { + uint8_t type; + request_handler_t handler; +} request_handler_for_type_t; + + + + +static request_event_t request_event; /**< Needed a reponse event, to put the timepoint ticks into the structure */ +static response_event_t response_event; /**< Needed a reponse event, to put the reties and the function to call after success into the structure */ +static Timestamp response_timestamp; /**< Needed for the status-, and start-requests */ +static uint8_t response_clock_status; /**< Needed for the status-, and start-requests */ + +static app_fifo_t receive_notification_fifo; +static uint8_t receive_notification_buf[RECEIVE_NOTIFICATION_FIFO_SIZE]; +static volatile uint8_t processing_receive_notification = 0; /**< Flag that represents if the processing of a receive notification is still running. */ +static volatile uint8_t processing_response = 0; /**< Flag that represents if the processing of response is still running. */ +static volatile uint8_t streaming_started = 0; /**< Flag that represents if streaming is currently running. */ + +static uint8_t serialized_buf[REQUEST_HANDLER_SERIALIZED_BUFFER_SIZE]; + +/**< Store-messages to represent how the data should be stored (global because needed more than one) */ +static MicrophoneChunk microphone_chunk; +static ScanChunk scan_chunk; +static AccelerometerChunk accelerometer_chunk; +static AccelerometerInterruptChunk accelerometer_interrupt_chunk; +static BatteryChunk battery_chunk; + + + +static void receive_notification_handler(receive_notification_t receive_notification); +static void process_receive_notification(void * p_event_data, uint16_t event_size); + + +static void status_request_handler(void * p_event_data, uint16_t event_size); +static void start_microphone_request_handler(void * p_event_data, uint16_t event_size); +static void stop_microphone_request_handler(void * p_event_data, uint16_t event_size); +static void start_scan_request_handler(void * p_event_data, uint16_t event_size); +static void stop_scan_request_handler(void * p_event_data, uint16_t event_size); +static void start_accelerometer_request_handler(void * p_event_data, uint16_t event_size); +static void stop_accelerometer_request_handler(void * p_event_data, uint16_t event_size); +static void start_accelerometer_interrupt_request_handler(void * p_event_data, uint16_t event_size); +static void stop_accelerometer_interrupt_request_handler(void * p_event_data, uint16_t event_size); +static void start_battery_request_handler(void * p_event_data, uint16_t event_size); +static void stop_battery_request_handler(void * p_event_data, uint16_t event_size); +static void microphone_data_request_handler(void * p_event_data, uint16_t event_size); +static void scan_data_request_handler(void * p_event_data, uint16_t event_size); +static void accelerometer_data_request_handler(void * p_event_data, uint16_t event_size); +static void accelerometer_interrupt_data_request_handler(void * p_event_data, uint16_t event_size); +static void battery_data_request_handler(void * p_event_data, uint16_t event_size); +static void start_microphone_stream_request_handler(void * p_event_data, uint16_t event_size); +static void stop_microphone_stream_request_handler(void * p_event_data, uint16_t event_size); +static void start_scan_stream_request_handler(void * p_event_data, uint16_t event_size); +static void stop_scan_stream_request_handler(void * p_event_data, uint16_t event_size); +static void start_accelerometer_stream_request_handler(void * p_event_data, uint16_t event_size); +static void stop_accelerometer_stream_request_handler(void * p_event_data, uint16_t event_size); +static void start_accelerometer_interrupt_stream_request_handler(void * p_event_data, uint16_t event_size); +static void stop_accelerometer_interrupt_stream_request_handler(void * p_event_data, uint16_t event_size); +static void start_battery_stream_request_handler(void * p_event_data, uint16_t event_size); +static void stop_battery_stream_request_handler(void * p_event_data, uint16_t event_size); +static void identify_request_handler(void * p_event_data, uint16_t event_size); +static void test_request_handler(void * p_event_data, uint16_t event_size); +static void restart_request_handler(void * p_event_data, uint16_t event_size); + + +static void status_response_handler(void * p_event_data, uint16_t event_size); +static void start_microphone_response_handler(void * p_event_data, uint16_t event_size); +static void start_scan_response_handler(void * p_event_data, uint16_t event_size); +static void start_accelerometer_response_handler(void * p_event_data, uint16_t event_size); +static void start_accelerometer_interrupt_response_handler(void * p_event_data, uint16_t event_size); +static void start_battery_response_handler(void * p_event_data, uint16_t event_size); +static void microphone_data_response_handler(void * p_event_data, uint16_t event_size); +static void scan_data_response_handler(void * p_event_data, uint16_t event_size); +static void accelerometer_data_response_handler(void * p_event_data, uint16_t event_size); +static void accelerometer_interrupt_data_response_handler(void * p_event_data, uint16_t event_size); +static void battery_data_response_handler(void * p_event_data, uint16_t event_size); +static void stream_response_handler(void * p_event_data, uint16_t event_size); +static void test_response_handler(void * p_event_data, uint16_t event_size); + + +static request_handler_for_type_t request_handlers[] = { + { + .type = Request_status_request_tag, + .handler = status_request_handler, + }, + { + .type = Request_start_microphone_request_tag, + .handler = start_microphone_request_handler, + }, + { + .type = Request_stop_microphone_request_tag, + .handler = stop_microphone_request_handler, + }, + { + .type = Request_start_scan_request_tag, + .handler = start_scan_request_handler, + }, + { + .type = Request_stop_scan_request_tag, + .handler = stop_scan_request_handler, + }, + { + .type = Request_start_accelerometer_request_tag, + .handler = start_accelerometer_request_handler, + }, + { + .type = Request_stop_accelerometer_request_tag, + .handler = stop_accelerometer_request_handler, + }, + { + .type = Request_start_accelerometer_interrupt_request_tag, + .handler = start_accelerometer_interrupt_request_handler, + }, + { + .type = Request_stop_accelerometer_interrupt_request_tag, + .handler = stop_accelerometer_interrupt_request_handler, + }, + { + .type = Request_start_battery_request_tag, + .handler = start_battery_request_handler, + }, + { + .type = Request_stop_battery_request_tag, + .handler = stop_battery_request_handler, + }, + { + .type = Request_microphone_data_request_tag, + .handler = microphone_data_request_handler, + }, + { + .type = Request_scan_data_request_tag, + .handler = scan_data_request_handler, + }, + { + .type = Request_accelerometer_data_request_tag, + .handler = accelerometer_data_request_handler, + }, + { + .type = Request_accelerometer_interrupt_data_request_tag, + .handler = accelerometer_interrupt_data_request_handler, + }, + { + .type = Request_battery_data_request_tag, + .handler = battery_data_request_handler, + }, + { + .type = Request_start_microphone_stream_request_tag, + .handler = start_microphone_stream_request_handler, + }, + { + .type = Request_stop_microphone_stream_request_tag, + .handler = stop_microphone_stream_request_handler, + }, + { + .type = Request_start_scan_stream_request_tag, + .handler = start_scan_stream_request_handler, + }, + { + .type = Request_stop_scan_stream_request_tag, + .handler = stop_scan_stream_request_handler, + }, + { + .type = Request_start_accelerometer_stream_request_tag, + .handler = start_accelerometer_stream_request_handler, + }, + { + .type = Request_stop_accelerometer_stream_request_tag, + .handler = stop_accelerometer_stream_request_handler, + }, + { + .type = Request_start_accelerometer_interrupt_stream_request_tag, + .handler = start_accelerometer_interrupt_stream_request_handler, + }, + { + .type = Request_stop_accelerometer_interrupt_stream_request_tag, + .handler = stop_accelerometer_interrupt_stream_request_handler, + }, + { + .type = Request_start_battery_stream_request_tag, + .handler = start_battery_stream_request_handler, + }, + { + .type = Request_stop_battery_stream_request_tag, + .handler = stop_battery_stream_request_handler, + }, + + + { + .type = Request_identify_request_tag, + .handler = identify_request_handler, + }, + { + .type = Request_test_request_tag, + .handler = test_request_handler, + }, + { + .type = Request_restart_request_tag, + .handler = restart_request_handler, + } +}; + + + +ret_code_t request_handler_init(void) { + ret_code_t ret = sender_init(); + if(ret != NRF_SUCCESS) return ret; + + sender_set_receive_notification_handler(receive_notification_handler); + + ret = app_fifo_init(&receive_notification_fifo, receive_notification_buf, sizeof(receive_notification_buf)); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + +/**@brief Handler that is called by the sender-module, when a notification was received. + * + * @details It puts the received notification in the notification-fifo and schedules the process_receive_notification to process the notification. + */ +static void receive_notification_handler(receive_notification_t receive_notification) { + // Put the receive_notification into the fifo + uint32_t available_len = 0; + uint32_t notification_size = sizeof(receive_notification); + app_fifo_write(&receive_notification_fifo, NULL, &available_len); + if(available_len < notification_size) { + debug_log("REQUEST_HANDLER: Not enough bytes in Notification FIFO: %u < %u\n", available_len, notification_size); + return; + } + + app_fifo_write(&receive_notification_fifo, (uint8_t*) &receive_notification, ¬ification_size); + + app_sched_event_put(NULL, 0, process_receive_notification); + +} + +static void finish_receive_notification(void) { + processing_receive_notification = 0; +} + +static void finish_and_reschedule_receive_notification(void) { + processing_receive_notification = 0; + app_sched_event_put(NULL, 0, process_receive_notification); +} + +static ret_code_t start_receive_notification(app_sched_event_handler_t reschedule_handler) { + if(processing_receive_notification) { + app_sched_event_put(NULL, 0, reschedule_handler); + return NRF_ERROR_BUSY; + } + processing_receive_notification = 1; + return NRF_SUCCESS; +} + + +static void finish_response(void) { + processing_response = 0; +} + +static ret_code_t start_response(app_sched_event_handler_t reschedule_handler) { + if(processing_response) { // Check if we are allowed to prepare and send our response, if not reschedule the response-handler again + app_sched_event_put(NULL, 0, reschedule_handler); + return NRF_ERROR_BUSY; + } + processing_response = 1; + return NRF_SUCCESS; +} + + +// Called when await data failed, or decoding the notification failed, or request does not exist, or transmitting the response failed (because disconnected or something else) +static void finish_error(void) { + app_fifo_flush(&receive_notification_fifo); + debug_log("REQUEST_HANDLER: Error while processing request/response --> Disconnect!!!\n"); + sender_disconnect(); // To clear the RX- and TX-FIFO + finish_receive_notification(); + finish_response(); + streaming_started = 0; + storer_invalidate_iterators(); +} + + +static ret_code_t receive_notification_fifo_peek(receive_notification_t* receive_notification, uint32_t index) { + uint32_t notification_size = sizeof(receive_notification_t); + uint32_t available_size; + app_fifo_read(&receive_notification_fifo, NULL, &available_size); + if(available_size < notification_size*(index+1)) { + return NRF_ERROR_NOT_FOUND; + } + + uint8_t tmp[notification_size]; + for(uint32_t i = 0; i < notification_size; i++) { + app_fifo_peek(&receive_notification_fifo, i + index*notification_size, &(tmp[i])); + } + + memcpy(receive_notification, tmp, notification_size); + return NRF_SUCCESS; +} + +static void receive_notification_fifo_consume(void) { + uint32_t notification_size = sizeof(receive_notification_t); + uint8_t tmp[notification_size]; + app_fifo_read(&receive_notification_fifo, tmp, ¬ification_size); +} + +static void receive_notification_fifo_set_receive_notification(receive_notification_t* receive_notification, uint32_t index) { + uint32_t notification_size = sizeof(receive_notification_t); + uint32_t available_size; + app_fifo_read(&receive_notification_fifo, NULL, &available_size); + if(available_size < notification_size*(index+1)) { + return; + } + uint8_t tmp[notification_size]; + memcpy(tmp, receive_notification, notification_size); + // Manually set the bytes of the notification in the FIFO + uint32_t start_index = receive_notification_fifo.read_pos + index*notification_size; + for(uint32_t i = 0; i < notification_size; i++) { + receive_notification_fifo.p_buf[(start_index + i) & receive_notification_fifo.buf_size_mask] = tmp[i]; + } +} + +/** + * This function could handle multiple queued receive notifications. + * It searches for the length of the packet and then splits the + * queued receive notifications that belongs to the actual whole packet + * and removes the receive notificatins from the queue. + * + * Then it calls the corresponding request handler. + * If the request-handler is done, it finishes the processing of the receive notification, + * and reschedules the processing of new receive notifications. + */ +static void process_receive_notification(void * p_event_data, uint16_t event_size) { + // Check if we can start the processing of the receive notification, otherwise reschedule + if(start_receive_notification(process_receive_notification) != NRF_SUCCESS) + return; + + // Get the first receive-notification in the FIFO. If there is no in the queue --> we are finish with processing receive notifications. + receive_notification_t receive_notification; + ret_code_t ret = receive_notification_fifo_peek(&receive_notification, 0); + if(ret == NRF_ERROR_NOT_FOUND) { + app_fifo_flush(&receive_notification_fifo); + finish_receive_notification(); + return; + } + + // Get the timestamp and clock-sync status before processing the request! + response_timestamp.seconds = receive_notification.timepoint_seconds; + response_timestamp.ms = receive_notification.timepoint_milliseconds; + response_clock_status = systick_is_synced(); + + uint64_t timepoint_ticks = receive_notification.timepoint_ticks; + + // Wait for the length header bytes + uint8_t length_header[2]; + ret = sender_await_data(length_header, 2, AWAIT_DATA_TIMEOUT_MS); + if(ret != NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: sender_await_data() error for length header\n"); + app_fifo_flush(&receive_notification_fifo); + finish_error(); + return; + } + + uint16_t len = (((uint16_t)length_header[0]) << 8) | ((uint16_t)length_header[1]); + + // Now wait for the actual data + ret = sender_await_data(serialized_buf, len, AWAIT_DATA_TIMEOUT_MS); + if(ret != NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: sender_await_data() error\n"); + app_fifo_flush(&receive_notification_fifo); + finish_error(); + return; + } + + // Here we assume that we got enough receive notifications to receive all the data, + // so we need to consume all notifications in the notification-fifo that corresponds to this data + uint16_t consume_len = len + 2; // + 2 for the header + uint32_t consume_index = 0; + while(consume_len > 0) { + ret = receive_notification_fifo_peek(&receive_notification, consume_index); + if(ret == NRF_ERROR_NOT_FOUND) { + app_fifo_flush(&receive_notification_fifo); + finish_error(); + return; + } + if(receive_notification.notification_len <= consume_len) { + consume_len -= receive_notification.notification_len; + // Increment the consume_index here, to consume the right number of notifications in the end + consume_index++; + } else { + // Adapt the notification-len of the consume_index-th notification + receive_notification.notification_len = receive_notification.notification_len - consume_len; + //debug_log("REQUEST_HANDLER: Adapt %u. notification len to %u\n", consume_index, receive_notification.notification_len); + receive_notification_fifo_set_receive_notification(&receive_notification, consume_index); + // Set consume len to 0 + consume_len = 0; + } + } + //debug_log("REQUEST_HANDLER: Consume %u notifications\n", consume_index); + + // Now manually consume the notifications + for(uint32_t i = 0; i < consume_index; i++) { + receive_notification_fifo_consume(); + } + + + // Now decode the serialized notification + memset(&(request_event.request), 0, sizeof(request_event.request)); + tb_istream_t istream = tb_istream_from_buffer(serialized_buf, len); + uint8_t decode_status = tb_decode(&istream, Request_fields, &(request_event.request), TB_BIG_ENDIAN); + if(decode_status == 0) { + debug_log("REQUEST_HANDLER: Error decoding\n"); + finish_error(); + return; + } + debug_log("REQUEST_HANDLER: Decoded successfully\n"); + + if(istream.bytes_read < len) { + debug_log("REQUEST_HANDLER: Warning decoding: %u bytes have not been read.\n", len - istream.bytes_read); + } + + request_event.request_timepoint_ticks = timepoint_ticks; + + + debug_log("REQUEST_HANDLER: Which request type: %u, Ticks: %u\n", request_event.request.which_type, request_event.request_timepoint_ticks); + + request_handler_t request_handler = NULL; + for(uint8_t i = 0; i < sizeof(request_handlers)/sizeof(request_handler_for_type_t); i++) { + if(request_event.request.which_type == request_handlers[i].type) { + request_handler = request_handlers[i].handler; + break; + } + + } + if(request_handler != NULL) { + + // If we have received a request, we know that the hub is still active --> reset all timeouts of the sampling + sampling_reset_timeouts(); + + // These request-handlers finish the processing of the receive notification + request_handler(NULL, 0); + } else { + // Should actually not happen, but to be sure... + debug_log("REQUEST_HANDLER: Have not found a corresponding request handler for which_type: %u\n", request_event.request.which_type); + finish_error(); + } +} + + +static void send_response(void * p_event_data, uint16_t event_size) { + // On transmit failure, reschedule itself + response_event.response_fail_handler = send_response; + + // Check if we already had too much retries + if(response_event.response_retries > RESPONSE_MAX_TRANSMIT_RETRIES) { + finish_error(); + return; + } + + response_event.response_retries++; + + // Encoding has not to be done every time, but to don't have an extra function for that, we just do it here.. + + + tb_ostream_t ostream = tb_ostream_from_buffer(&serialized_buf[2], sizeof(serialized_buf) - 2); + uint8_t encode_status = tb_encode(&ostream, Response_fields, &(response_event.response), TB_BIG_ENDIAN); + uint32_t len = ostream.bytes_written; + + + + + if(encode_status == 0) { + debug_log("REQUEST_HANDLER: Error encoding response, len: %u!\n", len); + finish_error(); + return; + } + + + + ret_code_t ret = NRF_SUCCESS; + + serialized_buf[0] = (uint8_t)((((uint16_t) len) & 0xFF00) >> 8); + serialized_buf[1] = (uint8_t)(((uint16_t) len) & 0xFF); + ret = sender_transmit(serialized_buf, len+2, TRANSMIT_DATA_TIMEOUT_MS); + + debug_log("REQUEST_HANDLER: Transmit status %u!\n", ret); + + + if(ret == NRF_SUCCESS) { + + if(response_event.response_success_handler != NULL) { + app_sched_event_put(NULL, 0, response_event.response_success_handler); + } + + finish_response(); // We are now done with this reponse + + } else { + if(ret == NRF_ERROR_NO_MEM) // Only reschedule a fail handler, if we could not transmit because of memory problems + if(response_event.response_fail_handler != NULL) { // This function should actually always be set.. + app_sched_event_put(NULL, 0, response_event.response_fail_handler); + // Here we are not done with the response, so don't call the finish_response()-function + return; + } + // Some BLE-problems occured (e.g. disconnected...) + finish_error(); + } +} + + +/** + Every request-handler has an response-handler! +*/ + + + + + +/**< These are the response handlers, that are called by the request-handlers. */ + +static void status_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(status_response_handler) != NRF_SUCCESS) + return; + + + response_event.response.which_type = Response_status_response_tag; + response_event.response.type.status_response.clock_status = response_clock_status; + response_event.response.type.status_response.microphone_status = (sampling_get_sampling_configuration() & SAMPLING_MICROPHONE) ? 1 : 0; + response_event.response.type.status_response.scan_status = (sampling_get_sampling_configuration() & SAMPLING_SCAN) ? 1 : 0; + response_event.response.type.status_response.accelerometer_status = (sampling_get_sampling_configuration() & SAMPLING_ACCELEROMETER) ? 1 : 0; + response_event.response.type.status_response.accelerometer_interrupt_status = (sampling_get_sampling_configuration() & SAMPLING_ACCELEROMETER_INTERRUPT) ? 1 : 0; + response_event.response.type.status_response.battery_status = (sampling_get_sampling_configuration() & SAMPLING_BATTERY) ? 1 : 0; + response_event.response.type.status_response.timestamp = response_timestamp; + response_event.response.type.status_response.battery_data.voltage = battery_get_voltage(); + + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + send_response(NULL, 0); + +} + +static void start_microphone_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(start_microphone_response_handler) != NRF_SUCCESS) + return; + + + response_event.response.which_type = Response_start_microphone_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + response_event.response.type.start_microphone_response.timestamp = response_timestamp; + + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + send_response(NULL, 0); +} + + +static void start_scan_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(start_scan_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_start_scan_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + response_event.response.type.start_scan_response.timestamp = response_timestamp; + + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + send_response(NULL, 0); +} + +static void start_accelerometer_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(start_accelerometer_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_start_accelerometer_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + response_event.response.type.start_accelerometer_response.timestamp = response_timestamp; + + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + send_response(NULL, 0); +} + +static void start_accelerometer_interrupt_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(start_accelerometer_interrupt_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_start_accelerometer_interrupt_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + response_event.response.type.start_accelerometer_interrupt_response.timestamp = response_timestamp; + + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + send_response(NULL, 0); +} + + +static void start_battery_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(start_battery_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_start_battery_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + response_event.response.type.start_battery_response.timestamp = response_timestamp; + + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + send_response(NULL, 0); +} + + +static void microphone_data_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(microphone_data_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_microphone_data_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = microphone_data_response_handler; + + + ret_code_t ret = storer_get_next_microphone_chunk(µphone_chunk); + if(ret == NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: Found microphone data..\n"); + // Send microphone data + response_event.response.type.microphone_data_response.last_response = 0; + response_event.response.type.microphone_data_response.timestamp = microphone_chunk.timestamp; + response_event.response.type.microphone_data_response.sample_period_ms = microphone_chunk.sample_period_ms; + uint32_t microphone_data_count = (microphone_chunk.microphone_data_count > PROTOCOL_MICROPHONE_DATA_SIZE) ? PROTOCOL_MICROPHONE_DATA_SIZE : microphone_chunk.microphone_data_count; + response_event.response.type.microphone_data_response.microphone_data_count = microphone_data_count; + memcpy(response_event.response.type.microphone_data_response.microphone_data, microphone_chunk.microphone_data, microphone_data_count*sizeof(MicrophoneData)); + + send_response(NULL, 0); + } else if(ret == NRF_ERROR_NOT_FOUND || ret == NRF_ERROR_INVALID_STATE) { + debug_log("REQUEST_HANDLER: Could not fetch Mic-data. Sending end Header..\n"); + response_event.response.type.microphone_data_response.last_response = 1; + response_event.response.type.microphone_data_response.microphone_data_count = 0; + + // Send end-header + response_event.response_success_handler = NULL; + send_response(NULL, 0); + } else { + app_sched_event_put(NULL, 0, microphone_data_response_handler); + } +} + + +static void scan_data_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(scan_data_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_scan_data_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = scan_data_response_handler; + + + + ret_code_t ret = storer_get_next_scan_chunk(&scan_chunk); + if(ret == NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: Found scan data..\n"); + // Send scan data + response_event.response.type.scan_data_response.last_response = 0; + response_event.response.type.scan_data_response.timestamp = scan_chunk.timestamp; + uint32_t scan_result_data_count = (scan_chunk.scan_result_data_count > PROTOCOL_SCAN_DATA_SIZE) ? PROTOCOL_SCAN_DATA_SIZE : scan_chunk.scan_result_data_count; + response_event.response.type.scan_data_response.scan_result_data_count = scan_result_data_count; + memcpy(response_event.response.type.scan_data_response.scan_result_data, scan_chunk.scan_result_data, scan_result_data_count*sizeof(ScanResultData)); + + + send_response(NULL, 0); + } else if(ret == NRF_ERROR_NOT_FOUND || ret == NRF_ERROR_INVALID_STATE) { + debug_log("REQUEST_HANDLER: Could not fetch Scan-data. Sending end Header..\n"); + response_event.response.type.scan_data_response.last_response = 1; + response_event.response.type.scan_data_response.scan_result_data_count = 0; + + // Send end-header + response_event.response_success_handler = NULL; + send_response(NULL, 0); + } else { + app_sched_event_put(NULL, 0, scan_data_response_handler); + } +} + + +static void accelerometer_data_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(accelerometer_data_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_accelerometer_data_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = accelerometer_data_response_handler; + + + ret_code_t ret = storer_get_next_accelerometer_chunk(&accelerometer_chunk); + if(ret == NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: Found accelerometer data..\n"); + // Send accelerometer data + response_event.response.type.accelerometer_data_response.last_response = 0; + response_event.response.type.accelerometer_data_response.timestamp = accelerometer_chunk.timestamp; + uint32_t accelerometer_data_count = (accelerometer_chunk.accelerometer_data_count > PROTOCOL_ACCELEROMETER_DATA_SIZE) ? PROTOCOL_ACCELEROMETER_DATA_SIZE : accelerometer_chunk.accelerometer_data_count; + response_event.response.type.accelerometer_data_response.accelerometer_data_count = accelerometer_data_count; + memcpy(response_event.response.type.accelerometer_data_response.accelerometer_data, accelerometer_chunk.accelerometer_data, accelerometer_data_count*sizeof(AccelerometerData)); + + + send_response(NULL, 0); + } else if(ret == NRF_ERROR_NOT_FOUND || ret == NRF_ERROR_INVALID_STATE) { + debug_log("REQUEST_HANDLER: Could not fetch accelerometer-data. Sending end Header..\n"); + response_event.response.type.accelerometer_data_response.last_response = 1; + response_event.response.type.accelerometer_data_response.accelerometer_data_count = 0; + + // Send end-header + response_event.response_success_handler = NULL; + send_response(NULL, 0); + } else { + app_sched_event_put(NULL, 0, accelerometer_data_response_handler); + } +} + + +static void accelerometer_interrupt_data_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(accelerometer_interrupt_data_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_accelerometer_interrupt_data_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = accelerometer_interrupt_data_response_handler; + + + ret_code_t ret = storer_get_next_accelerometer_interrupt_chunk(&accelerometer_interrupt_chunk); + if(ret == NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: Found accelerometer interrupt data..\n"); + // Send accelerometer interrupt data + response_event.response.type.accelerometer_interrupt_data_response.last_response = 0; + response_event.response.type.accelerometer_interrupt_data_response.timestamp = accelerometer_interrupt_chunk.timestamp; + + send_response(NULL, 0); + } else if(ret == NRF_ERROR_NOT_FOUND || ret == NRF_ERROR_INVALID_STATE) { + debug_log("REQUEST_HANDLER: Could not fetch accelerometer interrupt-data. Sending end Header..\n"); + response_event.response.type.accelerometer_interrupt_data_response.last_response = 1; + + // Send end-header + response_event.response_success_handler = NULL; + send_response(NULL, 0); + } else { + app_sched_event_put(NULL, 0, accelerometer_interrupt_data_response_handler); + } +} + + +static void battery_data_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(battery_data_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_battery_data_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = battery_data_response_handler; + + ret_code_t ret = storer_get_next_battery_chunk(&battery_chunk); + if(ret == NRF_SUCCESS) { + debug_log("REQUEST_HANDLER: Found battery data..\n"); + // Send accelerometer interrupt data + response_event.response.type.battery_data_response.last_response = 0; + response_event.response.type.battery_data_response.timestamp = battery_chunk.timestamp; + response_event.response.type.battery_data_response.battery_data = battery_chunk.battery_data; + + send_response(NULL, 0); + } else if(ret == NRF_ERROR_NOT_FOUND || ret == NRF_ERROR_INVALID_STATE) { + debug_log("REQUEST_HANDLER: Could not fetch battery-data. Sending end Header..\n"); + response_event.response.type.battery_data_response.last_response = 1; + + // Send end-header + response_event.response_success_handler = NULL; + send_response(NULL, 0); + } else { + app_sched_event_put(NULL, 0, battery_data_response_handler); + } +} + +static void stream_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(stream_response_handler) != NRF_SUCCESS) + return; + + + response_event.response.which_type = Response_stream_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + response_event.response.type.stream_response.battery_stream_count = 0; + response_event.response.type.stream_response.microphone_stream_count = 0; + response_event.response.type.stream_response.scan_stream_count = 0; + response_event.response.type.stream_response.accelerometer_stream_count = 0; + response_event.response.type.stream_response.accelerometer_interrupt_stream_count = 0; + + systick_get_timestamp(&response_event.response.type.stream_response.timestamp.seconds, &response_event.response.type.stream_response.timestamp.ms); + + sampling_configuration_t sampling_configuration = sampling_get_sampling_configuration(); + #if ACCELEROMETER_PRESENT + if(sampling_configuration & STREAMING_ACCELEROMETER) { + uint32_t n = circular_fifo_get_size(&accelerometer_stream_fifo) / sizeof(AccelerometerStream); + n = n > PROTOCOL_ACCELEROMETER_STREAM_SIZE ? PROTOCOL_ACCELEROMETER_STREAM_SIZE : n; + response_event.response.type.stream_response.accelerometer_stream_count = n; + uint32_t len = n*sizeof(AccelerometerStream); + circular_fifo_read(&accelerometer_stream_fifo, (uint8_t*) response_event.response.type.stream_response.accelerometer_stream, &len); + } + + if(sampling_configuration & STREAMING_ACCELEROMETER_INTERRUPT) { + uint32_t n = circular_fifo_get_size(&accelerometer_interrupt_stream_fifo) / sizeof(AccelerometerInterruptStream); + n = n > PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE ? PROTOCOL_ACCELEROMETER_INTERRUPT_STREAM_SIZE : n; + response_event.response.type.stream_response.accelerometer_interrupt_stream_count = n; + uint32_t len = n*sizeof(AccelerometerInterruptStream); + circular_fifo_read(&accelerometer_interrupt_stream_fifo, (uint8_t*) response_event.response.type.stream_response.accelerometer_interrupt_stream, &len); + } + #endif + + if(sampling_configuration & STREAMING_BATTERY) { + uint32_t n = circular_fifo_get_size(&battery_stream_fifo) / sizeof(BatteryStream); + n = n > PROTOCOL_BATTERY_STREAM_SIZE ? PROTOCOL_BATTERY_STREAM_SIZE : n; + response_event.response.type.stream_response.battery_stream_count = n; + uint32_t len = n*sizeof(BatteryStream); + circular_fifo_read(&battery_stream_fifo, (uint8_t*) response_event.response.type.stream_response.battery_stream, &len); + } + + if(sampling_configuration & STREAMING_MICROPHONE) { + uint32_t n = circular_fifo_get_size(µphone_stream_fifo) / sizeof(MicrophoneStream); + n = n > PROTOCOL_MICROPHONE_STREAM_SIZE ? PROTOCOL_MICROPHONE_STREAM_SIZE : n; + response_event.response.type.stream_response.microphone_stream_count = n; + uint32_t len = n*sizeof(MicrophoneStream); + circular_fifo_read(µphone_stream_fifo, (uint8_t*) response_event.response.type.stream_response.microphone_stream, &len); + } + + if(sampling_configuration & STREAMING_SCAN) { + uint32_t n = circular_fifo_get_size(&scan_stream_fifo) / sizeof(ScanStream); + n = n > PROTOCOL_SCAN_STREAM_SIZE ? PROTOCOL_SCAN_STREAM_SIZE : n; + response_event.response.type.stream_response.scan_stream_count = n; + uint32_t len = n*sizeof(ScanStream); + circular_fifo_read(&scan_stream_fifo, (uint8_t*) response_event.response.type.stream_response.scan_stream, &len); + } + + + + // Only send the response if we have sth to send + if( response_event.response.type.stream_response.battery_stream_count || + response_event.response.type.stream_response.microphone_stream_count || + response_event.response.type.stream_response.scan_stream_count || + response_event.response.type.stream_response.accelerometer_stream_count || + response_event.response.type.stream_response.accelerometer_interrupt_stream_count) { + + send_response(NULL, 0); + } else { + finish_response(); + } + + // E.g. when disconnected or any other error occured + if(!streaming_started) { + // Stop all streamings + sampling_stop_accelerometer(1); + sampling_stop_accelerometer_interrupt(1); + sampling_stop_battery(1); + sampling_stop_microphone(1); + sampling_stop_scan(1); + } + + // Check if we need to cancel the streaming + if((!(sampling_configuration & STREAMING_ACCELEROMETER || sampling_configuration & STREAMING_ACCELEROMETER_INTERRUPT || + sampling_configuration & STREAMING_BATTERY || sampling_configuration & STREAMING_MICROPHONE || sampling_configuration & STREAMING_SCAN)) || !streaming_started) { + streaming_started = 0; + } else { + // Put itself manually on the scheduler again --> if not ready with sending the response start_response() will reschedule + // It is done this way (not with the response_event.response_success_handler) because if sth wents wrong during sending + // this function won't be called anymore to disable the streaming for all peripherals. + // Another reason is that we probably don't want to send everytime this stream_response_handler is called + // because we don't have anything to send (all counts = 0) + app_sched_event_put(NULL, 0, stream_response_handler); + } +} + + + +static void test_response_handler(void * p_event_data, uint16_t event_size) { + if(start_response(test_response_handler) != NRF_SUCCESS) + return; + + response_event.response.which_type = Response_test_response_tag; + response_event.response_retries = 0; + response_event.response_success_handler = NULL; + + selftest_status_t status = selftest_test(); + + response_event.response.type.test_response.test_failed = (uint8_t) status; + + send_response(NULL, 0); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/**< These are the request handlers that actually call the response-handlers via the scheduler */ + + + +static void status_request_handler(void * p_event_data, uint16_t event_size) { + + // Set the timestamp: + Timestamp timestamp = request_event.request.type.status_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + if(request_event.request.type.status_request.has_badge_assignement) { + + BadgeAssignement badge_assignement; + badge_assignement = request_event.request.type.status_request.badge_assignement; + + ret_code_t ret = advertiser_set_badge_assignement(badge_assignement); + if(ret == NRF_ERROR_INTERNAL) { + app_sched_event_put(NULL, 0, status_request_handler); + return; + } else if(ret != NRF_SUCCESS) { + finish_error(); + return; + } + // ret should be NRF_SUCCESS here + } + + app_sched_event_put(NULL, 0, status_response_handler); + // Don't finish it here, but in the response-handler (because of the response_timestamp and response_clock_status) + //finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. +} + + +static void start_microphone_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = request_event.request.type.start_microphone_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_microphone_request.timeout; + uint16_t period_ms = (request_event.request).type.start_microphone_request.period_ms; + + debug_log("REQUEST_HANDLER: Start microphone with timeout: %u, period ms %u\n", timeout, period_ms); + + ret_code_t ret = sampling_start_microphone(timeout*60*1000, period_ms, 0); + debug_log("REQUEST_HANDLER: Ret sampling_start_microphone: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + app_sched_event_put(NULL, 0, start_microphone_response_handler); + // Don't finish it here, but in the response-handler (because of the response_timestamp and response_clock_status) + //finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_microphone_request_handler); + } +} + +static void stop_microphone_request_handler(void * p_event_data, uint16_t event_size) { + + sampling_stop_microphone(0); + + debug_log("REQUEST_HANDLER: Stop microphone\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + +static void start_scan_request_handler(void * p_event_data, uint16_t event_size) { + + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_scan_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_scan_request.timeout; + uint16_t window = (request_event.request).type.start_scan_request.window; + uint16_t interval = (request_event.request).type.start_scan_request.interval; + uint16_t duration = (request_event.request).type.start_scan_request.duration; + uint16_t period = (request_event.request).type.start_scan_request.period; + uint8_t aggregation_type = (request_event.request).type.start_scan_request.aggregation_type; + + if(duration > period) + period = duration; + + debug_log("REQUEST_HANDLER: Start scanning with timeout: %u, window: %u, interval: %u, duration: %u, period: %u, aggregation_type: %u\n", timeout, window, interval, duration, period, aggregation_type); + BadgeAssignement badge_assignement; + advertiser_get_badge_assignement(&badge_assignement); + ret_code_t ret = sampling_start_scan(timeout*60*1000, period, interval, window, duration, badge_assignement.group, aggregation_type, 0); + debug_log("REQUEST_HANDLER: Ret sampling_start_scan: %d\n\r", ret); + + + if(ret == NRF_SUCCESS) { + app_sched_event_put(NULL, 0, start_scan_response_handler); + // Don't finish it here, but in the response-handler (because of the response_timestamp and response_clock_status) + //finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_scan_request_handler); + } +} + +static void stop_scan_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_scan(0); + debug_log("REQUEST_HANDLER: Stop scan\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + +static void start_accelerometer_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_accelerometer_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_accelerometer_request.timeout; + uint8_t operating_mode = (request_event.request).type.start_accelerometer_request.operating_mode; + uint8_t full_scale = (request_event.request).type.start_accelerometer_request.full_scale; + uint16_t datarate = (request_event.request).type.start_accelerometer_request.datarate; + uint16_t fifo_sampling_period_ms = (request_event.request).type.start_accelerometer_request.fifo_sampling_period_ms; + + debug_log("REQUEST_HANDLER: Start accelerometer with timeout: %u, operating_mode: %u, full_scale: %u, datarate: %u, fifo_sampling_period_ms: %u\n", timeout, operating_mode, full_scale, datarate, fifo_sampling_period_ms); + + ret_code_t ret = sampling_start_accelerometer(timeout*60*1000, operating_mode, full_scale, datarate, fifo_sampling_period_ms, 0); + debug_log("REQUEST_HANDLER: Ret sampling_start_accelerometer: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + app_sched_event_put(NULL, 0, start_accelerometer_response_handler); + // Don't finish it here, but in the response-handler (because of the response_timestamp and response_clock_status) + //finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_accelerometer_request_handler); + } +} + +static void stop_accelerometer_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_accelerometer(0); + debug_log("REQUEST_HANDLER: Stop accelerometer\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + + +static void start_accelerometer_interrupt_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_accelerometer_interrupt_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_accelerometer_interrupt_request.timeout; + uint16_t threshold_mg = (request_event.request).type.start_accelerometer_interrupt_request.threshold_mg; + uint16_t minimal_duration_ms = (request_event.request).type.start_accelerometer_interrupt_request.minimal_duration_ms; + uint16_t ignore_duration_ms = (request_event.request).type.start_accelerometer_interrupt_request.ignore_duration_ms; + + debug_log("REQUEST_HANDLER: Start accelerometer interrupt with timeout: %u, threshold_mg: %u, minimal_duration_ms: %u, ignore_duration_ms: %u\n", timeout, threshold_mg, minimal_duration_ms, ignore_duration_ms); + + ret_code_t ret = sampling_start_accelerometer_interrupt(timeout*60*1000, threshold_mg, minimal_duration_ms, ignore_duration_ms, 0); + debug_log("REQUEST_HANDLER: Ret sampling_start_accelerometer_interrupt: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + app_sched_event_put(NULL, 0, start_accelerometer_interrupt_response_handler); + // Don't finish it here, but in the response-handler (because of the response_timestamp and response_clock_status) + //finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_accelerometer_interrupt_request_handler); + } +} + +static void stop_accelerometer_interrupt_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_accelerometer_interrupt(0); + debug_log("REQUEST_HANDLER: Stop accelerometer interrupt\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + +static void start_battery_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_battery_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_battery_request.timeout; + uint16_t period_ms = (request_event.request).type.start_battery_request.period_ms; + + debug_log("REQUEST_HANDLER: Start battery with timeout %u, period_ms: %u\n", timeout, period_ms); + + ret_code_t ret = sampling_start_battery(timeout*60*1000, period_ms, 0); + debug_log("REQUEST_HANDLER: Ret sampling_start_battery: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + app_sched_event_put(NULL, 0, start_battery_response_handler); + // Don't finish it here, but in the response-handler (because of the response_timestamp and response_clock_status) + //finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification. + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_battery_request_handler); + } +} +static void stop_battery_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_battery(0); + debug_log("REQUEST_HANDLER: Stop battery\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + +static void microphone_data_request_handler(void * p_event_data, uint16_t event_size) { + Timestamp timestamp = request_event.request.type.microphone_data_request.timestamp; + debug_log("REQUEST_HANDLER: Pull microphone data since: %u s, %u ms\n", timestamp.seconds, timestamp.ms); + + ret_code_t ret = storer_find_microphone_chunk_from_timestamp(timestamp, µphone_chunk); + if(ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) { + app_sched_event_put(NULL, 0, microphone_data_response_handler); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, microphone_data_request_handler); + } +} + + +static void scan_data_request_handler(void * p_event_data, uint16_t event_size) { + Timestamp timestamp = request_event.request.type.scan_data_request.timestamp; + debug_log("REQUEST_HANDLER: Pull scan data since: %u s, %u ms\n", timestamp.seconds, timestamp.ms); + + ret_code_t ret = storer_find_scan_chunk_from_timestamp(timestamp, &scan_chunk); + if(ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) { + app_sched_event_put(NULL, 0, scan_data_response_handler); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, scan_data_request_handler); + } +} + + +static void accelerometer_data_request_handler(void * p_event_data, uint16_t event_size) { + + Timestamp timestamp = request_event.request.type.accelerometer_data_request.timestamp; + debug_log("REQUEST_HANDLER: Pull accelerometer data since: %u s, %u ms\n", timestamp.seconds, timestamp.ms); + + ret_code_t ret = storer_find_accelerometer_chunk_from_timestamp(timestamp, &accelerometer_chunk); + if(ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) { + app_sched_event_put(NULL, 0, accelerometer_data_response_handler); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, accelerometer_data_request_handler); + } +} + + +static void accelerometer_interrupt_data_request_handler(void * p_event_data, uint16_t event_size) { + + Timestamp timestamp = request_event.request.type.accelerometer_interrupt_data_request.timestamp; + debug_log("REQUEST_HANDLER: Pull accelerometer interrupt data since: %u s, %u ms\n", timestamp.seconds, timestamp.ms); + + ret_code_t ret = storer_find_accelerometer_interrupt_chunk_from_timestamp(timestamp, &accelerometer_interrupt_chunk); + if(ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) { + app_sched_event_put(NULL, 0, accelerometer_interrupt_data_response_handler); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, accelerometer_interrupt_data_request_handler); + } +} + + +static void battery_data_request_handler(void * p_event_data, uint16_t event_size) { + Timestamp timestamp = request_event.request.type.battery_data_request.timestamp; + debug_log("REQUEST_HANDLER: Pull battery data since: %u s, %u ms\n", timestamp.seconds, timestamp.ms); + + ret_code_t ret = storer_find_battery_chunk_from_timestamp(timestamp, &battery_chunk); + if(ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) { + app_sched_event_put(NULL, 0, battery_data_response_handler); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, battery_data_request_handler); + } +} + +static void start_microphone_stream_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = request_event.request.type.start_microphone_stream_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_microphone_stream_request.timeout; + uint16_t period_ms = (request_event.request).type.start_microphone_stream_request.period_ms; + + debug_log("REQUEST_HANDLER: Start microphone stream with timeout: %u, period ms %u\n", timeout, period_ms); + + ret_code_t ret = sampling_start_microphone(timeout*60*1000, period_ms, 1); + debug_log("REQUEST_HANDLER: Ret sampling_start_microphone stream: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + if(!streaming_started) { + streaming_started = 1; + app_sched_event_put(NULL, 0, stream_response_handler); + } + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_microphone_stream_request_handler); + } +} + +static void stop_microphone_stream_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_microphone(1); + + debug_log("REQUEST_HANDLER: Stop microphone stream\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + +static void start_scan_stream_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_scan_stream_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_scan_stream_request.timeout; + uint16_t window = (request_event.request).type.start_scan_stream_request.window; + uint16_t interval = (request_event.request).type.start_scan_stream_request.interval; + uint16_t duration = (request_event.request).type.start_scan_stream_request.duration; + uint16_t period = (request_event.request).type.start_scan_stream_request.period; + uint8_t aggregation_type = (request_event.request).type.start_scan_stream_request.aggregation_type; + + if(duration > period) + period = duration; + + debug_log("REQUEST_HANDLER: Start scanning stream with timeout: %u, window: %u, interval: %u, duration: %u, period: %u\n", timeout, window, interval, duration, period); + BadgeAssignement badge_assignement; + advertiser_get_badge_assignement(&badge_assignement); + ret_code_t ret = sampling_start_scan(timeout*60*1000, period, interval, window, duration, badge_assignement.group, aggregation_type, 1); + debug_log("REQUEST_HANDLER: Ret sampling_start_scan stream: %d\n\r", ret); + + + if(ret == NRF_SUCCESS) { + if(!streaming_started) { + streaming_started = 1; + app_sched_event_put(NULL, 0, stream_response_handler); + } + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_scan_stream_request_handler); + } +} + +static void stop_scan_stream_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_scan(1); + + debug_log("REQUEST_HANDLER: Stop scan stream\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + + + +static void start_accelerometer_stream_request_handler(void * p_event_data, uint16_t event_size) { + + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_accelerometer_stream_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_accelerometer_stream_request.timeout; + uint8_t operating_mode = (request_event.request).type.start_accelerometer_stream_request.operating_mode; + uint8_t full_scale = (request_event.request).type.start_accelerometer_stream_request.full_scale; + uint16_t datarate = (request_event.request).type.start_accelerometer_stream_request.datarate; + uint16_t fifo_sampling_period_ms = (request_event.request).type.start_accelerometer_stream_request.fifo_sampling_period_ms; + + debug_log("REQUEST_HANDLER: Start accelerometer stream with timeout: %u, operating_mode: %u, full_scale: %u, datarate: %u, fifo_sampling_period_ms: %u\n", timeout, operating_mode, full_scale, datarate, fifo_sampling_period_ms); + + ret_code_t ret = sampling_start_accelerometer(timeout*60*1000, operating_mode, full_scale, datarate, fifo_sampling_period_ms, 1); + debug_log("REQUEST_HANDLER: Ret sampling_start_accelerometer stream: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + if(!streaming_started) { + streaming_started = 1; + app_sched_event_put(NULL, 0, stream_response_handler); + } + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_accelerometer_stream_request_handler); + } +} +static void stop_accelerometer_stream_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_accelerometer(1); + + debug_log("REQUEST_HANDLER: Stop accelerometer stream\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + + + +static void start_accelerometer_interrupt_stream_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_accelerometer_interrupt_stream_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_accelerometer_interrupt_stream_request.timeout; + uint16_t threshold_mg = (request_event.request).type.start_accelerometer_interrupt_stream_request.threshold_mg; + uint16_t minimal_duration_ms = (request_event.request).type.start_accelerometer_interrupt_stream_request.minimal_duration_ms; + uint16_t ignore_duration_ms = (request_event.request).type.start_accelerometer_interrupt_stream_request.ignore_duration_ms; + + debug_log("REQUEST_HANDLER: Start accelerometer interrupt stream with timeout: %u, threshold_mg: %u, minimal_duration_ms: %u, ignore_duration_ms: %u\n", timeout, threshold_mg, minimal_duration_ms, ignore_duration_ms); + + ret_code_t ret = sampling_start_accelerometer_interrupt(timeout*60*1000, threshold_mg, minimal_duration_ms, ignore_duration_ms, 1); + debug_log("REQUEST_HANDLER: Ret sampling_start_accelerometer_interrupt stream: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + if(!streaming_started) { + streaming_started = 1; + app_sched_event_put(NULL, 0, stream_response_handler); + } + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_accelerometer_interrupt_stream_request_handler); + } +} +static void stop_accelerometer_interrupt_stream_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_accelerometer_interrupt(1); + + debug_log("REQUEST_HANDLER: Stop accelerometer interrupt stream\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + + + + +static void start_battery_stream_request_handler(void * p_event_data, uint16_t event_size) { + // Set the timestamp: + Timestamp timestamp = (request_event.request).type.start_battery_stream_request.timestamp; + systick_set_timestamp(request_event.request_timepoint_ticks, timestamp.seconds, timestamp.ms); + advertiser_set_status_flag_is_clock_synced(1); + + uint32_t timeout = (request_event.request).type.start_battery_stream_request.timeout; + uint16_t period_ms = (request_event.request).type.start_battery_stream_request.period_ms; + + debug_log("REQUEST_HANDLER: Start battery stream with timeout %u, period_ms: %u\n", timeout, period_ms); + + ret_code_t ret = sampling_start_battery(timeout*60*1000, period_ms, 1); + debug_log("REQUEST_HANDLER: Ret sampling_start_battery stream: %d\n\r", ret); + + if(ret == NRF_SUCCESS) { + if(!streaming_started) { + streaming_started = 1; + app_sched_event_put(NULL, 0, stream_response_handler); + } + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification + } else { + // TODO: Error counter for rescheduling + app_sched_event_put(NULL, 0, start_battery_stream_request_handler); + } +} +static void stop_battery_stream_request_handler(void * p_event_data, uint16_t event_size) { + sampling_stop_battery(1); + + debug_log("REQUEST_HANDLER: Stop battery stream\n"); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + +static void identify_request_handler(void * p_event_data, uint16_t event_size) { + uint16_t timeout = (request_event.request).type.identify_request.timeout; + (void) timeout; + debug_log("REQUEST_HANDLER: Identify request with timeout: %u\n", timeout); + #ifndef UNIT_TEST + nrf_gpio_pin_write(RED_LED, LED_ON); + systick_delay_millis(timeout*1000); + nrf_gpio_pin_write(RED_LED, LED_OFF); + #endif + + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} +static void test_request_handler(void * p_event_data, uint16_t event_size) { + debug_log("REQUEST_HANDLER: Test request handler\n"); + + app_sched_event_put(NULL, 0, test_response_handler); + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + +static void restart_request_handler(void * p_event_data, uint16_t event_size) { + debug_log("REQUEST_HANDLER: Restart request handler\n"); + #ifndef UNIT_TEST + NVIC_SystemReset(); + #endif + finish_and_reschedule_receive_notification(); // Now we are done with processing the request --> we can now advance to the next receive-notification +} + + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/request_handler_lib_02v1.h b/firmware/nRF_badge/data_collector/incl/request_handler_lib_02v1.h new file mode 100644 index 0000000..11bdb48 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/request_handler_lib_02v1.h @@ -0,0 +1,31 @@ +#ifndef __REQUEST_HANDLER_LIB_H +#define __REQUEST_HANDLER_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + + +/**@brief Function to initialize the request-handler mechanism. + * + * @details When sth is received via the ble-interface, the notification handler is called. + * The notification is processed (and the ticks at the receive timepoint is saved to set the internal clock correctly). + * After the processing of the notification a corresponding request-handler is called, that interprets and processes + * the received data. Optionally the request-handler calls a response handler that generates a response and sends + * it via the ble-interface. + * + * + * @note app-scheduler has to be initialized before! + * @note sender_lib has to be initialized before! + * @note advertiser_lib has to be initialized before! + * @note storer_lib has to be initialized before! + * @note sampling_lib has to be initialized before! + * @note systick_lib has to be initialized before! + */ + +ret_code_t request_handler_init(void); + + + + +#endif + diff --git a/firmware/nRF_badge/data_collector/incl/rtc_timing.c b/firmware/nRF_badge/data_collector/incl/rtc_timing.c deleted file mode 100644 index def8d08..0000000 --- a/firmware/nRF_badge/data_collector/incl/rtc_timing.c +++ /dev/null @@ -1,164 +0,0 @@ -#include -#include - -#include "rtc_timing.h" -#include "ble_setup.h" -#include "custom_board.h" - -// This is a slight optimization. Our timer wakes our chip whenever the timer goes off, -// so we want to wake it as little as necessary. Thus, we want out clock timer to rarely tick, but we needs millisecond -// precision. This is tricky, because the RTC timer only has 24 bits and app timers can only last <= 512 seconds. -// -// So, we set a timer that ticks every 100 (<=512) seconds, and then we calculate the current time based on time -// since last clock tick using our RTC timer through app_timer_cnt_get(). -#define CLOCK_TICK_MILLIS (100 * 1000) - -#define MILLIS_PER_TICK (1000.f / ((APP_PRESCALER + 1) * APP_TIMER_CLOCK_FREQ)) - -volatile bool countdownOver = false; //used to give rtc_timing access to sleep from main loop - -static uint32_t mCountdownTimer; -static uint32_t mBLETimeoutTimer; -static uint32_t mLEDTimeoutTimer; -static uint32_t mClock; - -static uint32_t mClockInMillis; -static uint32_t mLastClockTickTimerCount; - -static void on_countdown_timeout(void * p_context) { - countdownOver = true; -} - -static void on_ble_timeout(void * p_context) { - debug_log("Connection timeout. Disconnecting...\r\n"); - BLEforceDisconnect(); -} - -static void on_led_timeout(void * p_context) { - nrf_gpio_pin_write(LED_2,LED_OFF); -} - -static void on_clock_timeout(void * p_context) { - app_timer_cnt_get(&mLastClockTickTimerCount); - mClockInMillis += CLOCK_TICK_MILLIS; -} - -void rtc_config(void) -{ - APP_TIMER_INIT(APP_PRESCALER, APP_MAX_TIMERS, APP_OP_QUEUE_SIZE, false); - - app_timer_create(&mCountdownTimer, APP_TIMER_MODE_SINGLE_SHOT, on_countdown_timeout); - app_timer_create(&mBLETimeoutTimer, APP_TIMER_MODE_SINGLE_SHOT, on_ble_timeout); - app_timer_create(&mLEDTimeoutTimer, APP_TIMER_MODE_SINGLE_SHOT, on_led_timeout); - - app_timer_create(&mClock, APP_TIMER_MODE_REPEATED, on_clock_timeout); - app_timer_start(mClock, APP_TIMER_TICKS(CLOCK_TICK_MILLIS, APP_PRESCALER), NULL); -} - -static void start_singleshot_timer(uint32_t timer_id, unsigned long ms) { - if (ms > 130000UL) { // 130 seconds. - ms = 130000UL; // avoid overflow in calculation of compareTicks below. - } - - app_timer_stop(timer_id); // Stop the timer if running, new timers preempt old ones. - app_timer_start(timer_id, APP_TIMER_TICKS(ms, APP_PRESCALER), NULL); -} - -void countdown_set(unsigned long ms) -{ - countdownOver = false; - start_singleshot_timer(mCountdownTimer, ms); -} - - -void ble_timeout_set(unsigned long ms) -{ -#ifndef DEBUG_LOG_ENABLE - start_singleshot_timer(mBLETimeoutTimer, ms); -#endif -} - -void ble_timeout_cancel() -{ - app_timer_stop(mBLETimeoutTimer); -} - -void led_timeout_set(unsigned long ms) -{ - start_singleshot_timer(mLEDTimeoutTimer, ms); -} - -void led_timeout_cancel() -{ - app_timer_stop(mLEDTimeoutTimer); -} - -uint32_t timer_comparison_ticks_now(void) { - uint32_t timer_ticks; - app_timer_cnt_get(&timer_ticks); - return timer_ticks; -} - -uint32_t timer_comparison_ticks_since_start(uint32_t ticks_start) { - uint32_t current_time; - app_timer_cnt_get(¤t_time); - - uint32_t ticks_since_start; - app_timer_cnt_diff_compute(current_time, ticks_start, &ticks_since_start); - - return ticks_since_start; -} - -float timer_comparison_millis_since_start(uint32_t ticks_start) { - return timer_comparison_ticks_since_start(ticks_start) * MILLIS_PER_TICK; -} - -unsigned long millis(void) { - // We ensure that millis() calls are atomic operations, so that the clock does not tick during out calculations. - // If we do not ensure this, in rare cases, a clock tick interrupt will cause mClockInMillis and - // mLastClockTickTimerCount to be mismatched. - unsigned long millis; - CRITICAL_REGION_ENTER(); - millis = mClockInMillis + (unsigned long) timer_comparison_millis_since_start(mLastClockTickTimerCount); - CRITICAL_REGION_EXIT(); - - return millis; -} - - -unsigned long lastMillis; //last time now() was called - -struct -{ - unsigned long s; - unsigned long ms; -} masterTime; - -unsigned long now() -{ - unsigned long difference = millis() - lastMillis; - while (difference >= 1000) { - masterTime.s++; - lastMillis += 1000; - difference = millis() - lastMillis; - } - //difference is now the fractional part of the timestamp in ms, from 0-999 - masterTime.ms = difference; - return masterTime.s; -} - -unsigned long nowFractional() -{ - return masterTime.ms; -} - -void setTimeFractional(unsigned long timestamp, unsigned long fractional) -{ - masterTime.s = timestamp; - lastMillis = millis() - fractional; //we want difference=millis()-lastMillis to be the fractional part -} - -void setTime(unsigned long timestamp) -{ - setTimeFractional(timestamp, 0); -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/rtc_timing.h b/firmware/nRF_badge/data_collector/incl/rtc_timing.h deleted file mode 100644 index 158cab9..0000000 --- a/firmware/nRF_badge/data_collector/incl/rtc_timing.h +++ /dev/null @@ -1,122 +0,0 @@ -//sdfsdf - -#ifndef RTC_TIMING_H -#define RTC_TIMING_H - -#define APP_PRESCALER 0 -#define APP_MAX_TIMERS 12 -#define APP_OP_QUEUE_SIZE 6 - -#include "nrf_drv_config.h" -#include "nrf_soc.h" -#include "app_error.h" - -#include "debug_log.h" - -volatile bool countdownOver; //set true when the countdown interrupt triggers -volatile bool sleep; //whether we should sleep (so actions like data sending can override sleep) - -#define CONNECTION_TIMEOUT_MS 6000UL -//#define CONNECTION_TIMEOUT_MS 6000000UL - -/** - * initialize rtc - */ -void rtc_config(void); - - -/** - * set a compare interrupt to occur a number of milliseconds from now (i.e. to break from sleep) - * NOTE: Maximum countdown time is 130 seconds. - */ -void countdown_set(unsigned long ms); - - -/** - * similar to countdown_set, but used to keep track of BLE connection timeout. - */ -void ble_timeout_set(unsigned long ms); - -/** - * cancel the ble timeout counter - */ -void ble_timeout_cancel(); - -/** - * similar to countdown_set, but used to keep track of LED indicator timeout. - */ -void led_timeout_set(unsigned long ms); - -/** - * cancel the LED timeout counter - */ -void led_timeout_cancel(); - - -/** - * returns 32768Hz ticks of RTC, extended to 43 bits (from built-in 24 bits) - */ -unsigned long long ticks(void); - -/** - * emulate functionality of millis() in arduino - * divide rtc count by 32768=2^15, get number of seconds since timer started - * x1000 or 1000000 for millis, micros. (~30.5us precision) - */ -unsigned long millis(void); - -/** - * Returns a timer tick starting point for timer_comparison_millis_since_start comparisons that start at - * the current time. - * Safe to use from interupt context. - * - * @return the current time as a value that can be used as an argument for timer_comparison_millis_since_start - */ -uint32_t timer_comparison_ticks_now(void); - -/** - * Returns the number of timer ticks (in terms of TIMER_TICKS_MS with APP_PRESCALER) since the given timer ticks time. - * - * Used to optimize code as the NRF lacks an FPU, so float operations are slow. Generally, only use when profiling - * indicates a performance reason to do so. Otherwise, use timer_comparison_millis_since_start. - */ -uint32_t timer_comparison_ticks_since_start(uint32_t ticks_start); - -/** - * Returns the number of millis since the starting point ticks_start (from timer_comparison_ticks_now). - * Has percision down to 1/APP_TIMER_FREQS seconds. Can be used for comparisons up to ~512 ms long. - * Safe to use from interupt context - * - * @return the number of milliseconds since ticks_start as a float - */ -float timer_comparison_millis_since_start(uint32_t ticks_start); - -/** - * get current timestamp (set with setTime) in seconds (most likely epoch time) - * Inspired by Arduino Time library - */ -unsigned long now(); - -/** - * get fractional part of timestamp in ms, i.e. returns 0-999. - * For better time precision, while still keeping simple compatibility with epoch time - * Corresponds to time when now() was last called, not actual current time! - */ -unsigned long nowFractional(); - -/** - * set current timestamp (set with setTime) - * In seconds, like Unix time, but extra parameter to set it partway through a full second. - */ -void setTimeFractional(unsigned long timestamp, unsigned long fractional); - -/** - * set current timestamp (set with setTime) - * In seconds, like Unix time - */ -void setTime(unsigned long timestamp); - - - - -#endif //TIMING_H \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/sampling_lib.c b/firmware/nRF_badge/data_collector/incl/sampling_lib.c new file mode 100644 index 0000000..72b20cd --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/sampling_lib.c @@ -0,0 +1,1265 @@ +#include "sampling_lib.h" +#include "app_scheduler.h" +#include "app_timer.h" +#include "chunk_messages.h" +#include "stream_messages.h" +#include "processing_lib.h" +#include "timeout_lib.h" + +#include "accel_lib.h" +#include "battery_lib.h" +#include "microphone_lib.h" +#include "scanner_lib.h" +#include "advertiser_lib.h" + +#include "systick_lib.h" + +// TODO: remove +#include "debug_lib.h" + + + +#define SAMPLING_ACCEL_ENABLED ACCELEROMETER_PRESENT + + + + +#define ABS(x) (((x) >= 0)? (x) : -(x)) + + +#define AGGREGATE_SCAN_SAMPLE_MAX(sample, aggregated) ((sample) > (aggregated) ? (sample) : (aggregated)) +#define PROCESS_SCAN_SAMPLE_MAX(aggregated, count) (aggregated) + +#define AGGREGATE_SCAN_SAMPLE_MEAN(sample, aggregated) ((aggregated) + (sample)) +#define PROCESS_SCAN_SAMPLE_MEAN(aggregated, count) ((aggregated)/(count)) + +#define MICROPHONE_READING_PERIOD_MS (1000.0f / 700.0f) +#define MICROPHONE_READING_SLEEP_RATIO 0.075 +#define MICROPHONE_READING_WINDOW_MS (MICROPHONE_READING_PERIOD_MS * MICROPHONE_READING_SLEEP_RATIO) + + +static sampling_configuration_t sampling_configuration; + + + + +#if SAMPLING_ACCEL_ENABLED +chunk_fifo_t accelerometer_chunk_fifo; +circular_fifo_t accelerometer_stream_fifo; +static AccelerometerChunk* accelerometer_chunk = NULL; +typedef struct { + uint32_t accelerometer_timeout_ms; + uint32_t accelerometer_stream_timeout_ms; + accel_datarate_t accelerometer_datarate; + accel_operating_mode_t accelerometer_operating_mode; + accel_full_scale_t accelerometer_full_scale; + uint16_t accelerometer_fifo_sampling_period_ms; +} sampling_accelerometer_parameters_t; +static sampling_accelerometer_parameters_t sampling_accelerometer_parameters; +static uint32_t accelerometer_timeout_id; +static uint32_t accelerometer_stream_timeout_id; + +chunk_fifo_t accelerometer_interrupt_chunk_fifo; +circular_fifo_t accelerometer_interrupt_stream_fifo; +static AccelerometerInterruptChunk* accelerometer_interrupt_chunk = NULL; +static uint32_t accelerometer_interrupt_ignore_duration_ms = 0; +static uint32_t accelerometer_interrupt_timeout_id; +static uint32_t accelerometer_interrupt_stream_timeout_id; +#endif + +chunk_fifo_t battery_chunk_fifo; +circular_fifo_t battery_stream_fifo; +static BatteryChunk* battery_chunk = NULL; +typedef struct { + uint32_t battery_timeout_ms; + uint32_t battery_stream_timeout_ms; + uint32_t battery_period_ms; +} sampling_battery_parameters_t; +static sampling_battery_parameters_t sampling_battery_parameters; +static uint32_t battery_timeout_id; +static uint32_t battery_stream_timeout_id; + +chunk_fifo_t microphone_chunk_fifo; +circular_fifo_t microphone_stream_fifo; +static MicrophoneChunk* microphone_chunk = NULL; +static const float microphone_aggregated_period_ms = MICROPHONE_READING_PERIOD_MS; +typedef struct { + uint32_t microphone_timeout_ms; + uint32_t microphone_stream_timeout_ms; + uint16_t microphone_period_ms; +} sampling_microphone_parameters_t; +static sampling_microphone_parameters_t sampling_microphone_parameters; +static uint32_t microphone_aggregated = 0; +static uint32_t microphone_aggregated_count = 0; +static uint32_t microphone_timeout_id; +static uint32_t microphone_stream_timeout_id; + +chunk_fifo_t scan_sampling_chunk_fifo; +circular_fifo_t scan_stream_fifo; +static ScanSamplingChunk* scan_sampling_chunk = NULL; +typedef struct { + uint32_t scan_timeout_ms; + uint32_t scan_stream_timeout_ms; + uint16_t scan_period_seconds; + uint16_t scan_interval_ms; + uint16_t scan_window_ms; + uint16_t scan_duration_seconds; + uint8_t scan_group_filter; + uint8_t scan_aggregation_type; /**< The type of the aggregation: [SCAN_CHUNK_AGGREGATE_TYPE_MAX] == MAX, [SCAN_CHUNK_AGGREGATE_TYPE_MEAN] == MEAN */ +} sampling_scan_parameters_t; +static sampling_scan_parameters_t sampling_scan_parameters; +/* +static uint16_t scan_interval_ms = 0; +static uint16_t scan_window_ms = 0; +static uint16_t scan_duration_seconds = 0; +static uint8_t scan_group_filter = 0; +static uint8_t scan_aggregation_type = SCAN_CHUNK_AGGREGATE_TYPE_MAX; +*/ + +static int32_t scan_aggregated_rssi[SCAN_SAMPLING_CHUNK_DATA_SIZE]; /**< Temporary array to aggregate the rssi-data */ +static uint32_t scan_timeout_id; +static uint32_t scan_stream_timeout_id; +/**< The scan:period is directly setted by starting the timer */ + + +APP_TIMER_DEF(sampling_accelerometer_fifo_timer); +void sampling_accelerometer_fifo_callback(void* p_context); +void sampling_setup_accelerometer_chunk(void); +void sampling_finalize_accelerometer_chunk(void); +void sampling_timeout_accelerometer(void); +void sampling_timeout_accelerometer_stream(void); + +APP_TIMER_DEF(sampling_accelerometer_interrupt_reset_timer); /**< The timer that resets the interrupt of the accelerometer. */ +void sampling_accelerometer_interrupt_callback(accel_interrupt_event_t const * p_event); +void sampling_accelerometer_interrupt_reset_callback(void* p_context); +void sampling_setup_accelerometer_interrupt_chunk(void * p_event_data, uint16_t event_size); /**< Because it has to be called via the scheduler */ +void sampling_finalize_accelerometer_interrupt_chunk(void); +void sampling_timeout_accelerometer_interrupt(void); +void sampling_timeout_accelerometer_interrupt_stream(void); + +APP_TIMER_DEF(sampling_battery_timer); +void sampling_battery_callback(void* p_context); +void sampling_setup_battery_chunk(void); +void sampling_finalize_battery_chunk(void); +void sampling_timeout_battery(void); +void sampling_timeout_battery_stream(void); + +APP_TIMER_DEF(sampling_microphone_timer); +APP_TIMER_DEF(sampling_microphone_aggregated_timer); +void sampling_microphone_callback(void* p_context); +void sampling_microphone_aggregated_callback(void* p_context); +void sampling_setup_microphone_chunk(void); +void sampling_finalize_microphone_chunk(void); +void sampling_timeout_microphone(void); +void sampling_timeout_microphone_stream(void); + +APP_TIMER_DEF(sampling_scan_timer); +void sampling_scan_callback(void* p_context); /**< Starts a scanning cycle */ +void sampling_on_scan_timeout_callback(void); +void sampling_on_scan_report_callback(scanner_scan_report_t* scanner_scan_report); +void sampling_setup_scan_sampling_chunk(void); +void sampling_finalize_scan_sampling_chunk(void); +void sampling_timeout_scan(void); +void sampling_timeout_scan_stream(void); + + +ret_code_t sampling_init(void) { + ret_code_t ret = NRF_SUCCESS; + (void) ret; + + sampling_configuration = (sampling_configuration_t) 0; + + #if SAMPLING_ACCEL_ENABLED + /********************* ACCELEROMETER ***************************/ + ret = accel_init(); + if(ret != NRF_SUCCESS) return ret; + + // Configure the accelerometer + ret = accel_set_axis((accel_axis_t) (ACCEL_X_AXIS_ENABLE | ACCEL_Y_AXIS_ENABLE | ACCEL_Z_AXIS_ENABLE)); + if(ret != NRF_SUCCESS) return ret; + ret = accel_set_fifo(ACCEL_FIFO_STREAM_ENABLE); + if(ret != NRF_SUCCESS) return ret; + ret = accel_set_HP_filter(ACCEL_HP_FILTER_ENABLE); + if(ret != NRF_SUCCESS) return ret; + + // create a timer for sampling of the accelerometer data + ret = app_timer_create(&sampling_accelerometer_fifo_timer, APP_TIMER_MODE_REPEATED, sampling_accelerometer_fifo_callback); + if(ret != NRF_SUCCESS) return ret; + + // initialize the chunk-fifo for the accelereometer + CHUNK_FIFO_INIT(ret, accelerometer_chunk_fifo, 2, sizeof(AccelerometerChunk), 0); + if(ret != NRF_SUCCESS) return ret; + + // initialize the circular-fifo for the stream of the accelerometer + CIRCULAR_FIFO_INIT(ret, accelerometer_stream_fifo, sizeof(AccelerometerStream) * STREAM_ACCELEROMETER_FIFO_SIZE); + if(ret != NRF_SUCCESS) return ret; + + ret = timeout_register(&accelerometer_timeout_id, sampling_timeout_accelerometer); + if(ret != NRF_SUCCESS) return ret; + ret = timeout_register(&accelerometer_stream_timeout_id, sampling_timeout_accelerometer_stream); + if(ret != NRF_SUCCESS) return ret; + + /********************* ACCELEROMETER INTERRUPT ***************************/ + // create a timer for reset of the interrupt + ret = app_timer_create(&sampling_accelerometer_interrupt_reset_timer, APP_TIMER_MODE_SINGLE_SHOT, sampling_accelerometer_interrupt_reset_callback); + if(ret != NRF_SUCCESS) return ret; + + // initialize the chunk-fifo for the accelereometer interrupt + CHUNK_FIFO_INIT(ret, accelerometer_interrupt_chunk_fifo, 2, sizeof(AccelerometerInterruptChunk), 0); + if(ret != NRF_SUCCESS) return ret; + + // initialize the circular-fifo for the stream of the accelerometer interrupt + CIRCULAR_FIFO_INIT(ret, accelerometer_interrupt_stream_fifo, sizeof(AccelerometerInterruptStream) * STREAM_ACCELEROMETER_INTERRUPT_FIFO_SIZE); + if(ret != NRF_SUCCESS) return ret; + + ret = timeout_register(&accelerometer_interrupt_timeout_id, sampling_timeout_accelerometer_interrupt); + if(ret != NRF_SUCCESS) return ret; + ret = timeout_register(&accelerometer_interrupt_stream_timeout_id, sampling_timeout_accelerometer_interrupt_stream); + if(ret != NRF_SUCCESS) return ret; + + #endif + + + /************************ BATTERY *************************************/ + + ret = battery_init(); + if(ret != NRF_SUCCESS) return ret; + + // create a timer for battery measurement + ret = app_timer_create(&sampling_battery_timer, APP_TIMER_MODE_REPEATED, sampling_battery_callback); + if(ret != NRF_SUCCESS) return ret; + + // initialize the chunk-fifo for the battery data + CHUNK_FIFO_INIT(ret, battery_chunk_fifo, 2, sizeof(BatteryChunk), 0); + if(ret != NRF_SUCCESS) return ret; + + // initialize the circular-fifo for the stream of the battery data + CIRCULAR_FIFO_INIT(ret, battery_stream_fifo, sizeof(BatteryStream) * STREAM_BATTERY_FIFO_SIZE); + if(ret != NRF_SUCCESS) return ret; + + ret = timeout_register(&battery_timeout_id, sampling_timeout_battery); + if(ret != NRF_SUCCESS) return ret; + ret = timeout_register(&battery_stream_timeout_id, sampling_timeout_battery_stream); + if(ret != NRF_SUCCESS) return ret; + + + /********************* MICROPHONE ***********************************/ + microphone_init(); + + // create a timer for microphone sampling + ret = app_timer_create(&sampling_microphone_timer, APP_TIMER_MODE_REPEATED, sampling_microphone_callback); + if(ret != NRF_SUCCESS) return ret; + + // create a timer for microphone accumulation + ret = app_timer_create(&sampling_microphone_aggregated_timer, APP_TIMER_MODE_REPEATED, sampling_microphone_aggregated_callback); + if(ret != NRF_SUCCESS) return ret; + + // initialize the chunk-fifo for the microphone data + CHUNK_FIFO_INIT(ret, microphone_chunk_fifo, 3, sizeof(MicrophoneChunk), 0); + if(ret != NRF_SUCCESS) return ret; + + // initialize the circular-fifo for the stream of the microphone data + CIRCULAR_FIFO_INIT(ret, microphone_stream_fifo, sizeof(MicrophoneStream) * STREAM_MICROPHONE_FIFO_SIZE); + if(ret != NRF_SUCCESS) return ret; + + ret = timeout_register(µphone_timeout_id, sampling_timeout_microphone); + if(ret != NRF_SUCCESS) return ret; + ret = timeout_register(µphone_stream_timeout_id, sampling_timeout_microphone_stream); + if(ret != NRF_SUCCESS) return ret; + + /********************* SCAN ***************************************/ + scanner_init(); + + // Set timeout and report callback function + scanner_set_on_scan_timeout_callback(sampling_on_scan_timeout_callback); + scanner_set_on_scan_report_callback(sampling_on_scan_report_callback); + + // create a timer for scans + ret = app_timer_create(&sampling_scan_timer, APP_TIMER_MODE_REPEATED, sampling_scan_callback); + if(ret != NRF_SUCCESS) return ret; + + // initialize the chunk-fifo for the scan data + CHUNK_FIFO_INIT(ret, scan_sampling_chunk_fifo, 1, sizeof(ScanSamplingChunk), 0); + if(ret != NRF_SUCCESS) return ret; + + // initialize the circular-fifo for the stream of the scan data + CIRCULAR_FIFO_INIT(ret, scan_stream_fifo, sizeof(ScanStream) * STREAM_SCAN_FIFO_SIZE); + if(ret != NRF_SUCCESS) return ret; + + ret = timeout_register(&scan_timeout_id, sampling_timeout_scan); + if(ret != NRF_SUCCESS) return ret; + ret = timeout_register(&scan_stream_timeout_id, sampling_timeout_scan_stream); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + +sampling_configuration_t sampling_get_sampling_configuration(void) { + return sampling_configuration; +} + +void sampling_reset_timeouts(void) { + #if SAMPLING_ACCEL_ENABLED + timeout_reset(accelerometer_timeout_id); + timeout_reset(accelerometer_stream_timeout_id); + timeout_reset(accelerometer_interrupt_timeout_id); + timeout_reset(accelerometer_interrupt_stream_timeout_id); + #endif + timeout_reset(battery_timeout_id); + timeout_reset(battery_stream_timeout_id); + timeout_reset(microphone_timeout_id); + timeout_reset(microphone_stream_timeout_id); + timeout_reset(scan_timeout_id); + timeout_reset(scan_stream_timeout_id); +} + + + + +ret_code_t sampling_start_accelerometer(uint32_t timeout_ms, uint8_t operating_mode, uint8_t full_scale, uint16_t datarate, uint16_t fifo_sampling_period_ms, uint8_t streaming) { + ret_code_t ret = NRF_SUCCESS; + + #if SAMPLING_ACCEL_ENABLED + + accel_datarate_t accelerometer_datarate; + accel_operating_mode_t accelerometer_operating_mode; + accel_full_scale_t accelerometer_full_scale; + + + if(operating_mode == 0) + accelerometer_operating_mode = ACCEL_POWER_DOWN_MODE; + else if(operating_mode == 1) + accelerometer_operating_mode = ACCEL_LOW_POWER_MODE; + else if(operating_mode == 2) + accelerometer_operating_mode = ACCEL_NORMAL_MODE; + else if(operating_mode == 3) + accelerometer_operating_mode = ACCEL_HIGH_RESOLUTION_MODE; + else + accelerometer_operating_mode = ACCEL_LOW_POWER_MODE; + + + + if(full_scale <= 2) + accelerometer_full_scale = ACCEL_FULL_SCALE_2G; + else if(full_scale <= 4) + accelerometer_full_scale = ACCEL_FULL_SCALE_4G; + else if(full_scale <= 8) + accelerometer_full_scale = ACCEL_FULL_SCALE_8G; + else if(full_scale <= 16) + accelerometer_full_scale = ACCEL_FULL_SCALE_16G; + else + accelerometer_full_scale = ACCEL_FULL_SCALE_4G; + + + + if(datarate <= 1) + accelerometer_datarate = ACCEL_DATARATE_1_HZ; + else if(datarate <= 10) + accelerometer_datarate = ACCEL_DATARATE_10_HZ; + else if(datarate <= 25) + accelerometer_datarate = ACCEL_DATARATE_25_HZ; + else if(datarate <= 50) + accelerometer_datarate = ACCEL_DATARATE_50_HZ; + else if(datarate <= 100) + accelerometer_datarate = ACCEL_DATARATE_100_HZ; + else if(datarate <= 200) + accelerometer_datarate = ACCEL_DATARATE_200_HZ; + else if(datarate <= 400) + accelerometer_datarate = ACCEL_DATARATE_400_HZ; + else + accelerometer_datarate = ACCEL_DATARATE_10_HZ; + + + ret = accel_set_full_scale(accelerometer_full_scale); + if(ret != NRF_SUCCESS) return ret; + + ret = accel_set_datarate(accelerometer_datarate); + if(ret != NRF_SUCCESS) return ret; + + ret = accel_set_operating_mode(accelerometer_operating_mode); + if(ret != NRF_SUCCESS) return ret; + + + + uint8_t parameters_changed_sampling = 0; + // Check if the parameters has been changed + if((sampling_accelerometer_parameters.accelerometer_timeout_ms != timeout_ms) && !streaming) { + parameters_changed_sampling = 1; + } + + if(sampling_accelerometer_parameters.accelerometer_datarate != accelerometer_datarate || + sampling_accelerometer_parameters.accelerometer_operating_mode != accelerometer_operating_mode || + sampling_accelerometer_parameters.accelerometer_full_scale != accelerometer_full_scale || + sampling_accelerometer_parameters.accelerometer_fifo_sampling_period_ms != fifo_sampling_period_ms) + { + parameters_changed_sampling = 1; + } + + // Update the parameters + sampling_accelerometer_parameters.accelerometer_timeout_ms = (!streaming) ? timeout_ms : sampling_accelerometer_parameters.accelerometer_timeout_ms; + sampling_accelerometer_parameters.accelerometer_stream_timeout_ms = (streaming) ? timeout_ms : sampling_accelerometer_parameters.accelerometer_stream_timeout_ms; + sampling_accelerometer_parameters.accelerometer_datarate = accelerometer_datarate; + sampling_accelerometer_parameters.accelerometer_operating_mode = accelerometer_operating_mode; + sampling_accelerometer_parameters.accelerometer_full_scale = accelerometer_full_scale; + sampling_accelerometer_parameters.accelerometer_fifo_sampling_period_ms = fifo_sampling_period_ms; + + + + + if(!streaming) { + if((sampling_configuration & SAMPLING_ACCELEROMETER) == 0 || parameters_changed_sampling) { // Only stop and start the sampling if it is not already running or the parameters changed + debug_log("SAMPLING: (Re-)start accelerometer sampling\n"); + // Stop the sampling-timer that was probably already started + app_timer_stop(sampling_accelerometer_fifo_timer); + + // If we want to start normal sampling + sampling_setup_accelerometer_chunk(); + + // Now start the sampling-timer + ret = app_timer_start(sampling_accelerometer_fifo_timer, APP_TIMER_TICKS(fifo_sampling_period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | SAMPLING_ACCELEROMETER); + advertiser_set_status_flag_accelerometer_enabled(1); + + timeout_start(accelerometer_timeout_id, timeout_ms); + } else { + debug_log("SAMPLING: Ignoring start accelerometer sampling\n"); + } + + } else { + // If already sampling, but the parameters changed + if((sampling_configuration & SAMPLING_ACCELEROMETER) && parameters_changed_sampling) { + debug_log("SAMPLING: (Re-)start accelerometer sampling on stream request (because parameters changed)\n"); + app_timer_stop(sampling_accelerometer_fifo_timer); + sampling_setup_accelerometer_chunk(); + ret = app_timer_start(sampling_accelerometer_fifo_timer, APP_TIMER_TICKS(fifo_sampling_period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + } else if((sampling_configuration & SAMPLING_ACCELEROMETER) == 0) { // If we are not already sampling the accelerometer, we have to start the sampling-timer + debug_log("SAMPLING: Start accelerometer stream\n"); + // Stop the sampling-timer that was probably already started + app_timer_stop(sampling_accelerometer_fifo_timer); + + // Now start the sampling-timer + ret = app_timer_start(sampling_accelerometer_fifo_timer, APP_TIMER_TICKS(fifo_sampling_period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + } else { + debug_log("SAMPLING: Nothing to do to start accelerometer stream\n"); + } + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | STREAMING_ACCELEROMETER); + + timeout_start(accelerometer_stream_timeout_id, timeout_ms); + } + + + #endif + + + + return ret; +} + +ret_code_t sampling_stop_accelerometer(uint8_t streaming) { + ret_code_t ret = NRF_SUCCESS; + #if SAMPLING_ACCEL_ENABLED + // Check if we are allowed to stop the accelerometer-timer, and to disable the accelerometer + if(!streaming) { + if((sampling_configuration & STREAMING_ACCELEROMETER) == 0) { // We are only allowed to stop the accelerometer-timer, if we don't stream + app_timer_stop(sampling_accelerometer_fifo_timer); + // Only disable the accelerometer, if no interrupts should be generated + if((sampling_configuration & SAMPLING_ACCELEROMETER_INTERRUPT) == 0 && (sampling_configuration & STREAMING_ACCELEROMETER_INTERRUPT) == 0) { + ret = accel_set_operating_mode(ACCEL_POWER_DOWN_MODE); + if(ret != NRF_SUCCESS) return ret; + } + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(SAMPLING_ACCELEROMETER)); + advertiser_set_status_flag_accelerometer_enabled(0); + } else { + if((sampling_configuration & SAMPLING_ACCELEROMETER) == 0) { // We are only allowed to stop the accelerometer-timer, if we don't sample + app_timer_stop(sampling_accelerometer_fifo_timer); + // Only disable the accelerometer, if no interrupts should be generated + if((sampling_configuration & SAMPLING_ACCELEROMETER_INTERRUPT) == 0 && (sampling_configuration & STREAMING_ACCELEROMETER_INTERRUPT) == 0) { + ret = accel_set_operating_mode(ACCEL_POWER_DOWN_MODE); + if(ret != NRF_SUCCESS) return ret; + } + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(STREAMING_ACCELEROMETER)); + } + + + #endif + + + + return ret; +} + +void sampling_timeout_accelerometer(void) { + debug_log("SAMPLING: Accelerometer timed out --> stopping\n"); + sampling_stop_accelerometer(0); +} +void sampling_timeout_accelerometer_stream(void) { + debug_log("SAMPLING: Accelerometer stream timed out --> stopping\n"); + sampling_stop_accelerometer(1); +} + + +void sampling_setup_accelerometer_chunk(void) { + #if SAMPLING_ACCEL_ENABLED + // Open a chunk in the FIFO + chunk_fifo_write_open(&accelerometer_chunk_fifo, (void**) &accelerometer_chunk, NULL); + + systick_get_timestamp(&((accelerometer_chunk->timestamp).seconds), &((accelerometer_chunk->timestamp).ms)); + accelerometer_chunk->accelerometer_data_count = 0; + + debug_log("SAMPLING: Setup accelerometer chunk\n"); + #endif +} + +void sampling_finalize_accelerometer_chunk(void) { + #if SAMPLING_ACCEL_ENABLED + debug_log("SAMPLING: Finalize accelerometer chunk\n"); + + // Close the chunk in the FIFO + chunk_fifo_write_close(&accelerometer_chunk_fifo); + + sampling_setup_accelerometer_chunk(); // Setup a new chunk + + app_sched_event_put(NULL, 0, processing_process_accelerometer_chunk); + #endif +} + + + +/**@brief This callback funcion is called when the sampling-timer of the accelerometer is invoked. + * + * @param[in] p_context Pointer to context that is provided by the timer. Should be NULL. + */ +void sampling_accelerometer_fifo_callback(void* p_context) { + #if SAMPLING_ACCEL_ENABLED + if((sampling_configuration & SAMPLING_ACCELEROMETER) == 0 && (sampling_configuration & STREAMING_ACCELEROMETER) == 0) + return; + + int16_t x[32], y[32], z[32]; + + uint32_t num = accelerometer_chunk->accelerometer_data_count; + uint32_t remaining_num_samples = ACCELEROMETER_CHUNK_DATA_SIZE - num; + uint8_t num_samples = 0; + // Read the accelerometer + ret_code_t ret = accel_read_acceleration(x, y, z, &num_samples, remaining_num_samples); + if(ret != NRF_SUCCESS) + return; + //debug_log("SAMPLING: Read accel fifo: n=%u, remain=%u, ms=%u\n", num_samples, remaining_num_samples, (uint32_t) systick_get_millis()); + if(sampling_configuration & SAMPLING_ACCELEROMETER) { // Fill the chunk if we want to + for(uint8_t i = 0; i < num_samples; i++) { + accelerometer_chunk->accelerometer_data[num + i].acceleration = (ABS(x[i]) + ABS(y[i]) + ABS(z[i])); + } + + + // Increment the number of read samples + accelerometer_chunk->accelerometer_data_count = num + ((uint32_t) num_samples); + + // Check if the max number of samples has been reached + if(accelerometer_chunk->accelerometer_data_count >= ACCELEROMETER_CHUNK_DATA_SIZE) { + sampling_finalize_accelerometer_chunk(); // Finalize the opened chunk + } + } + + if(sampling_configuration & STREAMING_ACCELEROMETER) { // Put the elements on the stream if we want to + AccelerometerStream accelerometer_stream; + for(uint8_t i = 0; i < num_samples; i++) { + accelerometer_stream.accelerometer_raw_data.raw_acceleration[0] = x[i]; + accelerometer_stream.accelerometer_raw_data.raw_acceleration[1] = y[i]; + accelerometer_stream.accelerometer_raw_data.raw_acceleration[2] = z[i]; + circular_fifo_write(&accelerometer_stream_fifo, (uint8_t*) &accelerometer_stream, sizeof(accelerometer_stream)); + } + } + #endif +} + + + + + + +/********************************* ACCELEROMETER INTERRUPT ************************/ + + +ret_code_t sampling_start_accelerometer_interrupt(uint32_t timeout_ms, uint16_t threshold_mg, uint16_t minimal_duration_ms, uint32_t ignore_duration_ms, uint8_t streaming) { + ret_code_t ret = NRF_SUCCESS; + #if SAMPLING_ACCEL_ENABLED + // Check if we need to set up some default parameters for the accelerometer + if((sampling_configuration & SAMPLING_ACCELEROMETER) == 0 && (sampling_configuration & STREAMING_ACCELEROMETER) == 0) { + ret = accel_set_full_scale(ACCEL_FULL_SCALE_4G); + if(ret != NRF_SUCCESS) return ret; + ret = accel_set_datarate(ACCEL_DATARATE_10_HZ); + if(ret != NRF_SUCCESS) return ret; + ret = accel_set_operating_mode(ACCEL_LOW_POWER_MODE); + if(ret != NRF_SUCCESS) return ret; + } + + // We don't need to check the parameters here, because we always update them + + ret = accel_set_motion_interrupt_parameters(threshold_mg, minimal_duration_ms); + if(ret != NRF_SUCCESS) return ret; + + accel_set_interrupt_handler(sampling_accelerometer_interrupt_callback); + + ret = accel_set_interrupt(ACCEL_MOTION_INTERRUPT); + if(ret != NRF_SUCCESS) return ret; + + + accelerometer_interrupt_ignore_duration_ms = ignore_duration_ms; + + + if(!streaming) { + debug_log("SAMPLING: (Re-)start accelerometer interrupt sampling\n"); + sampling_setup_accelerometer_interrupt_chunk(NULL, 0); + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | SAMPLING_ACCELEROMETER_INTERRUPT); + advertiser_set_status_flag_accelerometer_interrupt_enabled(1); + + timeout_start(accelerometer_interrupt_timeout_id, timeout_ms); + } else { + debug_log("SAMPLING: Start accelerometer interrupt stream\n"); + sampling_configuration = (sampling_configuration_t) (sampling_configuration | STREAMING_ACCELEROMETER_INTERRUPT); + timeout_start(accelerometer_interrupt_stream_timeout_id, timeout_ms); + } + + #endif + return ret; +} + + +ret_code_t sampling_stop_accelerometer_interrupt(uint8_t streaming) { + ret_code_t ret = NRF_SUCCESS; + #if SAMPLING_ACCEL_ENABLED + // Check if we are allowed to disable interrupts and to disable the accelerometer + if(!streaming) { + if((sampling_configuration & STREAMING_ACCELEROMETER_INTERRUPT) == 0) { + ret = accel_set_interrupt(ACCEL_NO_INTERRUPT); + if(ret != NRF_SUCCESS) return ret; + if((sampling_configuration & SAMPLING_ACCELEROMETER) == 0 && (sampling_configuration & STREAMING_ACCELEROMETER) == 0) { + ret = accel_set_operating_mode(ACCEL_POWER_DOWN_MODE); + if(ret != NRF_SUCCESS) return ret; + } + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(SAMPLING_ACCELEROMETER_INTERRUPT)); + advertiser_set_status_flag_accelerometer_interrupt_enabled(0); + } else { + if((sampling_configuration & SAMPLING_ACCELEROMETER_INTERRUPT) == 0) { + ret = accel_set_interrupt(ACCEL_NO_INTERRUPT); + if(ret != NRF_SUCCESS) return ret; + if((sampling_configuration & SAMPLING_ACCELEROMETER) == 0 && (sampling_configuration & STREAMING_ACCELEROMETER) == 0) { + ret = accel_set_operating_mode(ACCEL_POWER_DOWN_MODE); + if(ret != NRF_SUCCESS) return ret; + } + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(STREAMING_ACCELEROMETER_INTERRUPT)); + } + + #endif + return ret; +} + +void sampling_timeout_accelerometer_interrupt(void) { + debug_log("SAMPLING: Accelerometer interrupt timed out --> stopping\n"); + sampling_stop_accelerometer_interrupt(0); +} +void sampling_timeout_accelerometer_interrupt_stream(void) { + debug_log("SAMPLING: Accelerometer interrupt stream timed out --> stopping\n"); + sampling_stop_accelerometer_interrupt(1); +} + +void sampling_setup_accelerometer_interrupt_chunk(void * p_event_data, uint16_t event_size) { + #if SAMPLING_ACCEL_ENABLED + debug_log("SAMPLING: Setup accelerometer interrupt chunk\n"); + + // Open a chunk in the FIFO + chunk_fifo_write_open(&accelerometer_interrupt_chunk_fifo, (void**) &accelerometer_interrupt_chunk, NULL); + + // Reset the interrupt + accel_reset_interrupt(); + #endif +} + +void sampling_finalize_accelerometer_interrupt_chunk(void) { + #if SAMPLING_ACCEL_ENABLED + debug_log("SAMPLING: Finalize accelerometer interrupt chunk\n"); + + // Close the chunk in the FIFO + chunk_fifo_write_close(&accelerometer_interrupt_chunk_fifo); + + app_sched_event_put(NULL, 0, processing_process_accelerometer_interrupt_chunk); + + // Don't call sampling_setup_accelerometer_interrupt_chunk() here because it should be called via the reset-timer + #endif + +} + +void sampling_accelerometer_interrupt_reset_callback(void* p_context) { + #if SAMPLING_ACCEL_ENABLED + // We have to call setup_accelerometer_interrupt_chunk via the scheduler, + // because the SPI-transfer might have the same IRQ-Priority like this function (called by the timer) + // and so the SPI-transfer would never terminate. + + app_sched_event_put(NULL, 0, sampling_setup_accelerometer_interrupt_chunk); + #endif +} + + +/**@brief This callback funcion is called when there is an interrupt event generated by the accelerometer. + * + * @note Keep in mind that accel_reset_interrupt() has to be called every time an interrupt was generated (otherwise there won't be new interrupts). + * But it is important to not call this function in this callback function because of the interrupt priorities of the spi-interface and the INT1-Pin. + * + * @param[in] p_event Pointer to event that is provided by the accelerometer interrupt handler. Should be ACCEL_MOTION_INTERRUPT. + */ +void sampling_accelerometer_interrupt_callback(accel_interrupt_event_t const * p_event) { + #if SAMPLING_ACCEL_ENABLED + debug_log("SAMPLING: sampling_accelerometer_interrupt_callback\n"); + + if((sampling_configuration & SAMPLING_ACCELEROMETER_INTERRUPT) == 0 && (sampling_configuration & STREAMING_ACCELEROMETER_INTERRUPT) == 0) + return; + + + if(sampling_configuration & SAMPLING_ACCELEROMETER_INTERRUPT) { + systick_get_timestamp(&(accelerometer_interrupt_chunk->timestamp.seconds), &(accelerometer_interrupt_chunk->timestamp.ms)); + + sampling_finalize_accelerometer_interrupt_chunk(); + } + + + if(sampling_configuration & STREAMING_ACCELEROMETER_INTERRUPT) { // Put the elements on the stream if we want to + AccelerometerInterruptStream accelerometer_interrupt_stream; + + systick_get_timestamp(&(accelerometer_interrupt_stream.timestamp.seconds), &(accelerometer_interrupt_stream.timestamp.ms)); + + circular_fifo_write(&accelerometer_interrupt_stream_fifo, (uint8_t*) &accelerometer_interrupt_stream, sizeof(accelerometer_interrupt_stream)); + + } + + + // Now start the reset timer that should reset the interrupt after a certain period of time. + app_timer_start(sampling_accelerometer_interrupt_reset_timer, APP_TIMER_TICKS(accelerometer_interrupt_ignore_duration_ms, 0), NULL); + #endif +} + + +/***************************** BATTERY ************************************/ +ret_code_t sampling_start_battery(uint32_t timeout_ms, uint32_t period_ms, uint8_t streaming) { + ret_code_t ret = NRF_SUCCESS; + + + + uint8_t parameters_changed_sampling = 0; + // Check if the parameters has been changed + if((sampling_battery_parameters.battery_timeout_ms != timeout_ms) && !streaming) { + parameters_changed_sampling = 1; + } + + + if(sampling_battery_parameters.battery_period_ms != period_ms) { + parameters_changed_sampling = 1; + } + + // Update the parameters + sampling_battery_parameters.battery_timeout_ms = (!streaming) ? timeout_ms : sampling_battery_parameters.battery_timeout_ms; + sampling_battery_parameters.battery_stream_timeout_ms = (streaming) ? timeout_ms : sampling_battery_parameters.battery_stream_timeout_ms; + sampling_battery_parameters.battery_period_ms = period_ms; + + + if(!streaming) { + if((sampling_configuration & SAMPLING_BATTERY) == 0 || parameters_changed_sampling) { // Only stop and start the sampling if it is not already running or the parameters changed + debug_log("SAMPLING: (Re-)start battery sampling\n"); + // Stop the sampling-timer that was probably already started + app_timer_stop(sampling_battery_timer); + + sampling_setup_battery_chunk(); + + // Now start the sampling-timer + ret = app_timer_start(sampling_battery_timer, APP_TIMER_TICKS(period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | SAMPLING_BATTERY); + advertiser_set_status_flag_battery_enabled(1); + + timeout_start(battery_timeout_id, timeout_ms); + } else { + debug_log("SAMPLING: Ignoring start battery sampling\n"); + } + } else { + // If already sampling but parameters changed + if((sampling_configuration & SAMPLING_BATTERY) && parameters_changed_sampling) { + debug_log("SAMPLING: (Re-)start battery sampling on stream request (because parameters changed)\n"); + app_timer_stop(sampling_battery_timer); + sampling_setup_battery_chunk(); + ret = app_timer_start(sampling_battery_timer, APP_TIMER_TICKS(period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + } else if((sampling_configuration & SAMPLING_BATTERY) == 0) { // If we are not already sampling the battery, we have to start the sampling-timer + debug_log("SAMPLING: Start battery stream\n"); + // Stop the sampling-timer that was probably already started + app_timer_stop(sampling_battery_timer); + // Now start the sampling-timer + ret = app_timer_start(sampling_battery_timer, APP_TIMER_TICKS(period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + } else { + debug_log("SAMPLING: Nothing to do to start battery stream\n"); + } + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | STREAMING_BATTERY); + timeout_start(battery_stream_timeout_id, timeout_ms); + } + + return ret; +} + +void sampling_stop_battery(uint8_t streaming) { + + // Check if we are allowed to disable the battery timer + if(!streaming) { + if((sampling_configuration & STREAMING_BATTERY) == 0) { + app_timer_stop(sampling_battery_timer); + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(SAMPLING_BATTERY)); + advertiser_set_status_flag_battery_enabled(0); + } else { + if((sampling_configuration & SAMPLING_BATTERY) == 0) { + app_timer_stop(sampling_battery_timer); + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(STREAMING_BATTERY)); + } + +} + +void sampling_timeout_battery(void) { + debug_log("SAMPLING: Battery timed out --> stopping\n"); + sampling_stop_battery(0); +} +void sampling_timeout_battery_stream(void) { + debug_log("SAMPLING: Battery stream timed out --> stopping\n"); + sampling_stop_battery(1); +} + +void sampling_battery_callback(void* p_context) { + //debug_log("SAMPLING: sampling_battery_callback\n"); + + if((sampling_configuration & SAMPLING_BATTERY) == 0 && (sampling_configuration & STREAMING_BATTERY) == 0) + return; + + + float voltage = battery_get_voltage(); + + if(sampling_configuration & SAMPLING_BATTERY) { + systick_get_timestamp(&(battery_chunk->timestamp.seconds), &(battery_chunk->timestamp.ms)); + battery_chunk->battery_data.voltage = voltage; + sampling_finalize_battery_chunk(); + } + + if(sampling_configuration & STREAMING_BATTERY) { // Put the elements on the stream if we want to + BatteryStream battery_stream; + battery_stream.battery_data.voltage = voltage; + circular_fifo_write(&battery_stream_fifo, (uint8_t*) &battery_stream, sizeof(battery_stream)); + } +} + +void sampling_setup_battery_chunk(void) { + // Open a chunk in the FIFO + chunk_fifo_write_open(&battery_chunk_fifo, (void**) &battery_chunk, NULL); + +} + +void sampling_finalize_battery_chunk(void) { + // Close the chunk in the FIFO + chunk_fifo_write_close(&battery_chunk_fifo); + + sampling_setup_battery_chunk(); + + app_sched_event_put(NULL, 0, processing_process_battery_chunk); +} + + + +/************************** MICROPHONE ****************************/ +ret_code_t sampling_start_microphone(uint32_t timeout_ms, uint16_t period_ms, uint8_t streaming) { + ret_code_t ret = NRF_SUCCESS; + + uint8_t parameters_changed_sampling = 0; + // Check if the parameters has been changed + if((sampling_microphone_parameters.microphone_timeout_ms != timeout_ms) && !streaming) { + parameters_changed_sampling = 1; + } + + + + if(sampling_microphone_parameters.microphone_period_ms != period_ms) { + parameters_changed_sampling = 1; + } + + // Update the parameters + sampling_microphone_parameters.microphone_timeout_ms = (!streaming) ? timeout_ms : sampling_microphone_parameters.microphone_timeout_ms; + sampling_microphone_parameters.microphone_stream_timeout_ms = (streaming) ? timeout_ms : sampling_microphone_parameters.microphone_stream_timeout_ms; + sampling_microphone_parameters.microphone_period_ms = period_ms; + + if(!streaming) { + if((sampling_configuration & SAMPLING_MICROPHONE) == 0 || parameters_changed_sampling) { // Only stop and start the sampling if it is not already running or the parameters changed + debug_log("SAMPLING: (Re-)start microphone sampling\n"); + // Stop the sampling-timer that was probably already started + app_timer_stop(sampling_microphone_timer); + app_timer_stop(sampling_microphone_aggregated_timer); + + sampling_setup_microphone_chunk(); + + // Now start the sampling-timer + ret = app_timer_start(sampling_microphone_timer, APP_TIMER_TICKS(period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + + // Now start the average-sampling-timer + ret = app_timer_start(sampling_microphone_aggregated_timer, APP_TIMER_TICKS(microphone_aggregated_period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | SAMPLING_MICROPHONE); + advertiser_set_status_flag_microphone_enabled(1); + + timeout_start(microphone_timeout_id, timeout_ms); + } else { + debug_log("SAMPLING: Ignoring start microphone sampling\n"); + } + } else { + // If already sampling but parameters changed + if((sampling_configuration & SAMPLING_MICROPHONE) && parameters_changed_sampling) { + debug_log("SAMPLING: (Re-)start microphone sampling on stream request (because parameters changed)\n"); + app_timer_stop(sampling_microphone_timer); + app_timer_stop(sampling_microphone_aggregated_timer); + sampling_setup_microphone_chunk(); + ret = app_timer_start(sampling_microphone_timer, APP_TIMER_TICKS(period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + ret = app_timer_start(sampling_microphone_aggregated_timer, APP_TIMER_TICKS(microphone_aggregated_period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + } else if((sampling_configuration & SAMPLING_MICROPHONE) == 0) { // If we are not already sampling the microphone, we have to start the sampling-timer + debug_log("SAMPLING: Start microphone stream\n"); + app_timer_stop(sampling_microphone_timer); + app_timer_stop(sampling_microphone_aggregated_timer); + ret = app_timer_start(sampling_microphone_timer, APP_TIMER_TICKS(period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + ret = app_timer_start(sampling_microphone_aggregated_timer, APP_TIMER_TICKS(microphone_aggregated_period_ms, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + } else { + debug_log("SAMPLING: Nothing to do to start microphone stream\n"); + } + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | STREAMING_MICROPHONE); + timeout_start(microphone_stream_timeout_id, timeout_ms); + } + + + return ret; +} + +void sampling_stop_microphone(uint8_t streaming) { + + // Check if we are allowed to disable the microphone timers + if(!streaming) { + if((sampling_configuration & STREAMING_MICROPHONE) == 0) { + app_timer_stop(sampling_microphone_timer); + app_timer_stop(sampling_microphone_aggregated_timer); + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(SAMPLING_MICROPHONE)); + advertiser_set_status_flag_microphone_enabled(0); + } else { + if((sampling_configuration & SAMPLING_MICROPHONE) == 0) { + app_timer_stop(sampling_microphone_timer); + app_timer_stop(sampling_microphone_aggregated_timer); + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(STREAMING_MICROPHONE)); + } + +} + +void sampling_timeout_microphone(void) { + debug_log("SAMPLING: Microphone timed out --> stopping\n"); + sampling_stop_microphone(0); +} +void sampling_timeout_microphone_stream(void) { + debug_log("SAMPLING: Microphone stream timed out --> stopping\n"); + sampling_stop_microphone(1); +} + +void sampling_microphone_callback(void* p_context) { + if(microphone_aggregated_count == 0) { + debug_log("SAMPLING: Microphone aggregated count == 0!\n"); + return; + } + + if(microphone_aggregated_count <= 5) { + debug_log("SAMPLING: Microphone aggregated count <= 5. We need more samples!\n"); + } + + uint32_t tmp = (microphone_aggregated/(microphone_aggregated_count/2)); + uint8_t value = (tmp > 255) ? 255 : ((uint8_t) tmp); + + + microphone_aggregated = 0; + microphone_aggregated_count = 0; + + if(sampling_configuration & SAMPLING_MICROPHONE) { + microphone_chunk->microphone_data[microphone_chunk->microphone_data_count].value = value; + microphone_chunk->microphone_data_count++; + //debug_log("SAMPLING: Mic value: %u, %u\n", value, microphone_chunk->microphone_data_count); + if(microphone_chunk->microphone_data_count >= MICROPHONE_CHUNK_DATA_SIZE) { + sampling_finalize_microphone_chunk(); + } + } + + if(sampling_configuration & STREAMING_MICROPHONE) { + MicrophoneStream microphone_stream; + microphone_stream.microphone_data.value = value; + circular_fifo_write(µphone_stream_fifo, (uint8_t*) µphone_stream, sizeof(microphone_stream)); + } + + +} + +void sampling_microphone_aggregated_callback(void* p_context) { + uint64_t end_ticks = systick_get_ticks_since_start() + APP_TIMER_TICKS(MICROPHONE_READING_WINDOW_MS, 0); + while(end_ticks > systick_get_ticks_since_start()) { + uint8_t value = 0; + ret_code_t ret = microphone_read(&value); + if(ret != NRF_SUCCESS) continue; + microphone_aggregated += value; + microphone_aggregated_count++; + } +} + +void sampling_setup_microphone_chunk(void) { + debug_log("SAMPLING: sampling_setup_microphone_chunk\n"); + + microphone_aggregated = 0; + microphone_aggregated_count = 0; + + // Open a chunk in the FIFO + chunk_fifo_write_open(µphone_chunk_fifo, (void**) µphone_chunk, NULL); + + systick_get_timestamp(&(microphone_chunk->timestamp.seconds), &(microphone_chunk->timestamp.ms)); + microphone_chunk->sample_period_ms = sampling_microphone_parameters.microphone_period_ms; + microphone_chunk->microphone_data_count = 0; +} + +void sampling_finalize_microphone_chunk(void) { + debug_log("SAMPLING: sampling_finalize_microphone_chunk\n"); + // Close the chunk in the FIFO + chunk_fifo_write_close(µphone_chunk_fifo); + + sampling_setup_microphone_chunk(); // Setup a new chunk + + app_sched_event_put(NULL, 0, processing_process_microphone_chunk); +} + +/************************** SCAN *********************************/ +ret_code_t sampling_start_scan(uint32_t timeout_ms, uint16_t period_seconds, uint16_t interval_ms, uint16_t window_ms, uint16_t duration_seconds, uint8_t group_filter, uint8_t aggregation_type, uint8_t streaming) { + ret_code_t ret = NRF_SUCCESS; + + uint8_t parameters_changed_sampling = 0; + + if((timeout_ms != sampling_scan_parameters.scan_timeout_ms) && !streaming) + parameters_changed_sampling = 1; + + if( period_seconds != sampling_scan_parameters.scan_period_seconds || + interval_ms != sampling_scan_parameters.scan_interval_ms || + window_ms != sampling_scan_parameters.scan_window_ms || + duration_seconds != sampling_scan_parameters.scan_duration_seconds || + group_filter != sampling_scan_parameters.scan_group_filter || + aggregation_type != sampling_scan_parameters.scan_aggregation_type) + { + parameters_changed_sampling = 1; + } + + // Update the parameters + sampling_scan_parameters.scan_timeout_ms = (!streaming) ? timeout_ms : sampling_scan_parameters.scan_timeout_ms; + sampling_scan_parameters.scan_stream_timeout_ms = (!streaming) ? timeout_ms : sampling_scan_parameters.scan_stream_timeout_ms; + sampling_scan_parameters.scan_period_seconds = period_seconds; + sampling_scan_parameters.scan_interval_ms = interval_ms; + sampling_scan_parameters.scan_window_ms = window_ms; + sampling_scan_parameters.scan_duration_seconds = duration_seconds; + sampling_scan_parameters.scan_group_filter = group_filter; + sampling_scan_parameters.scan_aggregation_type = aggregation_type; + + + + if(!streaming) { + if((sampling_configuration & SAMPLING_SCAN) == 0 || parameters_changed_sampling) { // Only stop and start the sampling if it is not already running or the parameter changed + debug_log("SAMPLING: (Re-)start scan sampling\n"); + // Stop the sampling-timer that was probably already started + app_timer_stop(sampling_scan_timer); + + // Now start the sampling-timer + ret = app_timer_start(sampling_scan_timer, APP_TIMER_TICKS(((uint32_t)period_seconds)*1000, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | SAMPLING_SCAN); + advertiser_set_status_flag_scan_enabled(1); + + timeout_start(scan_timeout_id, timeout_ms); + } else { + debug_log("SAMPLING: Ignoring start scan sampling\n"); + } + } else { + // If we are already sampling, we need to restart everything, if the parameters changed for sampling + if((sampling_configuration & SAMPLING_SCAN) && parameters_changed_sampling) { + debug_log("SAMPLING: (Re-)start scan sampling on stream request (because parameters changed)\n"); + app_timer_stop(sampling_scan_timer); + ret = app_timer_start(sampling_scan_timer, APP_TIMER_TICKS(((uint32_t)period_seconds)*1000, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + // timeout_start(scan_timeout_id, timeout_ms); // Don't update the timeout for the sampling + } else if((sampling_configuration & SAMPLING_SCAN) == 0) { // If we are not already sampling the scanner, we have to start the sampling-timer + debug_log("SAMPLING: Start scan stream\n"); + app_timer_stop(sampling_scan_timer); + ret = app_timer_start(sampling_scan_timer, APP_TIMER_TICKS(((uint32_t)period_seconds)*1000, 0), NULL); + if(ret != NRF_SUCCESS) return ret; + } else { // else case: sampling && !parameters_changed + debug_log("SAMPLING: Nothing to do to start scan stream\n"); + } + + sampling_configuration = (sampling_configuration_t) (sampling_configuration | STREAMING_SCAN); + + timeout_start(scan_stream_timeout_id, timeout_ms); + } + + + + + return ret; +} + +void sampling_stop_scan(uint8_t streaming) { + + // Check if we are allowed to disable the scan timer + if(!streaming) { + if((sampling_configuration & STREAMING_SCAN) == 0) { + app_timer_stop(sampling_scan_timer); + scanner_stop_scanning(); + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(SAMPLING_SCAN)); + advertiser_set_status_flag_scan_enabled(0); + } else { + if((sampling_configuration & SAMPLING_SCAN) == 0) { + app_timer_stop(sampling_scan_timer); + scanner_stop_scanning(); + } + sampling_configuration = (sampling_configuration_t) (sampling_configuration & ~(STREAMING_SCAN)); + } + +} + +void sampling_timeout_scan(void) { + debug_log("SAMPLING: Sampling timed out --> stopping\n"); + sampling_stop_scan(0); +} +void sampling_timeout_scan_stream(void) { + debug_log("SAMPLING: Sampling stream timed out --> stopping\n"); + sampling_stop_scan(1); +} + +void sampling_scan_callback(void* p_context) { + if(sampling_configuration & SAMPLING_SCAN) { + sampling_setup_scan_sampling_chunk(); + } + // Start the scan procedure: + ret_code_t ret = scanner_start_scanning(sampling_scan_parameters.scan_interval_ms, sampling_scan_parameters.scan_window_ms, sampling_scan_parameters.scan_duration_seconds); + // If an error occurs, directly call the timeout-callback + if(ret != NRF_SUCCESS) sampling_on_scan_timeout_callback(); +} + +void sampling_on_scan_timeout_callback(void) { + if(sampling_configuration & SAMPLING_SCAN) { + sampling_finalize_scan_sampling_chunk(); + } +} + +void sampling_on_scan_report_callback(scanner_scan_report_t* scanner_scan_report) { + debug_log("SAMPLING: Scan report: ID %u, RSSI: %d\n", scanner_scan_report->ID, scanner_scan_report->rssi); + + if(scanner_scan_report->group != sampling_scan_parameters.scan_group_filter) { + return; + } + + + if(sampling_configuration & SAMPLING_SCAN) { + + uint8_t prev_seen = 0; + for(uint32_t i = 0; i < scan_sampling_chunk->scan_result_data_count; i++) { + if(scan_sampling_chunk->scan_result_data[i].scan_device.ID == scanner_scan_report->ID) { // We already added it + if(scan_sampling_chunk->scan_result_data[i].count < 255) { // Check if we haven't 255 counts for this device + + // Check which aggregation type to use: + if(sampling_scan_parameters.scan_aggregation_type == SCAN_CHUNK_AGGREGATE_TYPE_MAX) { + scan_aggregated_rssi[i] = AGGREGATE_SCAN_SAMPLE_MAX(scan_aggregated_rssi[i], scanner_scan_report->rssi); + } else { // Use mean + scan_aggregated_rssi[i] = AGGREGATE_SCAN_SAMPLE_MEAN(scan_aggregated_rssi[i], scanner_scan_report->rssi); + } + scan_sampling_chunk->scan_result_data[i].count++; + } + prev_seen = 1; + break; + } + } + + if(!prev_seen) { + if(scan_sampling_chunk->scan_result_data_count < SCAN_SAMPLING_CHUNK_DATA_SIZE) { + scan_aggregated_rssi[scan_sampling_chunk->scan_result_data_count] = scanner_scan_report->rssi; + scan_sampling_chunk->scan_result_data[scan_sampling_chunk->scan_result_data_count].scan_device.ID = scanner_scan_report->ID; + scan_sampling_chunk->scan_result_data[scan_sampling_chunk->scan_result_data_count].scan_device.rssi = 0; // We could not set it here (it is aggregated) + scan_sampling_chunk->scan_result_data[scan_sampling_chunk->scan_result_data_count].count = 1; + scan_sampling_chunk->scan_result_data_count++; + } + } + } + + if(sampling_configuration & STREAMING_SCAN) { + ScanStream scan_stream; + scan_stream.scan_device.ID = scanner_scan_report->ID; + scan_stream.scan_device.rssi = scanner_scan_report->rssi; + circular_fifo_write(&scan_stream_fifo, (uint8_t*) &scan_stream, sizeof(scan_stream)); + } + + +} + +void sampling_setup_scan_sampling_chunk(void) { + + debug_log("SAMPLING: sampling_setup_scan_sampling_chunk\n"); + // Open a chunk in the FIFO + chunk_fifo_write_open(&scan_sampling_chunk_fifo, (void**) &scan_sampling_chunk, NULL); + + systick_get_timestamp(&(scan_sampling_chunk->timestamp.seconds), &(scan_sampling_chunk->timestamp.ms)); + scan_sampling_chunk->scan_result_data_count = 0; + + +} + +void sampling_finalize_scan_sampling_chunk(void) { + + debug_log("SAMPLING: sampling_finalize_scan_sampling_chunk\n"); + // Now interpret/Process the aggregated rssi values: + for(uint32_t i = 0; i < scan_sampling_chunk->scan_result_data_count; i++) { + if(sampling_scan_parameters.scan_aggregation_type == SCAN_CHUNK_AGGREGATE_TYPE_MAX) { // Use max + scan_sampling_chunk->scan_result_data[i].scan_device.rssi = (int8_t) PROCESS_SCAN_SAMPLE_MAX(scan_aggregated_rssi[i], (int32_t)scan_sampling_chunk->scan_result_data[i].count); + } else { // Use mean + scan_sampling_chunk->scan_result_data[i].scan_device.rssi = (int8_t) PROCESS_SCAN_SAMPLE_MEAN(scan_aggregated_rssi[i], (int32_t)scan_sampling_chunk->scan_result_data[i].count); + } + + + //debug_log("SAMPLING: Scanning ended: ID %u, RSSI %d, count %u\n", scan_sampling_chunk->scan_result_data[i].scan_device.ID, scan_sampling_chunk->scan_result_data[i].scan_device.rssi, scan_sampling_chunk->scan_result_data[i].count); + //systick_delay_millis(100); + } + //debug_log("SAMPLING: Scanning ended. Seen devices: %u\n", scan_sampling_chunk->scan_result_data_count); + + // Close the chunk in the FIFO + chunk_fifo_write_close(&scan_sampling_chunk_fifo); + + + app_sched_event_put(NULL, 0, processing_process_scan_sampling_chunk); + +} + diff --git a/firmware/nRF_badge/data_collector/incl/sampling_lib.h b/firmware/nRF_badge/data_collector/incl/sampling_lib.h new file mode 100644 index 0000000..a4619fd --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/sampling_lib.h @@ -0,0 +1,199 @@ +/**@file + * @details + * + */ + +#ifndef __SAMPLING_LIB_H +#define __SAMPLING_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes +#include "chunk_fifo_lib.h" +#include "circular_fifo_lib.h" + + + +typedef enum { + SAMPLING_ACCELEROMETER = (1 << 0), + STREAMING_ACCELEROMETER = (1 << 1), + SAMPLING_ACCELEROMETER_INTERRUPT = (1 << 2), + STREAMING_ACCELEROMETER_INTERRUPT = (1 << 3), + SAMPLING_BATTERY = (1 << 4), + STREAMING_BATTERY = (1 << 5), + SAMPLING_MICROPHONE = (1 << 6), + STREAMING_MICROPHONE = (1 << 7), + SAMPLING_SCAN = (1 << 8), + STREAMING_SCAN = (1 << 9), +} sampling_configuration_t; + + +/**< Declaration of the chunk-fifos and stream-fifos of the different data-sources */ +extern chunk_fifo_t accelerometer_chunk_fifo; +extern circular_fifo_t accelerometer_stream_fifo; +extern chunk_fifo_t accelerometer_interrupt_chunk_fifo; +extern circular_fifo_t accelerometer_interrupt_stream_fifo; +extern chunk_fifo_t battery_chunk_fifo; +extern circular_fifo_t battery_stream_fifo; +extern chunk_fifo_t microphone_chunk_fifo; +extern circular_fifo_t microphone_stream_fifo; +extern chunk_fifo_t scan_sampling_chunk_fifo; +extern circular_fifo_t scan_stream_fifo; + + +/**@brief This function initializes all the data-sources and -structures needed to sample the different data-sources. + * + * @details It initalizes all the different data-source modules (accel (if available), battery, scanner, microphone. + * For the data-sampling chunk-fifos are used to provide an efficient and clean way to perform fast data-sampling and data-exchange. + * For the data-streaming circular-fifos are used. + * Furthermore it initializes the timeout-mechanism for the data-sources. + * + * @retval NRF_SUCCESS If everything was initialized correctly. + * @retval Otherwise an error code is provided. + * + * @note app-timer has to be initialized before! + * @note app-scheduler has to be initialized before! + * @note timeout_lib has to be initialized before! + * @note systick_lib has to be initialized before! + * @note advertiser_lib has to be initialized before! + */ +ret_code_t sampling_init(void); + + +/**@brief Function to retrieve the current sampling-/streaming-configuration (So which data-sources are currently sampling). + * + * @retval The current sampling-configuration. + */ +sampling_configuration_t sampling_get_sampling_configuration(void); + +/**@brief Function to retrieve the current sampling-/streaming-configuration (So which data-sources are currently sampling). + * + * @retval The current sampling-configuration. + */ +void sampling_reset_timeouts(void); + + +/**@brief Function to start the accelerometer data recording or streaming. + * + * @param[in] timeout_ms The timeout for the accelerometer in milliseconds (0 --> no timeout). + * @param[in] operating_mode [0 == power_down, 1 == low_power, 2 == normal, 3 == high_resolution] + * @param[in] full_scale [2, 4, 8, 16] + * @param[in] datrate [1, 10, 25, 50, 100, 200, 400] + * @param[in] fifo_sampling_period_ms The sampling period in ms of the accelerometer output FIFO. + * @param[in] streaming Flag if streaming or data acquisition should be enabled [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +ret_code_t sampling_start_accelerometer(uint32_t timeout_ms, uint8_t operating_mode, uint8_t full_scale, uint16_t datarate, uint16_t fifo_sampling_period_ms, uint8_t streaming); + +/**@brief Function to stop the accelerometer data recording or streaming. + * + * @param[in] streaming Flag if streaming or data acquisition should be stopped [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +ret_code_t sampling_stop_accelerometer(uint8_t streaming); + + + + +/**@brief Function to start the accelerometer data recording or streaming. + * + * @param[in] timeout_ms The timeout for the accelerometer-interrupt in milliseconds (0 --> no timeout). + * @param[in] threshold_mg The threshold in mg that has to be exceeded to generate an interrupt. + * @param[in] minimal_duration_ms The minimal duration the acceleration has to exceed the threshold to generate an interrupt. + * @param[in] ignore_duration_ms The time after an interrupt where new interrupt should be ignored. + * @param[in] streaming Flag if streaming or data acquisition should be enabled [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +ret_code_t sampling_start_accelerometer_interrupt(uint32_t timeout_ms, uint16_t threshold_mg, uint16_t minimal_duration_ms, uint32_t ignore_duration_ms, uint8_t streaming); + +/**@brief Function to stop the accelerometer-interrupt data recording or streaming. + * + * @param[in] streaming Flag if streaming or data acquisition should be stopped [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +ret_code_t sampling_stop_accelerometer_interrupt(uint8_t streaming); + + + + +/**@brief Function to start the battery data recording or streaming. + * + * @param[in] timeout_ms The timeout for the battery in milliseconds (0 --> no timeout). + * @param[in] period_ms The period at which the battery-sampling should be done. + * @param[in] streaming Flag if streaming or data acquisition should be enabled [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +ret_code_t sampling_start_battery(uint32_t timeout_ms, uint32_t period_ms, uint8_t streaming); + +/**@brief Function to stop the battery data recording or streaming. + * + * @param[in] streaming Flag if streaming or data acquisition should be stopped [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +void sampling_stop_battery(uint8_t streaming); + + + + + +/**@brief Function to start the microphone data recording or streaming. + * + * @param[in] timeout_ms The timeout for the microphone in milliseconds (0 --> no timeout). + * @param[in] period_ms The period at which the microphone-average-sampling should be done. + * @param[in] streaming Flag if streaming or data acquisition should be enabled [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +ret_code_t sampling_start_microphone(uint32_t timeout_ms, uint16_t period_ms, uint8_t streaming); + +/**@brief Function to stop the microphone data recording or streaming. + * + * @param[in] streaming Flag if streaming or data acquisition should be stopped [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +void sampling_stop_microphone(uint8_t streaming); + + + +/**@brief Function to start the scan data recording or streaming. + * + * @param[in] timeout_ms The timeout for the scan in milliseconds (0 --> no timeout). + * @param[in] period_seconds The period at which the scan-sampling should be done. + * @param[in] interval_ms The interval of the scan. + * @param[in] window_ms The window of the scan. + * @param[in] duration_seconds The duration of the scan. + * @param[in] group_filter The group that should be filtered out. If 0xFF nothing is filtered. + * @param[in] aggregation_type The aggregation type for the seen devices: [SCAN_CHUNK_AGGREGATE_TYPE_MAX] == MAX, [SCAN_CHUNK_AGGREGATE_TYPE_MEAN] == MEAN. + * @param[in] streaming Flag if streaming or data acquisition should be enabled [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +ret_code_t sampling_start_scan(uint32_t timeout_ms, uint16_t period_seconds, uint16_t interval_ms, uint16_t window_ms, uint16_t duration_seconds, uint8_t group_filter, uint8_t aggregation_type, uint8_t streaming); + +/**@brief Function to stop the scan data recording or streaming. + * + * @param[in] streaming Flag if streaming or data acquisition should be stopped [0 == data-acquisistion, 1 == streaming]. + * + * @retval NRF_SUCCESS If everything was ok. + * @retval Otherwise an error code is returned. + */ +void sampling_stop_scan(uint8_t streaming); + + + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/scanner.c b/firmware/nRF_badge/data_collector/incl/scanner.c deleted file mode 100644 index 2679ae9..0000000 --- a/firmware/nRF_badge/data_collector/incl/scanner.c +++ /dev/null @@ -1,522 +0,0 @@ -#include -#include - -#include "battery.h" -#include "scanner.h" - -#if MAX_AGGR - #define AGGR_SAMPLE(sample, datum) (datum > sample ? datum: sample) - #define PROCESS_SAMPLE(aggregated, count) aggregated - #define RESTORE_SAMPLE(processed, count) processed -#else - #define AGGR_SAMPLE(sample, datum) (datum+sample) - #define PROCESS_SAMPLE(aggregated, count) ((aggregated)/(count)) - #define RESTORE_SAMPLE(processed, count) ((processed)*(count)) -#endif - - -static uint32_t mScanTimer; - -static void scan_task(void * p_context) { - if (scanner_enable) { - // don't start scans if BLE is inactive (paused) - if(BLEgetStatus() != BLE_INACTIVE) { - uint32_t result = startScan(); - APP_ERROR_CHECK(result); - debug_log("SCANNER: Started scan.\r\n"); - lastScanTime = millis(); - } - } -} - -static unsigned short hexStringToShort(unsigned char * string) -{ - unsigned short result = 0; - for (int place = 0; place < 4; place++) { - result *= 16; - if (string[place] >= '0' && string[place] <= '9') { - result += string[place] - '0'; - } - else if (string[place] >= 'A' && string[place] <= 'F') { - result += string[place] - 'A' + 0xA; - } - else if (string[place] >= 'a' && string[place] <= 'f') { - result += string[place] - 'a' + 0xa; - } - else return 0xffff; // invalid string - } - return result; -} - - -//================================ Scanning + scan result storage =============================== -//=============================================================================================== - -void scanner_init() -{ - scan_params.active = 0; //passive scanning, only looking for advertising packets - scan_params.selective = 0; //non-selective, don't use whitelist - scan_params.p_whitelist = NULL; //no whitelist - scan_params.interval = (SCAN_INTERVAL * 1000) / 625; //scan_params uses interval in units of 0.625ms - scan_params.window = (SCAN_WINDOW * 1000) / 625; //window also in units of 0.625ms - scan_params.timeout = SCAN_TIMEOUT; //timeout is in s - - scanPeriod_ms = 1000UL * SCAN_PERIOD; - - //scan_state = SCAN_IDLE; - - app_timer_create(&mScanTimer, APP_TIMER_MODE_REPEATED, scan_task); // makes an app timer that interrupts repeatedly to execute scan_task - -} - - -uint32_t startScan() -{ - sd_ble_gap_scan_stop(); // stop any in-progress scans - - scan.num = 0; - scan.numbeacons = 0; - scan.timestamp = now(); - - scan_state = SCANNER_SCANNING; - - return sd_ble_gap_scan_start(&scan_params); -} - - -void BLEonAdvReport(ble_gap_evt_adv_report_t* advReport) -{ - - signed char rssi = advReport->rssi; - - if (rssi < MINIMUM_RSSI) { - return; // ignore signals that are too weak. - } - - unsigned char dataLength = advReport->dlen; - unsigned char* data = (unsigned char*)advReport->data; - unsigned char index = 0; - - unsigned char* namePtr = NULL; - //unsigned char nameLen = 0; - unsigned char* manufDataPtr = NULL; - unsigned char manufDataLen = 0; - - unsigned char group = BAD_GROUP; - unsigned short ID = BAD_ID; - - while ((namePtr == NULL || manufDataPtr == NULL) && index < dataLength) { - unsigned char fieldLen = data[index]; - index++; - unsigned char fieldType = data[index]; - if (fieldType == BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME || fieldType == BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME) { - namePtr = &data[index + 1]; // skip field type byte - //nameLen = fieldLen - 1; - } - else if (fieldType == BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA) { - manufDataPtr = &data[index + 1]; // skip field type byte - manufDataLen = fieldLen - 1; - } - index += fieldLen; - } - - if (manufDataLen == BADGE_MANUF_DATA_LEN) { - if (namePtr != NULL && memcmp(namePtr,(const uint8_t *)DEVICE_NAME,strlen(DEVICE_NAME)) == 0) { - custom_adv_data_t badgeAdvData; - memcpy(&badgeAdvData,&manufDataPtr[2],CUSTOM_ADV_DATA_LEN); // skip past company ID; ensure data is properly aligned - ID = badgeAdvData.ID; - group = badgeAdvData.group; - debug_log("---Badge seen: group %d, ID %.4X, rssi %d.\r\n",(int)group,(int)ID,(int)rssi); - } - } - else if (manufDataLen == IBEACON_MANUF_DATA_LEN) { - iBeacon_data_t iBeaconData; - memcpy(&iBeaconData,manufDataPtr,IBEACON_MANUF_DATA_LEN); // ensure data is properly aligned - if (iBeaconData.companyID == COMPANY_ID_APPLE && iBeaconData.type == IBEACON_TYPE_PROXIMITY) { - // major/minor values are big-endian - unsigned short major = ((unsigned short)iBeaconData.major[0] * 256) + iBeaconData.major[1]; - UNUSED_VARIABLE(major); - unsigned short minor = ((unsigned short)iBeaconData.minor[0] * 256) + iBeaconData.minor[1]; - debug_log("---iBeacon seen: major %d, minor %d, rssi %d.\r\n",(int)major,minor,(int)rssi); - group = iBeaconData.major[1]; // take only lower byte of major value - ID = minor; - } - } - - if (ID != BAD_ID && group == badgeAssignment.group){ // valid ID - - bool prevSeen = false; - - for(int i = 0; i < scan.num; i++) { // check through list of already seen devices - if(ID == scan.IDs[i]) { - scan.rssiSums[i] = AGGR_SAMPLE(scan.rssiSums[i], rssi); - scan.counts[i]++; - prevSeen = true; - break; - } - } - - if(!prevSeen) { // is it a new device and do we have space for it - if(scan.num < MAX_SCAN_RESULTS){ - scan.IDs[scan.num] = ID; - scan.rssiSums[scan.num] = rssi; - scan.counts[scan.num] = 1; - scan.num++; - scan.numbeacons += (ID >= BEACON_ID_THRESHOLD) ? 1 : 0; - - } - } - } - - /* - - // Parse the broadcast packet. (find+check name, find custom data if present) - while ((name == NULL || gotPayload == false) && index < dataLength) { - unsigned char fieldLen = data[index]; - index++; - unsigned char fieldType = data[index]; - if (fieldType == BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME || fieldType == BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME) { - name = &data[index + 1]; - if (memcmp(name,(const uint8_t *)DEVICE_NAME,strlen(DEVICE_NAME)) == 0) { - // name checks out - } - else if (name != NULL && memcmp(name,(const uint8_t *)BEACON_NAME,strlen(BEACON_NAME)) == 0) { - payload.group = BEACON_GROUP; - // our beacons are named "[BEACON_NAME]XXXX", where XXXX is a hex ID number. - payload.ID = hexStringToShort(&name[strlen(BEACON_NAME)]); - if (payload.ID != 0xffff) { // was there a valid ID in name? - gotPayload = true; - } - } - else { - gotPayload = false; - break; // if name doesn't match, stop parsing broadcast packet - } - } - else if(fieldType == BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA) { - int payloadLen = fieldLen - 3; // length of field minus field type byte and manuf ID word - if(payloadLen == CUSTOM_DATA_LEN) { - // Need to copy payload data so it is properly aligned. - memcpy(&payload,&data[index+3],CUSTOM_DATA_LEN); // skip past field type byte, manuf ID word - gotPayload = true; - } - } - index += fieldLen; - } - - if(gotPayload) { // did we see a valid device, and retrieve relevant data? - debug_log("---Badge seen: group %d, ID %hX, rssi %d.\r\n",(int)payload.group,payload.ID,(int)rssi); - if(payload.group == badgeAssignment.group || payload.group == BEACON_GROUP) { - bool prevSeen = false; - for(int i=0; irssi > seenDeviceB->rssi) { - return -1; // We want device A before device B in our list. - } else if (seenDeviceA->rssi == seenDeviceB->rssi) { - return 0; // We don't care whether deviceA or deviceB comes first. - } else if (seenDeviceA->rssi < seenDeviceB->rssi) { - return 1; // We want device A to come after device B in our list. - } - - // We should never get here? - APP_ERROR_CHECK_BOOL(false); - - return -1; -} - - -static int compareSeenDeviceByID(const void * a, const void * b) { - seenDevice_t * seenDeviceA = (seenDevice_t *) a; - seenDevice_t * seenDeviceB = (seenDevice_t *) b; - - if (seenDeviceA->ID > seenDeviceB->ID) { - return -1; // We want device A before device B in our list. - } else if (seenDeviceA->ID == seenDeviceB->ID) { - return 0; // We don't care whether deviceA or deviceB comes first. - } else if (seenDeviceA->ID < seenDeviceB->ID) { - return 1; // We want device A to come after device B in our list. - } - - // We should never get here? - APP_ERROR_CHECK_BOOL(false); - - return -1; -} - -// This function is kind of hacky. -// It sorts our global scan variable in place descendingly according to RSSI. -// It's written this way so we can easily put it into the existing codebase with minimal changes. -// We first store the closest beacons (up to BEACON_PRIORITY), and then use the remaining space -// for all other beacons and badges -static seenDevice_t seenDevices[MAX_SCAN_RESULTS]; -void sortScanByRSSIDescending(void) { - // Convert scan into an array of structs for sorting - for (int i = 0; i < scan.num; i++) { - seenDevices[i].ID = scan.IDs[i]; - seenDevices[i].rssi = PROCESS_SAMPLE(scan.rssiSums[i], scan.counts[i]); - seenDevices[i].count = scan.counts[i]; - } - - // Number of beacons that are going to be prioritized - int prioritized = (BEACON_PRIORITY < scan.numbeacons) ? BEACON_PRIORITY : scan.numbeacons; - - // Place beacons on top - qsort(seenDevices, (size_t)scan.num, sizeof(seenDevice_t), compareSeenDeviceByID); - - // Sort beacons by RSSI - qsort(seenDevices, (size_t)scan.numbeacons, sizeof(seenDevice_t), compareSeenDeviceByRSSI); - - // Keep prioritized beacons on top, sort the remaining beacons and badges by RSSI - qsort(seenDevices+prioritized, (size_t)(scan.num-prioritized), sizeof(seenDevice_t), compareSeenDeviceByRSSI); - - // Translate back to the original scan structure - for (int i = 0; i < scan.num; i++) { - scan.IDs[i] = seenDevices[i].ID; - scan.rssiSums[i] = RESTORE_SAMPLE(seenDevices[i].rssi, seenDevices[i].count); - scan.counts[i] = seenDevices[i].count; - } -} - - -bool updateScanner() -{ - bool scannerActive = true; - - debug_log("SCANNER: Saving scan results. %d devices seen\r\n",scan.num); - - int numSaved = 0; - int chunksUsed = 0; - - - if (scan.num > SCAN_DEVICES_PER_CHUNK) { - // We have scanned more devices than we can store in a chunk, prune off the top SCAN_DEVICES_PER_CHUNK - // devices by RSSI values - // This requirement is in place because even though we can currently handle storing > SCAN_DEVICES_PER_CHUNK, - // these chunks break the sender. - sortScanByRSSIDescending(); - debug_log("SCANNER: Pruned %d devices with low RSSI\r\n", scan.num - SCAN_DEVICES_PER_CHUNK); - scan.num = SCAN_DEVICES_PER_CHUNK; - } - - do { - // Fill chunk header - scanBuffer[scan.to].timestamp = scan.timestamp; - scanBuffer[scan.to].num = scan.num; - int scaledBatteryLevel = (int)(100.0*BatteryMonitor_getBatteryVoltage()) - 100; - scanBuffer[scan.to].batteryLevel = (scaledBatteryLevel <= 255) ? scaledBatteryLevel : 255; // clip scaled level - - debug_log(" C:%d\r\n",scan.to); - - // Fill chunk with results - int numLeft = scan.num - numSaved; - int numThisChunk = (numLeft <= SCAN_DEVICES_PER_CHUNK) ? numLeft : SCAN_DEVICES_PER_CHUNK; - for(int i = 0; i < numThisChunk; i++) { - scanBuffer[scan.to].devices[i].ID = scan.IDs[numSaved + i]; - scanBuffer[scan.to].devices[i].rssi = PROCESS_SAMPLE(scan.rssiSums[numSaved + i], scan.counts[numSaved + i]); - scanBuffer[scan.to].devices[i].count = scan.counts[numSaved + i]; - debug_log(" bdg ID#%.4hX, rssi %d, count %d\r\n", scanBuffer[scan.to].devices[i].ID, - (int)scanBuffer[scan.to].devices[i].rssi, - (int)scanBuffer[scan.to].devices[i].count ); - - } - numSaved += numThisChunk; - - // Terminate chunk - if(chunksUsed == 0) { // first chunk of saved results - if(numSaved >= scan.num) { // did all the results fit in the first chunk - scanBuffer[scan.to].check = scanBuffer[scan.to].timestamp; // mark as complete - } - else { - scanBuffer[scan.to].check = CHECK_TRUNC; - } - } - else { // else we're continuing results from a previous chunk - scanBuffer[scan.to].check = CHECK_CONTINUE; - } - //debug_log("--num: %d\r\n",scanBuffer[scan.to].num); - - chunksUsed++; - scan.to = (scan.to < LAST_SCAN_CHUNK) ? scan.to+1 : 0; - } while(numSaved < scan.num); - - debug_log("SCANNER: Done saving results. used %d chunks.\r\n",chunksUsed); - Storer_ScheduleBufferedDataStorage(); - - return scannerActive; - -} - - -void startScanner(unsigned short window_ms,unsigned short interval_ms,unsigned short duration_s,unsigned short period_s) -{ - scan_params.interval = (interval_ms * 1000UL) / 625; //scan_params uses interval in units of 0.625ms - scan_params.window = (window_ms * 1000UL) / 625; //window also in units of 0.625ms - scan_params.timeout = duration_s; //timeout is in s - - scanPeriod_ms = 1000UL * period_s; - - scanner_enable = true; - - app_timer_start(mScanTimer, APP_TIMER_TICKS(scanPeriod_ms, APP_PRESCALER), NULL); -} - -void stopScanner() -{ - scanner_enable = false; - - app_timer_stop(mScanTimer); - - Storer_ScheduleBufferedDataStorage(); -} - - -scan_state_t getScanState() -{ - return scan_state; -} - - -unsigned long getScanTimestamp(int chunk) -{ - ext_eeprom_wait(); - scan_header_t header; - ext_eeprom_read(EXT_ADDRESS_OF_CHUNK(chunk),header.buf,sizeof(header.buf)); - while(spi_busy()); - return header.timestamp; -} - -unsigned long getScanCheck(int chunk) -{ - ext_eeprom_wait(); - scan_tail_t tail; - ext_eeprom_read(EXT_ADDRESS_OF_CHUNK_CHECK(chunk),tail.buf,sizeof(tail.buf)); - while(spi_busy()); - return tail.check; -} - -void getScanChunk(scan_chunk_t* destPtr,int chunk) -{ - ext_eeprom_wait(); - ext_eeprom_read(EXT_ADDRESS_OF_CHUNK(chunk),destPtr->buf,sizeof(destPtr->buf)); - while(spi_busy()); -} - - -void printScanResult(scan_chunk_t* srcPtr) -{ - //scan_chunk_t readChunk; - //ext_eeprom_read(EXT_ADDRESS_OF_CHUNK(chunk),readChunk.buf,sizeof(readChunk.buf)); - - debug_log("Read: TS 0x%X, N %d, C 0x%X\r\n", (unsigned int)srcPtr->timestamp, - (int)srcPtr->num, - (unsigned int)srcPtr->check - ); - - for(int i = 0; i < SCAN_DEVICES_PER_CHUNK; i++) { - // Print all seen devices - if(i < srcPtr->num) { - debug_log(" #%X, R: %d, c: %d\r\n",(int)srcPtr->devices[i].ID,(signed int)srcPtr->devices[i].rssi - ,(int)srcPtr->devices[i].count); - } - // Prints the remaining contents of the chunk (not real data) - //else { - // debug_log(" // #%d, %d\r\n",(int)readChunk.devices[i].ID,(int)readChunk.devices[i].rssi); - //} - nrf_delay_ms(1); - } -} diff --git a/firmware/nRF_badge/data_collector/incl/scanner.h b/firmware/nRF_badge/data_collector/incl/scanner.h deleted file mode 100644 index 0195a75..0000000 --- a/firmware/nRF_badge/data_collector/incl/scanner.h +++ /dev/null @@ -1,262 +0,0 @@ -/* Methods to simplify access to an external Adesto AT25XE041B flash memory IC - * - */ - - -#ifndef SCANNER_H -#define SCANNER_H - - -#include -#include - -#include "nordic_common.h" -#include "nrf.h" -#include "nrf51_bitfields.h" -#include "app_util_platform.h" - -#include "nrf_drv_config.h" -#include "boards.h" - -#include "app_error.h" // error handling -#include "nrf_delay.h" // includes blocking delay functions -#include "nrf_gpio.h" // abstraction for dealing with gpio - -#include "analog.h" -#include "rtc_timing.h" // Timing-related code, incl. millis() -#include "ble_setup.h" // BLE operations -#include "debug_log.h" // UART debugging logger - // requires app_fifo, app_uart_fifo.c and retarget.c for printf to work properly -//#include "internal_flash.h" // Relating to internal flash operation - // relevant here because we cannot do BLE operations while manipulating - // internal flash -#include "ext_eeprom.h" // External flash manipulation - - -// Values from iBeacon spec -#define COMPANY_ID_APPLE 0x004C -#define IBEACON_TYPE_PROXIMITY 0x1502 -#define IBEACON_MANUF_DATA_LEN 0x19 - -// Structure of iBeacon manufacturer-specific data, after the 0xFF type specifier byte. -typedef struct -{ - unsigned short companyID; - unsigned short type; - unsigned char UUID[16]; - unsigned char major[2]; // big-endian - unsigned char minor[2]; // big-endian - unsigned char measuredPower; -} iBeacon_data_t; - - -//================================ Scanning + scan result storage =============================== -//=============================================================================================== - -/** - * External AT25XE041B flash (or EEPROM) - * - * Similarly to the internal flash, the external EEPROM is organized into "chunks" for storing scan data - * One chunk is one scan result, containing the ID, average RSSI, and number of sightings for each device - * In a large scan, this might require more than one chunk's worth of space. Such chunks are specially - * marked, and the data is divided into however many chunks are required. - * - * Chunk structure: - * [ timestamp ][num ][ padding ][ ID1 ][rssi1][count][ ID1 ][rssi1][count]...[ ID29 ][rssi29][count][ timestamp ] - * 32bits 8bit 24bit 16bit 8bit 8bit 16bit 8bit 8bit 16bit 8bit 8bit 32bit - * 128bytes total - * Timestamp is the timestamp of the beginning of the chunk. The end timestamp is used to - * mark complete chunks, similar to the internal flash chunks - * Type says what's in the chunk. E.g. a complete scan result report, or the second half - * of a scan results. Or a table of device addresses. - * Num is the number of devices seen in the entire scan. (may be more than what's listed in - * a single chunk) - */ - -#define EXT_CHUNK_SIZE 128 -#define EXT_ADDRESS_OF_CHUNK(chnk) ( (uint32_t)(chnk * 128) ) -#define EXT_ADDRESS_OF_CHUNK_CHECK(chnk) ( EXT_ADDRESS_OF_CHUNK(chnk) + 124 ) -#define EXT_FIRST_CHUNK 0 -#define EXT_FIRST_DATA_CHUNK 8 // earlier chunks are for persistent device list, configuration, etc -#define EXT_LAST_CHUNK 2047 // 524288 / 128 = 2^18 / EXT_CHUNK_SIZE = 2048 chunks total - - -#define CHECK_INCOMPLETE 0xFFFFFFFFUL // chunk check value for incomplete chunk -#define CHECK_TRUNC 0x7FFFFFFFUL // chunk check value for truncated chunk - scan results truncated, continue into next chunk(s) -#define CHECK_CONTINUE 0x3FFFFFFFUL // chunk check value for chunk that continues the data from a truncated scan chunk - // Note the num parameter in truncated/continuing chunks is the total devices seen in the whole scan, - // not the number reported in the chunk itself. - -#define SCAN_DEVICES_PER_CHUNK 29 // maximum devices that we can report in one chunk. See chunk structure. - - -/** - * Various data types for convenient EEPROM access - * - * The following types define unions between structs with relevant scan chunk members, - * and unsigned char arrays, for use with EEPROM data transfer functions. - * They also include the required 4 dummy bytes at the start. (see ext_eeprom.h) - * Example usage: - * scan_chunk_t storeChunk; - * storeChunk.devices[0].ID = 12; - * ext_flash_write(address,storeChunk.buf,sizeof(storeChunk.buf)); - */ - -// A type used within scan_chunk_t to represent a scan-seen device and corresponding signal strength -typedef struct -{ - unsigned short ID; - signed char rssi; - signed char count; -} seenDevice_t; - -// A type useful for dealing with entire scan chunks -typedef union -{ - struct - { - unsigned char dummy[4]; // Padding, for flash opcode, address. (4byte) - uint32_t timestamp; // timestamp of scan start 4byte - unsigned char num; // number of devices seen in scan 1byte - unsigned char batteryLevel; // battery voltage = 1V + 0.01V*level 1byte - unsigned char padding[2]; // padding to manually fill chunk 2byte - seenDevice_t devices[SCAN_DEVICES_PER_CHUNK]; // all the devices seen 29*4byte - uint32_t check; // copy of timestamp, to verify 4byte - }; // 4+128byte total - unsigned char buf[EXT_CHUNK_SIZE+4]; // everything above all in one array, for read/writing flash -} scan_chunk_t; - - -// A type useful for reading only the header (timestamp, type, number) of a scan chunk -typedef union -{ - struct - { - unsigned char dummy[4]; // (4byte) - uint32_t timestamp; // 4byte - unsigned char num; // 1byte - }; // 4+5byte total - unsigned char buf[9]; -} scan_header_t; - - -// A type useful for reading only the tail (check) of a scan chunk -typedef union -{ - struct - { - unsigned char dummy[4]; // (4byte) - uint32_t check; // 4byte - }; // 4+4byte total - unsigned char buf[8]; -} scan_tail_t; - - - - - -volatile bool scanner_enable; - -// Default scan timings -#define SCAN_WINDOW 100 // Milliseconds of active scanning -#define SCAN_INTERVAL 300 // Millisecond interval at which a scan window is performed -#define SCAN_TIMEOUT 5 // Scan duration, seconds. -#define SCAN_PERIOD 10 // Scan period, s. A scan is started at this interval. - - -ble_gap_scan_params_t scan_params; - -unsigned long lastScanTime; // millis() time of last scan -unsigned long scanPeriod_ms; // scan period in ms - -// For keeping track of current state of scanning -typedef enum scan_state_t -{ - SCANNER_IDLE = 0, // no scan-related activity occurring - SCANNER_SCANNING, // scan in process - SCANNER_SAVE, // need to save scan results in buffer -} scan_state_t; - -volatile scan_state_t scan_state; - - -#define MAX_SCAN_RESULTS 300 // max number of individual devices that will be reported in a scan -#define MAX_SCAN_COUNT 127 // maximum number of RSSI readings for one device reported in a scan - -#define BEACON_PRIORITY 4 // always store at least this many beacons -#define BEACON_ID_THRESHOLD 16000 // IDs at or above this threshold signify a beacon. IDs below this are badges -#define MINIMUM_RSSI (-120) -signed char minimumRSSI; // device seen on scan must reach this RSSI threshold to be acknowledged - - -#define MAX_AGGR true // indicates the function (true=max/false=mean) used to aggregate samples - - -void sortScanByRSSIDescending(void); - -struct -{ - int to; - volatile int timestamp; - volatile int num, numbeacons; - volatile unsigned short IDs[MAX_SCAN_RESULTS]; - volatile signed short rssiSums[MAX_SCAN_RESULTS]; - volatile signed char counts[MAX_SCAN_RESULTS]; -} scan; - - -#define SCAN_BUFFER_SIZE 10 -#define LAST_SCAN_CHUNK (SCAN_BUFFER_SIZE - 1) - -scan_chunk_t scanBuffer[SCAN_BUFFER_SIZE]; - - -/** - * Initialize scanning module - * Finds where in external flash scan data should be stored (after most recent past data) - */ -void scanner_init(); - - -/** - * Initiate BLE scan - * Scanning will occur for window_ms every interval_ms, and will stop after timeout_s - */ -//void startScan(int interval_ms, int window_ms, int timeout_s); -uint32_t startScan(); - - -void startScanner(unsigned short window_ms,unsigned short interval_ms,unsigned short duration_s,unsigned short period_s); -void stopScanner(); - - -unsigned long getScanTimestamp(int chunk); -unsigned long getScanCheck(int chunk); -void getScanChunk(scan_chunk_t* destPtr,int chunk); - -/** - * Scan callback - called when another device's advertising packet is detected. - * Updates the scanResults array with the new RSSI reading - */ -//Declared in ble_setup.h: BLEonAdvReport(uint8_t addr[6], int8_t rssi); - -/** - * Scanning timeout callback - called at end of scanning. - * Prints list of devices seen if debug log enabled - * Stores scan result to flash - */ -//Declared in ble_setup.h: BLEonScanTimeout(); - -// Must be called repeatedly to ensure scans are performed, and that scan results are stored. -bool updateScanner(); - -// Returns scan_state, the current status of scanning. -scan_state_t getScanState(); - -// Print a scan chunk to debug log -void printScanResult(scan_chunk_t* sourceChunk); - - - - -#endif //#ifndef SCANNER_H diff --git a/firmware/nRF_badge/data_collector/incl/scanner_lib.c b/firmware/nRF_badge/data_collector/incl/scanner_lib.c new file mode 100644 index 0000000..2b8e4cd --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/scanner_lib.c @@ -0,0 +1,158 @@ +#include "scanner_lib.h" +#include "ble_lib.h" +#include "advertiser_lib.h" +#include "string.h" + +#include "debug_lib.h" + +// Values from iBeacon spec +#define COMPANY_ID_APPLE 0x004C +#define IBEACON_TYPE_PROXIMITY 0x1502 +#define IBEACON_MANUF_DATA_LEN 0x19 + +/**< Structure of iBeacon manufacturer-specific data, after the 0xFF type specifier byte. */ +typedef struct +{ + uint16_t company_ID; + uint16_t type; + uint8_t UUID[16]; + uint8_t major[2]; // big-endian + uint8_t minor[2]; // big-endian + uint8_t measured_power; +} iBeacon_data_t; + +/**< The internal handlers for scan-timeout and -report events. */ +static void internal_on_scan_report_callback(ble_gap_evt_adv_report_t* scan_report); +static void internal_on_scan_timeout_callback(void); + +/**< The external handlers for scan-timeout and -report events. */ +static scanner_on_scan_timeout_callback_t external_on_scan_timeout_callback = NULL; +static scanner_on_scan_report_callback_t external_on_scan_report_callback = NULL; + + + + + +/**@brief Function that is called when a scan timeout occurs. + */ +static void internal_on_scan_timeout_callback(void) { + if(external_on_scan_timeout_callback != NULL) + external_on_scan_timeout_callback(); +} + +/**@brief Function that is called when a scan report is available. + * + * @param[in] scan_report The scan report of a BLE device. + */ +static void internal_on_scan_report_callback(ble_gap_evt_adv_report_t* scan_report) { + + + int8_t rssi = scan_report->rssi; + + if (rssi < SCANNER_MINIMUM_RSSI) { + return; // ignore signals that are too weak. + } + + scanner_scan_report_t scanner_scan_report; + scanner_scan_report.rssi = rssi; + scanner_scan_report.scanner_scan_device_type = SCAN_DEVICE_TYPE_UNKNOWN; + + uint8_t data_len = scan_report->dlen; + uint8_t* data = (uint8_t*) scan_report->data; + + uint8_t* name_ptr = NULL; + uint8_t* manuf_data_ptr = NULL; + uint8_t manuf_data_len = 0; + uint8_t index = 0; + + + + + while ((name_ptr == NULL || manuf_data_ptr == NULL) && index < data_len) { + uint8_t field_len = data[index]; + index++; + uint8_t field_type = data[index]; + if (field_type == BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME || field_type == BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME) { + name_ptr = &data[index + 1]; // skip field type byte + } + else if (field_type == BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA) { + manuf_data_ptr = &data[index + 1]; // skip field type byte + manuf_data_len = field_len - 1; + } + index += field_len; + } + + // To get the advertising message structure + /* + char tmp[200]; + sprintf(tmp, "Len (%u): ", data_len); + for(uint8_t i = 0; i < data_len; i++) + sprintf(&tmp[strlen(tmp)], "%02X, ", data[i]); + debug_log("SCANNER: Scan: %u, %u, %d, %s\n", name_ptr-data, manuf_data_ptr-data, rssi, tmp); + */ + if (manuf_data_len == advertiser_get_manuf_data_len()) { + if (name_ptr != NULL && memcmp(name_ptr,(const uint8_t *)ADVERTISING_DEVICE_NAME,strlen(ADVERTISING_DEVICE_NAME)) == 0) { + + scanner_scan_report.scanner_scan_device_type = SCAN_DEVICE_TYPE_BADGE; + BadgeAssignement badge_assignement; + + advertiser_get_badge_assignement_from_advdata(&badge_assignement, &manuf_data_ptr[2]); + scanner_scan_report.ID = badge_assignement.ID; + scanner_scan_report.group = badge_assignement.group; + + + //debug_log("SCANNER: ---Badge seen: group %d, ID %.4X, rssi %d.\r\n", scanner_scan_report.group, scanner_scan_report.ID, scanner_scan_report.rssi); + } + } else if (manuf_data_len == IBEACON_MANUF_DATA_LEN) { + iBeacon_data_t iBeacon_data; + memcpy(&iBeacon_data, manuf_data_ptr, IBEACON_MANUF_DATA_LEN); // ensure data is properly aligned + if (iBeacon_data.company_ID == COMPANY_ID_APPLE && iBeacon_data.type == IBEACON_TYPE_PROXIMITY) { + scanner_scan_report.scanner_scan_device_type = SCAN_DEVICE_TYPE_IBEACON; + + // major/minor values are big-endian + uint16_t major = ((uint16_t)iBeacon_data.major[0] * 256) + iBeacon_data.major[1]; + (void) major; // Unused variable + uint16_t minor = ((uint16_t)iBeacon_data.minor[0] * 256) + iBeacon_data.minor[1]; + + scanner_scan_report.ID = minor; // take only lower byte of major value + scanner_scan_report.group = iBeacon_data.major[1]; + //debug_log("SCANNER: ---iBeacon seen: major %d, minor %d, rssi %d.\r\n", major, minor, rssi); + } + } + + if(scanner_scan_report.scanner_scan_device_type == SCAN_DEVICE_TYPE_UNKNOWN) { + //debug_log("SCANNER: Unknown device seen: rssi %d.\r\n", rssi); + return; + } + + if(external_on_scan_report_callback != NULL) + external_on_scan_report_callback(&scanner_scan_report); + +} + + +void scanner_init(void) { + ble_set_on_scan_timeout_callback(internal_on_scan_timeout_callback); + ble_set_on_scan_report_callback(internal_on_scan_report_callback); +} + + +void scanner_set_on_scan_timeout_callback(scanner_on_scan_timeout_callback_t scanner_on_scan_timeout_callback) { + external_on_scan_timeout_callback = scanner_on_scan_timeout_callback; +} + + + +void scanner_set_on_scan_report_callback(scanner_on_scan_report_callback_t scanner_on_scan_report_callback) { + external_on_scan_report_callback = scanner_on_scan_report_callback; +} + + +ret_code_t scanner_start_scanning(uint16_t scan_interval_ms, uint16_t scan_window_ms, uint16_t scan_duration_seconds) { + return ble_start_scanning(scan_interval_ms, scan_window_ms, scan_duration_seconds); +} + + +void scanner_stop_scanning(void) { + ble_stop_scanning(); +} diff --git a/firmware/nRF_badge/data_collector/incl/scanner_lib.h b/firmware/nRF_badge/data_collector/incl/scanner_lib.h new file mode 100644 index 0000000..fe155f1 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/scanner_lib.h @@ -0,0 +1,76 @@ +#ifndef __SCANNER_LIB_H +#define __SCANNER_LIB_H + + +#include "stdint.h" +#include "sdk_errors.h" + +#define SCANNER_MINIMUM_RSSI (-120) + + +typedef enum { + SCAN_DEVICE_TYPE_UNKNOWN = 0, + SCAN_DEVICE_TYPE_BADGE = 1, + SCAN_DEVICE_TYPE_IBEACON = 2, +} scanner_scan_device_type_t; + +typedef struct { + uint16_t ID; // Here not taken badge_assignement, because it could also be an IBeacon + uint8_t group; + scanner_scan_device_type_t scanner_scan_device_type; + int8_t rssi; +} scanner_scan_report_t; + +typedef void (*scanner_on_scan_timeout_callback_t) (void); /**< The on scan timeout callback function type. */ +typedef void (*scanner_on_scan_report_callback_t) (scanner_scan_report_t* scanner_scan_report);/**< The on scan report callback function type. */ + + + +/**@brief Function to initialize the scanner. + * + * @note ble_init() has to be called before. + */ +void scanner_init(void); + + +/**@brief Function to set the on scan timeout callback function. + * + * @param [in] scanner_on_scan_timeout_callback The callback function that should be called. + */ +void scanner_set_on_scan_timeout_callback(scanner_on_scan_timeout_callback_t scanner_on_scan_timeout_callback); + + +/**@brief Function to set the on scan report callback function. + * + * @param [in] scanner_on_scan_report_callback The callback function that should be called. + */ +void scanner_set_on_scan_report_callback(scanner_on_scan_report_callback_t scanner_on_scan_report_callback); + + + +/**@brief Function to start a scan-operation. + * + * @param[in] scan_interval_ms The scan interval in milliseconds. + * @param[in] scan_window_ms The scan window in milliseconds. + * @param[in] scan_duration_seconds The scan duration in seconds. + * + * @retval NRF_SUCCESS Successfully initiated scanning procedure. + * @retval NRF_ERROR_INVALID_ADDR Invalid pointer supplied. + * @retval NRF_ERROR_INVALID_STATE Invalid state to perform operation. + * @retval NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied. + * @retval NRF_ERROR_BUSY The stack is busy, process pending events and retry. + * @retval NRF_ERROR_RESOURCES Not enough BLE role slots available. + * Stop one or more currently active roles (Central, Peripheral or Broadcaster) and try again + * + * @note The input-parameters of this function has to be chosen in a way that advertising is still possible. + */ +ret_code_t scanner_start_scanning(uint16_t scan_interval_ms, uint16_t scan_window_ms, uint16_t scan_duration_seconds); + + +/**@brief Function for stopping any ongoing scan-operation. + */ +void scanner_stop_scanning(void); + + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/self_test.c b/firmware/nRF_badge/data_collector/incl/self_test.c deleted file mode 100644 index efd3ac1..0000000 --- a/firmware/nRF_badge/data_collector/incl/self_test.c +++ /dev/null @@ -1,143 +0,0 @@ -#include "self_test.h" -#include "accel.h" - -/*bool testInternalFlash(void) -{ - // Erase all pages. Check values of all pages - // read entire flash - for (int c = FIRST_PAGE; c <= LAST_PAGE; c++) - { - uint32_t* addr = ADDRESS_OF_PAGE(c); //address of chunk - debug_log("%d\r\n",sizeof(*addr)); - } - - // erase a page - erasePageOfFlash(FIRST_PAGE); - // try writing one byte - uint32_t* addr = ADDRESS_OF_PAGE((FIRST_PAGE)); - ble_flash_word_write( addr, (uint32_t)1); - addr = ADDRESS_OF_PAGE((FIRST_PAGE)); - return (*addr == 1); -}*/ - -void testMicAddSample() { - unsigned int readingsCount = 0; //number of mic readings taken - unsigned long readingsSum = 0; //sum of mic readings taken } for computing average - - for (int i=0; i avg_with_sd; -} - - -void runSelfTests(){ - debug_log("Running self-tests.\r\n"); - // Blink both LEDs - nrf_gpio_pin_write(GREEN_LED,LED_ON); - nrf_gpio_pin_write(RED_LED,LED_ON); - nrf_delay_ms(LED_BLINK_MS); - nrf_gpio_pin_write(GREEN_LED,LED_OFF); - nrf_gpio_pin_write(RED_LED,LED_OFF); - nrf_delay_ms(LED_BLINK_MS); - - // ====== test internal flash ====== - debug_log("Testing internal flash\r\n"); - nrf_gpio_pin_write(GREEN_LED,LED_ON); - - //TODO: fix test after we decouple the storer from flash management. See bug on github - debug_log("... Skipping test\r\n"); - /* - if (storer_test()) - { - debug_log(" Success\r\n"); - } - else - { - debug_log(" Failed\r\n"); - while(1) {}; - } - */ - nrf_delay_ms(LED_BLINK_MS); - nrf_gpio_pin_write(GREEN_LED,LED_OFF); - nrf_delay_ms(LED_BLINK_MS); - - // ====== test external flash ====== - debug_log("Testing external flash\r\n"); - nrf_gpio_pin_write(GREEN_LED,LED_ON); - - // read/write - if (testExternalEEPROM()) { - debug_log(" Success\r\n"); - } - else{ - debug_log(" Failed\r\n"); - while(1) {}; - } - nrf_delay_ms(LED_BLINK_MS); - nrf_gpio_pin_write(GREEN_LED,LED_OFF); - nrf_delay_ms(LED_BLINK_MS); - - // ====== Accel ====== - if (EXIST_ACCL) { - accel_test(); //Test whoiam and set basic registers - accel_set_int_motion(); //Set internal movement detection with Interruput output - acc_self_test(); //Internal self_test LIS2DH - } - - // ====== test mic ===== - testMicInit(MIC_ZERO); - - while(1) // stay in infinite loop for test mic and accel - { - // ====== Feature Motion detect ====== - if (EXIST_ACCL) { - //tap_accel(); //For tap detection reading register - motion_interrupt(); //For internal movement detection reading interrupt pin - } - // ====== Feature Mic ====== - testMicAddSample();// update reading - if (testMicAboveThreshold()) { - nrf_gpio_pin_write(RED_LED,LED_ON); - nrf_delay_ms(100); - } - else { - nrf_gpio_pin_write(RED_LED,LED_OFF); - } - nrf_delay_ms(10); - } - while(1) {}; -} diff --git a/firmware/nRF_badge/data_collector/incl/self_test.h b/firmware/nRF_badge/data_collector/incl/self_test.h deleted file mode 100644 index 58b1324..0000000 --- a/firmware/nRF_badge/data_collector/incl/self_test.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef TESTER_ENABLE_H -#define TESTER_ENABLE_H - -#define LED_BLINK_MS 500 - -//NRF51DK has common cathode LEDs, i.e. gpio LOW turns LED on. -#ifdef BOARD_PCA10028 - #define LED_ON 0 - #define LED_OFF 1 -//Badges are common anode, the opposite. -#else - #define LED_ON 1 - #define LED_OFF 0 -#endif - -#include "accel.h" - -#include "nrf_delay.h" //includes blocking delay functions - -//#include "internal_flash.h" -#include "analog.h" -#include "storer.h" -#include "ext_eeprom.h" - -#define THRESH_BUFSIZE 32 //size of the thresholding buffer, in bytes -#define THRESH_SAMPLES 64 // number of samples averaged to create a single reading -#define THRESH_SD 1.2 // -//#define THRESH_MAGIC_NUMBER 15 // value to add to average to reduce sensitivity -#define THRESH_MAGIC_NUMBER 10 // value to add to average to reduce sensitivity, with 8bit conversion. - -struct -{ - uint8_t bytes[THRESH_BUFSIZE]; - int pos; - int zeroValue; -} threshold_buffer; - -/* -// Tests internal flash. Returns true on success -bool testInternalFlash(void); -*/ - -// adds a mic sample to the thresholding array. It will make -// THRESH_SAMPLES samples and average them to create a single reading -void testMicAddSample(); - -// init the sampling threshold buffer -void testMicInit(int zeroValue); -#endif // TESTER_ENABLE_H - -// returns the average of mic thresholding buffer -uint8_t testMicAvg(); - -// return true if last sample is above threshold -bool testMicAboveThreshold(); - -void runSelfTests(); - -uint8_t readRegister8(uint8_t reg); -void writeRegister8(uint8_t reg, uint8_t value); diff --git a/firmware/nRF_badge/data_collector/incl/selftest_lib.c b/firmware/nRF_badge/data_collector/incl/selftest_lib.c new file mode 100644 index 0000000..f91e8eb --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/selftest_lib.c @@ -0,0 +1,118 @@ +#include "selftest_lib.h" + +#include "microphone_lib.h" +#include "battery_lib.h" +#include "accel_lib.h" +#include "eeprom_lib.h" +#include "flash_lib.h" +#include "custom_board.h" +#include "nrf_gpio.h" +#include "nrf_delay.h" + +#include "debug_lib.h" + + + +static void simple_test_start(void) { + nrf_delay_ms(100); + nrf_gpio_pin_write(RED_LED, LED_ON); +} +static void simple_test_passed(void) { + nrf_delay_ms(100); + nrf_gpio_pin_write(RED_LED, LED_OFF); +} +static void user_intervention_test_start(void) { + nrf_delay_ms(200); + nrf_gpio_pin_write(RED_LED, LED_ON); + nrf_gpio_pin_write(GREEN_LED, LED_ON); +} +static void user_intervention_test_passed(void) { + for(uint8_t i = 0; i < 5; i++) { + nrf_delay_ms(100); + nrf_gpio_pin_write(RED_LED, LED_ON); + nrf_gpio_pin_write(GREEN_LED, LED_ON); + nrf_delay_ms(100); + nrf_gpio_pin_write(RED_LED, LED_OFF); + nrf_gpio_pin_write(GREEN_LED, LED_OFF); + } +} + +// All modules need to be initialized before using this function +selftest_status_t selftest_test(void) { + + debug_log("SELFTEST: Starting selftest...\n"); + selftest_status_t selftest_status = SELFTEST_PASSED; + + + /********** BATTERY **************/ + simple_test_start(); + debug_log("SELFTEST: Starting battery selftest:\n"); + if(!battery_selftest()) { + debug_log("SELFTEST: Battery selftest failed!!\n"); + selftest_status = (selftest_status_t) (selftest_status | SELFTEST_FAILED_BATTERY); + return selftest_status; + } else { + debug_log("SELFTEST: Battery selftest successful!!\n"); + } + simple_test_passed(); + + /************ EEPROM ****************/ + simple_test_start(); + debug_log("SELFTEST: Starting eeprom selftest:\n"); + if(!eeprom_selftest()) { + debug_log("SELFTEST: EEPROM selftest failed!!\n"); + selftest_status = (selftest_status_t) (selftest_status | SELFTEST_FAILED_EEPROM); + return selftest_status; + } else { + debug_log("SELFTEST: EEPROM selftest successful!!\n"); + } + simple_test_passed(); + + + /*********** FLASH *************/ + simple_test_start(); + debug_log("SELFTEST: Starting flash selftest:\n"); + if(!flash_selftest()) { + debug_log("SELFTEST: Flash selftest failed!!\n"); + selftest_status = (selftest_status_t) (selftest_status | SELFTEST_FAILED_FLASH); + return selftest_status; + } else { + debug_log("SELFTEST: Flash selftest successful!!\n"); + } + simple_test_passed(); + + /*************** MICROPHONE ******************/ + user_intervention_test_start(); + debug_log("SELFTEST: Starting microphone selftest: (Please make some noise!)\n"); + if(!microphone_selftest()) { + debug_log("SELFTEST: Microphone selftest failed!!\n"); + selftest_status = (selftest_status_t) (selftest_status | SELFTEST_FAILED_MICROPHONE); + user_intervention_test_start(); // So that both LEDs go on again + return selftest_status; + } else { + debug_log("SELFTEST: Microphone selftest successful!!\n"); + } + user_intervention_test_passed(); + + /************** ACCELEROMETER *********************/ + #if ACCELEROMETER_PRESENT + user_intervention_test_start(); + debug_log("SELFTEST: Starting accelerometer selftest: (Please move the badge!)\n"); + if(!accel_selftest()) { + debug_log("SELFTEST: Accelerometer selftest failed!!\n"); + selftest_status = (selftest_status_t) (selftest_status | SELFTEST_FAILED_ACCELEROMETER); + user_intervention_test_start(); // So that both LEDs go on again (just for safety) + return selftest_status; + } else { + debug_log("SELFTEST: Accelerometer selftest successful!!\n"); + } + user_intervention_test_passed(); + + #endif + + nrf_gpio_pin_write(GREEN_LED, LED_ON); + nrf_delay_ms(1000); + nrf_gpio_pin_write(GREEN_LED, LED_OFF); + + return selftest_status; +} diff --git a/firmware/nRF_badge/data_collector/incl/selftest_lib.h b/firmware/nRF_badge/data_collector/incl/selftest_lib.h new file mode 100644 index 0000000..6d7928f --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/selftest_lib.h @@ -0,0 +1,36 @@ +/**@file + */ + +#ifndef __SELFTEST_LIB_H +#define __SELFTEST_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + +typedef enum { + SELFTEST_PASSED = 0, + SELFTEST_FAILED_ACCELEROMETER = (1 << 0), + SELFTEST_FAILED_MICROPHONE = (1 << 1), + SELFTEST_FAILED_BATTERY = (1 << 2), + SELFTEST_FAILED_EEPROM = (1 << 3), + SELFTEST_FAILED_FLASH = (1 << 4), +} selftest_status_t; + +/**@brief Function to start the selftests of the peripherals. + * + * @details In the beginning the "simple" tests without user interaction are performed: + * The battery, eeprom and flash test. During these tests the red LED is turned on + * before each test and turned off, if the test was successful. + * If all the prior tests were passed tests that need user interventions are performed: + * The microphone and the accelerometer. During these tests both LEDs (red and green) are + * turned on. If a test was passed, both LEDs blink a couple of times. + * When all tests have been passed the green LED goes on for 1sec. + * + * The function will directly return, if one of the test fails --> The user can see which test failed + * because the LEDs won't go off. + * + * @retval The status of the self-test. If one test fails, it should have the value of this failed test. + */ +selftest_status_t selftest_test(void); + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/sender.c b/firmware/nRF_badge/data_collector/incl/sender.c deleted file mode 100644 index 6a2e4f9..0000000 --- a/firmware/nRF_badge/data_collector/incl/sender.c +++ /dev/null @@ -1,768 +0,0 @@ -/* - * INFORMATION **************************************************** - */ - -#include -#include "battery.h" -#include "sender.h" - -// External chunks will be loaded into this buffer all at once, for quicker access. -static int extChunkFrom; -static scan_chunk_t extChunk; - -static uint32_t mTimeoutCheckTimer; - -static void on_check_timeouts(void * p_context) { - // Collector timeout. Stop collector if server is unseen for a long time - if (collectorTimeout > 0) { // 0 means timeout disabled - if (isCollecting && (millis() - lastReceipt >= collectorTimeout)) { - debug_log("SENDER: collector timeout. Stopping collector...\r\n"); - stopCollector(); - } - } - - if (scannerTimeout > 0) { // 0 means timeout disabled - if (scanner_enable && (millis() - lastReceipt >= scannerTimeout)) { - debug_log("SENDER: scanner timeout. Stopping scanner...\r\n"); - stopScanner(); - } - } -} - -server_command_params_t unpackCommand(uint8_t* pkt, unsigned char len) -{ - server_command_params_t command; - command.receiptTime = millis(); - - if (len == 0) { - debug_log("SENDER: Invalid server packet.\r\n"); - command.cmd = CMD_INVALID; - return command; - } - - command.cmd = pkt[0]; - - switch (command.cmd) { - case CMD_STATUS: - debug_log("SENDER: Got STATUS request.\r\n"); - if (len != CMD_STATUS_LEN && len != CMD_STATUS_ASSIGN_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - memcpy(&command.timestamp,pkt+1,sizeof(unsigned long)); - memcpy(&command.ms, pkt+5,sizeof(unsigned short)); - if (len == CMD_STATUS_ASSIGN_LEN) { - debug_log(" with ASSIGN request\r\n"); - command.cmd = CMD_STATUS_ASSIGN; - memcpy(&command.ID, pkt+7,sizeof(unsigned short)); - memcpy(&command.group, pkt+9,sizeof(unsigned char)); - } - break; - case CMD_STARTREC: - debug_log("SENDER: Got STARTREC request.\r\n"); - if (len != CMD_STARTREC_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - memcpy(&command.timestamp,pkt+1,sizeof(unsigned long)); // get timestamp from packet - memcpy(&command.ms, pkt+5,sizeof(unsigned short)); // get milliseconds from packet - memcpy(&command.timeout, pkt+7,sizeof(unsigned short)); // get timeout from packet - break; - case CMD_ENDREC: - debug_log("SENDER: Got ENDREC request.\r\n"); - if (len != CMD_ENDREC_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - break; - case CMD_STARTSCAN: - debug_log("SENDER: Got STARTSCAN request.\r\n"); - if (len != CMD_STARTSCAN_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - memcpy(&command.timestamp,pkt+1,sizeof(unsigned long)); // get timestamp from packet - memcpy(&command.ms, pkt+5,sizeof(unsigned short)); // get milliseconds from packet - memcpy(&command.timeout, pkt+7,sizeof(unsigned short)); // get scan timeout from packet - memcpy(&command.window, pkt+9,sizeof(unsigned short)); // get scan window from packet - memcpy(&command.interval, pkt+11,sizeof(unsigned short)); // get scan interval from packet - memcpy(&command.duration, pkt+13,sizeof(unsigned short)); // get scan duration from packet - memcpy(&command.period, pkt+15,sizeof(unsigned short)); // get scan period from packet - break; - case CMD_ENDSCAN: - if (len != CMD_ENDSCAN_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - debug_log("SENDER: Got ENDSCAN request.\r\n"); - break; - case CMD_REQSINCE: - debug_log("SENDER: Got REQSINCE request.\r\n"); - if (len != CMD_REQSINCE_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - memcpy(&command.timestamp,pkt+1,sizeof(unsigned long)); - memcpy(&command.ms, pkt+5,sizeof(unsigned short)); - send.from = NO_CHUNK; - send.bufContents = SENDBUF_EMPTY; - send.loc = SEND_LOC_HEADER; - break; - case CMD_REQSCANS: - debug_log("SENDER: Got REQSCANS request.\r\n"); - if (len != CMD_REQSCANS_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - memcpy(&command.timestamp,pkt+1,sizeof(unsigned long)); - send.from = NO_CHUNK; - send.bufContents = SENDBUF_EMPTY; - send.loc = SEND_LOC_HEADER; - break; - case CMD_IDENTIFY: - debug_log("SENDER: Got IDENTIFY request.\r\n"); - if (len != CMD_IDENTIFY_LEN) { - command.cmd = CMD_INVALID; - debug_log(" Bad parameters.\r\n"); - break; - } - memcpy(&command.timeout,pkt+1,sizeof(unsigned short)); - break; - default: - debug_log("SENDER: Got INVALID request.\r\n"); - command.cmd = CMD_INVALID; - break; - } - return command; -} - -static bool timestampValid(unsigned long timestamp) -{ - return (timestamp > MODERN_TIME && timestamp < FUTURE_TIME); -} - -void sender_init() -{ - send.from = NO_CHUNK; - send.source = SRC_FLASH; - send.bufContents = SENDBUF_EMPTY; - send.loc = SEND_LOC_HEADER; - send.num = 0; - - dateReceived = false; - pendingCommand.cmd = CMD_NONE; - - app_timer_create(&mTimeoutCheckTimer, APP_TIMER_MODE_REPEATED, on_check_timeouts); - app_timer_start(mTimeoutCheckTimer, APP_TIMER_TICKS(60 * 1000, APP_PRESCALER), NULL); -} - - -static void setTimeFromCommand(server_command_params_t* command_p) -{ - // Set badge internal timestamp - // There might be a delay between command receipt and command handling, so account for that. - unsigned long msCorrection = millis() - command_p->receiptTime; - unsigned long sCorrection = 0; - while (msCorrection >= 1000UL) { - msCorrection -= 1000; - sCorrection++; - } - debug_log(" Setting time to %lX, %lums.\r\n",command_p->timestamp+sCorrection,command_p->ms+msCorrection); - setTimeFractional(command_p->timestamp+sCorrection,command_p->ms+msCorrection); - if (!dateReceived) { - updateAdvData(); - dateReceived = true; - } -} - -bool updateSender() -{ - - // This will be the function return value. if there is any sending operations in-progress, this will be set to true. - // if not, it will return false, so that the main loop knows it can go to sleep early. - bool senderActive = false; - - server_command_params_t command; - command = pendingCommand; // local copy, in case interrupt changes it. - - if (pendingCommand.cmd != CMD_NONE) { - senderActive = true; - lastReceipt = millis(); - } - - switch (command.cmd) { - case CMD_STATUS: // fall through - case CMD_STATUS_ASSIGN: - // If the packet is already prepared, try sending it. - if (send.bufContents == SENDBUF_STATUS) { - if (BLEwrite(send.buf,send.bufSize)) { - send.bufContents = SENDBUF_EMPTY; // buffer has been sent - - setTimeFromCommand(&command); - - pendingCommand.cmd = CMD_NONE; // we're done with that pending command - debug_log("SENDER: Sent status.\r\n"); - - if (command.cmd == CMD_STATUS_ASSIGN) { - badge_assignment_t cmdAssignment; - cmdAssignment.ID = command.ID; - cmdAssignment.group = command.group; - BLEsetBadgeAssignment(cmdAssignment); - } - } - } - // otherwise prepare status packet - else { - send.buf[0] = (dateReceived) ? 1 : 0; - send.buf[1] = (scanner_enable) ? 1 : 0; // SCANNING - send.buf[2] = (isCollecting) ? 1 : 0; // COLLECTING DATA - - // Reply with onboard timestamp (0 if none set) - unsigned long timestamp = 0; - unsigned short ms = 0; - if (dateReceived) { - timestamp = now(); - ms = nowFractional(); - } - memcpy(send.buf+3,×tamp,sizeof(unsigned long)); - memcpy(send.buf+7,&ms,sizeof(unsigned short)); - - float voltage = BatteryMonitor_getBatteryVoltage(); - memcpy(send.buf+9,&voltage,sizeof(float)); - - send.bufContents = SENDBUF_STATUS; - send.bufSize = SENDBUF_STATUS_SIZE; - } - break; // switch (command.cmd) - - case CMD_REQSINCE: - if (send.from == NO_CHUNK) { - // Walk back from most recent data till we find a chunk that includes the requested timestamp - - send.from = SEND_FROM_END; - send.source = SRC_REALTIME; - - if (collect.loc > 0) { - send.from = collect.to; // if there's anything in the real-time chunk, send it. - } - - // look for potential chunks, in RAM first, starting right before current collector chunk - int latestRAMchunk = (collect.to > 0) ? collect.to-1 : LAST_RAM_CHUNK; - // advance through all RAM chunks except current collecting chunk - for (int c=latestRAMchunk; c != collect.to; c = (c > 0) ? c-1 : LAST_RAM_CHUNK) { - unsigned long timestamp = micBuffer[c].timestamp; - unsigned long check = micBuffer[c].check; - - // Check to see if the candidate chunk is one we should send - if (timestampValid(timestamp) && (check == timestamp || check == CHECK_TRUNC)) { // is it a valid RAM chunk? - if (timestamp < command.timestamp) { - break; // stop looking if we've reached earlier than the requested time - } - send.from = c; - send.source = SRC_RAM; - } - } - - // send.from is now the earliest chunk in RAM that we should send. - // There might be earlier relevant chunks in FLASH though. - // FLASH might be only partly filled - many invalid chunks. If we see a few in a row, then we're - // probably at the end of valid data in FLASH, so we should stop looking. keep track with below variable - int invalid = 0; // counts how many invalid chunks we see in a row, when we encounter any. - - // look through FLASH, from latest stored chunk (one chunk before store.to) - int latestFLASHchunk = (store.to > 0) ? store.to-1 : LAST_FLASH_CHUNK; - // advance through all FLASH chunks except current storing chunk - for (int c=latestFLASHchunk; c != store.to; c = (c > 0) ? c-1 : LAST_FLASH_CHUNK) { - mic_chunk_t* chunkPtr = (mic_chunk_t*)ADDRESS_OF_CHUNK(c); - unsigned long timestamp = chunkPtr->timestamp; - unsigned long check = chunkPtr->check; - - // is it a valid chunk (check == timestamp and timestamp is valid, OR check is a special value) - if (timestampValid(timestamp) && (check == timestamp || check == CHECK_TRUNC)) { - if (timestamp < command.timestamp) { - break; // stop looking if we've reached earlier than the requested time - } - send.from = c; - send.source = SRC_FLASH; - invalid = 0; // reset counter of sequential invalid chunks - } - else { - invalid++; - if (invalid > 5) { - break; // stop looking if we've seen a bunch of invalid chunks in a row (i.e. end of FLASH data) - } - } - } - - debug_log("SENDER: sending mic data since: s:%c c:%d\r\n", - (send.source==SRC_FLASH) ? 'F' : ((send.source==SRC_RAM)?'R':'C') , - send.from); - - send.loc = SEND_LOC_HEADER; // we'll need to send a header first - - } // if (send.from == NO_CHUNK) - - // Otherwise, send a packet if we've already prepared it. - else if (send.bufContents == SENDBUF_HEADER || send.bufContents == SENDBUF_SAMPLES - || send.bufContents == SENDBUF_END) { - if (BLEwrite(send.buf,send.bufSize)) { - switch(send.bufContents) { - case SENDBUF_HEADER: - send.loc = 0; // If we finished sending a chunk header, we can start sending the chunk data. - break; - case SENDBUF_SAMPLES: - send.loc += send.bufSize; // If we finished sending a packet of data, advance through chunk - - // If we reached the end of the chunk, we need to advance to the next chunk (if there is one ready) - if (send.loc >= send.num) { - debug_log("SENDER: sent s:%c c:%d n:%d\r\n", - (send.source==SRC_FLASH) ? 'F' : ((send.source==SRC_RAM)?'R':'C'), - send.from, send.num); - // Advance to next chunk - switch(send.source) { - case SRC_FLASH: - // look for another unsent FLASH chunk - do { - // increment to next FLASH chunk - send.from = (send.from < LAST_FLASH_CHUNK) ? send.from+1 : 0; - mic_chunk_t* chunkPtr = (mic_chunk_t*)ADDRESS_OF_CHUNK(send.from); - - unsigned long timestamp = chunkPtr->timestamp; - unsigned long check = chunkPtr->check; - // is it a valid chunk - if (timestampValid(timestamp) && (check == timestamp || check == CHECK_TRUNC)) { - break; // stop looking if we found the next valid chunk - } - // If chunk isn't valid, we need to keep looking - } while (send.from != store.to); - - // If we haven't caught up with store.to yet, then we have more FLASH chunks to send. - if (send.from != store.to) { - break; // from switch(send.source) - } - - // Else we need to look through RAM next. Switch to RAM: - send.source = SRC_RAM; - send.from = collect.to; - // Fall through to advance to first RAM chunk: - case SRC_RAM: - // look for another unsent RAM chunk - do { - // increment to next RAM chunk - send.from = (send.from < LAST_RAM_CHUNK) ? send.from+1: 0; - - unsigned long timestamp = micBuffer[send.from].timestamp; - unsigned long check = micBuffer[send.from].check; - // is it a valid chunk - if (timestampValid(timestamp) && (check == timestamp || check == CHECK_TRUNC)) { - break; // from switch(send.source) - } - // If chunk isn't valid, we need to keep looking - } while (send.from != collect.to); - - // If we haven't caught up with collect.to yet, we have more RAM chunks to send - if (send.from != collect.to) { - break; // from switch(send.source) - } - - // Else if collect.loc > 0, there is real-time data to be sent from collect.to - else if (collect.loc > 0) { // have any samples been collected into the real-time chunk - send.source = SRC_REALTIME; - break; // from switch(send.source) - } - - // Else we're done. Fall through: - case SRC_REALTIME: - // if we've sent the realtime data, we're all done, and should send a null header - send.from = SEND_FROM_END; - break; - - default: - break; - } // switch(send.source) - - send.loc = SEND_LOC_HEADER; // need to send header of next chunk first - } // if (send.loc >= send.num) - // Else we need to send another data packet in this chunk - break; // from switch(send.bufContents) - case SENDBUF_END: - debug_log("SENDER: sent null header. REQSINCE complete.\r\n"); - pendingCommand.cmd = CMD_NONE; // if we sent the terminating null header, we're done sending data - senderActive = false; // all done with sending, updateSender will return this (false) - break; // from switch(send.bufContents) - default: - break; // from switch(send.bufContents) - } // switch(send.bufContents) - send.bufContents = SENDBUF_EMPTY; - } //if (BLEwrite(send.buf,send.bufSize)) - } // if (data packet was already prepared) - - // Otherwise, prepare a packet to be sent. - else { - // If we sent all the data, we need to send an empty header to terminate - if (send.from == SEND_FROM_END) { // terminating null header - send.bufSize = SENDBUF_HEADER_SIZE; - memset(send.buf,0,send.bufSize); // null header - send.bufContents = SENDBUF_END; - } - - // Else there's data to be sent - else { - // Get pointer to current chunk - mic_chunk_t* chunkPtr; - switch (send.source) { - case SRC_FLASH: - chunkPtr = (mic_chunk_t*)ADDRESS_OF_CHUNK(send.from); - break; - case SRC_RAM: - case SRC_REALTIME: - chunkPtr = &(micBuffer[send.from]); - break; - default: - debug_log("invalid source?\r\n"); - APP_ERROR_CHECK_BOOL(false); - break; - } - - if (send.loc == SEND_LOC_HEADER) { - // Compose header - memcpy(send.buf, &(chunkPtr->timestamp), sizeof(unsigned long)); // timestamp - memcpy(send.buf+4, &(chunkPtr->msTimestamp), sizeof(unsigned short)); // timestamp ms - memcpy(send.buf+6, &(chunkPtr->battery), sizeof(float)); // battery voltage - unsigned short period = samplePeriod; // cast to unsigned short - memcpy(send.buf+10, &period, sizeof(unsigned short)); // sample period ms - - if (send.source == SRC_REALTIME) { - send.num = collect.loc; // all collected samples so far - } - else if (chunkPtr->check == CHECK_TRUNC) { - // number of samples in truncated chunk is stored in the last byte of the sample array - // We need this because the compiler doesn't see our APP_ERROR as reseting the system. - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - send.num = chunkPtr->samples[SAMPLES_PER_CHUNK-1]; - } - else { - send.num = SAMPLES_PER_CHUNK; // full chunk - } - - unsigned char num = send.num; // cast to unsigned char - memcpy(send.buf+12, &num, sizeof(unsigned char)); // number of samples - send.bufContents = SENDBUF_HEADER; - send.bufSize = SENDBUF_HEADER_SIZE; - } - else { // else we're sending data - int samplesLeft = send.num - send.loc; - // Must send 20 or fewer samples at a time. - if (samplesLeft > SAMPLES_PER_PACKET) { - send.bufSize = SAMPLES_PER_PACKET; - } - else { - send.bufSize = samplesLeft; - } - memcpy(send.buf, &(chunkPtr->samples[send.loc]), send.bufSize); // fill buffer with samples - send.bufContents = SENDBUF_SAMPLES; - } - } - } - break; // switch (command.cmd) - - case CMD_REQSCANS: - // -------------------------------- - // ----- initializing sending ----- - // If send.from isn't set, then we just got the request, and we need to find where to start sending from - if (send.from == NO_CHUNK) { - send.from = SEND_FROM_END; - send.source = SRC_SCAN_RAM; - - // look for potential scan chunks, in RAM first, starting right before current scanner chunk - int latestScanChunk = (scan.to > 0) ? scan.to-1 : LAST_SCAN_CHUNK; - // advance through all RAM chunks except current collecting chunk - for (int c=latestScanChunk; c != scan.to; c = (c > 0) ? c-1 : LAST_SCAN_CHUNK) { - unsigned long timestamp = scanBuffer[c].timestamp; - unsigned long check = scanBuffer[c].check; - - // Check to see if the candidate chunk is one we should send - if ((check == timestamp || check == CHECK_TRUNC || check == CHECK_CONTINUE) && timestampValid(timestamp)) { - if (timestamp < command.timestamp) { - break; // stop looking if we've reached earlier than the requested time - } - send.from = c; - send.source = SRC_SCAN_RAM; - } - } - - // find earliest desired scan chunk - int invalid = 0; - int latestEXTchunk = (store.extTo > EXT_FIRST_DATA_CHUNK) ? store.extTo-1 : EXT_LAST_CHUNK; - // send.source = SRC_EXT; - - for(int c=latestEXTchunk; c != store.extTo; c = (c > EXT_FIRST_DATA_CHUNK) ? c-1 : EXT_LAST_CHUNK) { - unsigned long timestamp = getScanTimestamp(c); - unsigned long check = getScanCheck(c); - - // is it a valid chunk (check == timestamp and timestamp is valid or check is a special value) - if (timestampValid(timestamp) && (check == timestamp || check == CHECK_TRUNC)) { - if (timestamp < command.timestamp) { - break; // stop looking if we've reached earlier than the requested time - } - send.from = c; - send.source = SRC_EXT; - invalid = 0; // reset counter of sequential invalid chunks - } - else { - invalid++; - if (invalid > 5) { - break; // stop looking if we've seen a bunch of invalid chunks in a row (i.e. end of FLASH data) - } - } - } - - debug_log("SENDER: sending scans since: s:%c c:%d\r\n",(send.source==SRC_EXT) ? 'P' : 'Q', send.from); - - send.loc = SEND_LOC_HEADER; // we'll need to send a header first - - } // if (send.from == NO_CHUNK) - - // ----------------------------- - // ----- executing sending ----- - // If send.from is set, then we're actually sending data. - else if (send.bufContents == SENDBUF_SCANHEADER || send.bufContents == SENDBUF_SCANDEVICES - || send.bufContents == SENDBUF_END) { - if (BLEwrite(send.buf,send.bufSize)) { - switch (send.bufContents) { - case SENDBUF_SCANHEADER: - send.loc = 0; - break; - case SENDBUF_SCANDEVICES: - send.loc += send.bufSize / sizeof(seenDevice_t); - if (send.loc >= send.num) { - debug_log("SENDER: sent s:%c c:%d n:%d\r\n",(send.source==SRC_EXT) ? 'P' : 'Q', - send.from, send.num); - // advance to next chunk - switch(send.source) { - case SRC_EXT: - // look for another unsent EXT chunk - do { - // increment to next EXT chunk - send.from = (send.from < EXT_LAST_CHUNK) ? send.from+1 : EXT_FIRST_DATA_CHUNK; - - unsigned long timestamp = getScanTimestamp(send.from); - unsigned long check = getScanCheck(send.from); - // is it a valid chunk - if (timestampValid(timestamp) && check == timestamp) { - break; // from while(send.from != store.to) - } - // If chunk isn't valid, we need to keep looking - } while (send.from != store.extTo); - - // If we haven't caught up with store.extTo yet, we have more EXT scan chunks to send - if (send.from != store.extTo) { - break; // from switch(send.source) - } - - // Else we need to look through RAM next. Switch to RAM: - send.source = SRC_SCAN_RAM; - send.from = scan.to; - // and advance to first RAM chunk (below) - // Fall through: - case SRC_SCAN_RAM: - // look for another unsent RAM scan chunk - do { - // increment to next RAM chunk - send.from = (send.from < LAST_SCAN_CHUNK) ? send.from+1: 0; - - unsigned long timestamp = scanBuffer[send.from].timestamp; - unsigned long check = scanBuffer[send.from].check; - - // is it a valid chunk - // (check == timestamp and timestamp is valid, OR check is a special value) - if (timestampValid(timestamp) && check == timestamp) { - break; // from switch(send.source) - } - - // If chunk isn't valid, we need to keep looking - } while (send.from != scan.to); - - // If we caught up with scan.to, then we've sent all RAM scan chunks available. we're done - if (send.from == scan.to) { - send.from = SEND_FROM_END; - } - break; // from switch(send.source) - default: - break; - } // switch(send.source) - send.loc = SEND_LOC_HEADER; // need to send header for next chunk - } // if (send.loc >= send.num) - - break; - - case SENDBUF_END: - debug_log("SENDER: sent null header. REQSCANS complete.\r\n"); - pendingCommand.cmd = CMD_NONE; // if we sent the terminating null header, we're done sending data - senderActive = false; // all done with sending, updateSender will return this (false) - break; - default: - break; - } // switch(send.bufContents) - - - send.bufContents = SENDBUF_EMPTY; - } //if (BLEwrite(send.buf,send.bufSize)) - - } // if (data packet was already prepared) - - // -- else need to prepare packet - // If the send packet buffer is empty, we need to fill it. - else { - // If we sent all the scans, we need to send an empty header to terminate - if (send.from == SEND_FROM_END) { // terminating null header - send.bufSize = SENDBUF_SCANHEADER_SIZE; - memset(send.buf,0,send.bufSize); // null header - send.bufContents = SENDBUF_END; - } - - // Else there's data to be sent - else { - // Get pointer to current chunk - scan_chunk_t* scanChunkPtr; - switch (send.source) { - case SRC_SCAN_RAM: - scanChunkPtr = &(scanBuffer[send.from]); - break; - case SRC_EXT: - if (extChunkFrom != send.from) { - //debug_log("SENDER: Copying ext chunk %d to RAM\r\n",send.from); - getScanChunk(&extChunk,send.from); - extChunkFrom = send.from; - } - scanChunkPtr = &extChunk; - break; - default: - debug_log("invalid source\r\n"); - APP_ERROR_CHECK_BOOL(false); - break; - } - - if (send.loc == SEND_LOC_HEADER) { - // Compose header - send.num = scanChunkPtr->num; - // We need this because the compiler doesn't see our APP_ERROR as reseting the system. - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - float batteryVoltage = ((int)scanChunkPtr->batteryLevel + 100) / 100.0; - memcpy(send.buf, &(scanChunkPtr->timestamp), sizeof(unsigned long)); // timestamp - memcpy(send.buf+4, &batteryVoltage, sizeof(float)); // battery voltage - memcpy(send.buf+8, &(scanChunkPtr->num), sizeof(unsigned char)); // number of devices seen - send.bufContents = SENDBUF_SCANHEADER; - send.bufSize = SENDBUF_SCANHEADER_SIZE; - } - - else { // else we're sending data - // compose next packet of device data - int devicesLeft = send.num - send.loc; - if (devicesLeft > DEVICES_PER_PACKET) { - send.bufSize = sizeof(seenDevice_t) * DEVICES_PER_PACKET; - } - else { - send.bufSize = sizeof(seenDevice_t) * devicesLeft; - } - memcpy(send.buf, &(scanChunkPtr->devices[send.loc]),send.bufSize); - send.bufContents = SENDBUF_SCANDEVICES; - } - } - } - break; // switch (command.cmd) - - case CMD_STARTREC: // fall through - case CMD_STARTSCAN: - // If the packet is already prepared, try sending it. - if (send.bufContents == SENDBUF_TIMESTAMP) { // are we currently waiting to send status packet - if (BLEwrite(send.buf,send.bufSize)) { // try sending packet - send.bufContents = SENDBUF_EMPTY; // buffer has been sent - - setTimeFromCommand(&command); - - if (command.cmd == CMD_STARTREC) { - // Timeout value expressed as minutes - convert to ms. - debug_log("SENDER: starting collector, timeout %d minutes.\r\n",(int)command.timeout); - collectorTimeout = ((unsigned long)command.timeout) * 60UL * 1000UL; - startCollector(); - } - - else if (command.cmd == CMD_STARTSCAN) { - // Timeout value expressed as minutes - convert to ms. - debug_log("SENDER: starting scanner, timeout %d minutes.\r\n",(int)command.timeout); - scannerTimeout = ((unsigned long)command.timeout) * 60UL * 1000UL; - // If command specifies a 0 value, use default parameters - command.window = (command.window == 0) ? SCAN_WINDOW : command.window; - command.interval = (command.interval == 0) ? SCAN_INTERVAL : command.interval; - command.duration = (command.duration == 0) ? SCAN_TIMEOUT : command.duration; - command.period = (command.period == 0) ? SCAN_PERIOD : command.period; - - startScanner(command.window,command.interval,command.duration,command.period); - } - - pendingCommand.cmd = CMD_NONE; - } - } - else { // otherwise prepare timestamp packet - unsigned long timestamp = 0; - unsigned short ms = 0; - if (dateReceived) { - timestamp = now(); - ms = nowFractional(); - } - - memcpy(send.buf,×tamp,sizeof(unsigned long)); - memcpy(send.buf+4,&ms,sizeof(unsigned short)); - - send.bufContents = SENDBUF_TIMESTAMP; - send.bufSize = SENDBUF_TIMESTAMP_SIZE; - } - break; // switch (command.cmd) - - case CMD_ENDREC: - debug_log("SENDER: stopping collector.\r\n"); - stopCollector(); - pendingCommand.cmd = CMD_NONE; - break; // switch (command.cmd) - - case CMD_ENDSCAN: - debug_log("SENDER: stopping scanner.\r\n"); - stopScanner(); - pendingCommand.cmd = CMD_NONE; - break; // switch (command.cmd) - - case CMD_IDENTIFY: - if (command.timeout == 0) { - led_timeout_cancel(); - nrf_gpio_pin_write(LED_2,0); // clunky - sender.c doesn't see LED_OFF define - debug_log("SENDER: LED off.\r\n"); - } else { - if (command.timeout > 30) command.timeout = 30; // clip to 30seconds - unsigned long timeout_ms = ((unsigned long)command.timeout) * 1000UL; - led_timeout_set(timeout_ms); - nrf_gpio_pin_write(LED_2,1); // clunky - sender.c doesn't see LED_ON define - debug_log("SENDER: LED on for %ds.\r\n",command.timeout); - } - pendingCommand.cmd = CMD_NONE; - break; // switch (command.cmd) - - case CMD_NONE: - break; // switch (command.cmd) - - case CMD_INVALID: // fall through - default: - pendingCommand.cmd = CMD_NONE; - break; // switch (command.cmd) - - } // switch (command.cmd) - - return senderActive; -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/sender.h b/firmware/nRF_badge/data_collector/incl/sender.h deleted file mode 100644 index a6bef91..0000000 --- a/firmware/nRF_badge/data_collector/incl/sender.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * INFORMATION **************************************************** - */ - -#ifndef SENDER_H -#define SENDER_H - -#include -#include - -#include "debug_log.h" - -#include "nrf_drv_config.h" -#include "boards.h" - -#include "nrf_soc.h" - -#include "nrf_gpio.h" - -#include "ble_setup.h" -#include "collector.h" -#include "storer.h" -#include "scanner.h" - - -/* - * === Protocol Summary === - * -Command/Notes- . . . . . . -Server Sends- . . . . . . -Badge Responds- . - * - * CMD_STATUS - * Ask for badge status 's' (uchar) clock status (uchar) - 0, clock was unset; 1, clock was set - * and send date timestamp (ulong) scan status (uchar) - 1 if scanning - * Optionally, set ms (ushort) collector status (uchar) - 1 if collecting samples - * badge ID and group [ ID (ushort) ] timestamp (ulong) - 0 if none set - * [ group (uchar) ] ms (ushort) - 0 if none set - * battery voltage (float) - * Send ID=0xFFFF/group=0xFF to reset the persistent (non-volatile) assignments. - * . . . . . . . . - * CMD_STARTREC - * Start collecting data '1' (uchar) timestamp (ulong) - acknowledge time. 0 if none set. - * timestamp (ulong) ms (ushort) 0 if none set. - * ms (ushort) - * timeout (ushort) - stop recording if didn't see server for [timeout] minutes - * 0 for no timeout - * . . . . . . . . - * CMD_ENDREC - * Stop collecting data '0' (uchar) none - * . . . . . . . . - * CMD_STARTSCAN - * Start performing scans 'p' (uchar) timestamp (ulong) - acknowledge time. 0 if none set. - * timestamp (ulong) ms (ushort) 0 if none set. - * ms (ushort) - * timeout (ushort) - stop scanning if didn't see server for [timeout] minutes - * 0 for no scan timeout - * window (ushort) \ - * interval (ushort) } - short-scale scan parameters, ms. 0 for default values. - * duration (ushort) - duration of one scan, s. Called "timeout" in NRF APIs. 0 for default value - * period (ushort) - how often to perform scans, s. 0 for default. - * . . . . . . . . - * CMD_ENDSCAN - * Stop collecting data 'q' (uchar) none - * . . . . . . . . - * CMD_REQSINCE - * Request all data since 'r' (uchar) Chunks of data - * specified time, timestamp (ulong) Header (13bytes) - * from FLASH or RAM ms (ushort) timestamp (ulong) - * ms (ushort) - * voltage (float) - * sample period in ms (ushort) - * number of samples in chunk (uchar) - * Samples - * in packets of 20bytes - * End marker - * Dummy header - * all bytes 0 - * . . . . . . . . - * CMD_REQSCANS - * Request scan data 'b' (uchar) Scan results - * from ext. EEPROM. timestamp (ulong) Header (5bytes) - * timestamp (ulong) - * battery voltage (float) - * number of devices in scan (uchar) - * Devices, in packets of 20bytes (5 devices) - * Device ID (ushort) - * RSSI (signed char) - * Count (signed char) - * End marker - * Dummy header - * all bytes 0 - * . . . . . . . . - * CMD_IDENTIFY - * Light an LED for 'i' none (lights LED) - * specified duration timeout (ushort) - 0 to turn off LED - * . . . . . . . . - */ - -enum SERVER_COMMANDS -{ - CMD_NONE = 0, - CMD_INVALID = 255, - CMD_STATUS = 's', // server requests send badge status - CMD_STARTREC = '1', // server requests start collecting - CMD_ENDREC = '0', // server requests stop collecting - CMD_STARTSCAN = 'p', // server requests start scanning - CMD_ENDSCAN = 'q', // server requests stop scanning - CMD_REQSINCE = 'r', // server requests send all data, flash or ram, since time X - CMD_REQSCANS = 'b', // server requests send all scan results, since time X - CMD_IDENTIFY = 'i', // server requests light an LED for specified time - - CMD_STATUS_ASSIGN = 'S' // Only used internally to the badge, to distinguish status requests with optional ID/group assignment -}; - -enum SERVER_COMMAND_LENGTHS -{ - CMD_NONE_LEN = 0, - CMD_STATUS_LEN = 7, - CMD_STATUS_ASSIGN_LEN = 10, - CMD_STARTREC_LEN = 9, - CMD_ENDREC_LEN = 1, - CMD_STARTSCAN_LEN = 17, - CMD_ENDSCAN_LEN = 1, - CMD_REQSINCE_LEN = 7, - CMD_REQSCANS_LEN = 5, - CMD_IDENTIFY_LEN = 3 -}; - -typedef struct -{ - unsigned long receiptTime; // millis() time of command receipt - - // Timestamp receipt - unsigned long timestamp; - unsigned short ms; - unsigned short timeout; - unsigned short duration; - unsigned short period; - unsigned short window; - unsigned short interval; - unsigned short ID; - unsigned char group; - unsigned char cmd; // ordered for nice packing -} server_command_params_t; - -volatile server_command_params_t pendingCommand; - -volatile bool dateReceived; // whether the server has synced badge with date (e.g. thru status request) - -unsigned long lastReceipt; // time (i.e. millis) of the last command receipt from server -unsigned long collectorTimeout; // time after last command receipt to stop collecting (e.g. if server doesn't send stop command) -unsigned long scannerTimeout; // similar to collectorTimeout, but for scanner module. - - -enum SENDBUF_CONTENTS -{ - SENDBUF_EMPTY = 0, // no valid data in send buffer - SENDBUF_STATUS, // status report packet - SENDBUF_TIMESTAMP, // timestamp report packet, i.e. for startrec response - SENDBUF_HEADER, // chunk header packet, for data sending commands - SENDBUF_SAMPLES, // chunk samples packet, for data sending commands - SENDBUF_SCANHEADER, - SENDBUF_SCANDEVICES, - SENDBUF_END // null header, to mark end of data sending -}; - -// Special sendBuf sizes -#define SENDBUF_STATUS_SIZE 13 // see badge response structure above - 13 bytes total in status report packet -#define SENDBUF_HEADER_SIZE 13 // see badge response structure above - 13 bytes total in chunk header packet -#define SENDBUF_SCANHEADER_SIZE 9 -#define SENDBUF_TIMESTAMP_SIZE 6 // 4byte timestamp + 2byte milliseconds -#define SAMPLES_PER_PACKET 20 // maximum by BLE spec -#define DEVICES_PER_PACKET 5 // scan devices per packet - one scan device report is 4bytes - -// Special values for send parameters, to help keep track of sending progress -#define SEND_LOC_HEADER -1 // send.loc if header is to be sent -#define SEND_FROM_END -1 // send.from if we're finished sending, and should send a null header - -// To keep track of current origin of chunks being sent. see send struct below. -enum SEND_SOURCE -{ - SRC_FLASH, // sending from FLASH - SRC_RAM, // sending a complete (unstored) chunk from RAM - SRC_REALTIME, // sending an incomplete (collector in-progress) chunk from RAM - SRC_SCAN_RAM, // sending scan chunks from RAM - SRC_EXT // sending chunks stored to external EEPROM -}; - - -// Struct to organize various variables/states related to sending -struct -{ - int from; // current chunk we're sending from - int source; // where send.from refers to (see SEND_SOURCE above) - - int loc; // index of next data to be sent from chunk - SEND_LOC_HEADER if header is to be sent next - int num; // number of items (samples, devices) to be sent from send.from chunk - - unsigned char buf[20]; // Buffer for BLE sending - int bufContents; // Records contents of send buffer - int bufSize; // Records length of actual data in send buffer -} send; - - - -/* - * Accepts a command packet from server, and parses it for relevant parameters into pendingCommand struct. - * Called from BLEonReceive - */ -server_command_params_t unpackCommand(uint8_t* pkt, unsigned char len); - -/* - * Initialize sender module. Reset sending variables, and locate the earliest unsent chunk in FLASH (for unimplemented feature) - */ -void sender_init(); - -/* - * Handle sending operations. Must be called repeatedly, i.e. in main loop. - */ -bool updateSender(); - - - - - - -#endif //#ifndef SENDER_H - diff --git a/firmware/nRF_badge/data_collector/incl/sender_lib.c b/firmware/nRF_badge/data_collector/incl/sender_lib.c new file mode 100644 index 0000000..d3cc010 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/sender_lib.c @@ -0,0 +1,312 @@ +#include "sender_lib.h" + +#include "ble_lib.h" +#include "systick_lib.h" // Needed for the timeout-check +#include "app_fifo.h" +#include "app_util_platform.h" +#include "timeout_lib.h" // Needed for disconnecting after N milliseconds +#include "app_timer.h" +#ifndef UNIT_TEST +#include "custom_board.h" // For LED +#include "nrf_gpio.h" // For LED +#endif + +//#include "stdio.h" +//#include "string.h" +#include "debug_lib.h" + +#define TRANSMIT_QUEUED_BYTES_PERIOD_MS 5 /**< The timer period where the transmit_queued_bytes-function is called */ +#define TX_FIFO_SIZE 512 /**< Size of the transmit fifo of the BLE (has to be a power of two) */ +#define RX_FIFO_SIZE 128 /**< Size of the receive fifo of the BLE (has to be a power of two) */ +#define MAX_BYTES_PER_TRANSMIT 20 /**< Number of bytes that could be sent at once via the Nordic Uart Service */ +#define DISCONNECT_TIMEOUT_MS (15*1000) /**< The timeout after the sender should disconnect when no packet was transmitted successfully in this time */ +#ifdef DEBUG_LOG_ENABLE +#define DISCONNECT_TIMEOUT_ENABLED 0 /**< Disconnect timeout enabled */ +#else +#define DISCONNECT_TIMEOUT_ENABLED 1 /**< Disconnect timeout enabled */ +#endif + + + +static void on_connect_callback(void); +static void on_disconnect_callback(void); +static void on_transmit_callback(void); +static void on_receive_callback(uint8_t* data, uint16_t len); +static ret_code_t transmit_queued_bytes(void); +void transmit_queued_bytes_timer_callback(void* p_context); + +static volatile uint8_t connected = 0; /**< Flag, if there is a ble-connection. */ +static volatile uint8_t transmitting = 0; /**< Flag, if there is currently an ongoing transmit_queued_bytes()-operation. */ +static sender_receive_notification_handler_t receive_notification_handler = NULL; /**< External notification handler, that should be called if sth was received */ + + +static uint8_t tx_fifo_buf[TX_FIFO_SIZE]; +static uint8_t rx_fifo_buf[RX_FIFO_SIZE]; +static uint8_t transmit_buf[MAX_BYTES_PER_TRANSMIT]; /**< Buffer where the actual bytes to send will be buffered */ + +static app_fifo_t tx_fifo; /**< The fifo for transmitting */ +static app_fifo_t rx_fifo; /**< The fifo for receiving */ + +static uint32_t disconnect_timeout_id = 0; /**< The timeout-id for the disconnect timeout */ + +APP_TIMER_DEF(transmit_queued_bytes_timer); /**< The app-timer to periodically call the transmit_queued_bytes */ + + +/**@brief Function to reset the sender state. + */ +static void sender_reset(void) { + connected = 0; + transmitting = 0; + // Flush the tx and rx FIFO + app_fifo_flush(&tx_fifo); + app_fifo_flush(&rx_fifo); +} + + +/**@brief Function that is called when a connection was established. + */ +static void on_connect_callback(void) { + sender_reset(); + // Start the disconect-timeout: + #if DISCONNECT_TIMEOUT_ENABLED + timeout_start(disconnect_timeout_id, DISCONNECT_TIMEOUT_MS); + #endif + #ifdef DEBUG_LOG_ENABLE + #ifndef UNIT_TEST + nrf_gpio_pin_write(GREEN_LED, LED_ON); //turn on LED + #endif + #endif + connected = 1; + debug_log("SENDER: Connected callback\n"); +} + +/**@brief Function that is called when disconnected event occurs. + */ +static void on_disconnect_callback(void) { + sender_reset(); + // Stop the disconnect-timeout: + timeout_stop(disconnect_timeout_id); + connected = 0; + #ifdef DEBUG_LOG_ENABLE + #ifndef UNIT_TEST + nrf_gpio_pin_write(GREEN_LED, LED_OFF); //turn on LED + #endif + #endif + debug_log("SENDER: Disconnected callback\n"); +} + +/**@brief Function that is called when the transmission was successful. + * + * @details transmit_queued_bytes() is called to send the remaining bytes in the FIFO. + */ +static void on_transmit_callback(void) { + // Reschedule the transmit of the queued bytes + // transmit_queued_bytes(); +} + +/**@brief Function that is called when data are received. + * + * @details It calls the registered notification handler. + */ +static void on_receive_callback(uint8_t* data, uint16_t len) { + uint64_t timepoint_ticks = systick_get_ticks_since_start(); + uint32_t timepoint_seconds; + uint16_t timepoint_milliseconds; + systick_get_timestamp(&timepoint_seconds, &timepoint_milliseconds); + uint32_t len_32 = len; + app_fifo_write(&rx_fifo, data, &len_32); + //debug_log("SENDER: Received: %u\n", len_32); + if(receive_notification_handler != NULL) { + + // Prepare a receive_notification-struct and call the notification-handler + receive_notification_t receive_notification; + receive_notification.notification_len = len; + receive_notification.timepoint_ticks = timepoint_ticks; + receive_notification.timepoint_seconds = timepoint_seconds; + receive_notification.timepoint_milliseconds = timepoint_milliseconds; + receive_notification_handler(receive_notification); + } + // Reset the disconnect timeout timer if we receive sth + timeout_reset(disconnect_timeout_id); +} + +void transmit_queued_bytes_timer_callback(void* p_context) { + transmit_queued_bytes(); +} + + +/**@brief Function transmits the data in tx_fifo in small portions (MAX_BYTES_PER_TRANSMIT). + * + * @retval NRF_SUCCESS If the data were sent successfully. Otherwise, an error code is returned. + */ +static ret_code_t transmit_queued_bytes(void) { + if(!transmitting) { + app_timer_stop(transmit_queued_bytes_timer); + return NRF_ERROR_INVALID_STATE; + } + + //uint32_t ms = (uint32_t) systick_get_continuous_millis(); + //debug_log("Queued %u ms\n", ms); + + // Read out how many bytes have to be sent: + uint32_t remaining_size = 0; + CRITICAL_REGION_ENTER(); + app_fifo_read(&tx_fifo, NULL, &remaining_size); + // Calculate the actual number of bytes to sent in this operation + if(remaining_size == 0) { // Clear the transmitting-flag, if we have nothing to send anymore + transmitting = 0; + } + CRITICAL_REGION_EXIT(); + + uint32_t len = (remaining_size > sizeof(transmit_buf)) ? sizeof(transmit_buf) : remaining_size; + + ret_code_t ret = NRF_SUCCESS; + if(len > 0) { + // Read the bytes manually from the fifo to be efficient: + for(uint32_t i = 0; i < len; i++) + transmit_buf[i] = tx_fifo.p_buf[(tx_fifo.read_pos + i) & tx_fifo.buf_size_mask]; // extracted from app_fifo.c: "static __INLINE void fifo_peek(app_fifo_t * p_fifo, uint16_t index, uint8_t * p_byte)" + /* + char out_buf[100]; + sprintf(out_buf, "SENDER: Transmit (%u): ", (unsigned int) len); + for(uint8_t i = 0; i < len; i++) + sprintf(&out_buf[strlen(out_buf)], "%02X", transmit_buf[i]); + debug_log("%s\n",out_buf); + systick_delay_millis(100); + */ + // Now send the bytes via bluetooth + ret = ble_transmit(transmit_buf, len); + if(ret == NRF_SUCCESS) { // If the transmission was successful, we can "consume" the data in the fifo manually + tx_fifo.read_pos += len; + //timeout_reset(disconnect_timeout_id); + } + } else { + app_timer_stop(transmit_queued_bytes_timer); + } + return ret; +} + + +ret_code_t sender_init(void) { + + ret_code_t ret = app_fifo_init(&tx_fifo, tx_fifo_buf, sizeof(tx_fifo_buf)); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + ret = app_fifo_init(&rx_fifo, rx_fifo_buf, sizeof(rx_fifo_buf)); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + sender_reset(); + + // Register the disconnect-timeout: + ret = timeout_register(&disconnect_timeout_id, sender_disconnect); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + receive_notification_handler = NULL; + ble_set_on_connect_callback(on_connect_callback); + ble_set_on_disconnect_callback(on_disconnect_callback); + ble_set_on_transmit_callback(on_transmit_callback); + ble_set_on_receive_callback(on_receive_callback); + + + + ret = app_timer_create(&transmit_queued_bytes_timer, APP_TIMER_MODE_REPEATED, transmit_queued_bytes_timer_callback); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + + + + + +void sender_set_receive_notification_handler(sender_receive_notification_handler_t sender_receive_notification_handler) { + receive_notification_handler = sender_receive_notification_handler; +} + +uint32_t sender_get_received_data_size(void) { + // Read size of rx-fifo: + uint32_t available_size = 0; + app_fifo_read(&rx_fifo, NULL, &available_size); + return available_size; +} + +void sender_get_received_data(uint8_t* data, uint32_t* len) { + uint32_t available_size = sender_get_received_data_size(); + if(*len > available_size) + *len = available_size; + + app_fifo_read(&rx_fifo, data, len); +} + +ret_code_t sender_await_data(uint8_t* data, uint32_t len, uint32_t timeout_ms) { + if(!connected) + return NRF_ERROR_INVALID_STATE; + + uint64_t start_ms = systick_get_continuous_millis(); + + // Wait for data or timeout + while((sender_get_received_data_size() < len) && (systick_get_continuous_millis() <= start_ms + (uint64_t) timeout_ms)); + + // Check if we timed-out or have enough data received + if(sender_get_received_data_size() < len) + return NRF_ERROR_TIMEOUT; + + sender_get_received_data(data, &len); + + return NRF_SUCCESS; +} + +uint32_t sender_get_transmit_fifo_size(void) { + uint32_t available_size = 0; + app_fifo_write(&tx_fifo, NULL, &available_size); + + return TX_FIFO_SIZE - available_size; +} + +ret_code_t sender_transmit(const uint8_t* data, uint32_t len, uint32_t timeout_ms) { + if(!connected) + return NRF_ERROR_INVALID_STATE; + + + + // First check available space in tx-fifo: + uint64_t start_ms = systick_get_continuous_millis(); + uint32_t available_size = 0; + // Wait for available space in fifo or timeout + do { + app_fifo_write(&tx_fifo, NULL, &available_size); + } while((len > available_size) && systick_get_continuous_millis() <= start_ms + (uint64_t) timeout_ms); + + if(len > available_size) + return NRF_ERROR_NO_MEM; + + // Reset the disconnect timeout timer if we can queue the new packet + timeout_reset(disconnect_timeout_id); + + // If there is enough space in the FIFO, write the data to the FIFO: + ret_code_t ret; + CRITICAL_REGION_ENTER(); + // This is saved by a critical region because: + // when the transmit_queued_bytes() is called via the ble-ISR, and it reads out that no bytes are left in the TX-FIFO, and before setting the transmitting-flag to 0, + // the app_fifo_write(&tx_fifo,...)-function is called (only possible in Thread-mode not in normal interrupt-mode), it could happen that the transmit_queued_bytes() + // is not called again to start the transmitting for the new insert bytes. + ret = app_fifo_write(&tx_fifo, data, &len); + CRITICAL_REGION_EXIT(); + if(ret != NRF_SUCCESS) return ret; + + ret = NRF_SUCCESS; + if(!transmitting) { // Only start the transmit_queued_bytes()-function, if it is not already transmitting the data of the FIFO + transmitting = 1; + //ret = transmit_queued_bytes(); + //if(ret != NRF_SUCCESS) return ret; + app_timer_start(transmit_queued_bytes_timer, APP_TIMER_TICKS(TRANSMIT_QUEUED_BYTES_PERIOD_MS, 0), NULL); + } + return ret; +} + + +void sender_disconnect(void) { + //debug_log("SENDER: sender_disconnect()-called\n"); + sender_reset(); + ble_disconnect(); +} + diff --git a/firmware/nRF_badge/data_collector/incl/sender_lib.h b/firmware/nRF_badge/data_collector/incl/sender_lib.h new file mode 100644 index 0000000..f061957 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/sender_lib.h @@ -0,0 +1,101 @@ +#ifndef __SENDER_LIB_H +#define __SENDER_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + +/**< A structure that holds information about the received notification. */ +typedef struct { + uint16_t notification_len; + uint64_t timepoint_ticks; + uint32_t timepoint_seconds; + uint16_t timepoint_milliseconds; +} receive_notification_t; + + +/**< The on receive notification callback function type. */ +typedef void (*sender_receive_notification_handler_t) (receive_notification_t receive_notification); + + + +/**@brief Function to initialize the sender. + * + * @retval NRF_SUCCESS If initialization was successful. + * @retval NRF_ERROR_INTERNAL If the FIFOs couldn't be initialized correctly. + * @retval NRF_ERROR_NO_MEM If the disconnect timeout could not be registered. + * + * @note ble_init() has to be called before. + * @note systick_init() has to be called before. + * @note timeout_init() has to be called before. + */ +ret_code_t sender_init(void); + + +/**@brief Function set the notification handler, that should be called when a notification appears. + * + * @param[in] sender_receive_notification_handler The handler that should be called when a notification appears. + */ +void sender_set_receive_notification_handler(sender_receive_notification_handler_t sender_receive_notification_handler); + + +/**@brief Function to retrieve the available bytes in the receive RX-FIFO. + * + * @retval Number of available bytes in the receive RX-FIFO. + */ +uint32_t sender_get_received_data_size(void); + +/**@brief Function to read bytes from the RX-FIFO. + * + * @param[out] data Pointer where to store the read bytes. + * @param[in/out] len Pointer where the number of bytes to read is stored. + * If the specified number is larger than the number of bytes in RX-FIFO, + * only the number of available bytes is read, and len is adapted. + */ +void sender_get_received_data(uint8_t* data, uint32_t* len); + +/**@brief Function to read bytes from the RX-FIFO in blocking mode, with timeout. + * + * @param[out] data Pointer where to store the read bytes. + * @param[in] len Number of bytes to read. + * @param[in] timeout_ms The timeout in milliseconds. + * + * @retval NRF_SUCCESS If len bytes have been read from the RX-FIFO. + * @retval NRF_ERROR_INVALID_STATE If there is no alive BLE-connection. + * @retval NRF_ERROR_TIMEOUT If a timeout occured. + */ +ret_code_t sender_await_data(uint8_t* data, uint32_t len, uint32_t timeout_ms); + + +/**@brief Function that returns the number of bytes in the transmit-FIFO. + * + * @retval Number of bytes in the transmit-FIFO. + */ +uint32_t sender_get_transmit_fifo_size(void); + +/**@brief Function to transmit bytes via the internal TX-FIFO. + * + * @details If the data couldn't be put in the TX-FIFO in timeout_ms milliseconds, + * this function returns NRF_ERROR_NO_MEM. + * Internally, an app-timer is used to send the queued bytes in the FIFO. + * This timer periodically calls (every TRANSMIT_QUEUED_BYTES_PERIOD_MS) + * the (20 byte) ble-transmitting function, + * to transmit the bytes (probably it would return that it couldn't send the + * bytes currently, but the timer holds with trying). + * + * @param[in] data Pointer to data that should be sent. + * @param[in] len Number of bytes to read. + * @param[in] timeout_ms The timeout in milliseconds. + * + * @retval NRF_SUCCESS If len bytes have been read from the FIFO. + * @retval NRF_ERROR_INVALID_STATE If there is no alive BLE-connection. + * @retval NRF_ERROR_NO_MEM If a timeout occured. + * @retval Another error code generated by the ble-stack. + */ +ret_code_t sender_transmit(const uint8_t* data, uint32_t len, uint32_t timeout_ms); + +/**@brief Function to disconnect from the current active connection. + */ +void sender_disconnect(void); + + +#endif + diff --git a/firmware/nRF_badge/data_collector/incl/spi_lib.c b/firmware/nRF_badge/data_collector/incl/spi_lib.c new file mode 100644 index 0000000..7df3c2b --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/spi_lib.c @@ -0,0 +1,266 @@ +#include "spi_lib.h" + + + +// Sources: Multiple slaves?! https://devzone.nordicsemi.com/f/nordic-q-a/11056/nfr51822-spi-multi_slave-conrtol +// Control the SS Signal/Pin outside the functions of the SPI-library!! + + +#include "nrf_gpio.h" +#include "sdk_config.h" + +#define SPI_PERIPHERAL_NUMBER SPI_COUNT /**< Number of activated spi peripherals in sdk_config.h. */ + + + + + + +static volatile spi_operation_t spi_operations[SPI_PERIPHERAL_NUMBER] = {0}; /**< Array to save the current spi_operations (needed to check if there is an ongoing spi operation on the peripheral) */ +static const spi_instance_t * spi_instances[SPI_PERIPHERAL_NUMBER] = {NULL}; /**< Array of pointers to the current spi_instances (needed to deactivate the slave select pin in the interrupt handler of the peripheral after transmission) */ +static uint32_t spi_instance_number = 1; /**< spi_instance_number starts at 1 not 0 because all entries in the spi_instances-arrays are 0. So the check for the spi_instance_id-element may not work correctly. */ + +static spi_handler_t spi_handlers_transmit_bkgnd [SPI_PERIPHERAL_NUMBER]; /**< Array to save the application handlers for the transmit operation */ +static spi_handler_t spi_handlers_transmit_receive_bkgnd [SPI_PERIPHERAL_NUMBER]; /**< Array to save the application handlers for the transmit receive operation */ + + + + +/**@brief Functions for handling the spi interrupts internally. + * + * @details These handlers (for each peripheral there is an own handler) deactivate the ss-pin and call the + * specified application handlers (if they are not NULL). + */ +#if SPI0_ENABLED +static void spi_0_event_handler(nrf_drv_spi_evt_t* p_event) { + // The transfer no matter if send/receive or anything else! + if(p_event->type == NRF_DRV_SPI_EVENT_DONE) { + + spi_evt_t evt; + evt.type = SPI_TRANSFER_DONE; + + // The SS-Pin has to be "deacitvated" again (here we assume, that spi_instances[0] is not NULL!) + nrf_gpio_pin_set((*spi_instances[0]).nrf_drv_spi_config.ss_pin); + + // We are now ready with the operation + spi_operations[0] = SPI_NO_OPERATION; + + if(spi_handlers_transmit_bkgnd[0] != NULL) { + spi_handlers_transmit_bkgnd[0](&evt); + } + if(spi_handlers_transmit_receive_bkgnd[0] != NULL) { + spi_handlers_transmit_receive_bkgnd[0](&evt); + } + } +} +#endif + +#if SPI1_ENABLED +static void spi_1_event_handler(nrf_drv_spi_evt_t* p_event) { + // The transfer no matter if send/receive or anything else! + if(p_event->type == NRF_DRV_SPI_EVENT_DONE) { + + spi_evt_t evt; + evt.type = SPI_TRANSFER_DONE; + + // The SS-Pin has to be "deacitvated" again! (here we assume, that spi_instances[0] is not NULL!) + nrf_gpio_pin_set((*spi_instances[1]).nrf_drv_spi_config.ss_pin); + + spi_operations[1] = SPI_NO_OPERATION; + + if(spi_handlers_transmit_bkgnd[1] != NULL) { + spi_handlers_transmit_bkgnd[1](&evt); + } + if(spi_handlers_transmit_receive_bkgnd[1] != NULL) { + spi_handlers_transmit_receive_bkgnd[1](&evt); + } + } +} +#endif + + + +ret_code_t spi_init(spi_instance_t* spi_instance) { + + + // Small hack of handling the SS-Pin manually: + // local copy of the ss_pin + uint8_t ss_pin = (spi_instance->nrf_drv_spi_config).ss_pin; + + // Set the ss_pin to unused, so that the nrf_drv_spi-library doesn't use/set this pin! + (spi_instance->nrf_drv_spi_config).ss_pin = NRF_DRV_SPI_PIN_NOT_USED; + + ret_code_t ret = NRF_SUCCESS; + + + if(spi_instance->spi_peripheral == 0) { + + #if SPI0_ENABLED + spi_instance->nrf_drv_spi_instance.p_registers = NRF_SPI0; + spi_instance->nrf_drv_spi_instance.irq = SPI0_IRQ; + spi_instance->nrf_drv_spi_instance.drv_inst_idx = SPI0_INSTANCE_INDEX; + spi_instance->nrf_drv_spi_instance.use_easy_dma = SPI0_USE_EASY_DMA; + + ret = nrf_drv_spi_init(&(spi_instance->nrf_drv_spi_instance), &(spi_instance->nrf_drv_spi_config), (nrf_drv_spi_handler_t) spi_0_event_handler); + + #else + + ret = NRF_ERROR_INVALID_PARAM; + + #endif + + } else if(spi_instance->spi_peripheral == 1){ + + #if SPI1_ENABLED + spi_instance->nrf_drv_spi_instance.p_registers = NRF_SPI1; + spi_instance->nrf_drv_spi_instance.irq = SPI1_IRQ; + spi_instance->nrf_drv_spi_instance.drv_inst_idx = SPI1_INSTANCE_INDEX; + spi_instance->nrf_drv_spi_instance.use_easy_dma = SPI1_USE_EASY_DMA; + + ret = nrf_drv_spi_init(&(spi_instance->nrf_drv_spi_instance), &(spi_instance->nrf_drv_spi_config), (nrf_drv_spi_handler_t) spi_1_event_handler); + + #else + + ret = NRF_ERROR_INVALID_PARAM; + + #endif + } else { + ret = NRF_ERROR_INVALID_PARAM; + } + + + // Reset the pin again to the former value, so that the functions of this module can use it for manually set/reset the SS-Pin! + (spi_instance->nrf_drv_spi_config).ss_pin = ss_pin; + + + // ret could be: NRF_SUCCESS, NRF_ERROR_INVALID_STATE, NRF_ERROR_BUSY and NRF_INVALID_PARAM + // if it is NRF_ERROR_INVALID_STATE or NRF_ERROR_BUSY the specified peripheral has already been initialized, but this is ok. + if(ret == NRF_ERROR_INVALID_PARAM) { + return ret; + } + + // manually set the slave-select pin. + nrf_gpio_pin_set(ss_pin); + nrf_gpio_cfg_output(ss_pin); + + + spi_instance->spi_instance_id = spi_instance_number; + + spi_instance_number++; + + return NRF_SUCCESS; +} + + + +ret_code_t spi_transmit_bkgnd(const spi_instance_t* spi_instance, spi_handler_t spi_handler, const uint8_t* tx_data, uint32_t tx_data_len) { + + + // Check if there is already an operation working on this peripheral, if so return (because it should not/could not do read and write parallel) + if(spi_operations[spi_instance->spi_peripheral] != SPI_NO_OPERATION) { + return NRF_ERROR_BUSY; + } + + // Set that we are now sending! + spi_operations[spi_instance->spi_peripheral] = SPI_TRANSMIT_OPERATION; + + // Set the instance-array entry to the current instance, to retrieve the ss_pin in the IRQ-handler! + spi_instances[spi_instance->spi_peripheral] = spi_instance; + + // Set the send Handler for the send operation, set the other one to NULL! + spi_handlers_transmit_bkgnd [spi_instance->spi_peripheral] = spi_handler; + spi_handlers_transmit_receive_bkgnd [spi_instance->spi_peripheral] = NULL; + + static uint8_t tmp; // Dummy for RX + + // "activate" the SS-PIN + nrf_gpio_pin_clear((spi_instance->nrf_drv_spi_config).ss_pin); + + ret_code_t ret = nrf_drv_spi_transfer(&(spi_instance->nrf_drv_spi_instance), tx_data, tx_data_len, &tmp, 0); + + // ret could be: NRF_SUCCESS, NRF_ERROR_BUSY and NRF_ERROR_INVALID_ADDR + + // If there is no success, "deactivate" the SS-PIN and clear the SPI operation + if(ret != NRF_SUCCESS) { + nrf_gpio_pin_set((spi_instance->nrf_drv_spi_config).ss_pin); + spi_operations[spi_instance->spi_peripheral] = SPI_NO_OPERATION; + } + + return ret; +} + + +ret_code_t spi_transmit(const spi_instance_t* spi_instance, const uint8_t* tx_data, uint32_t tx_data_len) { + + ret_code_t ret = spi_transmit_bkgnd(spi_instance, NULL, tx_data, tx_data_len); + if(ret != NRF_SUCCESS) { + return ret; + } + + + // Waiting until the SPI operation has finished! + while(spi_get_operation(spi_instance) != SPI_NO_OPERATION); + + return NRF_SUCCESS; +} + + +ret_code_t spi_transmit_receive_bkgnd(const spi_instance_t* spi_instance, spi_handler_t spi_handler, const uint8_t* tx_data, uint32_t tx_data_len, uint8_t* rx_data, uint32_t rx_data_len) { + + + // Check if there is already an operation working on this peripheral, if so return (because it should not/could not do read and write parallel) + if(spi_operations[spi_instance->spi_peripheral] != SPI_NO_OPERATION) { + return NRF_ERROR_BUSY; + } + + // Set that we are now sending! + spi_operations[spi_instance->spi_peripheral] = SPI_TRANSMIT_RECEIVE_OPERATION; + + // Set the instance-array entry to the current instance, to retrieve the ss_pin in the IRQ-handler! + spi_instances[spi_instance->spi_peripheral] = spi_instance; + + // Set the send Handler for the send operation! + spi_handlers_transmit_bkgnd [spi_instance->spi_peripheral] = NULL; + spi_handlers_transmit_receive_bkgnd [spi_instance->spi_peripheral] = spi_handler; + + + // "activate" the SS-PIN + nrf_gpio_pin_clear((spi_instance->nrf_drv_spi_config).ss_pin); + + ret_code_t ret = nrf_drv_spi_transfer(&(spi_instance->nrf_drv_spi_instance), tx_data, tx_data_len, rx_data, rx_data_len); + + // ret could be: NRF_SUCCESS, NRF_ERROR_BUSY and NRF_ERROR_INVALID_ADDR + + // If there is no success, "deactivate" the SS-PIN and clear the SPI operation + if(ret != NRF_SUCCESS) { + nrf_gpio_pin_set((spi_instance->nrf_drv_spi_config).ss_pin); + spi_operations[spi_instance->spi_peripheral] = SPI_NO_OPERATION; + } + + return ret; +} + +ret_code_t spi_transmit_receive(const spi_instance_t* spi_instance, const uint8_t* tx_data, uint32_t tx_data_len, uint8_t* rx_data, uint32_t rx_data_len) { + + ret_code_t ret = spi_transmit_receive_bkgnd(spi_instance, NULL, tx_data, tx_data_len, rx_data, rx_data_len); + if(ret != NRF_SUCCESS) { + return ret; + } + + + // Waiting until the SPI operation has finished! + while(spi_get_operation(spi_instance) != SPI_NO_OPERATION); + + return NRF_SUCCESS; +} + + +// Actually this could/should be the peripheral ID?! +spi_operation_t spi_get_operation(const spi_instance_t* spi_instance){ + return spi_operations[spi_instance->spi_peripheral]; +} + + + + + diff --git a/firmware/nRF_badge/data_collector/incl/spi_lib.h b/firmware/nRF_badge/data_collector/incl/spi_lib.h new file mode 100644 index 0000000..b23c346 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/spi_lib.h @@ -0,0 +1,184 @@ +#ifndef __SPI_LIB_H +#define __SPI_LIB_H + + + +/** @file + * + * @brief SPI abstraction library. + * + * @details It enables to call the SPI peripherals from different contexts. + * If the selected SPI peripheral is currently in use, it will inform the other context by returning NRF_ERROR_BUSY. + * Each spi peripheral is specified by the values of nrf_drv_spi_config except the ss_pin. The ss_pin could be set independently for each spi instance. + * So it is important that every spi instance that uses the same spi peripheral has the same configuration except the ss_pin. + * + * There are actually two main capabilities: transmit and transmit_receive. The second can be used to receive data from the slave while transmitting. + */ + + +#include "sdk_common.h" // Needed for the definition of ret_code_t and the error-codes +#include "nrf_drv_spi.h" + + + +/**@brief The different SPI operations. These operations will be used to set the peripheral busy or not. */ +typedef enum { + SPI_NO_OPERATION = 0, /**< Currently no spi operation ongoing. */ + SPI_TRANSMIT_OPERATION = (1 << 0), /**< Currently there is a spi transmit operation ongoing. */ + SPI_TRANSMIT_RECEIVE_OPERATION = (1 << 1), /**< Currently there is a spi transmit receive operation ongoing. */ +} spi_operation_t; + + +/**@brief SPI driver event types, passed to the handler routine provided by the bkgnd-functions. */ +typedef enum +{ + SPI_TRANSFER_DONE, /**< Transfer done */ +} spi_evt_type_t; + +typedef struct +{ + spi_evt_type_t type; /**< Event type */ +} spi_evt_t; + +/** + * @brief SPI event handler type. + */ +typedef void (*spi_handler_t)(spi_evt_t const * p_event); + + + +/**@example Example of spi_instance_t + * + * + * spi_instance_t spi_instance; // Create an spi_instance-struct. + * + * spi_instance.spi_peripheral = 0; // Set the spi peripheral to 0 (the selected peripheral has to be enabled in sdk_config.h). + * spi_instance.nrf_drv_spi_config.frequency = NRF_DRV_SPI_FREQ_8M; // Set the spi frequency. NRF_DRV_SPI_FREQ_8M from nrf_drv_spi.h. + * spi_instance.nrf_drv_spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST; // Set the spi bit order. NRF_DRV_SPI_BIT_ORDER_MSB_FIRST from nrf_drv_spi.h. + * spi_instance.nrf_drv_spi_config.mode = NRF_DRV_SPI_MODE_3; // Set the spi mode. NRF_DRV_SPI_MODE_3 (means SCK active low, sample on trailing edge of clock) from nrf_drv_spi.h. + * spi_instance.nrf_drv_spi_config.orc = 0; // Seth the spi overrun-character. + * spi_instance.nrf_drv_spi_config.irq_priority = APP_IRQ_PRIORITY_LOW; // The interrupt priotity of the spi peripheral. APP_IRQ_PRIORITY_LOW from app_util_platform.h. + * spi_instance.nrf_drv_spi_config.ss_pin = 2; // Set the slave-select pin. + * spi_instance.nrf_drv_spi_config.miso_pin = 1; // Set the miso pin. + * spi_instance.nrf_drv_spi_config.mosi_pin = 4; // Set the mosi pin. + * spi_instance.nrf_drv_spi_config.sck_pin = 3; // Set the sck pin. + * + * spi_init(&spi_instance); // Initialize the spi_instance. + */ + + +/**@brief SPI instance type. */ +typedef struct { + uint8_t spi_peripheral; /**< Set to the desired spi peripheral. The Peripheral has to be enabled in the sdk_config.h file */ + nrf_drv_spi_config_t nrf_drv_spi_config; /**< Set the SPI configuration (possible parameters in nrf_drv_spi.h) */ + uint32_t spi_instance_id; /**< Instance index: Setted by the init-function (do not set!) */ + nrf_drv_spi_t nrf_drv_spi_instance; /**< The initialized low level spi instance: Setted by the init-function (do not set!) */ +} spi_instance_t; + + +/**@brief Function for initializing an instance for the spi peripheral. + * + * @details This functions actually checks if the specified spi_peripheral exists and sets the spi_instance_id of spi_instance. + * The application must set the spi_peripheral to a peripheral index that is activated in sdk_config.h. + * Furthermore, this function handles the slave-select pin initialization manually (because the nrf_drv_spi-library can't handle multiple slave select pins on the same peripheral). + * + * + * @param[in,out] spi_instance Pointer to an preconfigured spi_instance. + * + * @retval NRF_SUCCESS If the adc_instance was successfully initialized. + * @retval NRF_ERROR_INVALID_PARAM If the specified peripheral or the configuration is not correct. + */ +ret_code_t spi_init(spi_instance_t* spi_instance); + + +/**@brief Function for transmitting data in asynchronous/non-blocking/background mode. + * + * @details This is a non-blocking function. If there is already an ongoing spi operation this function returns NRF_ERROR_BUSY. + * If the operation was started successfully and terminates, the provided spi_handler is called with event: SPI_TRANSFER_DONE. + * + * @warning The transmit data must be kept in memory until the operation has terminated. + * + * @param[in] spi_instance Pointer to an initialized spi instance. + * @param[in] spi_handler Handler function that should be called if the operation is done, with event: SPI_TRANSFER_DONE. Could also be NULL if no handler should be called. + * @param[in] tx_data Pointer to the data to transmit. + * @param[in] tx_data_len Length of the data to transmit. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (transmit or transmit receive). + * @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data RAM region. + */ +ret_code_t spi_transmit_bkgnd(const spi_instance_t* spi_instance, spi_handler_t spi_handler, const uint8_t* tx_data, uint32_t tx_data_len); + + +/**@brief Function for transmitting data in blocking mode. + * + * @details Function uses internally spi_transmit_bkgnd() for transmitting the data + * and the spi_get_operation() to wait until the operation has terminated. + * + * @param[in] spi_instance Pointer to an initialized spi instance. + * @param[in] tx_data Pointer to the data to transmit. + * @param[in] tx_data_len Length of the data to transmit. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (transmit or transmit receive). + * @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data RAM region. + */ +ret_code_t spi_transmit(const spi_instance_t* spi_instance, const uint8_t* tx_data, uint32_t tx_data_len); + + +/**@brief Function for transmitting and receiving data in asynchronous/non-blocking/background mode. + * + * @details This is a non-blocking function. If there is already an ongoing spi operation this function returns NRF_ERROR_BUSY. + * If the operation was started successfully and terminates, the provided spi_handler is called with event: SPI_TRANSFER_DONE. + * + * @warning The transmit and receive data must be kept in memory until the operation has terminated. + * + * @param[in] spi_instance Pointer to an initialized spi instance. + * @param[in] spi_handler Handler function that should be called if the operation is done, with event: SPI_TRANSFER_DONE. Could also be NULL if no handler should be called. + * @param[in] tx_data Pointer to the data to transmit. + * @param[in] tx_data_len Length of the data to transmit. + * @param[in] rx_data Pointer to the receive buffer. + * @param[in] rx_data_len Length of the receive buffer. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (transmit or transmit receive). + * @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data RAM region. + */ +ret_code_t spi_transmit_receive_bkgnd(const spi_instance_t* spi_instance, spi_handler_t spi_handler, const uint8_t* tx_data, uint32_t tx_data_len, uint8_t* rx_data, uint32_t rx_data_len); + + + +/**@brief Function for transmitting and receiving data in blocking mode. + * + * @details Function uses internally spi_transmit_receive_bkgnd() for transmitting the data + * and the spi_get_operation() to wait until the operation has terminated. + * + * @warning The transmit and receive data must be kept in memory until the operation has terminated. + * + * @param[in] spi_instance Pointer to an initialized spi instance. + * @param[in] tx_data Pointer to the data to transmit. + * @param[in] tx_data_len Length of the data to transmit. + * @param[in] rx_data Pointer to the receive buffer. + * @param[in] rx_data_len Length of the receive buffer. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing operation (transmit or transmit receive). + * @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data RAM region. + */ +ret_code_t spi_transmit_receive(const spi_instance_t* spi_instance, const uint8_t* tx_data, uint32_t tx_data_len, uint8_t* rx_data, uint32_t rx_data_len); + + +/**@brief Function for retrieving the current spi status/operation. + * + * @details This function returns the current spi_operation_t. + * The application can check the status through this function, + * to decide whether the spi operation is done. + * + * @retval SPI_NO_OPERATION If there is currently no spi operation in process for the peripheral in spi_instance. + * @retval SPI_TRANSMIT_OPERATION If there is currently a spi transmit operation in process for the peripheral in spi_instance. + * @retval SPI_TRANSMIT_RECEIVE_OPERATION If there is currently a spi transmit receive operation in process for the peripheral in spi_instance. + */ +spi_operation_t spi_get_operation(const spi_instance_t* spi_instance); + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/storage1_lib.c b/firmware/nRF_badge/data_collector/incl/storage1_lib.c new file mode 100644 index 0000000..0752765 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storage1_lib.c @@ -0,0 +1,633 @@ +#include "storage1_lib.h" +#include "flash_lib.h" + +#include "stdio.h" +#include "string.h" // For memset function + + +#define STORAGE1_SIZE (FLASH_PAGE_SIZE_WORDS*FLASH_NUM_PAGES*sizeof(uint32_t)) /**< Size of storage1 in bytes */ + +#define STORAGE1_CLEARED_BYTE (0xFF) /**< The byte that should be written to storage1 to clear the cell. */ + +#ifdef UNIT_TEST + #define STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE 4 /**< Number of addresses in storage1_last_stored_element_addresses-array, during unit testing. */ +#else + #define STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE 15 /**< Number of addresses in storage1_last_stored_element_addresses-array, during normal operation. */ +#endif + +#define WORDS_BUF_SIZE 100 /**< The word buffer size for the storage/read operations. */ + +int32_t storage1_last_stored_element_addresses[STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE]; /**< Array to save the last/end addresses of stored elements */ + + +uint8_t backup_data[FLASH_PAGE_SIZE_WORDS*sizeof(uint32_t)]; /**< Array to backup a whole flash page, needed for restoring bytes after a page erase */ + + +uint32_t words_buf[WORDS_BUF_SIZE]; /** The buffer where big flash-operations (read/store) are splitted to */ + +/** @brief Retrieve the address of a page. + * + * @param[in] byte_address The address of a byte. + * + * @retval Address of the page that contains the specified byte address. Returns 0 if byte_address >= STORAGE1_SIZE. + */ +uint32_t storage1_get_page_address(uint32_t byte_address) { + if(byte_address >= storage1_get_size()) { + return 0; + } + + uint32_t page_size_bytes = flash_get_page_size_words()*sizeof(uint32_t); + + + return byte_address/page_size_bytes; +} + +/** @brief Retrieve the number of pages associated with the byte_address and byte_length. + * + * @param[in] byte_address The address of the first byte. + * @param[in] byte_length The number of bytes. + * + * @retval Number of pages where the data would be stored to. Returns 0 if byte_length == 0. + */ +uint32_t storage1_get_page_number(uint32_t byte_address, uint32_t byte_length) { + if(byte_address + byte_length > storage1_get_size()) + return 0; + + if(byte_length == 0) + return 0; + + uint32_t start_page_address = storage1_get_page_address(byte_address); + // Get address of page where the last byte would be written to + uint32_t end_page_address = storage1_get_page_address(byte_address + byte_length - 1); + + return ((end_page_address + 1) - start_page_address); + +} + +/** @brief Function to retrieve the number of elements in storage1_last_stored_element_addresses-array. + * + * + * @retval Number of elements in storage1_last_stored_element_addresses-array + */ +uint32_t storage1_get_last_stored_element_addresses_size(void) { + return STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE; +} + + +/** @brief Retrieve the address and number of pages that should be erased before writing. + * + * @details Calculates the address and number of pages that should be erased before writing in an optimal way. + * It tries to minimize the number of page erases by using the storage1_last_stored_element_addresses-array. + * The storage1_last_stored_element_addresses-array saves the addresses of the last written addresses + * that have been calculated by this function before. + * + * @param[in] address The address of the first byte to write. + * @param[in] length_data The number of bytes. + * @param[out] erase_start_page_address Pointer to erase page address. + * @param[out] erase_num_pages Pointer to number of pages to erase. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If specified address and length_data exceed the storage size. + */ +ret_code_t storage1_compute_pages_to_erase(uint32_t address, uint32_t length_data, uint32_t* erase_start_page_address, uint32_t* erase_num_pages) { + *erase_start_page_address = 0; + *erase_num_pages = 0; + + + if(address + length_data > storage1_get_size()) { + return NRF_ERROR_INVALID_PARAM; + } + if(length_data == 0) { + return NRF_SUCCESS; + } + + uint32_t start_page_address = storage1_get_page_address(address); + uint32_t num_pages = storage1_get_page_number(address, length_data); + + *erase_start_page_address = start_page_address; + *erase_num_pages = num_pages; + + + + // Iterate over all storage1_last_stored_element_addresses to reset addresses that would be overwritten or erased + // and to adapt the erase_start_page if possible. + for(uint32_t i = 0; i < STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE; i++) { + + if(storage1_last_stored_element_addresses[i] == -1) + continue; + + // Get local copy + uint32_t last_stored_element_page_address = storage1_get_page_address(storage1_last_stored_element_addresses[i]); + + if(last_stored_element_page_address == start_page_address) { + if(storage1_last_stored_element_addresses[i] < (int32_t)address) { + *erase_start_page_address = start_page_address + 1; + *erase_num_pages = num_pages - 1; + } + + storage1_last_stored_element_addresses[i] = -1; + } else { + // Check if storage1_last_stored_element_addresses[i] is on the page before, and equals "address-1" --> could be deleted + //if(storage1_last_stored_element_addresses[i] == (int32_t)address - 1) { + // storage1_last_stored_element_addresses[i] = -1; + //} + + // Check if storage1_last_stored_element_addresses[i] is in a page that will be written to + if(last_stored_element_page_address > start_page_address && last_stored_element_page_address < start_page_address + num_pages) { + storage1_last_stored_element_addresses[i] = -1; + } + } + } + + + int32_t last_stored_element_address = (int32_t) (address + length_data - 1); + + // if then end-address is the last address in a unit, we don't need to insert it (because this will only be deleted/overwritten, if a write to the same unit is performed again) + if(last_stored_element_address % storage1_get_unit_size() == storage1_get_unit_size() - 1) { + return NRF_SUCCESS; + } + + + // Search for the index, where to store the current address + uint32_t index = 0; + int32_t min_diff = storage1_get_size(); + for(uint32_t i = 0; i < STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE; i++) { + if(storage1_last_stored_element_addresses[i] == -1) { + index = i; + break; + } else if(storage1_last_stored_element_addresses[i] < last_stored_element_address && last_stored_element_address - storage1_last_stored_element_addresses[i] < min_diff) { + min_diff = last_stored_element_address - storage1_last_stored_element_addresses[i]; + index = i; + } + } + + storage1_last_stored_element_addresses[index] = last_stored_element_address; + + + return NRF_SUCCESS; +} + + +/** @brief Calculate the number of leading_num_bytes and final_num_bytes so that intermediate_num_bytes is word aligned. + * + * @details Example of clipping: The "|" is the word alignment separator, X is one byte: XX | XXXX | XXX + * leads to leading_num_bytes = 2, intermediate_num_bytes = 4, final_num_bytes = 3. + * If the data are within one single word, only intermediate_num_bytes is set to length_data. + * + * @param[in] address The address of the first byte to write. + * @param[in] length_data The number of bytes. + * @param[out] leading_num_bytes Pointer to leading number of bytes (0 if address is word aligned). + * @param[out] intermediate_num_bytes Pointer to intermediate number of bytes. Clipped to be word aligned. + * @param[out] final_num_bytes Pointer to final number of bytes (0 if address+length is word aligned). + */ +void storage1_compute_word_aligned_addresses(uint32_t address, uint32_t length_data, uint32_t* leading_num_bytes, uint32_t* intermediate_num_bytes, uint32_t* final_num_bytes) { + + + *leading_num_bytes = 0; + *final_num_bytes = 0; + *intermediate_num_bytes = 0; + + if(length_data == 0) + return; + + uint32_t round_off_start_address = address/sizeof(uint32_t); + uint32_t round_off_end_address = (address+length_data-1)/sizeof(uint32_t); + + // Check if the data range is just within one word + if((round_off_start_address == round_off_end_address) && (address % sizeof(uint32_t) != 0) && ((address + length_data) % sizeof(uint32_t) != 0)) { + *intermediate_num_bytes = length_data; + } else { + // Integer math truncation: round start address to next word address, subtract address, modulo word size + *leading_num_bytes = ((address / sizeof(uint32_t) + 1)*sizeof(uint32_t) - address) % sizeof(uint32_t); + + // Integer math truncation: round end address to previous word address, subtract it from the end address, modulo word size + *final_num_bytes = ((address + length_data) - (((address + length_data) / sizeof(uint32_t))*sizeof(uint32_t))) % sizeof(uint32_t); + + if(length_data >= (*final_num_bytes + *leading_num_bytes)) // only for safety reasons + *intermediate_num_bytes = length_data - (*final_num_bytes + *leading_num_bytes); + } +} + + +/** @brief Function for storing uint8-data in the 32bit-flash. + * + * @details The function splits the bytes into words (fills them if necessary with 0xFF) and stores them to flash. + * + * @param[in] address The address of the first byte to write. + * @param[in] data Pointer to the bytes to store. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If specified address and length_data exceed the storage size. + * @retval NRF_ERROR_BUSY If the underlying storage-module (here flash) is busy. + * @retval NRF_ERROR_INTERNAL If the underlying storage-module (here flash) was not correctly initialized. + * @retval NRF_ERROR_TIMEOUT If the operation timed out (sth. went wrong) or the operation takes too long. + */ +ret_code_t storage1_store_uint8_as_uint32(uint32_t address, uint8_t* data, uint32_t length_data) { + + if(address + length_data > storage1_get_size()) + return NRF_ERROR_INVALID_PARAM; + + if(length_data == 0) + return NRF_SUCCESS; + + + ret_code_t ret = NRF_SUCCESS; + + // Compute the word aligned addresses + uint32_t leading_num_bytes, intermediate_num_bytes, final_num_bytes; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + + uint32_t * words; + uint32_t word_address_aligned; + uint32_t length_words; + + + // first write the intermediate bytes as words to flash + if(intermediate_num_bytes > 0) { + uint32_t word; + if(intermediate_num_bytes < sizeof(uint32_t)) { // in this case leading_num_bytes and final_num_bytes should be 0. + + uint8_t tmp[sizeof(uint32_t)]; + for(uint8_t i = 0; i < sizeof(uint32_t); i++) + tmp[i] = 0xFF; + uint8_t start_index = (uint8_t)(address % sizeof(uint32_t)); + uint8_t end_index = (start_index + intermediate_num_bytes)% sizeof(uint32_t); // % sizeof(uint32_t) just for safety reasons + + for(uint8_t i = start_index; i < end_index; i++) { + tmp[i] = data[i - start_index]; + } + memcpy(&word, tmp, sizeof(uint32_t)); + // Cast to word pointer + words = &word; + // Calculate aligned word address with integer truncation + word_address_aligned = (address/sizeof(uint32_t)); + // Set the number of words to 1 + length_words = 1; + + // Store the words to flash + ret = flash_store(word_address_aligned, words, length_words); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + return ret; + } + } else { + // Cast to word pointer + //words = (uint32_t*) (&data[leading_num_bytes]); + // Calculate aligned word address with integer truncation + word_address_aligned = (address + leading_num_bytes)/sizeof(uint32_t); + // Calculate the number of words with integer truncation + length_words = intermediate_num_bytes/sizeof(uint32_t); + + + for(uint32_t i = 0; i < length_words/WORDS_BUF_SIZE; i++) { + memcpy(words_buf, &data[leading_num_bytes + i*sizeof(uint32_t)*WORDS_BUF_SIZE], sizeof(uint32_t) * WORDS_BUF_SIZE); + + // Store the words to flash + ret = flash_store(word_address_aligned + i*WORDS_BUF_SIZE, words_buf, WORDS_BUF_SIZE); + if(ret != NRF_SUCCESS) return ret; // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + } + // If there are remaining words to store, store them + uint32_t remaining_length_words = length_words % WORDS_BUF_SIZE; + if(remaining_length_words > 0) { + memcpy(words_buf, &data[leading_num_bytes + (length_words/WORDS_BUF_SIZE)*sizeof(uint32_t)*WORDS_BUF_SIZE], sizeof(uint32_t)*remaining_length_words); + + // Store the words to flash + ret = flash_store(word_address_aligned + (length_words/WORDS_BUF_SIZE)*WORDS_BUF_SIZE, words_buf, remaining_length_words); + if(ret != NRF_SUCCESS) return ret; // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + } + + } + } + + + // then write the leading bytes as words to flash + if(leading_num_bytes > 0) { + uint32_t word; + // Generate a word that is 0xFF where no data byte is. + uint8_t tmp[sizeof(uint32_t)]; + uint8_t start_index = sizeof(uint32_t) - leading_num_bytes; + for(uint8_t i = 0; i < sizeof(uint32_t); i++) { + if(i < start_index) { + tmp[i] = 0xFF; + } else { + tmp[i] = data[i - start_index]; + } + } + + memcpy(&word, tmp, sizeof(uint32_t)); + // Cast to word pointer + words = &word; + // Calculate aligned word address with integer truncation + word_address_aligned = (address/sizeof(uint32_t)); + // Set the number of words to 1 + length_words = 1; + + // Store the words to flash + ret = flash_store(word_address_aligned, words, length_words); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + return ret; + } + } + + + + + + + // eventually, write the final bytes as words to flash + if(final_num_bytes > 0) { + uint32_t word; + // Generate a word that is 0xFF where no data byte is. + uint8_t tmp[sizeof(uint32_t)]; + for(uint8_t i = 0; i < sizeof(uint32_t); i++) { + if(i < final_num_bytes) { + tmp[i] = data[i + leading_num_bytes + intermediate_num_bytes]; + } else { + tmp[i] = 0xFF; + } + } + memcpy(&word, tmp, sizeof(uint32_t)); + // Cast to word pointer + words = &word; + // Calculate aligned word address with integer truncation + word_address_aligned = (address + leading_num_bytes + intermediate_num_bytes)/sizeof(uint32_t); + // Set the number of words to 1 + length_words = 1; + + // Store the words to flash + ret = flash_store(word_address_aligned, words, length_words); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + return ret; + } + } + + + return NRF_SUCCESS; +} + + +/** @brief Function for reading uint8-data from the 32bit-flash. + * + * @details The function reads the words from flash and filters out the needed bytes. + * + * @param[in] address The address of the first byte to read. + * @param[in] data Pointer to memory where the bytes should be stored. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If specified address and length_data exceed the storage size. + */ +ret_code_t storage1_read_uint32_as_uint8(uint32_t address, uint8_t* data, uint32_t length_data) { + + if(address + length_data > storage1_get_size()) + return NRF_ERROR_INVALID_PARAM; + + if(length_data == 0) + return NRF_SUCCESS; + + ret_code_t ret = NRF_SUCCESS; + + // Compute the word aligned addresses + uint32_t leading_num_bytes, intermediate_num_bytes, final_num_bytes; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + + uint32_t word; + uint32_t word_address_aligned; + uint32_t length_words; + + + // first read the intermediate bytes from flash + if(intermediate_num_bytes > 0) { + if(intermediate_num_bytes < sizeof(uint32_t)) { // in this case leading_num_bytes and final_num_bytes should be 0. + + // Calculate aligned word address with integer truncation + word_address_aligned = (address/sizeof(uint32_t)); + // Set the number of words to 1 + length_words = 1; + + // Read the word from flash + ret = flash_read(word_address_aligned, &word, length_words); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_PARAM + return ret; + } + + + uint8_t * tmp = (uint8_t*) &word; + uint8_t start_index = (uint8_t)(address % sizeof(uint32_t)); + uint8_t end_index = (start_index + intermediate_num_bytes)% sizeof(uint32_t); // % sizeof(uint32_t) just for safety reasons + + for(uint8_t i = start_index; i < end_index; i++) { + data[i - start_index] = tmp[i]; + } + } else { + // Calculate aligned word address with integer truncation + word_address_aligned = (address + leading_num_bytes)/sizeof(uint32_t); + // Calculate the number of words with integer truncation + length_words = intermediate_num_bytes/sizeof(uint32_t); + + + for(uint32_t i = 0; i < length_words/WORDS_BUF_SIZE; i++) { + // Read the words from flash + ret = flash_read(word_address_aligned + i*WORDS_BUF_SIZE, words_buf, WORDS_BUF_SIZE); + if(ret != NRF_SUCCESS) return ret; // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + + memcpy(&data[leading_num_bytes + i*sizeof(uint32_t)*WORDS_BUF_SIZE], words_buf, sizeof(uint32_t) * WORDS_BUF_SIZE); + } + // If there are remaining words to store, store them + uint32_t remaining_length_words = length_words % WORDS_BUF_SIZE; + if(remaining_length_words > 0) { + // Read the words from flash + ret = flash_read(word_address_aligned + (length_words/WORDS_BUF_SIZE)*WORDS_BUF_SIZE, words_buf, remaining_length_words); + if(ret != NRF_SUCCESS) return ret; // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + + memcpy(&data[leading_num_bytes + (length_words/WORDS_BUF_SIZE)*sizeof(uint32_t)*WORDS_BUF_SIZE], words_buf, sizeof(uint32_t)*remaining_length_words); + } + } + } + + + // then read the leading bytes from flash + if(leading_num_bytes > 0) { + // Calculate aligned word address with integer truncation + word_address_aligned = (address/sizeof(uint32_t)); + // Set the number of words to 1 + length_words = 1; + + // Read the word from flash + ret = flash_read(word_address_aligned, &word, length_words); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_PARAM + return ret; + } + + uint8_t * tmp = (uint8_t*) &word; + uint8_t start_index = sizeof(uint32_t) - leading_num_bytes; + for(uint8_t i = start_index; i < sizeof(uint32_t); i++) { + data[i - start_index] = tmp[i]; + } + } + + + + // eventually, read the final bytes from flash + if(final_num_bytes > 0) { + + + // Calculate aligned word address with integer truncation + word_address_aligned = (address + leading_num_bytes + intermediate_num_bytes)/sizeof(uint32_t); + // Set the number of words to 1 + length_words = 1; + + // Read the word from flash + ret = flash_read(word_address_aligned, &word, length_words); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_PARAM + return ret; + } + + uint8_t * tmp = (uint8_t*) &word; + + for(uint8_t i = 0; i < final_num_bytes; i++) { + data[i + leading_num_bytes + intermediate_num_bytes] = tmp[i]; + } + } + return NRF_SUCCESS; +} + + + + + + +ret_code_t storage1_init(void) { + + + + for(uint32_t i = 0; i < STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE; i++) + storage1_last_stored_element_addresses[i] = -1; + + // Flag if the initialization has already be done and was successful + static uint8_t init_done = 0; + + // Directly return if the flash module was already initialized successfully (but only in normal operation, not in testing mode). + #ifndef UNIT_TEST + if(init_done) { + return NRF_SUCCESS; + } + #else // To not generate compiler warnings + (void) init_done; + #endif + + ret_code_t ret = flash_init(); + + if(ret == NRF_SUCCESS) { + init_done = 1; + } + + return ret; +} + + + + +ret_code_t storage1_store(uint32_t address, uint8_t* data, uint32_t length_data) { + + + if(address + length_data > storage1_get_size() || data == NULL) + return NRF_ERROR_INVALID_PARAM; + + if(length_data == 0) + return NRF_SUCCESS; + + ret_code_t ret; + + // Save the old data on the same page + uint32_t start_page_address = storage1_get_page_address(address); + uint32_t start_page_first_byte_address = start_page_address*flash_get_page_size_words()*sizeof(uint32_t); + uint32_t backup_data_length = address - start_page_first_byte_address; + if(backup_data_length > sizeof(backup_data)) // just for security reasons + return NRF_ERROR_INTERNAL; + + if(backup_data_length > 0) { + ret = storage1_read_uint32_as_uint8(start_page_first_byte_address, backup_data, backup_data_length); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_PARAM + return ret; + } + } + + + // Compute the pages to erase + uint32_t erase_start_page_address, erase_num_pages; + ret = storage1_compute_pages_to_erase(address, length_data, &erase_start_page_address, &erase_num_pages); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_PARAM + return ret; + } + // Erase the pages + if(erase_num_pages > 0) { + ret = flash_erase(erase_start_page_address, erase_num_pages); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + return ret; + } + } + + // Restore the backup data, but only if the first page was erased + if(backup_data_length > 0 && erase_start_page_address == start_page_address) { + ret = storage1_store_uint8_as_uint32(start_page_first_byte_address, backup_data, backup_data_length); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + return ret; + } + } + + // Finally store the data to flash + ret = storage1_store_uint8_as_uint32(address, data, length_data); + if(ret != NRF_SUCCESS) { // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INTERNAL, NRF_ERROR_INVALID_PARAM, NRF_ERROR_TIMEOUT + return ret; + } + + + + + return NRF_SUCCESS; +} + + + +ret_code_t storage1_read(uint32_t address, uint8_t* data, uint32_t length_data) { + if(address + length_data > storage1_get_size() || data == NULL) + return NRF_ERROR_INVALID_PARAM; + + if(length_data == 0) + return NRF_SUCCESS; + + ret_code_t ret = storage1_read_uint32_as_uint8(address, data, length_data); + return ret; +} + + +uint32_t storage1_get_unit_size(void) { + return flash_get_page_size_words()*sizeof(uint32_t); +} + +uint32_t storage1_get_size(void) { + return STORAGE1_SIZE; +} + +ret_code_t storage1_clear(uint32_t address, uint32_t length) { + uint32_t bytes_per_store_operation = 128; + uint8_t tmp[bytes_per_store_operation]; + ret_code_t ret = NRF_SUCCESS; + uint32_t address_offset = 0; + while(length > 0) { + memset(tmp, STORAGE1_CLEARED_BYTE, bytes_per_store_operation); // Only in case the store operations would change the data + if(length >= bytes_per_store_operation) { + ret = storage1_store(address + address_offset, tmp, bytes_per_store_operation); + if(ret != NRF_SUCCESS) return ret; + address_offset += bytes_per_store_operation; + length -= bytes_per_store_operation; + } else { + ret = storage1_store(address + address_offset, tmp, length); + if(ret != NRF_SUCCESS) return ret; + length = 0; + } + } + + return ret; +} diff --git a/firmware/nRF_badge/data_collector/incl/storage1_lib.h b/firmware/nRF_badge/data_collector/incl/storage1_lib.h new file mode 100644 index 0000000..81cbb5a --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storage1_lib.h @@ -0,0 +1,91 @@ +#ifndef __STORAGE1_LIB_H +#define __STORAGE1_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +/** @brief Function for initializing storage1 (and the underlying flash-module) + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INTERNAL If the underlying storage-module (here flash) could not correctly initialized. + */ +ret_code_t storage1_init(void); + + + + +/** @brief Function for storing bytes in the underlying storage-module (here flash). + * + * @details The function is optimized for sequential writing. + * It is optimized for handling STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE different + * addresses for sequential storing. It only erases pages if it is necessary and restores the data + * on the first erased page that are in front of the current data address. + * If the application needs more than STORAGE1_LAST_STORED_ELEMENT_ADDRESSES_SIZE different sequential + * addresses, this value has to be increased. + * The function converts the bytes to words and store them in the flash. + * If the store operation fails (because of e.g. softdevice) the internal last_stored_element_addresses-array + * is set nevertheless, so if the application stores to the same address again, it will be erased. + * + * @warning When storing data, all data that have been stored before on the same page but behind the new data, will be deleted. + * Furthermore, the erasing of pages before storing data to them could lead to inconsistent data, if the power supply is interrupted. + * The application has to take care of this (by using checksums for real critical data). + * + * @param[in] address The address of the first byte to write. + * @param[in] data Pointer to the bytes to store. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If data is NULL or specified address and length_data exceed the storage size. + * @retval NRF_ERROR_BUSY If the underlying storage-module (here flash) is busy. + * @retval NRF_ERROR_INTERNAL If the underlying storage-module (here flash) was not correctly initialized or if something went wrong during the calculation of the backup-data size. + * @retval NRF_ERROR_TIMEOUT If the operation timed out (sth. went wrong while flash storing/erasing) or the operation takes too long. + */ +ret_code_t storage1_store(uint32_t address, uint8_t* data, uint32_t length_data); + + +/** @brief Function for reading bytes from the underlying storage-module (here flash). + * + * + * @param[in] address The address of the first byte to read. + * @param[in] data Pointer to memory where the bytes should be stored. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If data is NULL or specified address and length_data exceed the storage size or data is not in RAM section. + */ +ret_code_t storage1_read(uint32_t address, uint8_t* data, uint32_t length_data); + + +/** @brief Function for retrieving the unit size of the storage-module. + * + * @details The unit size of a storage-module describes the minimum size of data that should be reserved for + * related data. + * Example: if you want to store two partitions of different data (that are not related). It is important + * to not store them on the same unit of the storage-module because it could happen that writing to + * partition 1 might destroy/delete data of partition 2 on the same unit (here: unit = flash page). + * + * @retval 1024 or 4096 Unit size of a flash page: NRF51 or NRF52. + */ +uint32_t storage1_get_unit_size(void); + + +/**@brief Function for reading the number of bytes in storage1. + * + * @retval Number of available bytes in storage1. + */ +uint32_t storage1_get_size(void); + + +/** @brief Function to clear/erase a defined address range. + * + * @param[in] address The address of the first byte to clear. + * @param[in] length The number of bytes to clear. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If specified address and length exceed the storage size. + * @retval NRF_ERROR_BUSY If the underlying storage-module is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t storage1_clear(uint32_t address, uint32_t length); + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/storage2_lib.c b/firmware/nRF_badge/data_collector/incl/storage2_lib.c new file mode 100644 index 0000000..16ac599 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storage2_lib.c @@ -0,0 +1,87 @@ +#include "storage2_lib.h" + +#include "eeprom_lib.h" + +#include "stdio.h" +#include "string.h" // For memset function + +#define STORAGE2_SIZE (EEPROM_SIZE) + +#define STORAGE2_CLEARED_BYTE (0xFF) /**< The byte that should be written to storage2 to clear the cell. */ + + +ret_code_t storage2_init(void) { + + static uint8_t init_done = 0; + + // Directly return if the flash module was already initialized successfully (but only in normal operation, not in testing mode). + #ifndef UNIT_TEST + if(init_done) { + return NRF_SUCCESS; + } + #else // To not generate compiler warnings + (void) init_done; + #endif + + ret_code_t ret = eeprom_init(); + + if(ret == NRF_SUCCESS) { + init_done = 1; + } + + return ret; +} + + + +ret_code_t storage2_store(uint32_t address, uint8_t* data, uint32_t length_data) { + if(address + length_data > storage2_get_size() || data == NULL) + return NRF_ERROR_INVALID_PARAM; + if(length_data == 0) + return NRF_SUCCESS; + + ret_code_t ret = eeprom_store(address, data, length_data); + return ret; +} + +ret_code_t storage2_read(uint32_t address, uint8_t* data, uint32_t length_data) { + if(address + length_data > storage2_get_size() || data == NULL) + return NRF_ERROR_INVALID_PARAM; + if(length_data == 0) + return NRF_SUCCESS; + + ret_code_t ret = eeprom_read(address, data, length_data); + return ret; +} + + + +uint32_t storage2_get_unit_size(void) { + return 1; +} + +uint32_t storage2_get_size(void) { + return STORAGE2_SIZE; +} + + +ret_code_t storage2_clear(uint32_t address, uint32_t length) { + uint32_t bytes_per_store_operation = 128; + uint8_t tmp[bytes_per_store_operation]; + ret_code_t ret = NRF_SUCCESS; + uint32_t address_offset = 0; + while(length > 0) { + memset(tmp, STORAGE2_CLEARED_BYTE, bytes_per_store_operation); // Only in case the store operations would change the data + if(length >= bytes_per_store_operation) { + ret = storage2_store(address + address_offset, tmp, bytes_per_store_operation); + if(ret != NRF_SUCCESS) return ret; + address_offset += bytes_per_store_operation; + length -= bytes_per_store_operation; + } else { + ret = storage2_store(address + address_offset, tmp, length); + if(ret != NRF_SUCCESS) return ret; + length = 0; + } + } + return ret; +} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/storage2_lib.h b/firmware/nRF_badge/data_collector/incl/storage2_lib.h new file mode 100644 index 0000000..31bb271 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storage2_lib.h @@ -0,0 +1,84 @@ +#ifndef __STORAGE2_LIB_H +#define __STORAGE2_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +/** @brief Function for initializing storage2 (and the underlying EEPROM-module) + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INTERNAL If the underlying storage-module (here EEPROM) could not correctly initialized (here: bad spi configuration). + * @retval NRF_ERROR_BUSY If the EEPROM unprotecting operation failed (because of an ongoing spi operation). + * @retval NRF_ERROR_TIMEOUT If the EEPROM unprotecting operation takes too long. + */ +ret_code_t storage2_init(void); + + + + +/** @brief Function for storing bytes in the underlying storage-module (here EEPROM). + * + * @details + * + * + * @param[in] address The address of the first byte to write. + * @param[in] data Pointer to the bytes to store. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If data is NULL or specified address and length_data exceed the storage size or data is not in RAM section. + * @retval NRF_ERROR_BUSY If the underlying storage-module (here EEPROM or SPI) is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t storage2_store(uint32_t address, uint8_t* data, uint32_t length_data); + + +/** @brief Function for reading bytes from the underlying storage-module (here EEPROM). + * + * + * @param[in] address The address of the first byte to read. + * @param[in] data Pointer to memory where the bytes should be stored. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If data is NULL or specified address and length_data exceed the storage size or data is not in RAM section. + * @retval NRF_ERROR_BUSY If the underlying storage-module (here EEPROM or SPI) is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t storage2_read(uint32_t address, uint8_t* data, uint32_t length_data); + + + +/** @brief Function for retrieving the unit size of the storage-module. + * + * @details The unit size of a storage-module describes the minimum size of data that should be reserved for + * related data. + * Example: if you want to store two partitions of different data (that are not related). It is important + * to not store them on the same unit of the storage-module because it could happen that writing to + * partition 1 might destroy/delete data of partition 2 on the same unit (here: unit = byte). + * + * @retval 1 Unit size of a EEPROM. + */ +uint32_t storage2_get_unit_size(void); + + +/**@brief Function for reading the number of bytes in storage2. + * + * @retval Number of available bytes in storage2. + */ +uint32_t storage2_get_size(void); + + +/** @brief Function to clear/erase a defined address range. + * + * @param[in] address The address of the first byte to clear. + * @param[in] length The number of bytes to clear. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If specified address and length exceed the storage size. + * @retval NRF_ERROR_BUSY If the underlying storage-module is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t storage2_clear(uint32_t address, uint32_t length); + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/storage_lib.c b/firmware/nRF_badge/data_collector/incl/storage_lib.c new file mode 100644 index 0000000..4eaefc2 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storage_lib.c @@ -0,0 +1,232 @@ +#include "storage_lib.h" + +#include "stdio.h" + +#include "storage1_lib.h" +#include "storage2_lib.h" + +typedef ret_code_t (*storage_init_function_t)(void); +typedef ret_code_t (*storage_read_function_t)(uint32_t address, uint8_t* data, uint32_t length_data); +typedef ret_code_t (*storage_store_function_t)(uint32_t address, uint8_t* data, uint32_t length_data); +typedef uint32_t (*storage_get_size_function_t)(void); +typedef uint32_t (*storage_get_unit_size_function_t)(void); +typedef ret_code_t (*storage_clear_function_t)(uint32_t address, uint32_t length); + + +#ifdef UNIT_TEST // Because currently the unit-tests are written for this configuration. But for an efficient filesystem (because of SWAP_PAGE) we need the EEPROM as first storage-module +storage_init_function_t storage_init_functions[] = {storage1_init, storage2_init}; +storage_read_function_t storage_read_functions[] = {storage1_read, storage2_read}; +storage_store_function_t storage_store_functions[] = {storage1_store, storage2_store}; +storage_get_size_function_t storage_get_size_functions[] = {storage1_get_size, storage2_get_size}; +storage_get_unit_size_function_t storage_get_unit_size_functions[] = {storage1_get_unit_size, storage2_get_unit_size}; +storage_clear_function_t storage_clear_functions[] = {storage1_clear, storage2_clear}; +#else +storage_init_function_t storage_init_functions[] = {storage2_init, storage1_init}; +storage_read_function_t storage_read_functions[] = {storage2_read, storage1_read}; +storage_store_function_t storage_store_functions[] = {storage2_store, storage1_store}; +storage_get_size_function_t storage_get_size_functions[] = {storage2_get_size, storage1_get_size}; +storage_get_unit_size_function_t storage_get_unit_size_functions[] = {storage2_get_unit_size, storage1_get_unit_size}; +storage_clear_function_t storage_clear_functions[] = {storage2_clear, storage1_clear}; +#endif + + +#define NUMBER_OF_STORAGE_MODULES (uint8_t) (sizeof(storage_init_functions)/sizeof(storage_init_function_t)) /**< The number of different storage modules */ +static uint32_t storage_sizes[NUMBER_OF_STORAGE_MODULES]; /**< Contains the sizes of the storage modules (set during init-function) */ + + + +/** @brief Function for retrieving the splitted address, data and length of the different storage-modules for a given address and data length. + * + * @param[in] address The address that should be splitted. + * @param[in] data Pointer to data to be splitted (Could also be NULL, if no data has to be splitted) + * @param[in] length_data The length of the data that should be splitted. + * @param[out] splitted_address Pointer to memory where the splitted-address should be stored to. + * @param[out] splitted_data Pointer to memory where the splitted-data-pointer should be stored to. + * @param[out] splitted_length_data Pointer to memory where the splitted length should be stored to. + * @param[in] storage_sizes Array of the sizes of the different storage modules. + * @param[in] number_of_storage_modules Number of different storage modules. + */ +void storage_split_to_storage_modules(uint32_t address, uint8_t* data, uint32_t length_data, uint32_t splitted_address[], uint8_t* splitted_data[], uint32_t splitted_length_data[], uint32_t storage_sizes[], uint8_t number_of_storage_modules) { + uint32_t tmp_address = address; + uint32_t tmp_length_data = length_data; + uint32_t cumulated_size = 0; + for(uint8_t i = 0; i < number_of_storage_modules; i++) { + uint32_t size = storage_sizes[i]; + + // Check if the address is within the i-th storage-module, and we have some data left + if(tmp_address >= cumulated_size && tmp_address < cumulated_size + size && tmp_length_data > 0) { + + splitted_address[i] = tmp_address - cumulated_size; + if(data != NULL) + splitted_data[i] = &data[tmp_address - address]; + else + splitted_data[i] = NULL; + + if(tmp_address + tmp_length_data <= cumulated_size + size) { + splitted_length_data[i] = tmp_length_data; + } else { + splitted_length_data[i] = (cumulated_size + size) - tmp_address; + } + tmp_address += splitted_length_data[i]; + tmp_length_data -= splitted_length_data[i]; + } else { // if we have nothing in the i-th storage module, set everything to NULL + splitted_address[i] = 0; + splitted_data[i] = NULL; + splitted_length_data[i] = 0; + } + + cumulated_size += size; + + } +} + + +ret_code_t storage_init(void) { + + ret_code_t ret; + + for(uint8_t i = 0; i < NUMBER_OF_STORAGE_MODULES; i++) { + + // Set the storage_sizes to the sizes of the different storage-modules + storage_sizes[i] = storage_get_size_functions[i](); + + ret = storage_init_functions[i](); + if(ret != NRF_SUCCESS) { + ret = NRF_ERROR_INTERNAL; + return ret; + } + + } + return NRF_SUCCESS; +} + + +ret_code_t storage_store(uint32_t address, uint8_t* data, uint32_t length_data) { + + if(address + length_data > storage_get_size() || data == NULL) { + return NRF_ERROR_INVALID_PARAM; + } + if(length_data == 0) + return NRF_SUCCESS; + + + uint32_t splitted_address[NUMBER_OF_STORAGE_MODULES]; + uint8_t* splitted_data[NUMBER_OF_STORAGE_MODULES]; + uint32_t splitted_length_data[NUMBER_OF_STORAGE_MODULES]; + + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, NUMBER_OF_STORAGE_MODULES); + + + for(uint8_t i = 0; i < NUMBER_OF_STORAGE_MODULES; i++) { + if(splitted_data[i] == NULL || splitted_length_data[i] == 0) continue; + + ret_code_t ret = storage_store_functions[i](splitted_address[i], splitted_data[i], splitted_length_data[i]); + if(ret != NRF_SUCCESS) + return ret; + } + + return NRF_SUCCESS; +} + +ret_code_t storage_read(uint32_t address, uint8_t* data, uint32_t length_data) { + + if(address + length_data > storage_get_size() || data == NULL) { + return NRF_ERROR_INVALID_PARAM; + } + if(length_data == 0) + return NRF_SUCCESS; + + + uint32_t splitted_address[NUMBER_OF_STORAGE_MODULES]; + uint8_t* splitted_data[NUMBER_OF_STORAGE_MODULES]; + uint32_t splitted_length_data[NUMBER_OF_STORAGE_MODULES]; + + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, NUMBER_OF_STORAGE_MODULES); + + + for(uint8_t i = 0; i < NUMBER_OF_STORAGE_MODULES; i++) { + + + if(splitted_data[i] == NULL || splitted_length_data[i] == 0) continue; + ret_code_t ret = storage_read_functions[i](splitted_address[i], splitted_data[i], splitted_length_data[i]); + if(ret != NRF_SUCCESS) + return ret; + } + + return NRF_SUCCESS; +} + +ret_code_t storage_get_unit_address_limits(uint32_t address, uint32_t length_data, uint32_t* start_unit_address, uint32_t* end_unit_address) { + if(address + length_data > storage_get_size()) { + return NRF_ERROR_INVALID_PARAM; + } + if(length_data == 0) + return NRF_ERROR_INVALID_PARAM; + + uint32_t cumulated_size = 0; + for(uint8_t i = 0; i < NUMBER_OF_STORAGE_MODULES; i++) { + uint32_t size = storage_sizes[i]; + + + + // Check if address is in the i-th storage module + if(address >= cumulated_size && address < cumulated_size + size) { + // The address relative to start address of the i-th storage module + uint32_t tmp_address = address - cumulated_size; + uint32_t unit_size = storage_get_unit_size_functions[i](); + + // Compute start_unit_address by integer truncation (round off) + *start_unit_address = (tmp_address/unit_size)*unit_size + cumulated_size; + } + + // Check if end-address is in the i-th storage module + if(address + length_data - 1 >= cumulated_size && address + length_data - 1 < cumulated_size + size) { + uint32_t tmp_address = address + length_data - 1 - cumulated_size; + uint32_t unit_size = storage_get_unit_size_functions[i](); + + // Compute end_unit_address by integer truncation (round up) + *end_unit_address = (tmp_address/unit_size + 1)*unit_size - 1 + cumulated_size; + + // If the end_unit_address is computed, the function can return --> leave for-loop + break; + } + cumulated_size += size; + } + + return NRF_SUCCESS; +} + +uint32_t storage_get_size(void) { + static uint32_t size = 0; + if(size == 0) { // Only compute the size once. + for(uint8_t i = 0; i < sizeof(storage_get_size_functions)/sizeof(storage_get_size_function_t); i++) { + size += storage_get_size_functions[i](); + } + } + + return size; +} + +ret_code_t storage_clear(uint32_t address, uint32_t length) { + if(address + length > storage_get_size()) { + return NRF_ERROR_INVALID_PARAM; + } + if(length == 0) + return NRF_SUCCESS; + + uint32_t splitted_address[NUMBER_OF_STORAGE_MODULES]; + uint8_t* splitted_data[NUMBER_OF_STORAGE_MODULES]; + uint32_t splitted_length[NUMBER_OF_STORAGE_MODULES]; + + storage_split_to_storage_modules(address, NULL, length, splitted_address, splitted_data, splitted_length, storage_sizes, NUMBER_OF_STORAGE_MODULES); + + + for(uint8_t i = 0; i < NUMBER_OF_STORAGE_MODULES; i++) { + if(splitted_length[i] == 0) continue; + ret_code_t ret = storage_clear_functions[i](splitted_address[i], splitted_length[i]); + if(ret != NRF_SUCCESS) + return ret; + } + + return NRF_SUCCESS; +} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/storage_lib.h b/firmware/nRF_badge/data_collector/incl/storage_lib.h new file mode 100644 index 0000000..0a5ea67 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storage_lib.h @@ -0,0 +1,99 @@ +#ifndef __STORAGE_LIB_H +#define __STORAGE_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +/** @brief Function for initializing + * + * @details The function initializes all registered storage-modules by calling their init-functions. + * The init-functions of the storage-modules should tolerate a multiple init-call, because + * it might happen that one storage-module could not be initialized currently. And when retrying + * the other modules should be able to handle multiple init-calls. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INTERNAL If the underlying storage-module could not correctly initialized. + * @retval NRF_ERROR_BUSY If the operation could not be performed currently, because the underlying storage module is busy or it depends on another module that is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t storage_init(void); + + + + +/** @brief Function for storing bytes to the storage. + * + * @details This function splits the data to store (if necessary) into the different storage-modules and + * calls the store-function of the different storage-modules (if there is sth to store). + * + * + * @param[in] address The address of the first byte to write. + * @param[in] data Pointer to the bytes to store. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If data is NULL or specified address and length_data exceed the storage size or data is not in RAM section. + * @retval NRF_ERROR_BUSY If the underlying storage-module is busy. + * @retval NRF_ERROR_INTERNAL If the underlying storage-module was not correctly initialized or if something went wrong during the execution of the function. + * @retval NRF_ERROR_TIMEOUT If the operation timed out (sth. went wrong) or the operation takes too long. + */ +ret_code_t storage_store(uint32_t address, uint8_t* data, uint32_t length_data); + + +/** @brief Function for reading bytes from the storage. + * + * @details This function splits the data to read (if necessary) into the different storage-modules and + * calls the read-function of the different storage-modules (if there is sth to read from). + * + * + * @param[in] address The address of the first byte to read. + * @param[in] data Pointer to memory where the bytes should be stored. + * @param[in] length_data The number of bytes. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If data is NULL or specified address and length_data exceed the storage size or data is not in RAM section. + * @retval NRF_ERROR_BUSY If the underlying storage-module is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t storage_read(uint32_t address, uint8_t* data, uint32_t length_data); + + +/** @brief Function for retrieving the storage-unit boundaries of an address-area. + * + * @details The function computes an area in storage where the specified address-area (address --> address + length_data - 1) + * is included with respect to the unit-sizes of the different modules. + * This function could be used to check whether some new data are stored to the same unit as some other data. + * Furthermore, it could be used to calculate a partition-size, because partitions should always be unit-aligned. + * + * + * @param[in] address The address of the first byte to read. + * @param[in] length_data The number of bytes. + * @param[out] start_unit_address Pointer to memory where the start address of the affected unit(s) should be stored. + * @param[out] end_unit_address Pointer to memory where the end address of the affected unit(s) should be stored. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If specified address and length_data exceed the storage size or length_data == 0. + */ +ret_code_t storage_get_unit_address_limits(uint32_t address, uint32_t length_data, uint32_t* start_unit_address, uint32_t* end_unit_address); + + +/**@brief Function for reading the number of bytes in storage. + * + * @retval Number of available bytes in storage. + */ +uint32_t storage_get_size(void); + + +/** @brief Function to clear/erase a defined address range. + * + * @param[in] address The address of the first byte to clear. + * @param[in] length The number of bytes to clear. + * + * @retval NRF_SUCCSS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If specified address and length exceed the storage size. + * @retval NRF_ERROR_BUSY If the underlying storage-module is busy. + * @retval NRF_ERROR_TIMEOUT If the operation takes too long. + */ +ret_code_t storage_clear(uint32_t address, uint32_t length); + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/storer.c b/firmware/nRF_badge/data_collector/incl/storer.c deleted file mode 100644 index 351b440..0000000 --- a/firmware/nRF_badge/data_collector/incl/storer.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * INFORMATION **************************************************** - */ - - - -#include "storer.h" - -#include - -volatile bool flashWorking; - - -typedef enum { - FLASH_WRITE, - FLASH_ERASE, -} flash_operation_type_t; - -typedef struct { - uint32_t * p_dest; - uint32_t * p_src; - uint32_t size; -} flash_write_operation_t; - -typedef struct { - uint32_t page_number; -} flash_erase_operation_t; - -typedef struct { - flash_operation_type_t operation_type; - - // Only valid if operation_type == FLASH_WRITE - flash_write_operation_t write; - - // Only valid if operation_type == FLASH_ERASE - flash_erase_operation_t erase; - -} flash_operation_t; - -static flash_operation_t mPendingFlashOperation; - - -static bool mStorerScheduled = false; - -// this is the last chunk before we enter program memory space -// const int LAST_CHUNK = ((int)(LAST_PAGE - FIRST_PAGE) << 3) + 7; // = numberOfPages*8 + 7 - - -void storer_init() -{ - store.from = 0; - store.to = 0; - - debug_log("-storer initialization-\r\n"); - - unsigned long lastStoredTime = MODERN_TIME; - - for (int c = 0; c <= LAST_FLASH_CHUNK; c++) { - mic_chunk_t* chunkPtr = (mic_chunk_t*)ADDRESS_OF_CHUNK(c); - - unsigned long timestamp = chunkPtr->timestamp; - unsigned long check = chunkPtr->check; - - //is the timestamp possibly valid? - if (timestamp < FUTURE_TIME && timestamp > MODERN_TIME) { - //is it a completely stored chunk? - if (timestamp == check || check == CHECK_TRUNC) { - //is it later than the latest stored one found so far? - if (timestamp > lastStoredTime) { - store.to = c; //keep track of latest stored chunk - lastStoredTime = timestamp; - } - } - } - - } - - if (lastStoredTime == MODERN_TIME) { // no valid chunk found - debug_log(" No stored chunks found. Will start from chunk 0.\r\n"); - store.to = LAST_FLASH_CHUNK; // will advance to chunk 0 below. - } - else { - debug_log(" Last stored chunk: %d at time: 0x%lX\r\n", store.to, lastStoredTime); - //printStorerChunk(store.to); - } - - - debug_log(" Initializing FLASH for storage...\r\n"); - - int newChunk = (store.to < LAST_FLASH_CHUNK) ? store.to+1 : 0; // next chunk in FLASH (first unused chunk) - - unsigned char oldPage = PAGE_OF_CHUNK(store.to); - unsigned char newPage = PAGE_OF_CHUNK(newChunk); - - // If new chunk is on a new page, we can just erase the whole page. - if (oldPage != newPage) { - debug_log(" erase pg%d.\r\n",(int)newPage); - nrf_delay_ms(20); - erasePageOfFlash(newPage); - while(flashWorking); - } - // If new chunk is on same page as old data, we need to erase while preserving the old data - else { - uint32_t* oldChunkAddr = ADDRESS_OF_CHUNK(store.to); // address of latest stored data - uint32_t* pageAddr = ADDRESS_OF_PAGE(newPage); // address of beginning of current page - - int bytesToErase = (int)(oldChunkAddr) - (int)(pageAddr); // how many bytes of current page we need to erase - int chunksToSave = (BYTES_PER_PAGE - bytesToErase)/CHUNK_SIZE; // how many old chunks are in this page - UNUSED_VARIABLE(chunksToSave); - - debug_log(" pg%d (%d chunks)-->RAM...", newPage, chunksToSave); - uint32_t temp[WORDS_PER_PAGE]; // temporary buffer for page - memcpy(temp, pageAddr, BYTES_PER_PAGE); // copy old data to buffer - debug_log("clear %dbytes...", bytesToErase); - memset(temp, 0xff, bytesToErase); // clear unused portion of page in buffer - - debug_log("erase pg%d...",(int)newPage); - nrf_delay_ms(20); - erasePageOfFlash(newPage); // erase page - while(flashWorking); - - debug_log("restore data...\r\n"); - nrf_delay_ms(10); - writeBlockToFlash(pageAddr, temp, WORDS_PER_PAGE); // replace data from buffer - while(flashWorking); - } - - store.to = newChunk; - debug_log(" Ready to store to chunk %d.\r\n",newChunk); - - store.extFrom = 0; - - // We need to find the most recent stored chunk, to start storing after it - - scan_header_t header; - scan_tail_t tail; - - ext_eeprom_wait(); - - store.extTo = EXT_FIRST_DATA_CHUNK; - unsigned long lastStoredTimestamp = 0; - for (int i = EXT_FIRST_DATA_CHUNK; i <= EXT_LAST_CHUNK; i++) { - unsigned int addr = EXT_ADDRESS_OF_CHUNK(i); - ext_eeprom_read(addr,header.buf,sizeof(header.buf)); // Beginning of the chunk - ext_eeprom_wait(); - ext_eeprom_read(addr+EXT_CHUNK_SIZE-4,tail.buf,sizeof(tail.buf)); // End of the chunk - ext_eeprom_wait(); - - // Print all written chunks - /*if(header.timestamp != 0xFFFFFFFFUL) - { - debug_log("CH: %d,TS: %X\r\n",i,(int)header.timestamp); - nrf_delay_ms(2); - } */ - - // is it a completely stored scan result chunk? - if (header.timestamp > MODERN_TIME && header.timestamp < FUTURE_TIME) { - if (tail.check == header.timestamp || tail.check == CHECK_TRUNC || tail.check == CHECK_CONTINUE) { - // is it the most recent one found yet? - if (header.timestamp >= lastStoredTimestamp) { - lastStoredTimestamp = header.timestamp; - store.extTo = i; - } - } - } - } - - if (lastStoredTimestamp == 0) { - debug_log(" No stored scans found.\r\n"); - } - else { - debug_log(" Last stored scan in chunk %d, timestamp %X.\r\n",(int)store.extTo,(int)lastStoredTimestamp); - } - - store.extTo = (store.extTo < EXT_LAST_CHUNK) ? store.extTo+1 : EXT_FIRST_DATA_CHUNK; - - - //store.extFrom = 0; - //store.extTo = EXT_FIRST_DATA_CHUNK; - ext_eeprom_global_unprotect(); - ext_eeprom_wait(); -} - -bool storer_test() -{ - debug_log("Erasing first page...\r\n"); - erasePageOfFlash(FIRST_PAGE); - while (flashWorking); - - debug_log("Writing a value to flash...\r\n"); - uint32_t* addr = ADDRESS_OF_PAGE(FIRST_PAGE); - uint32_t value = 0x42; - writeBlockToFlash(addr, &value, 1); // write one word to FLASH - while (flashWorking); - - uint32_t readVal1 = *addr; - uint32_t readVal2 = *(addr+1); - - if (readVal1 != value) { - debug_log("***Read wrong value? Wrote 0x%X, read 0x%X.\r\n",(unsigned int)value,(unsigned int)readVal1); - return false; - } - if (readVal2 != 0xffffffffUL) { - debug_log("***Not erased? Read 0x%X from erased memory.\r\n",(unsigned int)readVal2); - return false; - } - return true; -} - -static void Storer_StoreAssignment(void) { - lastStoredAssignment.ID = badgeAssignment.ID; - lastStoredAssignment.group = badgeAssignment.group; - lastStoredAssignment.magicNumber = STORED_ASSIGNMENT_MAGIC_NUMBER; - ext_eeprom_write(STORED_ASSIGNMENT_ADDRESS,lastStoredAssignment.buf,sizeof(lastStoredAssignment.buf)); - ext_eeprom_wait(); - debug_log("STORER: stored assignment ID:0x%hX group:%d.\r\n", lastStoredAssignment.ID, lastStoredAssignment.group); -} - -static void Storer_AdvanceToNextMicrophoneChunk(void) { - unsigned char oldPage = PAGE_OF_CHUNK(store.to); // get page of just finished chunk - int newChunk = (store.to < LAST_FLASH_CHUNK) ? store.to+1 : 0; // advance to next chunk - unsigned char newPage = PAGE_OF_CHUNK(newChunk); // get page of next chunk - - if (oldPage != newPage) { - store.to = newChunk; - store.from = (store.from < LAST_RAM_CHUNK) ? store.from+1 : 0; // advance to next RAM chunk - erasePageOfFlash(newPage); // Erase the page. FLASH success system event will schedule next steps. - } else { - store.to = newChunk; - store.from = (store.from < LAST_RAM_CHUNK) ? store.from+1 : 0; // advance to next RAM chunk - Storer_ScheduleBufferedDataStorage(); // If we don't need to erase, schedule next steps ourselves. - } -} - -static void Storer_AdvanceToNextScanChunk(void) { - scanBuffer[store.extFrom].check = CHECK_STORED; - store.extTo = (store.extTo < EXT_LAST_CHUNK) ? store.extTo+1 : EXT_FIRST_DATA_CHUNK; - store.extFrom = (store.extFrom < LAST_SCAN_CHUNK) ? store.extFrom+1 : 0; // advance to next RAM chunk - Storer_ScheduleBufferedDataStorage(); // Schedule next scan chunk to be stored. -} - -static void Storer_StoreMicrophoneChunk(int dst_in_flash, int src_in_ram) { - debug_log("STORER: writing RAM chunk %d to FLASH chunk %d\r\n", src_in_ram, dst_in_flash); - //printCollectorChunk(store.from); - writeBlockToFlash(ADDRESS_OF_CHUNK(dst_in_flash), micBuffer[src_in_ram].wordBuf, WORDS_PER_CHUNK); - // The flash success event handler will queue the next events. -} - -static void Storer_StoreScanChunk(int dst_in_flash, int src_in_ram) { - debug_log("STORER: writing SCAN chunk %d to EXT chunk %d\r\n", src_in_ram, dst_in_flash); - //printScanResult(&scanBuffer[store.extFrom]); - ext_eeprom_write(EXT_ADDRESS_OF_CHUNK(dst_in_flash),scanBuffer[src_in_ram].buf, sizeof(scanBuffer[src_in_ram].buf)); - ext_eeprom_wait(); - Storer_AdvanceToNextScanChunk(); -} - -// Scans RAM for any data that has not been stored to flash and stores it to flash. -// Schedules any follow-up calls if needed based upon callbacks. -static void Storer_StoreBufferedData(void) { - if (flashWorking) { - debug_log("StoreBufferedData called but flash operation is pending, will retry.\r\n"); - // Can't do storage work if flash operation pending - return; - } - - if (lastStoredAssignment.ID != badgeAssignment.ID || lastStoredAssignment.group != badgeAssignment.group) { - Storer_StoreAssignment(); - } else if (store.from != collect.to) { - if (micBuffer[store.from].check == micBuffer[store.from].timestamp || - micBuffer[store.from].check == CHECK_TRUNC) { - // Storable chunk in collector RAM. - Storer_StoreMicrophoneChunk(store.to, store.from); - } else { - store.from = (store.from < LAST_RAM_CHUNK) ? store.from + 1 : 0; // somehow an invalid RAM chunk - move on - } - } else if (store.extFrom != scan.to) { - if (scanBuffer[store.extFrom].check == scanBuffer[store.extFrom].timestamp - || scanBuffer[store.extFrom].check == CHECK_TRUNC - || scanBuffer[store.extFrom].check == CHECK_CONTINUE) { - Storer_StoreScanChunk(store.extTo, store.extFrom); - } else { - // else invalid chunk, move on - store.extFrom = (store.extFrom < LAST_SCAN_CHUNK) ? store.extFrom + 1 : 0; - } - } -} - -static void StorerSchedulerCallbackHandler_StoreBufferedData(void *p_event_data, uint16_t event_size) { - Storer_StoreBufferedData(); - mStorerScheduled = false; -} - -void Storer_ScheduleBufferedDataStorage(void) { - if (mStorerScheduled == false) { - uint32_t err_code = app_sched_event_put(NULL, 0, StorerSchedulerCallbackHandler_StoreBufferedData); - APP_ERROR_CHECK(err_code); - - mStorerScheduled = true; - } -} - -// TODO: This should be decoupled from the store struct -static void flash_write_success_callback(void) { - debug_log("STORER: stored RAM chunk %d to FLASH chunk %d\r\n", store.from, store.to); - - flashWorking = false; - micBuffer[store.from].check = CHECK_STORED; // mark RAM chunk as stored (don't need to keep track of it in RAM) - Storer_AdvanceToNextMicrophoneChunk(); // Advance to the next chunk (may require erasing new page) -} - -// TODO: This should be decoupled from the store struct -static void flash_erase_success_callback(void) { - debug_log("STORER: erased page %d\r\n", (int) PAGE_OF_CHUNK(store.to)); - - flashWorking = false; - Storer_ScheduleBufferedDataStorage(); -} - -// On flash write/erase timeouts, we should retry, according to Page 21 of the S130 Specification. -static void flash_write_timeout_callback(flash_write_operation_t operation) { - debug_log("STORER: Flash write timeout, retrying..."); - sd_flash_write(operation.p_dest, operation.p_src, operation.size); -} - -static void flash_erase_timeout_callback(flash_erase_operation_t operation) { - debug_log("STORER: Flash erase timeout, retrying..."); - sd_flash_page_erase(operation.page_number); -} - -void storer_on_sys_evt(uint32_t sys_evt) { - // TODO this can probably be done much better if we switch to the PStorage SDK API. - if (sys_evt == NRF_EVT_FLASH_OPERATION_ERROR) { - switch (mPendingFlashOperation.operation_type) { - case FLASH_WRITE: - flash_write_timeout_callback(mPendingFlashOperation.write); - break; - case FLASH_ERASE: - flash_erase_timeout_callback(mPendingFlashOperation.erase); - break; - } - } - - if (sys_evt == NRF_EVT_FLASH_OPERATION_SUCCESS) { - switch (mPendingFlashOperation.operation_type) { - case FLASH_WRITE: - flash_write_success_callback(); - break; - case FLASH_ERASE: - flash_erase_success_callback(); - break; - } - } -} - - -void printStorerChunk(int chunk) -{ - if (chunk > LAST_FLASH_CHUNK || chunk < 0) { // invalid chunk - debug_log("ERR: Invalid storer chunk to print\r\n"); - return; - } - - mic_chunk_t* chunkPtr = (mic_chunk_t*)ADDRESS_OF_CHUNK(chunk); - - debug_log("-FLASH chunk %d:\r\n",chunk); - debug_log("ts: 0x%lX - ms: %hd - ba: %d -- ch: 0x%lX",(*chunkPtr).timestamp, (*chunkPtr).msTimestamp, - (int)((*chunkPtr).battery*1000), (*chunkPtr).check ); - nrf_delay_ms(3); - for (int i = 0; i < SAMPLES_PER_CHUNK; i++) { - if (i%10 == 0) debug_log("\r\n "); - unsigned char sample = (*chunkPtr).samples[i]; - if (sample != INVALID_SAMPLE) { - debug_log("%3u, ",(int)sample); - } - else { - debug_log("- , "); - } - nrf_delay_ms(2); - } - debug_log("\r\n---\r\n"); - nrf_delay_ms(10); -} - - -badge_assignment_t getStoredBadgeAssignment() -{ - badge_assignment_t assignment; - ext_eeprom_wait(); - ext_eeprom_read(STORED_ASSIGNMENT_ADDRESS,lastStoredAssignment.buf,sizeof(lastStoredAssignment.buf)); - // If magic number is not present, then the data found is not actually a valid assignment. - if (lastStoredAssignment.magicNumber != STORED_ASSIGNMENT_MAGIC_NUMBER) { - lastStoredAssignment.group = 0xff; // invalid - lastStoredAssignment.ID = 0xffff; // invalid - } - assignment.ID = lastStoredAssignment.ID; - assignment.group = lastStoredAssignment.group; - return assignment; -} - - - -// If we try an illegal flash write, something's very wrong, so we should not continue. -void writeBlockToFlash(uint32_t* to, uint32_t* from, int numWords) { - uint8_t page = PAGE_FROM_ADDRESS(to); - if (page > LAST_PAGE || page < FIRST_PAGE) { - debug_log("Invalid block write address\r\n"); - APP_ERROR_CHECK_BOOL(false); - } else { - flashWorking = true; - flash_operation_t op = {FLASH_WRITE, {to, from, (uint32_t) numWords}, {0}}; - mPendingFlashOperation = op; - - sd_flash_write(to, from, (uint32_t) numWords); - } -} - -void erasePageOfFlash(uint8_t page) -{ - if (page > LAST_PAGE || page < FIRST_PAGE) { - debug_log("Invalid flash erase address\r\n"); - APP_ERROR_CHECK_BOOL(false); - } - else { - flashWorking = true; - flash_operation_t op = {FLASH_ERASE, {0}, {page}}; - mPendingFlashOperation = op; - - sd_flash_page_erase(page); - } -} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/storer.h b/firmware/nRF_badge/data_collector/incl/storer.h deleted file mode 100644 index 8fecea1..0000000 --- a/firmware/nRF_badge/data_collector/incl/storer.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * INFORMATION **************************************************** - */ - -#ifndef STORER_H -#define STORER_H - -#include -#include - -#include "debug_log.h" - -#include "nrf_drv_config.h" -#include "boards.h" - -#include "nrf_soc.h" - -#include "ble_setup.h" -#include "ext_eeprom.h" - - - - -/**=================== various constants/macros for defining "chunks" ============= - * - * Data in flash needs to have periodic timestamps, to keep it ordered. - * Timestamp is first 4 bytes of chunk; repeated at last 4 bytes after chunk has been sent - * For robustness, flash data storage starts at the end, page 251, backwards, i.e. : - * . ...Page 250 ] [ Page 251 ] - * ...(CHNK9)(CHNK8)] [(CHNK7 )( CHNK6 )( CHNK5 )( CHNK4 )( CHNK3 )( CHNK2 )( CHNK1 )(CHNK0)] - * - * So there are 3 representations of locations in memory: chunk index, actual byte/word address, and page index - * The following defines and macros establish these representations and conversions between them - * - */ - - -// Ideally this would be the first page free after program space. -// May be acquirable from linker script. But the following value is a (conservative) estimated value. -#define FIRST_PAGE 155 -#define LAST_PAGE 251 // last available FLASH page - rest is reserved - -#define BYTES_PER_PAGE 1024 //1kB pages in nrf51 flash -#define WORDS_PER_PAGE 256 //4-byte words, 1024/4 -// divide the memory address by 1024 for page index -#define PAGE_FROM_ADDRESS(address) ((uint8_t)((uint32_t)address >> 10)) -// multiply the page index by 1024 for memory address -#define ADDRESS_OF_PAGE(page) ((uint32_t*)(page << 10)) - - -#define CHUNK_SIZE 128 //8 chunks per 1kB page. -#define WORDS_PER_CHUNK 32 //4-byte words, 128/4 -#define LAST_CHUNK_ADDRESS ((LAST_PAGE*1024)+(7*CHUNK_SIZE)) // = last page address + offset of last chunk = 251*1024 + 7*128 -// multiply chunk index by 128, count back from last memory address to get address of chunk -#define ADDRESS_OF_CHUNK(chnk) ( (uint32_t*)(LAST_CHUNK_ADDRESS - (chnk << 7)) ) -// divide chunk index by 8, count back from last page -#define PAGE_OF_CHUNK(chnk) ((uint8_t)(LAST_PAGE - (chnk >> 3))) -// this is the last chunk before we enter program memory space -//const int LAST_CHUNK; // = numberOfPages*8 + 7, defined in flash_handling.c -#define LAST_FLASH_CHUNK (((LAST_PAGE - FIRST_PAGE) << 3) + 7) // = numberOfPages*8 + 7 - -#define NO_CHUNK 0xffff - - -#define MODERN_TIME 1434240000UL // Unix time in the recent past (sometime June 2015), used to check for reasonable timestamps -#define FUTURE_TIME 1893456000UL // Unix time somewhere around 2030, used to check for reasonable timestamps - -#define CHECK_STORED 0x2UL // Used to mark a RAM chunk once it's been stored to FLASH (to avoid having 2 identical copies) - -#include "collector.h" -#include "analog.h" -#include "scanner.h" -#include "sender.h" - - -// Struct for keeping track of storing mic data to RAM -struct -{ - int from; // which chunk in RAM buffer we're currently storing from - int to; // which chunk in flash we're currently storing to - - int extFrom; // which chunk in scanner RAM buffer we're currently storing from - int extTo; // which chunk in external FLASH/EEPROM we're currently storing to -} store; - - -void storer_init(); - -bool storer_test(); - -void storer_on_sys_evt(uint32_t sys_evt); - -void printStorerChunk(int chunk); - - -#define STORED_ASSIGNMENT_ADDRESS 10 // arbitrary address in EEPROM - -// Magic number is stored to EEPROM along with ID and group #, to denote a valid stored assignment. -#define STORED_ASSIGNMENT_MAGIC_NUMBER 0xfa - - -typedef union -{ - struct - { - unsigned char dummy[4]; // (4byte) - unsigned char magicNumber; // (1byte) - unsigned char group; // (1byte) - unsigned short ID; // (2byte) - }; // 4+4byte total - unsigned char buf[8]; -} stored_assignment_t; - -stored_assignment_t lastStoredAssignment; - -badge_assignment_t getStoredBadgeAssignment(); - -void Storer_ScheduleBufferedDataStorage(void); - -// Functions for dealing with flash. Protect from invalid writes/erases. -void writeWordToFlash(uint32_t* addr, uint32_t val); -void writeBlockToFlash(uint32_t* to, uint32_t* from, int numWords); -void erasePageOfFlash(uint8_t page); - - -#endif //#ifndef STORER_H - diff --git a/firmware/nRF_badge/data_collector/incl/storer_lib.c b/firmware/nRF_badge/data_collector/incl/storer_lib.c new file mode 100644 index 0000000..372d4a3 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storer_lib.c @@ -0,0 +1,440 @@ +#include "storer_lib.h" +#include "filesystem_lib.h" +#include "debug_lib.h" +#include "chunk_messages.h" +#include "tinybuf.h" +#include "string.h" // For memset-function + + + + +#define STORER_SERIALIZED_BUFFER_SIZE 512 + + + + +static uint8_t serialized_buf[STORER_SERIALIZED_BUFFER_SIZE]; + +static uint16_t partition_id_badge_assignement; +static uint16_t partition_id_battery_chunks; +static uint16_t partition_id_microphone_chunks; +static uint16_t partition_id_scan_chunks; +static uint16_t partition_id_accelerometer_interrupt_chunks; +static uint16_t partition_id_accelerometer_chunks; + + +static uint8_t microphone_chunks_found_timestamp = 0; +static uint8_t scan_chunks_found_timestamp = 0; +static uint8_t battery_chunks_found_timestamp = 0; +static uint8_t accelerometer_interrupt_chunks_found_timestamp = 0; +static uint8_t accelerometer_chunks_found_timestamp = 0; +/* + + +static uint16_t partition_id_accelerometer_data; +static uint16_t partition_id_accelerometer_interrupts; +*/ + + +/**@brief Function that registers all needed partitions to the filesystem. + * + * @retval NRF_SUCCESS If operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If the required-size == 0, or element_len == 0 in a static partition. + * @retval NRF_ERROR_NO_MEM If there were already more than MAX_NUMBER_OF_PARTITIONS partition-registrations, or if the available size for the partition is too small. + * @retval NRF_ERROR_INTERNAL If there was an internal error (e.g. data couldn't be read because of busy). + */ +ret_code_t storer_register_partitions(void) { + ret_code_t ret; + /******************* BADGE ASSIGNEMENT **********************/ + uint32_t serialized_badge_assignement_len = tb_get_max_encoded_len(BadgeAssignement_fields); + // Required size for badge_assignment + uint32_t required_size = PARTITION_METADATA_SIZE + STORER_BADGE_ASSIGNEMENT_NUMBER*(serialized_badge_assignement_len + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE); + // Register a static partition with CRC for the badge-assignement + ret = filesystem_register_partition(&partition_id_badge_assignement, &required_size, 0, 1, serialized_badge_assignement_len); + if(ret != NRF_SUCCESS) return ret; + //debug_log("STORER: Available size: %u\n", filesystem_get_available_size()); + + /****************** BATTERY *****************************/ + uint32_t serialized_battery_data_len = tb_get_max_encoded_len(BatteryChunk_fields); + // Required size for battery_data + required_size = PARTITION_METADATA_SIZE + STORER_BATTERY_DATA_NUMBER*(serialized_battery_data_len + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE); + // Register a static partition without CRC for the battery-data + ret = filesystem_register_partition(&partition_id_battery_chunks, &required_size, 0, 0, serialized_battery_data_len); + if(ret != NRF_SUCCESS) return ret; + //debug_log("STORER: Available size: %u\n", filesystem_get_available_size()); + + /****************** MICROPHONE ******************************/ + uint32_t serialized_microphone_data_len = tb_get_max_encoded_len(MicrophoneChunk_fields); + // Required size for microphone data + required_size = PARTITION_METADATA_SIZE + STORER_MICROPHONE_DATA_NUMBER * (serialized_microphone_data_len + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE); + // Register a static partition with CRC for the microphone-data + ret = filesystem_register_partition(&partition_id_microphone_chunks, &required_size, 0, 1, serialized_microphone_data_len); + if(ret != NRF_SUCCESS) return ret; + //debug_log("STORER: Available size: %u\n", filesystem_get_available_size()); + + /******************* SCAN *********************************/ + uint32_t max_serialized_scan_data_len = tb_get_max_encoded_len(ScanChunk_fields); + // Required size for scan data + required_size = PARTITION_METADATA_SIZE + STORER_SCAN_DATA_NUMBER * (max_serialized_scan_data_len + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE + PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE); + // Register a dynamic partition with CRC for the scan-data + ret = filesystem_register_partition(&partition_id_scan_chunks, &required_size, 1, 1, 0); + if(ret != NRF_SUCCESS) return ret; + //debug_log("STORER: Available size: %u\n", filesystem_get_available_size()); + + /****************** ACCELEROMETER INTERRUPT *****************/ + uint32_t serialized_accelerometer_interrupt_data_len = tb_get_max_encoded_len(AccelerometerInterruptChunk_fields); + // Required size for accelerometer interrupt-data + required_size = PARTITION_METADATA_SIZE + STORER_ACCELEROMETER_INTERRUPT_DATA_NUMBER * (serialized_accelerometer_interrupt_data_len + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE); + // Register a static partition with CRC for the accelerometer interrupt-data + ret = filesystem_register_partition(&partition_id_accelerometer_interrupt_chunks, &required_size, 0, 1, serialized_accelerometer_interrupt_data_len); + if(ret != NRF_SUCCESS) return ret; + //debug_log("STORER: Available size: %u\n", filesystem_get_available_size()); + + /********************** ACCELEROMETER ***********************/ + uint32_t serialized_accelerometer_data_len = tb_get_max_encoded_len(AccelerometerChunk_fields); + // Required size for accelerometer data + required_size = PARTITION_METADATA_SIZE + STORER_ACCELEROMETER_DATA_NUMBER * (serialized_accelerometer_data_len + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE); + // Register a static partition with CRC for the accelerometer-data + ret = filesystem_register_partition(&partition_id_accelerometer_chunks, &required_size, 0, 1, serialized_accelerometer_data_len); + if(ret != NRF_SUCCESS) return ret; + debug_log("STORER: Available size: %u\n", filesystem_get_available_size()); + + + return NRF_SUCCESS; +} + +ret_code_t storer_init(void) { + ret_code_t ret; + ret = filesystem_init(); + if(ret != NRF_SUCCESS) return ret; + + ret = storer_register_partitions(); + if(ret != NRF_SUCCESS) return ret; + + return NRF_SUCCESS; +} + +ret_code_t storer_clear(void) { + ret_code_t ret = filesystem_clear_partition(partition_id_badge_assignement); + if(ret != NRF_SUCCESS) return ret; + + ret = filesystem_clear_partition(partition_id_battery_chunks); + if(ret != NRF_SUCCESS) return ret; + + ret = filesystem_clear_partition(partition_id_microphone_chunks); + if(ret != NRF_SUCCESS) return ret; + + ret = filesystem_clear_partition(partition_id_scan_chunks); + if(ret != NRF_SUCCESS) return ret; + + ret = filesystem_clear_partition(partition_id_accelerometer_interrupt_chunks); + if(ret != NRF_SUCCESS) return ret; + + ret = filesystem_clear_partition(partition_id_accelerometer_chunks); + if(ret != NRF_SUCCESS) return ret; + + return ret; +} + + + +/**@brief Function to compare two timestamps. + * + * @retval -1 timestamp1 > timestamp2 + * @retval 0 timestamp1 == timestamp2 + * @retval 1 timestamp1 < timestamp2 + */ +int8_t storer_compare_timestamps(Timestamp timestamp1, Timestamp timestamp2) { + uint64_t t1 = ((uint64_t)timestamp1.seconds)*1000 + timestamp1.ms; + uint64_t t2 = ((uint64_t)timestamp2.seconds)*1000 + timestamp2.ms; + return (t1 > t2) ? -1 : ((t2 > t1) ? 1 : 0); +} + +ret_code_t storer_clear_badge_assignement(void) { + return filesystem_clear_partition(partition_id_badge_assignement); +} + +ret_code_t storer_store_badge_assignement(BadgeAssignement* badge_assignement) { + tb_ostream_t ostream = tb_ostream_from_buffer(serialized_buf, sizeof(serialized_buf)); + uint8_t encode_status = tb_encode(&ostream, BadgeAssignement_fields, badge_assignement, TB_LITTLE_ENDIAN); + if(!encode_status) return NRF_ERROR_INVALID_DATA; + + return filesystem_store_element(partition_id_badge_assignement, serialized_buf, ostream.bytes_written); +} + + +ret_code_t storer_read_badge_assignement(BadgeAssignement* badge_assignement) { + memset(badge_assignement, 0, sizeof(BadgeAssignement)); + ret_code_t ret = filesystem_iterator_init(partition_id_badge_assignement); // Get the latest stored assignement + if(ret != NRF_SUCCESS) { + filesystem_iterator_invalidate(partition_id_badge_assignement); + return ret; + } + uint16_t element_len, record_id; + ret = filesystem_iterator_read_element(partition_id_badge_assignement, serialized_buf, &element_len, &record_id); + filesystem_iterator_invalidate(partition_id_badge_assignement); + + if(ret != NRF_SUCCESS) return ret; + + tb_istream_t istream = tb_istream_from_buffer(serialized_buf, element_len); + uint8_t decode_status = tb_decode(&istream, BadgeAssignement_fields, badge_assignement, TB_LITTLE_ENDIAN); + if(!decode_status) return NRF_ERROR_INVALID_DATA; + + return NRF_SUCCESS; +} + + + +/**@brief Function to store a chunk of data in a partition. + * + * @param[in] partition_id The partition_id where to store the chunk. + * @param[in] message_fields The message fields need to encode the message-chunk with tinybuf. + * @param[in] message Pointer to the message-chunk that should be encoded and stored. + * + * @retval NRF_ERROR_NO_MEM If the element is too big, to be stored in the partition. + * @retval NRF_ERROR_INTERNAL Busy or iterator is pointing to the same address we want to write to. + * @retval NRF_ERROR_INVALID_DATA If encoding fails. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t store_chunk(uint16_t partition_id, const tb_field_t message_fields[], void* message) { + tb_ostream_t ostream = tb_ostream_from_buffer(serialized_buf, sizeof(serialized_buf)); + uint8_t encode_status = tb_encode(&ostream, message_fields, message, TB_LITTLE_ENDIAN); + if(!encode_status) return NRF_ERROR_INVALID_DATA; + + return filesystem_store_element(partition_id, serialized_buf, ostream.bytes_written); +} + + +/**@brief Function to find a chunk in the partition based on its timestamp. + * + * @details This function is normally used in connection with get_next_chunk(). + * It tries to step back in the partition until it finds the oldest chunk + * that is still greater than the timestamp. It uses the iterator of the partition + * to step back. + * + * @param[in] timestamp The timestamp since when the data should be requested. + * @param[in] partition_id The partition_id where to search the chunk. + * @param[in] message_fields The message fields need to decode the message-chunk with tinybuf. + * @param[out] message Pointer to a message-chunk (needed for internal decoding). + * @param[out] message_timestamp Pointer to the timestamp entry in the message-chunk. + * @param[out] found_timestamp Pointer to a flag-variable that expresses, if an "old" element with a greater timestamp was found. + * + * @retval NRF_ERROR_INTERNAL Busy + * @retval NRF_ERROR_INVALID_STATE Iterator invalidated/no data found. + * @retval NRF_SUCCESS If everything was fine. + */ +static ret_code_t find_chunk_from_timestamp(Timestamp timestamp, uint16_t partition_id, const tb_field_t message_fields[], void* message, Timestamp* message_timestamp, uint8_t* found_timestamp) { + + *found_timestamp = 0; + + ret_code_t ret = filesystem_iterator_init(partition_id); + // If there are no data in partition --> directly return + if(ret != NRF_SUCCESS) { + filesystem_iterator_invalidate(partition_id); + return ret; + } + + + while(1) { + uint16_t element_len, record_id; + ret = filesystem_iterator_read_element(partition_id, serialized_buf, &element_len, &record_id); + // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_DATA, NRF_ERROR_INVALID_STATE, NRF_ERROR_INTERNAL + if(!(ret == NRF_ERROR_INVALID_DATA || ret == NRF_SUCCESS)) { + filesystem_iterator_invalidate(partition_id); + return ret; + } + + if(ret == NRF_SUCCESS) { // Only try to decode when the data are not corrupted (NRF_ERROR_INVALID_DATA) + + // Decode the current element + tb_istream_t istream = tb_istream_from_buffer(serialized_buf, element_len); + uint8_t decode_status = tb_decode(&istream, message_fields, message, TB_LITTLE_ENDIAN); + if(decode_status) { + if(storer_compare_timestamps(*message_timestamp, timestamp) == 1) { + // We have found the timestamp --> we need to go to the next again + ret = filesystem_iterator_next(partition_id); + // ret could be NRF_SUCCESS, NRF_ERROR_NOT_FOUND, NRF_ERROR_INVALID_STATE, NRF_ERROR_INTERNAL + if(!(ret == NRF_ERROR_NOT_FOUND || ret == NRF_SUCCESS)) { + filesystem_iterator_invalidate(partition_id); + return ret; + } + // ret could be NRF_SUCCESS, NRF_ERROR_NOT_FOUND + if(ret == NRF_SUCCESS) { + *found_timestamp = 1; + } else { // If we have not found a "next" element (because the current one is the latest), we haven't a valid timestamp + // So if the storer_get_next_..._data()-function is called, + // it tries directly to move to the next-element and it will return NRF_ERROR_NOT_FOUND (except new data has been written since then) + *found_timestamp = 0; + } + // But we want to return NRF_SUCCESS when we have just not found the next-element + ret = (ret == NRF_ERROR_NOT_FOUND) ? NRF_SUCCESS : ret; + break; + } + } + } + // Otherwise go to the previous except there is no previous element any more + ret = filesystem_iterator_previous(partition_id); + // ret could be NRF_SUCCESS, NRF_ERROR_NOT_FOUND, NRF_ERROR_INVALID_STATE, NRF_ERROR_INTERNAL + if(!(ret == NRF_ERROR_NOT_FOUND || ret == NRF_SUCCESS)) { + filesystem_iterator_invalidate(partition_id); + return ret; + } + // ret could be NRF_SUCCESS, NRF_ERROR_NOT_FOUND + if(ret == NRF_ERROR_NOT_FOUND) { + // We have reached the end of the partition --> stop searching + *found_timestamp = 1; + ret = NRF_SUCCESS; + break; + } + } + return ret; // ret should be NRF_SUCCESS +} + + +/**@brief Function to get the next chunk in the partition based on the current status of the partition-iterator. + * + * @details This function is normally called after find_chunk_from_timestamp(). It tries to get the next element stored in partition + * and decodes it with the message_fields. + * + * + * @param[in] partition_id The partition_id where to search the chunk. + * @param[in] message_fields The message fields need to decode the message-chunk with tinybuf. + * @param[out] message Pointer to a message-chunk where to store the read chunk. + * @param[in] found_timestamp Pointer to a flag-variable that expresses, if an "old" element with a greater timestamp was found. + * + * @retval NRF_SUCCESS If an element was found and returned successfully. + * @retval NRF_ERROR_NOT_FOUND If no more element in the partition. + * @retval NRF_ERROR_INVALID_STATE If iterator not initialized or invalidated. + * @retval NRF_ERROR_INTERNAL If busy. + * + * @note The application needs to invalidate the iterator, if the function is not used until no more element is found (because it invalidates it then automatically). + */ +static ret_code_t get_next_chunk(uint16_t partition_id, const tb_field_t message_fields[], void* message, uint8_t* found_timestamp) { + ret_code_t ret; + uint16_t element_len, record_id; + // do-while-loop to ignore invalid data + do { + // Skip step to the next iterator element if found_timestamp is true + if(!(*found_timestamp)) { + ret = filesystem_iterator_next(partition_id); + // ret could be NRF_SUCCESS, NRF_ERROR_NOT_FOUND, NRF_ERROR_INVALID_STATE, NRF_ERROR_INTERNAL + if(ret != NRF_SUCCESS) { + filesystem_iterator_invalidate(partition_id); + return ret; + } + } + *found_timestamp = 0; + + // TODO: What happens if read failed, but we have already done a next-step successfully? + ret = filesystem_iterator_read_element(partition_id, serialized_buf, &element_len, &record_id); + // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_DATA, NRF_ERROR_INVALID_STATE, NRF_ERROR_INTERNAL + if(!(ret == NRF_ERROR_INVALID_DATA || ret == NRF_SUCCESS)) { + filesystem_iterator_invalidate(partition_id); + return ret; + } + // ret could be NRF_SUCCESS, NRF_ERROR_INVALID_DATA + if(ret == NRF_SUCCESS) { // Only decode if no invalid data + // Now decode it + tb_istream_t istream = tb_istream_from_buffer(serialized_buf, element_len); + uint8_t decode_status = tb_decode(&istream, message_fields, message, TB_LITTLE_ENDIAN); + if(!decode_status) ret = NRF_ERROR_INVALID_DATA; // Set to invalid data, to proceed in the while-loop + } + } while(ret == NRF_ERROR_INVALID_DATA); + + return ret; // Should actually always be NRF_SUCCESS +} + + + +void storer_invalidate_iterators(void) { + filesystem_iterator_invalidate(partition_id_accelerometer_chunks); + filesystem_iterator_invalidate(partition_id_accelerometer_interrupt_chunks); + filesystem_iterator_invalidate(partition_id_battery_chunks); + filesystem_iterator_invalidate(partition_id_scan_chunks); + filesystem_iterator_invalidate(partition_id_microphone_chunks); +} + + +ret_code_t storer_store_accelerometer_chunk(AccelerometerChunk* accelerometer_chunk) { + return store_chunk(partition_id_accelerometer_chunks, AccelerometerChunk_fields, accelerometer_chunk); +} + +ret_code_t storer_find_accelerometer_chunk_from_timestamp(Timestamp timestamp, AccelerometerChunk* accelerometer_chunk) { + memset(accelerometer_chunk, 0, sizeof(AccelerometerChunk)); + return find_chunk_from_timestamp(timestamp, partition_id_accelerometer_chunks, AccelerometerChunk_fields, accelerometer_chunk, &(accelerometer_chunk->timestamp), &accelerometer_chunks_found_timestamp); +} + +ret_code_t storer_get_next_accelerometer_chunk(AccelerometerChunk* accelerometer_chunk) { + memset(accelerometer_chunk, 0, sizeof(AccelerometerChunk)); + return get_next_chunk(partition_id_accelerometer_chunks, AccelerometerChunk_fields, accelerometer_chunk, &accelerometer_chunks_found_timestamp); +} + + + +ret_code_t storer_store_accelerometer_interrupt_chunk(AccelerometerInterruptChunk* accelerometer_interrupt_chunk) { + return store_chunk(partition_id_accelerometer_interrupt_chunks, AccelerometerInterruptChunk_fields, accelerometer_interrupt_chunk); +} + +ret_code_t storer_find_accelerometer_interrupt_chunk_from_timestamp(Timestamp timestamp, AccelerometerInterruptChunk* accelerometer_interrupt_chunk) { + memset(accelerometer_interrupt_chunk, 0, sizeof(AccelerometerInterruptChunk)); + return find_chunk_from_timestamp(timestamp, partition_id_accelerometer_interrupt_chunks, AccelerometerInterruptChunk_fields, accelerometer_interrupt_chunk, &(accelerometer_interrupt_chunk->timestamp), &accelerometer_interrupt_chunks_found_timestamp); +} + +ret_code_t storer_get_next_accelerometer_interrupt_chunk(AccelerometerInterruptChunk* accelerometer_interrupt_chunk) { + memset(accelerometer_interrupt_chunk, 0, sizeof(AccelerometerInterruptChunk)); + return get_next_chunk(partition_id_accelerometer_interrupt_chunks, AccelerometerInterruptChunk_fields, accelerometer_interrupt_chunk, &accelerometer_interrupt_chunks_found_timestamp); +} + + + + + + + +ret_code_t storer_store_battery_chunk(BatteryChunk* battery_chunk) { + return store_chunk(partition_id_battery_chunks, BatteryChunk_fields, battery_chunk); +} + +ret_code_t storer_find_battery_chunk_from_timestamp(Timestamp timestamp, BatteryChunk* battery_chunk) { + memset(battery_chunk, 0, sizeof(BatteryChunk)); + return find_chunk_from_timestamp(timestamp, partition_id_battery_chunks, BatteryChunk_fields, battery_chunk, &(battery_chunk->timestamp), &battery_chunks_found_timestamp); +} + +ret_code_t storer_get_next_battery_chunk(BatteryChunk* battery_chunk) { + memset(battery_chunk, 0, sizeof(BatteryChunk)); + return get_next_chunk(partition_id_battery_chunks, BatteryChunk_fields, battery_chunk, &battery_chunks_found_timestamp); +} + + + + + +ret_code_t storer_store_scan_chunk(ScanChunk* scan_chunk) { + return store_chunk(partition_id_scan_chunks, ScanChunk_fields, scan_chunk); +} + +ret_code_t storer_find_scan_chunk_from_timestamp(Timestamp timestamp, ScanChunk* scan_chunk) { + memset(scan_chunk, 0, sizeof(ScanChunk)); + return find_chunk_from_timestamp(timestamp, partition_id_scan_chunks, ScanChunk_fields, scan_chunk, &(scan_chunk->timestamp), &scan_chunks_found_timestamp); +} + +ret_code_t storer_get_next_scan_chunk(ScanChunk* scan_chunk) { + memset(scan_chunk, 0, sizeof(ScanChunk)); + return get_next_chunk(partition_id_scan_chunks, ScanChunk_fields, scan_chunk, &scan_chunks_found_timestamp); +} + + + +ret_code_t storer_store_microphone_chunk(MicrophoneChunk* microphone_chunk) { + return store_chunk(partition_id_microphone_chunks, MicrophoneChunk_fields, microphone_chunk); +} + +ret_code_t storer_find_microphone_chunk_from_timestamp(Timestamp timestamp, MicrophoneChunk* microphone_chunk) { + memset(microphone_chunk, 0, sizeof(MicrophoneChunk)); + return find_chunk_from_timestamp(timestamp, partition_id_microphone_chunks, MicrophoneChunk_fields, microphone_chunk, &(microphone_chunk->timestamp), µphone_chunks_found_timestamp); +} + +ret_code_t storer_get_next_microphone_chunk(MicrophoneChunk* microphone_chunk) { + memset(microphone_chunk, 0, sizeof(MicrophoneChunk)); + return get_next_chunk(partition_id_microphone_chunks, MicrophoneChunk_fields, microphone_chunk, µphone_chunks_found_timestamp); +} diff --git a/firmware/nRF_badge/data_collector/incl/storer_lib.h b/firmware/nRF_badge/data_collector/incl/storer_lib.h new file mode 100644 index 0000000..ea70c34 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/storer_lib.h @@ -0,0 +1,212 @@ +#ifndef __STORER_LIB_H +#define __STORER_LIB_H + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes +#include "chunk_messages.h" + +/**< The number of entries in each partition (can be adopted on the user's needs) */ +#define STORER_BADGE_ASSIGNEMENT_NUMBER 1 +#define STORER_BATTERY_DATA_NUMBER 100 +#define STORER_MICROPHONE_DATA_NUMBER 1340 +#define STORER_SCAN_DATA_NUMBER 960 +#define STORER_ACCELEROMETER_INTERRUPT_DATA_NUMBER 50 +#define STORER_ACCELEROMETER_DATA_NUMBER 50 + + +/**@brief Function to initialize the storer-module. + * @details It initializes the filesystem and registers all the needed partitions. + * You can change the above values for the number of chunks stored in the filesystem. + * If debug is enabled it should print out the number of available bytes after registration of all partitions. + * + * @retval NRF_SUCCESS If everything was fine. + * @retval Otherwise an error code is returned. + * + * @note All the storing/reading stuff must be done in main-context, because they share same buffer for serialization. + * And the storage-modules allow only to be used in main-context and should not be interrupted. + */ +ret_code_t storer_init(void); + + +/**@brief Function to clear all partitions. + * @details It does not erase all the memory but uses the filesystem_clear_partition-function to clear only the headers of the partitions. + * + * @retval NRF_SUCCESS If everything was fine. + * @retval NRF_ERROR_INTERNAL Busy. + */ +ret_code_t storer_clear(void); + + +/**@brief Function to clear the badge-assignement in the filesystem. + * + * @retval NRF_SUCCESS If everything was fine. + * @retval NRF_ERROR_INTERNAL Busy. + */ +ret_code_t storer_clear_badge_assignement(void); + +/**@brief Function to store a badge-assignement in the filesystem. + * + * @retval NRF_ERROR_INTERNAL Busy. + * @retval NRF_ERROR_INVALID_DATA If encoding fails. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_store_badge_assignement(BadgeAssignement* badge_assignement); + +/**@brief Function to read the last stored badge-assignement. + * + * @retval NRF_ERROR_INTERNAL Busy. + * @retval NRF_ERROR_INVALID_STATE Iterator invalidated/no data found. + * @retval NRF_ERROR_INVALID_DATA If the CRC does not match. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_read_badge_assignement(BadgeAssignement* badge_assignement); + + +/**@brief Function to invalidate all iterators of the chunk-partitions. + * @note This function has to be called when the application can't + * call the get_next_..._chunk()-function anymore (e.g. because of disconnect), + * to invalidate all the iterators, so that new chunks can overwrite the chunks, + * the iterator is currently pointing to. + */ +void storer_invalidate_iterators(void); + + + + + +/**@brief Function to store an accelerometer chunk in the accelerometer-partition. + * @retval NRF_ERROR_NO_MEM If the element is too big, to be stored in the partition. + * @retval NRF_ERROR_INTERNAL Busy or iterator is pointing to the same address we want to write to. + * @retval NRF_ERROR_INVALID_DATA If encoding fails. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_store_accelerometer_chunk(AccelerometerChunk* accelerometer_chunk); + +/**@brief Function to find an accelerometer chunk from timestamp and set the iterator of the partition. (For detailed description: find_chunk_from_timestamp()) + * @retval NRF_ERROR_INTERNAL Busy. + * @retval NRF_ERROR_INVALID_STATE Iterator invalidated/no data found. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_find_accelerometer_chunk_from_timestamp(Timestamp timestamp, AccelerometerChunk* accelerometer_chunk); + +/**@brief Function to get the next accelerometer chunk from the iterator of the partition. (For detailed description: get_next_chunk()) + * @retval NRF_SUCCESS If an element was found and returned successfully. + * @retval NRF_ERROR_NOT_FOUND If no more element in the partition. + * @retval NRF_ERROR_INVALID_STATE If iterator not initialized or invalidated. + * @retval NRF_ERROR_INTERNAL If busy. + */ +ret_code_t storer_get_next_accelerometer_chunk(AccelerometerChunk* accelerometer_chunk); + + + + + + +/**@brief Function to store an accelerometer-interrupt chunk in the accelerometer-interrupt-partition. + * @retval NRF_ERROR_NO_MEM If the element is too big, to be stored in the partition. + * @retval NRF_ERROR_INTERNAL Busy or iterator is pointing to the same address we want to write to. + * @retval NRF_ERROR_INVALID_DATA If encoding fails. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_store_accelerometer_interrupt_chunk(AccelerometerInterruptChunk* accelerometer_interrupt_chunk); + +/**@brief Function to find an accelerometer-interrupt chunk from timestamp and set the iterator of the partition. (For detailed description: find_chunk_from_timestamp()) + * @retval NRF_ERROR_INTERNAL Busy. + * @retval NRF_ERROR_INVALID_STATE Iterator invalidated/no data found. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_find_accelerometer_interrupt_chunk_from_timestamp(Timestamp timestamp, AccelerometerInterruptChunk* accelerometer_interrupt_chunk); + +/**@brief Function to get the next accelerometer-interrupt chunk from the iterator of the partition. (For detailed description: get_next_chunk()) + * @retval NRF_SUCCESS If an element was found and returned successfully. + * @retval NRF_ERROR_NOT_FOUND If no more element in the partition. + * @retval NRF_ERROR_INVALID_STATE If iterator not initialized or invalidated. + * @retval NRF_ERROR_INTERNAL If busy. + */ +ret_code_t storer_get_next_accelerometer_interrupt_chunk(AccelerometerInterruptChunk* accelerometer_interrupt_chunk); + + + + + + +/**@brief Function to store a battery chunk in the battery-partition. + * @retval NRF_ERROR_NO_MEM If the element is too big, to be stored in the partition. + * @retval NRF_ERROR_INTERNAL Busy or iterator is pointing to the same address we want to write to. + * @retval NRF_ERROR_INVALID_DATA If encoding fails. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_store_battery_chunk(BatteryChunk* battery_chunk); + +/**@brief Function to find a battery chunk from timestamp and set the iterator of the partition. (For detailed description: find_chunk_from_timestamp()) + * @retval NRF_ERROR_INTERNAL Busy. + * @retval NRF_ERROR_INVALID_STATE Iterator invalidated/no data found. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_find_battery_chunk_from_timestamp(Timestamp timestamp, BatteryChunk* battery_chunk); + +/**@brief Function to get the next battery chunk from the iterator of the partition. (For detailed description: get_next_chunk()) + * @retval NRF_SUCCESS If an element was found and returned successfully. + * @retval NRF_ERROR_NOT_FOUND If no more element in the partition. + * @retval NRF_ERROR_INVALID_STATE If iterator not initialized or invalidated. + * @retval NRF_ERROR_INTERNAL If busy. + */ +ret_code_t storer_get_next_battery_chunk(BatteryChunk* battery_chunk); + + + + + + +/**@brief Function to store a scan chunk in the scan-partition. + * @retval NRF_ERROR_NO_MEM If the element is too big, to be stored in the partition. + * @retval NRF_ERROR_INTERNAL Busy or iterator is pointing to the same address we want to write to. + * @retval NRF_ERROR_INVALID_DATA If encoding fails. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_store_scan_chunk(ScanChunk* scan_chunk); + +/**@brief Function to find a scan chunk from timestamp and set the iterator of the partition. (For detailed description: find_chunk_from_timestamp()) + * @retval NRF_ERROR_INTERNAL Busy. + * @retval NRF_ERROR_INVALID_STATE Iterator invalidated/no data found. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_find_scan_chunk_from_timestamp(Timestamp timestamp, ScanChunk* scan_chunk); + +/**@brief Function to get the next scan chunk from the iterator of the partition. (For detailed description: get_next_chunk()) + * @retval NRF_SUCCESS If an element was found and returned successfully. + * @retval NRF_ERROR_NOT_FOUND If no more element in the partition. + * @retval NRF_ERROR_INVALID_STATE If iterator not initialized or invalidated. + * @retval NRF_ERROR_INTERNAL If busy. + */ +ret_code_t storer_get_next_scan_chunk(ScanChunk* scan_chunk); + + + + + + +/**@brief Function to store a microphone chunk in the microphone-partition. + * @retval NRF_ERROR_NO_MEM If the element is too big, to be stored in the partition. + * @retval NRF_ERROR_INTERNAL Busy or iterator is pointing to the same address we want to write to. + * @retval NRF_ERROR_INVALID_DATA If encoding fails. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_store_microphone_chunk(MicrophoneChunk* microphone_chunk); + +/**@brief Function to find a microphone chunk from timestamp and set the iterator of the partition. (For detailed description: find_chunk_from_timestamp()) + * @retval NRF_ERROR_INTERNAL Busy. + * @retval NRF_ERROR_INVALID_STATE Iterator invalidated/no data found. + * @retval NRF_SUCCESS If everything was fine. + */ +ret_code_t storer_find_microphone_chunk_from_timestamp(Timestamp timestamp, MicrophoneChunk* microphone_chunk); + +/**@brief Function to get the next microphone chunk from the iterator of the partition. (For detailed description: get_next_chunk()) + * @retval NRF_SUCCESS If an element was found and returned successfully. + * @retval NRF_ERROR_NOT_FOUND If no more element in the partition. + * @retval NRF_ERROR_INVALID_STATE If iterator not initialized or invalidated. + * @retval NRF_ERROR_INTERNAL If busy. + */ +ret_code_t storer_get_next_microphone_chunk(MicrophoneChunk* microphone_chunk); + +#endif + diff --git a/firmware/nRF_badge/data_collector/incl/stream_messages.c b/firmware/nRF_badge/data_collector/incl/stream_messages.c new file mode 100644 index 0000000..33e80a2 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/stream_messages.c @@ -0,0 +1,28 @@ +#include "tinybuf.h" +#include "stream_messages.h" + +const tb_field_t BatteryStream_fields[2] = { + {513, tb_offsetof(BatteryStream, battery_data), 0, 0, tb_membersize(BatteryStream, battery_data), 0, 0, 0, &BatteryData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t MicrophoneStream_fields[2] = { + {513, tb_offsetof(MicrophoneStream, microphone_data), 0, 0, tb_membersize(MicrophoneStream, microphone_data), 0, 0, 0, &MicrophoneData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t ScanStream_fields[2] = { + {513, tb_offsetof(ScanStream, scan_device), 0, 0, tb_membersize(ScanStream, scan_device), 0, 0, 0, &ScanDevice_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerStream_fields[2] = { + {513, tb_offsetof(AccelerometerStream, accelerometer_raw_data), 0, 0, tb_membersize(AccelerometerStream, accelerometer_raw_data), 0, 0, 0, &AccelerometerRawData_fields}, + TB_LAST_FIELD, +}; + +const tb_field_t AccelerometerInterruptStream_fields[2] = { + {513, tb_offsetof(AccelerometerInterruptStream, timestamp), 0, 0, tb_membersize(AccelerometerInterruptStream, timestamp), 0, 0, 0, &Timestamp_fields}, + TB_LAST_FIELD, +}; + diff --git a/firmware/nRF_badge/data_collector/incl/stream_messages.h b/firmware/nRF_badge/data_collector/incl/stream_messages.h new file mode 100644 index 0000000..92d3ce1 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/stream_messages.h @@ -0,0 +1,41 @@ +#ifndef __STREAM_MESSAGES_H +#define __STREAM_MESSAGES_H + +#include +#include "tinybuf.h" +#include "common_messages.h" + +#define STREAM_BATTERY_FIFO_SIZE 10 +#define STREAM_MICROPHONE_FIFO_SIZE 100 +#define STREAM_SCAN_FIFO_SIZE 100 +#define STREAM_ACCELEROMETER_FIFO_SIZE 100 +#define STREAM_ACCELEROMETER_INTERRUPT_FIFO_SIZE 10 + + +typedef struct { + BatteryData battery_data; +} BatteryStream; + +typedef struct { + MicrophoneData microphone_data; +} MicrophoneStream; + +typedef struct { + ScanDevice scan_device; +} ScanStream; + +typedef struct { + AccelerometerRawData accelerometer_raw_data; +} AccelerometerStream; + +typedef struct { + Timestamp timestamp; +} AccelerometerInterruptStream; + +extern const tb_field_t BatteryStream_fields[2]; +extern const tb_field_t MicrophoneStream_fields[2]; +extern const tb_field_t ScanStream_fields[2]; +extern const tb_field_t AccelerometerStream_fields[2]; +extern const tb_field_t AccelerometerInterruptStream_fields[2]; + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/stream_messages.tb b/firmware/nRF_badge/data_collector/incl/stream_messages.tb new file mode 100644 index 0000000..62cf571 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/stream_messages.tb @@ -0,0 +1,37 @@ +import common_messages + +extern Timestamp; +extern BatteryData; +extern MicrophoneData; +extern ScanDevice; +extern AccelerometerRawData; + + +define { + STREAM_BATTERY_FIFO_SIZE = 10; + STREAM_MICROPHONE_FIFO_SIZE = 100; + STREAM_SCAN_FIFO_SIZE = 100; + STREAM_ACCELEROMETER_FIFO_SIZE = 100; + STREAM_ACCELEROMETER_INTERRUPT_FIFO_SIZE = 10; +} + +message BatteryStream { + required BatteryData battery_data; +} + +message MicrophoneStream { + required MicrophoneData microphone_data; +} + +message ScanStream { + required ScanDevice scan_device; +} + +message AccelerometerStream { + required AccelerometerRawData accelerometer_raw_data; +} + +message AccelerometerInterruptStream { + required Timestamp timestamp; +} + diff --git a/firmware/nRF_badge/data_collector/incl/system_event_lib.c b/firmware/nRF_badge/data_collector/incl/system_event_lib.c new file mode 100644 index 0000000..01da2e9 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/system_event_lib.c @@ -0,0 +1,53 @@ +#include "system_event_lib.h" + +#include "softdevice_handler.h" + + +static volatile system_event_handler_t event_handlers[MAX_NUMBER_SYSTEM_EVENT_HANDLER]; /**< Array to save the application system event handlers that are called when a system event is generated. */ +static volatile uint32_t number_system_event_handler = 0; /**< Number of registered application system event handlers. */ + + + +/**@brief Internal system event callback for calling the registered application system event handlers. + * + * @param[in] sys_evt The generated system_event that should be transfered to the application system event handlers. + */ +static void system_event_dispatch(uint32_t sys_evt) +{ + // Call all the registered event handlers + for(uint32_t i = 0; i < number_system_event_handler; i++){ + event_handlers[i](sys_evt); + } +} + + + + +void system_event_init(void) { + // Only initialize the module once! + static uint8_t init_done = 0; + if(init_done == 0) { + // Register with the SoftDevice handler module for system events. + uint32_t err_code = softdevice_sys_evt_handler_set(system_event_dispatch); + + (void)(err_code); + //APP_ERROR_CHECK(err_code); + + number_system_event_handler = 0; + init_done = 1; + } +} + + + + +ret_code_t system_event_register_handler(system_event_handler_t event_handler) { + if(number_system_event_handler >= MAX_NUMBER_SYSTEM_EVENT_HANDLER) { + return NRF_ERROR_NO_MEM; + } + + event_handlers[number_system_event_handler] = event_handler; + number_system_event_handler++; + + return NRF_SUCCESS; +} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/system_event_lib.h b/firmware/nRF_badge/data_collector/incl/system_event_lib.h new file mode 100644 index 0000000..53a5db4 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/system_event_lib.h @@ -0,0 +1,59 @@ +#ifndef __SYSTEM_EVENT_LIB_H +#define __SYSTEM_EVENT_LIB_H + + +/** @file + * + * @brief System event abstraction library. + * + * @details This module enables the application to register for system-events that are called when a softdevice system event occured. + * + */ + + +#include "sdk_common.h" // Needed for the definition of ret_code_t and the error-codes + +#define MAX_NUMBER_SYSTEM_EVENT_HANDLER 4 /**< Max number of system events that could be registered */ + + +/** + * @brief System event handler type. + */ +typedef void (*system_event_handler_t)(uint32_t sys_evt); + + +/**@example Initializing the Softdevice + * + * nrf_clock_lf_cfg_t clock_lf_cfg = {.source = NRF_CLOCK_LF_SRC_XTAL, + * .rc_ctiv = 0, + * .rc_temp_ctiv = 0, + * .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM}; + * + * + * SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL); + */ + + +/**@brief Function for initializing the system event module. + * + * @details This function should only called once. But there is an internal mechanism + * that prevents from multiple initialization. + * + * @note The Softdevice has to be initialized to use this module. + * + */ +void system_event_init(void); + + +/**@brief Function to register an application system event handler. + * + * @retval NRF_SUCCESS If the registration was succesful. + * @retval NRF_ERROR_NO_MEM If the registration failed because there are too many registered system_event_handlers. + */ +ret_code_t system_event_register_handler(system_event_handler_t event_handler); + + + + + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/systick_lib.c b/firmware/nRF_badge/data_collector/incl/systick_lib.c new file mode 100644 index 0000000..58c6bda --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/systick_lib.c @@ -0,0 +1,222 @@ +#include "systick_lib.h" + +#include "app_timer.h" +#include "app_util_platform.h" // Needed for the definitions of CRITICAL_REGION_EXIT/-ENTER + +#include "debug_lib.h" + + + +#define SYSTICK_TIMER_CALLBACK_PERIOD_MS (100*1000) + +APP_TIMER_DEF(systick_timer); + +static volatile uint64_t ticks_since_start = 0; /**< Ticks of the 32768Hz oscillator since start/init()-call. */ +static volatile uint32_t former_ticks = 0; /**< The number of real RTC-ticks of the former systick-callback. */ + +static volatile uint8_t millis_synced = 0; /**< Flag if the systick is synced with an external time-source. */ +static volatile uint64_t millis_offset = 0; /**< The millis offset at ticks_at_offset setted externally. It is the y-axis offset of the straightline equation. */ +static volatile uint64_t ticks_at_offset = 0; /**< The ticks at the millis_offset. It is the x-value for the y-axis offset of the straightline equation. */ +static volatile float millis_per_ticks = (1000.0f / ((0 + 1) * APP_TIMER_CLOCK_FREQ)); /**< Variable that represents the millis per ticks. It is the slope of the straigtline equation.*/ +static float millis_per_ticks_default = (1000.0f / ((0 + 1) * APP_TIMER_CLOCK_FREQ)); /**< Variable that represents the default millis per ticks. It is the slope of the straigtline equation (needed for systick_get_continous_millis()).*/ + +// +/- 20ppm oscillator --> +/- 0.655 Hz deviation. But we are conservative --> up to 50Hz deviation possible +// default millis per ticks: 0.03051757812 + +#define CLOCK_FREQ_DEVIATION_HZ 5.0f +#define MIN_MILLIS_PER_TICKS (1000.0f / ((0 + 1.0f) * (APP_TIMER_CLOCK_FREQ + CLOCK_FREQ_DEVIATION_HZ))) +#define MAX_MILLIS_PER_TICKS (1000.0f / ((0 + 1.0f) * (APP_TIMER_CLOCK_FREQ - CLOCK_FREQ_DEVIATION_HZ))) + +static void systick_callback(void* p_context); + +ret_code_t systick_init(uint8_t prescaler) { + static uint8_t init_done = 0; + + if(!init_done) { + ret_code_t ret; + ticks_since_start = 0; + former_ticks = app_timer_cnt_get(); + + millis_synced = 0; + millis_offset = 0; + ticks_at_offset = systick_get_ticks_since_start(); + millis_per_ticks = (1000.0f / ((prescaler + 1) * APP_TIMER_CLOCK_FREQ)); + + millis_per_ticks_default = (1000.0f / ((prescaler + 1) * APP_TIMER_CLOCK_FREQ)); + + // Create the systick_timer + ret = app_timer_create(&systick_timer, APP_TIMER_MODE_REPEATED, systick_callback); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + // Start the systick timer + ret = app_timer_start(systick_timer, APP_TIMER_TICKS(SYSTICK_TIMER_CALLBACK_PERIOD_MS, 0), NULL); + if(ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; + + init_done = 1; + } + + return NRF_SUCCESS; +} + + +/**@brief Callback-handler function that is called when the systick-timer expires. + * + * @details To compensate a possibly deferred execution (> 100secs) of the callback (because + * of another job with higher or equal priority is not finished yet), a schema of + * former_ticks and cur_ticks is used, and the calculation is based on these values + * not on the constant of ~100secs. + * + * @param[in] p_context Pointer to context provided via the timer (should/could be NULL). + */ +static void systick_callback(void* p_context) { + //debug_log("SYSTICK: Systick-callback\n"); + uint32_t diff_ticks; + CRITICAL_REGION_ENTER(); + uint32_t cur_ticks = app_timer_cnt_get(); + + app_timer_cnt_diff_compute(cur_ticks, former_ticks, &diff_ticks); + former_ticks = cur_ticks; + + // Increment the ticks_since_start variable + ticks_since_start += diff_ticks; + + CRITICAL_REGION_EXIT(); +} + + +uint64_t systick_get_ticks_since_start(void) { + uint64_t tmp = 0; + CRITICAL_REGION_ENTER(); + uint32_t diff_ticks; + uint32_t cur_ticks = app_timer_cnt_get(); + app_timer_cnt_diff_compute(cur_ticks, former_ticks, &diff_ticks); + tmp = ticks_since_start + diff_ticks; + CRITICAL_REGION_EXIT(); + return tmp; +} + + +void systick_set_millis(uint64_t ticks_since_start_at_sync, uint64_t millis_sync) { + + //debug_log("SYSTICK: Systick_set_millis: %u, (%u, %u), %f, %u, (%u, %u)\n", (uint32_t) ticks_since_start_at_sync, (uint32_t)(millis_sync/1000), (uint32_t) (millis_sync%1000), millis_per_ticks, (uint32_t) ticks_at_offset, (uint32_t)(millis_offset/1000), (uint32_t) (millis_offset%1000)); + + uint64_t cur_ticks_since_start = systick_get_ticks_since_start(); + if(ticks_since_start_at_sync > cur_ticks_since_start) // Only a safety query (the ticks_since_start_at_sync has to be <= the current ticks since start) + ticks_since_start_at_sync = cur_ticks_since_start; + + if(ticks_since_start_at_sync < ticks_at_offset) { // Only a safety query (the ticks_since_start_at_sync has to be >= ticks_at_offset) + ticks_since_start_at_sync = ticks_at_offset; + } + + if(!millis_synced) { // If the function is called the first time, the millis_offset and ticks_at_offset is updated immediately. + millis_offset = millis_sync; + ticks_at_offset = ticks_since_start_at_sync; + millis_synced = 1; + return; + } + + + debug_log("SYSTICK: millis_sync: %u%03u; ticks_at_sync: %u\n", (uint32_t)(millis_sync/1000), (uint32_t) (millis_sync%1000), (uint32_t) ticks_since_start_at_sync); + + + int32_t error_millis = 0; + CRITICAL_REGION_ENTER(); + error_millis = (int32_t)(((int64_t)millis_sync) - ((int64_t)(((int64_t)(millis_per_ticks * (ticks_since_start_at_sync - ticks_at_offset))) + millis_offset))); + CRITICAL_REGION_EXIT(); + (void) error_millis; + debug_log("SYSTICK: error_millis: %d\n", error_millis); + + /* + // Easiest way: Just set it + CRITICAL_REGION_ENTER(); + millis_offset = millis_sync; + ticks_at_offset = ticks_since_start_at_sync; + CRITICAL_REGION_EXIT(); + */ + + + // More complicate way: Adapt the slope (millis_per_ticks) via an moving average filter + + float new_millis_per_ticks = 0; + uint64_t delta_ticks = 0; + uint64_t delta_millis = 0; + float alpha = 0.1; + CRITICAL_REGION_ENTER(); + delta_ticks = ((ticks_since_start_at_sync > ticks_at_offset) ? (ticks_since_start_at_sync - ticks_at_offset) : 0); + delta_millis = ((millis_sync > millis_offset) ? (millis_sync - millis_offset) : 0); + new_millis_per_ticks = 0; + if(delta_ticks == 0) { + new_millis_per_ticks = MAX_MILLIS_PER_TICKS; + } else if(delta_millis == 0) { + new_millis_per_ticks = MIN_MILLIS_PER_TICKS; + } else { // Calculate + new_millis_per_ticks = ((float)delta_millis)/((float)delta_ticks); + //TODO: remove + //want_new_millis_per_ticks = new_millis_per_ticks; + new_millis_per_ticks = (new_millis_per_ticks < MIN_MILLIS_PER_TICKS) ? MIN_MILLIS_PER_TICKS : ((new_millis_per_ticks > MAX_MILLIS_PER_TICKS) ? MAX_MILLIS_PER_TICKS : new_millis_per_ticks); + } + + // Now average the new_millis_per_ticks with the global millis_per_ticks via an moving average filter: + millis_per_ticks = new_millis_per_ticks*alpha + millis_per_ticks*(1-alpha); + + // Calculate the new millis_offset via the new millis_per_ticks + millis_offset = millis_sync; + ticks_at_offset = ticks_since_start_at_sync; + CRITICAL_REGION_EXIT(); + + debug_log("SYSTICK: updated millis_per_ticks: %f\n", millis_per_ticks); + + + // TODO: Or an even more complex way: Linear regression of N samples! + /* + */ + + return; +} + +void systick_set_timestamp(uint64_t ticks_since_start_at_sync, uint32_t seconds_sync, uint16_t milliseconds_sync) { + uint64_t millis_sync = ((uint64_t)seconds_sync) * 1000 + ((uint64_t) milliseconds_sync); + systick_set_millis(ticks_since_start_at_sync, millis_sync); +} + +uint64_t systick_get_millis(void) { + + uint64_t cur_ticks_since_start = systick_get_ticks_since_start(); + uint64_t millis = 0; + + CRITICAL_REGION_ENTER(); + millis = ((uint64_t)(millis_per_ticks*(cur_ticks_since_start - ticks_at_offset))) + millis_offset; + CRITICAL_REGION_EXIT(); + + return millis; +} + +uint64_t systick_get_continuous_millis(void) { + uint64_t cur_ticks_since_start = systick_get_ticks_since_start(); + uint64_t millis = 0; + + CRITICAL_REGION_ENTER(); + millis = ((uint64_t)(millis_per_ticks_default*(cur_ticks_since_start - 0))) + 0; + CRITICAL_REGION_EXIT(); + + return millis; + +} + +void systick_delay_millis(uint64_t millis) { + uint64_t end_millis = systick_get_continuous_millis() + millis; + while(systick_get_continuous_millis() < end_millis); +} + + +uint8_t systick_is_synced(void) { + return millis_synced; +} + +void systick_get_timestamp(uint32_t* seconds, uint16_t* milliseconds) { + uint64_t millis = systick_get_millis(); + *seconds = (uint32_t) (millis/1000); + *milliseconds = (uint16_t) (millis % 1000); + //debug_log("SYSTICK: Systick_get_timestamp: (%u, %u)\n", (uint32_t)(*seconds), (uint32_t) (*milliseconds)); + +} diff --git a/firmware/nRF_badge/data_collector/incl/systick_lib.h b/firmware/nRF_badge/data_collector/incl/systick_lib.h new file mode 100644 index 0000000..15c12f3 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/systick_lib.h @@ -0,0 +1,110 @@ +/**@file + * @details This module provides high accuracy timing for the application. + * To synchronize the internal timestamps/millis the set-functions could be used. + * Internally the timing works with an app_timer at a low frequency (~100secs). + * When this timer expires an internal ticks_since_start-variable is incremented. + * The actual computation of the current milliseconds/timestamp is based on a + * straightline equation: + * millis = millis_per_ticks * (cur_ticks - ticks_at_offset) + millis_offset. + * The parameters millis_per_ticks, ticks_at_offset and millis_offset are set initial in the init-function + * and are updated each time a synchronize set-function is called. + */ + +#ifndef __SYSTICK_LIB_H +#define __SYSTICK_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +/**@brief Function for initializing the systick-module. + * + * @details The function creates the app-timer and starts it at a period of ~100 secs. + * + * @note The app_timer-module has to be initalized before, e.g. via APP_TIMER_INIT(...). + * + * @param[in] prescaler The prescaler of the app-timer module. + * + * @retval NRF_SUCCESS If the systick-module initialization was successful. + * @retval NRF_ERROR_INTERNAL If there was an error while creating and/or starting the app-timer. + */ +ret_code_t systick_init(uint8_t prescaler); + + +/**@brief Function for retrieving the ticks since start. + * + * @details Theses ticks are an reflection of the 32768Hz-oscillator with the difference that + * these ticks won't overflow at 0x00FFFFFF like the real oscillator ticks do. + * + * @retval The ticks since start of the module (since the call of the init()-function). + */ +uint64_t systick_get_ticks_since_start(void); + +/**@brief Function for retrieving the current millis. + * + * @details If the systick-module is unsynced (because no external timestamp was processed yet) + * the millis_offset variable of the straightline equation is 0 and ticks_at_offset + * is the number of ticks at the init()-function call. + * Otherwise (if there was an external sync) the millis_offset and ticks_at_offset + * are calculated (and set) in the systick_set_millis()-function. + * + * @retval The current milliseconds of the system (synced or unsynced with an external time-source). + */ +uint64_t systick_get_millis(void); + +/**@brief Function for retrieving contoninuous milliseconds. + * + * @details This timebasis is not and could not be synchronized with an external time-source. + * Therefore, the milliseconds are continious incremented without any jumps. + * This is useful if the application wants to do timeout based stuff with time-deltas. + * + * @retval The current continous milliseconds of the system (unsynced with an external time-source). Starts at 0. + */ +uint64_t systick_get_continuous_millis(void); + + +/**@brief Function for retrieving the current timestamp (seconds, milliseconds): E.g. 50 seconds and 400 milliseconds. + * + * @details Internally the function just calls the get_millis()-function to calculate the seconds and milliseconds. + * + * @param[out] seconds Pointer to a variable where to store the current seconds of the system (synced or unsynced with an external time-source). + * @param[out] seconds Pointer to a variable where to store the current not finished milliseconds of a seconds (synced or unsynced with an external time-source). + */ +void systick_get_timestamp(uint32_t* seconds, uint16_t* milliseconds); + +/**@brief Function for synchronizing the systick-module with an external time-source via milliseconds. + * + * @details The function needs the ticks since start at the sync timepoint (e.g. the timepoint a BLE-packet was actually received) + * This function adapts the millis_per_ticks parameters of the straightline equation with the use of an exponential moving average filter. + * This method has the advantage of easy implementation and a stability against jitter in the BLE-stack or sth like that. + * + * @param[in] ticks_since_start_at_sync The ticks since start (retrieved via systick_get_ticks_since_start()) at the sync-timepoint. + * @param[in] millis_sync The milliseconds of the external time-source received at ticks_since_start_at_sync. + */ +void systick_set_millis(uint64_t ticks_since_start_at_sync, uint64_t millis_sync); + +/**@brief Function for synchronizing the systick-module with an external time-source via an timestamp. + * + * @details Internally the function just calls the systick_set_millis()-function. + * + * @param[in] ticks_since_start_at_sync The ticks since start (retrieved via systick_get_ticks_since_start()) at the sync-timepoint. + * @param[in] seconds_sync The seconds of the external time-source received at ticks_since_start_at_sync. + * @param[in] milliseconds_sync The milliseconds of the external time-source received at ticks_since_start_at_sync. + */ +void systick_set_timestamp(uint64_t ticks_since_start_at_sync, uint32_t seconds_sync, uint16_t milliseconds_sync); + +/**@brief Function to check, whether the millis/timestamps of this module are synced with an external time-source or not. + * + * @retval 1 If synced with external time-source. + * @retval 0 If not synced with external time-source. + */ +uint8_t systick_is_synced(void); + +/**@brief Function for actively waiting for a specific amount of milliseconds. + * + * @param[in] millis The number of milliseconds to actively delay the system. + */ +void systick_delay_millis(uint64_t millis); + + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/timeout_lib.c b/firmware/nRF_badge/data_collector/incl/timeout_lib.c new file mode 100644 index 0000000..2225128 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/timeout_lib.c @@ -0,0 +1,183 @@ +#include "timeout_lib.h" +#include "app_timer.h" +#include "systick_lib.h" +#include "stdio.h" +#include "stdlib.h" // Needed for NULL definition +#include "app_util_platform.h" + +#define MAX_MILLIS_PER_TIMEOUT_TIMER (100*1000) + +typedef struct { + volatile uint32_t remaining_ms; + volatile uint32_t timeout_ms; + volatile uint8_t active; + timeout_handler_t timeout_handler; +} timeout_t; + + +static timeout_t registered_timeouts[MAX_NUMBER_OF_TIMEOUTS]; +static uint32_t number_of_registered_timeouts = 0; + +APP_TIMER_DEF(timeout_timer); +static void timeout_timer_callback(void* p_context); +volatile uint8_t timeout_timer_running = 0; +static volatile uint64_t timeout_timer_start_ms = 0; + +// App-timer has to be initialized before +// Systick has to be initialized before +ret_code_t timeout_init(void) { + number_of_registered_timeouts = 0; + timeout_timer_running = 0; + + ret_code_t ret = app_timer_create(&timeout_timer, APP_TIMER_MODE_SINGLE_SHOT, timeout_timer_callback); + + return ret; +} + +ret_code_t timeout_register(uint32_t* timeout_id, timeout_handler_t timeout_handler) { + if(number_of_registered_timeouts >= MAX_NUMBER_OF_TIMEOUTS) + return NRF_ERROR_NO_MEM; + + *timeout_id = number_of_registered_timeouts; + + timeout_t timeout; + timeout.timeout_handler = timeout_handler; + timeout.active = 0; + timeout.remaining_ms = 0; + timeout.timeout_ms = 0; + + registered_timeouts[*timeout_id] = timeout; + + number_of_registered_timeouts++; + return NRF_SUCCESS; +} + +/**@brief Function to retrieve the minimal remaining milliseconds of all timeouts. + * + * @retval UINT32_MAX If no active timeout was found. + * @retval Otherwise the minimal remaining milliseconds. + */ +static uint32_t get_minimal_remaining_ms(void) { + uint32_t minimal_remaining_ms = UINT32_MAX; + for(uint32_t i = 0; i < number_of_registered_timeouts; i++) { + if(registered_timeouts[i].active) { + if(registered_timeouts[i].remaining_ms < minimal_remaining_ms) { + minimal_remaining_ms = registered_timeouts[i].remaining_ms; + } + } + } + + if(minimal_remaining_ms == UINT32_MAX) + return UINT32_MAX; + + minimal_remaining_ms = (minimal_remaining_ms > MAX_MILLIS_PER_TIMEOUT_TIMER) ? MAX_MILLIS_PER_TIMEOUT_TIMER : minimal_remaining_ms; + // Because APP-Timer needs at least 5 Ticks... + minimal_remaining_ms = (minimal_remaining_ms < 2) ? 2 : minimal_remaining_ms; + + return minimal_remaining_ms; +} + +/**@brief Function that adapts the remaining milliseconds of all timeouts and calls the handler if necessary. + * + * @retval UINT32_MAX If no active timeout was found. + * @retval Otherwise the minimal remaining milliseconds. + */ +static void adapt_remaining_ms(void) { + + uint32_t delta_ms = (uint32_t) (systick_get_continuous_millis() - timeout_timer_start_ms); + for(uint32_t i = 0; i < number_of_registered_timeouts; i++) { + if(registered_timeouts[i].active) { + if(registered_timeouts[i].remaining_ms < delta_ms) { + // Call the handler + if(registered_timeouts[i].timeout_handler != NULL) + registered_timeouts[i].timeout_handler(); + + registered_timeouts[i].active = 0; + } else { + // Adapt remaining-ms + registered_timeouts[i].remaining_ms -= delta_ms; + } + } + } + +} + +/**@brief Handler that is called by the timeout timer. + * + * @param[in] p_context Pointer to context provided by the timer. + */ +static void timeout_timer_callback(void* p_context) { + timeout_timer_running = 0; + + adapt_remaining_ms(); + + uint32_t minimal_remaining_ms = get_minimal_remaining_ms(); + + // If we have found another active timeout, we activate the timer again + if(minimal_remaining_ms < UINT32_MAX) { + timeout_timer_running = 1; + timeout_timer_start_ms = systick_get_continuous_millis(); + app_timer_start(timeout_timer, APP_TIMER_TICKS(minimal_remaining_ms, 0), NULL); + } +} + +ret_code_t timeout_start(uint32_t timeout_id, uint32_t timeout_ms) { + ret_code_t ret = NRF_SUCCESS; + + if(timeout_id >= number_of_registered_timeouts) + return NRF_ERROR_INVALID_PARAM; + + if(timeout_ms == 0) { + registered_timeouts[timeout_id].active = 0; + return NRF_SUCCESS; + } + + if(timeout_timer_running) { + app_timer_stop(timeout_timer); + adapt_remaining_ms(); + timeout_timer_running = 0; + } + + + registered_timeouts[timeout_id].timeout_ms = timeout_ms; + registered_timeouts[timeout_id].remaining_ms = timeout_ms; + registered_timeouts[timeout_id].active = 1; + + + uint32_t minimal_remaining_ms = get_minimal_remaining_ms(); + + if(minimal_remaining_ms < UINT32_MAX) { + timeout_timer_running = 1; + timeout_timer_start_ms = systick_get_continuous_millis(); + ret = app_timer_start(timeout_timer, APP_TIMER_TICKS(minimal_remaining_ms, 0), NULL); + } + + return ret; +} + +void timeout_stop(uint32_t timeout_id) { + if(timeout_id >= number_of_registered_timeouts) + return; + + registered_timeouts[timeout_id].active = 0; + uint8_t found_active = 0; + for(uint32_t i = 0; i < number_of_registered_timeouts; i++) { + if(registered_timeouts[i].active) { + found_active = 1; + break; + } + } + // If there are no active timeouts anymore --> stop the timer + if(!found_active) { + timeout_timer_running = 0; + app_timer_stop(timeout_timer); + } +} + +void timeout_reset(uint32_t timeout_id) { + if(registered_timeouts[timeout_id].active) + timeout_start(timeout_id, registered_timeouts[timeout_id].timeout_ms); +} + + + diff --git a/firmware/nRF_badge/data_collector/incl/timeout_lib.h b/firmware/nRF_badge/data_collector/incl/timeout_lib.h new file mode 100644 index 0000000..ded6ec4 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/timeout_lib.h @@ -0,0 +1,71 @@ +/**@file + * This module provides a timeout-handling mechanism. + * The application can register up to MAX_NUMBER_OF_TIMEOUTS timeouts. + * After starting the timeout (timeout_start) the application needs + * to reset the timeout. If it is not resetet in timeout_ms, + * the timeout-handler is called. + * + * Internally, the module uses one app-timer to create alarms at certain intervals, + * to check for timeouts. + */ + +#ifndef __TIMEOUT_LIB_H +#define __TIMEOUT_LIB_H + +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + + +#define MAX_NUMBER_OF_TIMEOUTS 20 /**< Maximal number of registerable timeouts. */ + +/**@brief Timeout handler type. */ +typedef void (*timeout_handler_t)(void); + +/**@brief Function to initialize the timeout-module. + * + * @retval NRF_SUCCESS If the module was initialized successfully. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or + * the timer is running. + * + * @note app_timer_init() has to be called before. + * @note systick_init() has to be called before. + */ +ret_code_t timeout_init(void); + +/**@brief Function to register a timeout. + * + * @param[out] timeout_id Pointer to timeout_id that is set by this function. + * @param[in] timeout_handler The timeout handler that is called when a timeout occurs. + * + * @retval NRF_SUCCESS If the module was initialized successfully. + * @retval NRF_ERROR_NO_MEM If already MAX_NUMBER_OF_TIMEOUTS were registered. + */ +ret_code_t timeout_register(uint32_t* timeout_id, timeout_handler_t timeout_handler); + +/**@brief Function to start a timeout. + * + * @param[in] timeout_id The timeout_id that should be started. + * @param[in] timeout_ms The time in milliseconds after the timeout should occur. + * Could also be 0, then no timeout will be started. + * + * @retval NRF_SUCCESS If the module was initialized successfully. + * @retval NRF_ERROR_INVALID_PARAM If timeout_id was not registered. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer + * has not been created. + * @retval NRF_ERROR_NO_MEM If the timer operations queue was full. + */ +ret_code_t timeout_start(uint32_t timeout_id, uint32_t timeout_ms); + +/**@brief Function to stop a timeout. + * + * @param[in] timeout_id The timeout_id that should be stopped. + */ +void timeout_stop(uint32_t timeout_id); + +/**@brief Function to reset a timeout. + * + * @param[in] timeout_id The timeout_id that should be resetted. + */ +void timeout_reset(uint32_t timeout_id); + +#endif \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/incl/uart_commands.c b/firmware/nRF_badge/data_collector/incl/uart_commands.c deleted file mode 100644 index 8e39e7e..0000000 --- a/firmware/nRF_badge/data_collector/incl/uart_commands.c +++ /dev/null @@ -1,60 +0,0 @@ -// -// Created by Andrew Bartow on 11/3/16. -// - -#include -#include - -#include "uart_commands.h" -#include "debug_log.h" - -#define MAX_COMMAND_LEN 32 -#define COMMAND_BUF_SIZE (MAX_COMMAND_LEN + 1) - -static char mCommandBuffer[COMMAND_BUF_SIZE]; -static int mCommandBufferPos = 0; - -// Command Handlers -static void on_restart_command(void) { - NVIC_SystemReset(); -} - -// Command lookup table, maps textual string commands to methods executed when they're received. -static uart_command_t mUARTCommands[] = { - { - .command = "restart", - .handler = on_restart_command, - }, -}; - -// Dispatches appropriate handler for matching command. -// 'command' should be null terminated string <= MAX_COMMAND_LEN -static void on_command_received(const char * command) { - int numCommands = sizeof(mUARTCommands) / sizeof(mUARTCommands[0]); - for (int i = 0; i < numCommands; i++) { - if (strcmp(command, mUARTCommands[i].command) == 0) { - mUARTCommands[i].handler(); - return; - } - } - - debug_log("Unknown command: %s\n", command); -} - -void UARTCommands_ProcessChar(char commandChar) { - app_uart_put(commandChar); - - if (commandChar == '\n' || commandChar == '\r') { - if (mCommandBufferPos != 0) { - on_command_received(mCommandBuffer); - memset(mCommandBuffer, 0, sizeof(mCommandBuffer)); - mCommandBufferPos = 0; - } - } else { - if (mCommandBufferPos < MAX_COMMAND_LEN) { - mCommandBuffer[mCommandBufferPos] = commandChar; - mCommandBufferPos++; - } - } -} - diff --git a/firmware/nRF_badge/data_collector/incl/uart_commands.h b/firmware/nRF_badge/data_collector/incl/uart_commands.h deleted file mode 100644 index 6913a45..0000000 --- a/firmware/nRF_badge/data_collector/incl/uart_commands.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Andrew Bartow on 11/3/16. -// - -#ifndef OPENBADGE_UART_COMMANDS_H -#define OPENBADGE_UART_COMMANDS_H - -#define MAX_COMMAND_LEN 32 -#define COMMAND_BUF_SIZE (MAX_COMMAND_LEN + 1) - -typedef void (*uart_command_handler_t)(void); - -typedef struct { - char command[COMMAND_BUF_SIZE]; - uart_command_handler_t handler; -} uart_command_t; - -/** - * This method should be called when a char is recieved that is to be processed by the UART Command Handler. - * Internally buffers the characters and and calls the necessary command handlers. - * - * @param commandChar char recieved over UART - */ -void UARTCommands_ProcessChar(char commandChar); - -#endif //OPENBADGE_UART_COMMANDS_H diff --git a/firmware/nRF_badge/data_collector/incl/uart_commands_lib.c b/firmware/nRF_badge/data_collector/incl/uart_commands_lib.c new file mode 100644 index 0000000..1ed24dd --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/uart_commands_lib.c @@ -0,0 +1,116 @@ +#include "uart_commands_lib.h" +#include "uart_lib.h" +#include "debug_lib.h" +#include "app_util_platform.h" +#include "custom_board.h" + +#ifdef DEBUG_LOG_ENABLE + + +#define MAX_COMMAND_LEN 32 +#define COMMAND_BUF_SIZE (MAX_COMMAND_LEN + 1) + +#define UART_RX_BUFFER_SIZE COMMAND_BUF_SIZE + +typedef void (*uart_command_handler_t)(void); + +typedef struct { + char command[COMMAND_BUF_SIZE]; + uart_command_handler_t handler; +} uart_command_t; + +static char command_buffer[COMMAND_BUF_SIZE]; +static int command_buffer_pos = 0; + +static uart_instance_t uart_instance; /**< The private uart_instance. */ + + + +static void on_uart_event(uart_evt_t const * p_event); +static void on_command_received(const char * command); + +/**@brief Command handler function that is called when the restart-command was received. + */ +static void on_restart_command(void) { + NVIC_SystemReset(); +} + +/**< The command lookup table, maps textual string commands to methods executed when they're received. */ +static uart_command_t uart_commands[] = { + { + .command = "restart", + .handler = on_restart_command, + }, +}; + + +ret_code_t uart_commands_init(void) { + + + + uart_instance.uart_peripheral = 0; + uart_instance.nrf_drv_uart_config.baudrate = (nrf_uart_baudrate_t) NRF_UART_BAUDRATE_115200; + uart_instance.nrf_drv_uart_config.hwfc = HWFC_ENABLED ? NRF_UART_HWFC_ENABLED : NRF_UART_HWFC_DISABLED; + uart_instance.nrf_drv_uart_config.interrupt_priority = APP_IRQ_PRIORITY_MID; + uart_instance.nrf_drv_uart_config.parity = NRF_UART_PARITY_EXCLUDED; + uart_instance.nrf_drv_uart_config.pselcts = UART_CTS_PIN; + uart_instance.nrf_drv_uart_config.pselrts = UART_RTS_PIN; + uart_instance.nrf_drv_uart_config.pselrxd = UART_RX_PIN; + uart_instance.nrf_drv_uart_config.pseltxd = UART_TX_PIN; + + ret_code_t ret; + UART_BUFFER_INIT(&uart_instance, UART_RX_BUFFER_SIZE, 0, &ret); + + if(ret != NRF_SUCCESS) return ret; + + memset(command_buffer, 0, sizeof(command_buffer)); + ret = uart_receive_buffer_bkgnd(&uart_instance, on_uart_event); + + + return ret; +} + +/**@brief Function that matches the received command to a command-handler and dispatches this handler. + */ +static void on_command_received(const char * command) { + int num_commands = sizeof(uart_commands) / sizeof(uart_commands[0]); + for (int i = 0; i < num_commands; i++) { + if (strcmp(command, uart_commands[i].command) == 0) { + uart_commands[i].handler(); + return; + } + } + + debug_log("UART_COMMANDS: Unknown command: %s\n", command); +} + + +/**@brief The callback-function that is called when there was sth received via the UART-interface. + */ +static void on_uart_event(uart_evt_t const * p_event) { + while(uart_receive_buffer_get(&uart_instance, (uint8_t*) &command_buffer[command_buffer_pos]) == NRF_SUCCESS) { + if(command_buffer[command_buffer_pos] == '\n' || command_buffer[command_buffer_pos] == '\r') { + command_buffer[command_buffer_pos] = 0; + on_command_received(command_buffer); + memset(command_buffer, 0, sizeof(command_buffer)); + command_buffer_pos = 0; + break; + } + if(command_buffer_pos < COMMAND_BUF_SIZE - 1) + command_buffer_pos++; + else { + command_buffer_pos = 0; + memset(command_buffer, 0, sizeof(command_buffer)); + } + + } + +} + +#else + +ret_code_t uart_commands_init(void) { + return NRF_SUCCESS; +} + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/uart_commands_lib.h b/firmware/nRF_badge/data_collector/incl/uart_commands_lib.h new file mode 100644 index 0000000..b30c47a --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/uart_commands_lib.h @@ -0,0 +1,15 @@ +#ifndef __UART_COMMANDS_LIB_H +#define __UART_COMMANDS_LIB_H + +#include "sdk_errors.h" + +/**@brief Function that initializes the serial-command support. + * + * @note This module uses the uart-instance of the debug-module + * (so when using this module you need to enable debugging and call debug_init() before this function), + * because the current implementation of uart_lib does not allow + * different uart-instances to send and to receive data. + */ +ret_code_t uart_commands_init(void); + +#endif diff --git a/firmware/nRF_badge/data_collector/incl/uart_lib.c b/firmware/nRF_badge/data_collector/incl/uart_lib.c new file mode 100644 index 0000000..1889c9b --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/uart_lib.c @@ -0,0 +1,578 @@ +#include "uart_lib.h" + + + +#include // Needed for the printf-function +#include // Needed for the printf-function +#include // Needed for the vsnprintf-function + + + +#define UART_PERIPHERAL_NUMBER UART_COUNT /**< Number of activated uart peripherals in sdk_config.h. */ + + +#define MAX_BYTES_PER_TRANSMIT_OPERATION 255 /**< Maximum number of bytes transmitted in each transmission step (large packets are split). */ + + + +static volatile uart_operation_t uart_operations[UART_PERIPHERAL_NUMBER] = {0}; /**< Array to save the current uart_operations (needed to check if there is an ongoing uart operation on the peripheral) */ +static uart_instance_t * uart_instances_transmit[UART_PERIPHERAL_NUMBER] = {NULL}; /**< Array of pointers to the current uart_instances for transmitting (needed for rescheduling transmit) */ +static uart_instance_t * uart_instances_receive[UART_PERIPHERAL_NUMBER] = {NULL}; /**< Array of pointers to the current uart_instances for receiving (needed for rescheduling one byte receive operations) */ +static uint32_t uart_instance_number = 1; /**< uart_instance_number starts at 1 not 0 because all entries in the uart_instances-arrays are 0. So the check for the uart_instance_id-element may not work correctly. */ + +static uart_handler_t uart_handlers_transmit_bkgnd [UART_PERIPHERAL_NUMBER]; /**< Array to save the application handlers for the transmit operation */ +static uart_handler_t uart_handlers_receive_bkgnd [UART_PERIPHERAL_NUMBER]; /**< Array to save the application handlers for the receive operation */ +static uart_handler_t uart_handlers_receive_buffer_bkgnd [UART_PERIPHERAL_NUMBER]; /**< Array to save the application handlers for the receive-buffer operation */ + + +/**@brief UART transmit operation type. Needed to reschedule transmit operation that are too long. */ +typedef struct { + const uint8_t* p_remaining_data; /**< Pointer to the next data that has to be sent */ + uint32_t remaining_data_len; /**< Number of bytes that are pending to be sent */ +} uart_transmit_operation_t; + + + +static volatile uart_transmit_operation_t uart_transmit_operations [UART_PERIPHERAL_NUMBER]; /**< Array is needed to keep track of already sent data packets, to reschedule a transmit on the remaining bytes */ +static volatile uint8_t uart_receive_buffer_operations [UART_PERIPHERAL_NUMBER]; /**< This is actually the buffer for one receive operation (1 Byte Buffer) */ + + +/**@brief Function for handling the uart interrupts internally. + * + * @details This handler (only one because there is only one uart peripheral) + * does different things depending on the current operation. + * If TRANSMIT_OPERATION, it checks for remaining bytes to transmit + * and reschedules the transmit of the remaining bytes, otherwise it calls + * the specified application handler (if it is not NULL) with event: UART_TRANSMIT_DONE or UART_ERROR. + * If RECEIVE_OPERATION, the specified application handler (if it is not NULL) with event: UART_RECEIVE_DONE or UART_ERROR. + * If UART_RECEIVE_BUFFER_OPERATION, the one byte receive function is rescheduled and calls + * the specified application handler (if it is not NULL) with event: UART_RECEIVE_DONE or UART_ERROR. + */ +#if UART0_ENABLED +static void uart_0_event_handler(nrf_drv_uart_event_t * p_event, void * p_context) { + uart_evt_t evt; + + if(p_event->type == NRF_DRV_UART_EVT_TX_DONE) { + + // Check if we have some remaining bytes to send? + uint32_t remaining_data_len = uart_transmit_operations[0].remaining_data_len; + const uint8_t* p_remaining_data = uart_transmit_operations[0].p_remaining_data; + if(remaining_data_len > 0 && p_remaining_data != NULL) { + + // Check if we are ready after this or if we have to reschedule the transmission again? + if(remaining_data_len <= MAX_BYTES_PER_TRANSMIT_OPERATION) { + uart_transmit_operations[0].p_remaining_data = NULL; + uart_transmit_operations[0].remaining_data_len = 0; + } else { + uart_transmit_operations[0].p_remaining_data = p_remaining_data + MAX_BYTES_PER_TRANSMIT_OPERATION; + uart_transmit_operations[0].remaining_data_len = remaining_data_len - MAX_BYTES_PER_TRANSMIT_OPERATION; + } + + + if(uart_instances_transmit[0]== NULL) + return; + + nrf_drv_uart_tx(&((*uart_instances_transmit[0]).nrf_drv_uart_instance), p_remaining_data, remaining_data_len); + + + } else { // We are done with transmiting + evt.type = UART_TRANSMIT_DONE; + + // Clear the UART_TRANSMIT_OPERATION flag + uart_operations[0] &= ~(UART_TRANSMIT_OPERATION); + + // Call the Callback handler if there exists one! + if(uart_handlers_transmit_bkgnd[0] != NULL) { + uart_handlers_transmit_bkgnd[0](&evt); + } + } + + } else if(p_event->type == NRF_DRV_UART_EVT_RX_DONE) { + + // Check the operation, if we are in operation receiving or receiving-buffer + if(uart_operations[0] & UART_RECEIVE_OPERATION) { + evt.type = UART_RECEIVE_DONE; + + if(uart_instances_receive[0] == NULL) + return; + + nrf_drv_uart_rx_disable(&((*uart_instances_receive[0]).nrf_drv_uart_instance)); + + // Clear the UART_RECEIVE_OPERATION flag + uart_operations[0] &= ~(UART_RECEIVE_OPERATION); + + // Call the Callback handler if there exists one! + if(uart_handlers_receive_bkgnd[0] != NULL) { + uart_handlers_receive_bkgnd[0](&evt); + } + + } else if(uart_operations[0] & UART_RECEIVE_BUFFER_OPERATION) { + evt.type = UART_DATA_AVAILABLE; + + + if(uart_instances_receive[0] == NULL) + return; + + // Reschedule the one byte receive operation + nrf_drv_uart_rx( &((*uart_instances_receive[0]).nrf_drv_uart_instance), (uint8_t*) &uart_receive_buffer_operations[0], 1); + + + // If you want you can check here if ((write_index + 1)%MAX == read_index) --> don't insert new elements into the buffer (until they have been read) + // This is the case, if we have so much bytes, that our fifo will overflow --> of course in this case the new data get lost (but might be better than losing all the old data) + // But you can just store size - 1 elements with this check! + + if(((*uart_instances_receive[0]).uart_buffer.rx_buf_write_index + 1) % (*uart_instances_receive[0]).uart_buffer.rx_buf_size != (*uart_instances_receive[0]).uart_buffer.rx_buf_read_index) { + + + // Now insert the currently received 1 Byte buffer to the uart-fifo-buffer! + (*uart_instances_receive[0]).uart_buffer.rx_buf [(*uart_instances_receive[0]).uart_buffer.rx_buf_write_index] = uart_receive_buffer_operations[0]; + // Increment the write index + (*uart_instances_receive[0]).uart_buffer.rx_buf_write_index = ((*uart_instances_receive[0]).uart_buffer.rx_buf_write_index + 1) % (*uart_instances_receive[0]).uart_buffer.rx_buf_size; + + + // Call the Callback handler if there exists one! + if(uart_handlers_receive_buffer_bkgnd[0] != NULL) { + uart_handlers_receive_buffer_bkgnd[0](&evt); + } + } + + } + } else if(p_event->type == NRF_DRV_UART_EVT_ERROR) { + + evt.type = UART_ERROR; + + // Check which operations are ongoing, call the corresponding handlers, and clear the operation bit! + if(uart_operations[0] & UART_TRANSMIT_OPERATION) { + uart_operations[0] &= ~(UART_TRANSMIT_OPERATION); + if(uart_handlers_transmit_bkgnd[0] != NULL) { + uart_handlers_transmit_bkgnd[0](&evt); + } + } + if(uart_operations[0] & UART_RECEIVE_OPERATION) { + uart_operations[0] &= ~(UART_RECEIVE_OPERATION); + if(uart_handlers_receive_bkgnd[0] != NULL) { + uart_handlers_receive_bkgnd[0](&evt); + } + } + if(uart_operations[0] & UART_RECEIVE_BUFFER_OPERATION) { + uart_operations[0] &= ~(UART_RECEIVE_BUFFER_OPERATION); + if(uart_handlers_receive_buffer_bkgnd[0] != NULL) { + uart_handlers_receive_buffer_bkgnd[0](&evt); + } + } + + } + + +} +#endif + + +ret_code_t uart_init(uart_instance_t* uart_instance) { + + // Check if the selected uart peripheral exists + if(uart_instance->uart_peripheral == 0) { + #if UART0_ENABLED + uart_instance->nrf_drv_uart_instance.drv_inst_idx = UART0_INSTANCE_INDEX; + uart_instance->nrf_drv_uart_instance.reg.p_uart = (NRF_UART_Type *) NRF_UART0; + nrf_drv_uart_init(&(uart_instance->nrf_drv_uart_instance), &(uart_instance->nrf_drv_uart_config), uart_0_event_handler); + #else + return NRF_ERROR_INVALID_PARAM; + #endif + } else { + return NRF_ERROR_INVALID_PARAM; + } + + + uart_instance->uart_instance_id = uart_instance_number; + + uart_instance_number++; + + return NRF_SUCCESS; +} + + + +ret_code_t uart_printf_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler, const char* format, ...) { + + // Check if the tx buffer that we want to use is not NULL + if(uart_instance->uart_buffer.tx_buf == NULL) { + return NRF_ERROR_NULL; + } + + uint32_t n = uart_instance->uart_buffer.tx_buf_size; + + va_list args; + va_start(args, format); + int ret_vsnprintf = vsnprintf((char*) uart_instance->uart_buffer.tx_buf, n, format, args); + va_end(args); + + // Check if the output of vsnprintf is correct + if(ret_vsnprintf < 0 || ret_vsnprintf >= n) { + return NRF_ERROR_NO_MEM; + } + + + return uart_transmit_bkgnd(uart_instance, uart_handler, uart_instance->uart_buffer.tx_buf, ret_vsnprintf); +} + + +ret_code_t uart_printf_abort_bkgnd(const uart_instance_t* uart_instance) { + return uart_transmit_abort_bkgnd(uart_instance); +} + + +ret_code_t uart_printf(uart_instance_t* uart_instance, const char* format, ...) { + + // Check if the tx buffer that we want to use is not NULL + if(uart_instance->uart_buffer.tx_buf == NULL) { + return NRF_ERROR_NULL; + } + + + uint32_t n = uart_instance->uart_buffer.tx_buf_size; + + va_list args; + va_start(args, format); + + int ret_vsnprintf = vsnprintf((char*) uart_instance->uart_buffer.tx_buf, n, format, args); + va_end(args); + + + // Check if the output of vsnprintf is correct + if(ret_vsnprintf < 0 || ret_vsnprintf >= n) { + return NRF_ERROR_NO_MEM; + } + + + return uart_transmit(uart_instance, uart_instance->uart_buffer.tx_buf, ret_vsnprintf); +} + + + + + +ret_code_t uart_transmit_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler, const uint8_t* tx_data, uint32_t tx_data_len) { + + // Get the peripheral_index + uint8_t peripheral_index = uart_instance->uart_peripheral; + + // Check if there is an ongoing transmit operation. Not check if there is no operation, because we can transmit while receiving! + if((uart_operations[peripheral_index] & UART_TRANSMIT_OPERATION)) { + return NRF_ERROR_BUSY; + } + + // Set the UART_TRANSMIT_OPERATION flag + uart_operations[peripheral_index] |= UART_TRANSMIT_OPERATION; + + // Set the current uart instance (it is needed in transmit-case, because we must handle remaining bytes to transmit in IRQ-Handler) + uart_instances_transmit[peripheral_index] = uart_instance; + + // Set the handler that has to be called + uart_handlers_transmit_bkgnd[peripheral_index] = uart_handler; + + + // Check the data length of the data to transmit. If bigger than 255, the transmission has to be splitted in smaller parts. + if(tx_data_len <= MAX_BYTES_PER_TRANSMIT_OPERATION) { + uart_transmit_operations[peripheral_index].p_remaining_data = NULL; + uart_transmit_operations[peripheral_index].remaining_data_len = 0; + } else { + uart_transmit_operations[peripheral_index].p_remaining_data = tx_data + MAX_BYTES_PER_TRANSMIT_OPERATION; + uart_transmit_operations[peripheral_index].remaining_data_len = tx_data_len - MAX_BYTES_PER_TRANSMIT_OPERATION; + tx_data_len = MAX_BYTES_PER_TRANSMIT_OPERATION; // Correct the data len sent with the first transmission + } + + + ret_code_t ret = nrf_drv_uart_tx(&(uart_instance->nrf_drv_uart_instance), tx_data, tx_data_len); + + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_INVALID_ADDR or NRF_ERROR_FORBIDDEN + + // Set error code to internal to reduce error codes + if(ret == NRF_ERROR_FORBIDDEN) + ret = NRF_ERROR_INTERNAL; + + // If no success clear the UART_TRANSMIT_OPERATION flag + + if(ret != NRF_SUCCESS) { + // Stop the ongoing transmit operation + nrf_drv_uart_tx_abort(&(uart_instance->nrf_drv_uart_instance)); + + // Clear the Transmit operation + uart_operations[peripheral_index] &= ~(UART_TRANSMIT_OPERATION); + } + + return ret; +} + + +ret_code_t uart_transmit_abort_bkgnd(const uart_instance_t* uart_instance) { + // Get the peripheral_index + uint8_t peripheral_index = uart_instance->uart_peripheral; + + // Check if there is an ongoing transmit operation. If there is no transmit operation, directly return + if((uart_operations[peripheral_index] & UART_TRANSMIT_OPERATION) == 0) { + return NRF_SUCCESS; + } + + // You should only be able to abort an transmit operation, if your own instance is currently running. So check if the instance id is the same + if((uart_instances_transmit[peripheral_index] != NULL) && ((*uart_instances_transmit[peripheral_index]).uart_instance_id != uart_instance->uart_instance_id)) { + return NRF_ERROR_INVALID_STATE; + } + // If NULL (it shouldn't be NULL here, because we have an ongoing operation) -> just proceed as normal.. + + + + // Now stop the ongoing transmit operation + nrf_drv_uart_tx_abort(&(uart_instance->nrf_drv_uart_instance)); + + // Clear the Transmit operation + uart_operations[peripheral_index] &= ~(UART_TRANSMIT_OPERATION); + + return NRF_SUCCESS; +} + + +ret_code_t uart_transmit(uart_instance_t* uart_instance, const uint8_t* tx_data, uint32_t tx_data_len) { + + ret_code_t ret = uart_transmit_bkgnd(uart_instance, NULL, tx_data, tx_data_len); + if(ret != NRF_SUCCESS) { + return ret; + } + + // Waiting until the UART operation has finished! + while(uart_get_operation(uart_instance) & UART_TRANSMIT_OPERATION); + + return NRF_SUCCESS; +} + + + + +ret_code_t uart_receive_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler, uint8_t* rx_data, uint32_t rx_data_len) { + // Get the peripheral_index + uint8_t peripheral_index = uart_instance->uart_peripheral; + + + // Check whether there is an ongoing receive or receive-buffer operation + if((uart_operations[peripheral_index] & UART_RECEIVE_OPERATION) || (uart_operations[peripheral_index] & UART_RECEIVE_BUFFER_OPERATION)) { + return NRF_ERROR_BUSY; + } + + // Set the UART_RECEIVE_OPERATION flag + uart_operations[peripheral_index] |= UART_RECEIVE_OPERATION; + + // Set the current uart instance (it is not needed in receive-case, because we need it only in receive-buffer case) + uart_instances_receive[peripheral_index] = uart_instance; + + // Set the handler that has to be called, reset the receive-buffer handler! + uart_handlers_receive_bkgnd[peripheral_index] = uart_handler; + uart_handlers_receive_buffer_bkgnd[peripheral_index] = NULL; // We actually don't need to do this, but ok.. + + + nrf_drv_uart_rx_enable(&(uart_instance->nrf_drv_uart_instance)); + + ret_code_t ret = nrf_drv_uart_rx(&(uart_instance->nrf_drv_uart_instance), rx_data, rx_data_len); + + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_FORBIDDEN, NRF_ERROR_INTERNAL and NRF_ERROR_INVALID_ADDR + + // Set error code to internal to reduce error codes + if(ret == NRF_ERROR_FORBIDDEN || ret == NRF_ERROR_INTERNAL) + ret = NRF_ERROR_INTERNAL; + + // If no success clear the UART_RECEIVE_OPERATION flag + if(ret != NRF_SUCCESS) { + + // Now stop the ongoing receive operation + nrf_drv_uart_rx_abort(&(uart_instance->nrf_drv_uart_instance)); + // And disable the receive process! + nrf_drv_uart_rx_disable(&(uart_instance->nrf_drv_uart_instance)); + + + uart_operations[peripheral_index] &= ~(UART_RECEIVE_OPERATION); + } + + return ret; + +} + + +ret_code_t uart_receive_abort_bkgnd(const uart_instance_t* uart_instance) { + + // Get the peripheral_index + uint8_t peripheral_index = uart_instance->uart_peripheral; + + // Check if there is an ongoing receive operation. If there is no receive operation, directly return + if((uart_operations[peripheral_index] & UART_RECEIVE_OPERATION) == 0) { + return NRF_SUCCESS; + } + + // You should only be able to abort an receive operation, if your own instance is currently running. So check if the instance id is the same + if((uart_instances_receive[peripheral_index] != NULL) && ((*uart_instances_receive[peripheral_index]).uart_instance_id != uart_instance->uart_instance_id)) { + return NRF_ERROR_INVALID_STATE; + } + // If NULL (it shouldn't be NULL here, because we have an ongoing operation) -> just proceed as normal.. + + + // Now stop the ongoing receive operation + nrf_drv_uart_rx_abort(&(uart_instance->nrf_drv_uart_instance)); + // And disable the receive process! + nrf_drv_uart_rx_disable(&(uart_instance->nrf_drv_uart_instance)); + + // Clear the Receive operation + uart_operations[peripheral_index] &= ~(UART_RECEIVE_OPERATION); + + return NRF_SUCCESS; + +} + + +ret_code_t uart_receive(uart_instance_t* uart_instance, uint8_t* rx_data, uint32_t rx_data_len) { + + ret_code_t ret = uart_receive_bkgnd(uart_instance, NULL, rx_data, rx_data_len); + + if(ret != NRF_SUCCESS) { + return ret; + } + + // Waiting until the UART operation has finished! + while(uart_get_operation(uart_instance) & UART_RECEIVE_OPERATION); + + return NRF_SUCCESS; +} + + + + +ret_code_t uart_receive_buffer_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler){ + + // Get the peripheral_index + uint8_t peripheral_index = uart_instance->uart_peripheral; + + + // Check if rx-buffer is not NUL, because it is needed for buffering the incoming data + if(uart_instance->uart_buffer.rx_buf == NULL) + return NRF_ERROR_NULL; + + if(uart_instance->uart_buffer.rx_buf_size == 0) + return NRF_ERROR_NO_MEM; + + // Check whether there is an ongoing receive or receive-buffer operation + if((uart_operations[peripheral_index] & UART_RECEIVE_OPERATION) || (uart_operations[peripheral_index] & UART_RECEIVE_BUFFER_OPERATION)) { + return NRF_ERROR_BUSY; + } + + // Set the UART_RECEIVE_OPERATION flag + uart_operations[peripheral_index] |= UART_RECEIVE_BUFFER_OPERATION; + + // Set the current uart instance (because we need it to reinitiate a receive operation) + uart_instances_receive[peripheral_index] = uart_instance; + + // Reset the receive-read and -write index of the uart-buffer + (uart_instance->uart_buffer).rx_buf_read_index = 0; + (uart_instance->uart_buffer).rx_buf_write_index = 0; + + + // Set the handler that has to be called, reset the receive handler! + uart_handlers_receive_bkgnd[peripheral_index] = NULL; // We actually don't need to do this, but ok.. + uart_handlers_receive_buffer_bkgnd[peripheral_index] = uart_handler; + + // Enable the receive process + nrf_drv_uart_rx_enable(&(uart_instance->nrf_drv_uart_instance)); + + // Start the receive and buffer the data in the 1 Byte buffer + ret_code_t ret = nrf_drv_uart_rx(&(uart_instance->nrf_drv_uart_instance), (uint8_t*) &uart_receive_buffer_operations[peripheral_index], 1); + + // ret could be NRF_SUCCESS, NRF_ERROR_BUSY, NRF_ERROR_FORBIDDEN, NRF_ERROR_INTERNAL and NRF_ERROR_INVALID_ADDR + + // Set error code to internal to reduce error codes + if(ret == NRF_ERROR_FORBIDDEN || ret == NRF_ERROR_INTERNAL) + ret = NRF_ERROR_INTERNAL; + + + // If no success clear the UART_RECEIVE_BUFFER_OPERATION flag + if(ret != NRF_SUCCESS) { + + // Now stop the ongoing receive operation + nrf_drv_uart_rx_abort(&(uart_instance->nrf_drv_uart_instance)); + // And disable the receive process! + nrf_drv_uart_rx_disable(&(uart_instance->nrf_drv_uart_instance)); + + + uart_operations[peripheral_index] &= ~(UART_RECEIVE_BUFFER_OPERATION); + } + + return ret; + +} + +ret_code_t uart_receive_buffer_abort_bkgnd(const uart_instance_t* uart_instance) { + + // Get the peripheral_index + uint8_t peripheral_index = uart_instance->uart_peripheral; + + // Check if there is an ongoing receive-buffer operation. If there is no receive-buffer operation, directly return + if((uart_operations[peripheral_index] & UART_RECEIVE_BUFFER_OPERATION) == 0) { + return NRF_SUCCESS; + } + + // You should only be able to abort an receive-buffer operation, if your own instance is currently running. So check if the instance id is the same + if((uart_instances_receive[peripheral_index] != NULL) && ((*uart_instances_receive[peripheral_index]).uart_instance_id != uart_instance->uart_instance_id)) { + return NRF_ERROR_INVALID_STATE; + } + // If NULL (it shouldn't be NULL here, because we have an ongoing operation) -> just proceed as normal.. + + + + // Now stop the ongoing receive operation + nrf_drv_uart_rx_abort(&(uart_instance->nrf_drv_uart_instance)); + // And disable the receive process! + nrf_drv_uart_rx_disable(&(uart_instance->nrf_drv_uart_instance)); + + // Clear the Receive operation + uart_operations[peripheral_index] &= ~(UART_RECEIVE_BUFFER_OPERATION); + + return NRF_SUCCESS; + +} + +/**@brief Functions for retrieving the number of elements in the circular rx-buffer. + * + * @retval Number of elements in circular rx-buffer. + */ +static uint32_t uart_receive_buffer_get_elements(const uart_instance_t* uart_instance) { + uint32_t size = (uart_instance->uart_buffer).rx_buf_size; + uint32_t read_index = (uart_instance->uart_buffer).rx_buf_read_index; + uint32_t write_index = (uart_instance->uart_buffer).rx_buf_write_index; + uint32_t elements = 0; + + if(write_index >= read_index) { + elements = write_index - read_index; + } else { + elements = (size - read_index) + write_index; + } + + return elements; +} + +ret_code_t uart_receive_buffer_get(uart_instance_t* uart_instance, uint8_t* data_byte) { + + // It is important that we are now assuming that it's in the current instance, but we have not really a reference to it + + // Just reads out a byte from the circular buffer + uint32_t elements = uart_receive_buffer_get_elements(uart_instance); + + if(elements == 0) + return NRF_ERROR_NOT_FOUND; + + *data_byte = (uart_instance->uart_buffer).rx_buf[(uart_instance->uart_buffer).rx_buf_read_index]; + (uart_instance->uart_buffer).rx_buf_read_index = ((uart_instance->uart_buffer).rx_buf_read_index + 1) % (uart_instance->uart_buffer).rx_buf_size; + + return NRF_SUCCESS; +} + + + +uart_operation_t uart_get_operation(const uart_instance_t* uart_instance) { + return uart_operations[uart_instance->uart_peripheral]; +} + diff --git a/firmware/nRF_badge/data_collector/incl/uart_lib.h b/firmware/nRF_badge/data_collector/incl/uart_lib.h new file mode 100644 index 0000000..0b9a814 --- /dev/null +++ b/firmware/nRF_badge/data_collector/incl/uart_lib.h @@ -0,0 +1,398 @@ +#ifndef __UART_LIB_H +#define __UART_LIB_H + + + +/** @file + * + * @brief UART abstraction library. + * + * @details It enables to call the UART peripheral from different contexts. + * If the selected UART peripheral is currently in use, it will inform the other context by returning NRF_ERROR_BUSY. + * It is possible to transmit and receive/receive-buffer data parallel. + * There are two different receive-modes: normal receive and receive-buffer. + * In normal receive operation a predefined amount of data is received. + * In receive-buffer operation a variable amount of data can be received, + * the received data are buffered in the internal circular rx-buffer. + * To get the bytes from the circular rx-buffer the uart_receive_buffer_get()-function can be used. + * Furthermore, there is the capability to use a printf-styled function, to transmit formatted data via UART. + * For the printf-functions the tx-buffer in uart_buffer must be initialized. + * If float/double should be supported, the linker has to be configured accordingly: LDFLAGS += -u _printf_float. + */ + +#include "sdk_common.h" // Needed for the definition of ret_code_t and the error-codes +#include "nrf_drv_uart.h" + + + +/**@brief The different UART operations. These operations will be used to set the peripheral busy or not. */ +typedef enum { + UART_NO_OPERATION = 0, /**< Currently no uart operation ongoing. */ + UART_TRANSMIT_OPERATION = (1 << 0), /**< Currently an uart transmit operation ongoing. */ + UART_RECEIVE_OPERATION = (1 << 1), /**< Currently an uart receive operation ongoing. */ + UART_RECEIVE_BUFFER_OPERATION = (1 << 2), /**< Currently an uart receive to buffer operation ongoing. */ +} uart_operation_t; + + + +/**@brief The uart buffer type. Needed for the printf and receive-buffer operations. */ +typedef struct +{ + uint8_t * rx_buf; /**< Pointer to the circular RX buffer. */ + uint32_t rx_buf_size; /**< Size of the circular RX buffer. */ + volatile uint32_t rx_buf_read_index; /**< The read index of the circular RX buffer (uart_receive_buffer_get() reads from this index). */ + volatile uint32_t rx_buf_write_index; /**< The write index of the circular RX buffer (the internal receive-callback() writes to this index when receiving a byte) */ + uint8_t * tx_buf; /**< Pointer to the TX buffer. */ + uint32_t tx_buf_size; /**< Size of the TX buffer. */ +} uart_buffer_t; + + +/**@brief UART driver event types, passed to the handler routine provided by the bkgnd-functions. */ +typedef enum +{ + UART_TRANSMIT_DONE, /**< Transmit done */ + UART_RECEIVE_DONE, /**< Receive done */ + UART_DATA_AVAILABLE, /**< Data available */ + UART_ERROR, /**< Communication error */ +} uart_evt_type_t; + +typedef struct +{ + uart_evt_type_t type; /**< Event type */ +} uart_evt_t; + + + + + +/** + * @brief UART event handler type. + */ +typedef void (*uart_handler_t)(uart_evt_t const * p_event); + +/**@example Example of uart_instance_t + * + * + * uart_instance_t uart_instance; // Create an uart_instance-struct. + * + * uart_instance.uart_peripheral = 0; // Set the uart peripheral to 0 (the selected peripheral has to be enabled in sdk_config.h). + * uart_instance.nrf_drv_uart_config.baudrate = NRF_UART_BAUDRATE_115200; // Set the uart baudrate. NRF_UART_BAUDRATE_115200 from nrf_uart.h. + * uart_instance.nrf_drv_uart_config.hwfc = NRF_UART_HWFC_DISABLED; // Set the uart hardware flow control. NRF_UART_HWFC_DISABLED from nrf_uart.h. + * uart_instance.nrf_drv_uart_config.interrupt_priority = APP_IRQ_PRIORITY_LOW; // The interrupt priotity of the spi peripheral. APP_IRQ_PRIORITY_LOW from app_util_platform.h. + * uart_instance.nrf_drv_uart_config.parity = NRF_UART_PARITY_EXCLUDED; // Set the uart parity. NRF_UART_PARITY_EXCLUDED from nrf_uart.h. + * uart_instance.nrf_drv_uart_config.pselcts = 0; // Set the uart CTS-pin. + * uart_instance.nrf_drv_uart_config.pselrts = 0; // Set the uart RTS-pin. + * uart_instance.nrf_drv_uart_config.pselrxd = 11; // Set the uart RXD-pin. + * uart_instance.nrf_drv_uart_config.pseltxd = 10; // Set the uart TXD-pin. + * + * ret_code_t ret; + * UART_BUFFER_INIT(&uart_instance, &ret); // Initialize the uart_instance. + * + */ + +/**@brief UART instance type. */ +typedef struct { + uint8_t uart_peripheral; /**< Set to the desired uart peripheral. The Peripheral has to be enabled in the sdk_config.h file */ + nrf_drv_uart_config_t nrf_drv_uart_config; /**< Set the uart configuration (possible parameters in nrf_drv_uart.h) */ + uint32_t uart_instance_id; /**< Instance index: Setted by the init-function (do not set!) */ + uart_buffer_t uart_buffer; /**< The uart buffer used for printf and receive_buffer: If you want to use the buffer functionality, use the corresponding init MACRO. Setted by the init-function (do not set!) */ + nrf_drv_uart_t nrf_drv_uart_instance; /**< The initialized low level uart instance: Setted by the init-function (do not set!) */ +} uart_instance_t; + + + + + +/**@brief Macro for initializing the uart instance without a uart-buffer. + * + * @details This macro calls the uart_init()-function for initializing the uart instance. + * Furthermore, it sets the rx- and tx-buffer of uart_buffer to NULL. + * + * @note It is important that all the printf- and receive_buffer-functions won't work if UART_INIT() is used for initialization. + * + * @param[in,out] P_UART_INSTANCE Pointer to a preconfigured uart instance. + * @param[out] P_RET_CODE Pointer where the return value of uart_init() is written to. + */ +#define UART_INIT(P_UART_INSTANCE, P_RET_CODE) \ + do \ + { \ + uart_buffer_t uart_buffer; \ + uart_buffer.rx_buf = NULL; \ + uart_buffer.tx_buf = NULL; \ + \ + (P_UART_INSTANCE)->uart_buffer = uart_buffer; \ + (*(P_RET_CODE)) = uart_init(P_UART_INSTANCE); \ + } while (0) + + + + +/**@brief Macro for initializing the uart instance with uart-buffer so that the printf- and recevie_buffer-functions could work. + * + * @details This macro calls the uart_init()-function for initializing the uart instance. + * Furthermore, it initializes the uart_buffer so that the printf- and receive_buffer-functions can be used. + * + * @param[in,out] P_UART_INSTANCE Pointer to a preconfigured uart instance. + * @param[in] RX_BUF_SIZE Size of the static allocated circular receive-buffer. + * @param[in] TX_BUF_SIZE Size of the static allocated transmit-buffer. + * @param[out] P_RET_CODE Pointer where the return value of uart_init() is written to. + */ +#define UART_BUFFER_INIT(P_UART_INSTANCE, RX_BUF_SIZE, TX_BUF_SIZE, P_RET_CODE) \ + do \ + { \ + uart_buffer_t uart_buffer; \ + static uint8_t rx_buf[RX_BUF_SIZE]; \ + static uint8_t tx_buf[TX_BUF_SIZE]; \ + \ + uart_buffer.rx_buf = rx_buf; \ + uart_buffer.rx_buf_size = RX_BUF_SIZE; \ + uart_buffer.rx_buf_read_index = 0; \ + uart_buffer.rx_buf_write_index = 0; \ + uart_buffer.tx_buf = tx_buf; \ + uart_buffer.tx_buf_size = TX_BUF_SIZE; \ + \ + (P_UART_INSTANCE)->uart_buffer = uart_buffer; \ + (*(P_RET_CODE)) = uart_init(P_UART_INSTANCE); \ + } while (0) + + +/**@brief Function for initializing an instance for the uart peripheral. + * + * @details Initializes the low level uart driver. It allows multiple instances on the same peripheral + * (it assumes that the configuration is the same for all the different instances on the same peripheral) + * + * @note Always initialize with the Macros UART_BUFFER_INIT() or UART_INIT(). + * + * @param[in,out] uart_instance Pointer to an preconfigured uart_instance. + * + * @retval NRF_SUCCESS If the adc_instance was successfully initialized. + * @retval NRF_ERROR_INVALID_PARAM If the specified peripheral is not correct. + * @retval NRF_ERROR_INVALID_STATE If the peripheral was already initialized. + */ +ret_code_t uart_init(uart_instance_t* uart_instance); + + + +/**@brief Function for printing formatted data in asynchronous/non-blocking/background mode. + * + * @details This is a non-blocking function. If there is already an ongoing uart transmit operation this function returns NRF_ERROR_BUSY. + * Internally this function calls uart_transmit_bkgnd(). + * If the operation was started successfully and terminates, + * the provided uart_handler is called with event: UART_TRANSMIT_DONE or UART_ERROR. + * + * @note This function works only when the uart instance was initialized with UART_BUFFER_INIT() and the specified tx_buf-size is big enough. + * If the application needs to print float/double values, there has to be a linker flag: LDFLAGS += -u _printf_float. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[in] uart_handler Handler function that should be called if the operation is done, with event: UART_TRANSMIT_DONE or UART_ERROR. Could also be NULL if no handler should be called. + * @param[in] format The format string. + * @param[in] ... Variable input parameters, needed by the format string. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing transmit/printf operation. + * @retval NRF_ERROR_INTERNAL If there was an internal error in the lower level uart driver. + * @retval NRF_ERROR_INVALID_ADDR If tx_data does not point to RAM buffer (UARTE only). + * @retval NRF_ERROR_NULL If the tx-buffer in uart_buffer is NULL. + * @retval NRF_ERROR_NO_MEM If the formatted string is bigger than the tx-buffer size. + */ +ret_code_t uart_printf_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler, const char* format, ...); + + + +/**@brief Function for aborting a current data transmission. + * + * @details This function calls internally uart_transmit_abort_bkgnd(). + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_STATE If it is another/false uart_instance that tries to abort the current operation. + */ +ret_code_t uart_printf_abort_bkgnd(const uart_instance_t* uart_instance); + + + +/**@brief Function for printing formatted data in blocking mode. + * + * @details This function uses internally uart_transmit() to do a blocking transmit. + * + * @note This function works only when the uart instance was initialized with UART_BUFFER_INIT() and the specified tx_buf-size is big enough. + * If the application needs to print float/double values, there has to be a linker flag: LDFLAGS += -u _printf_float. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[in] format The format string. + * @param[in] ... Variable input paramters, needed by the format string. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing transmit/printf operation. + * @retval NRF_ERROR_INTERNAL If there was an internal error in the lower level uart driver. + * @retval NRF_ERROR_INVALID_ADDR If tx_data does not point to RAM buffer (UARTE only). + * @retval NRF_ERROR_NULL If the tx-buffer in uart_buffer is NULL. + * @retval NRF_ERROR_NO_MEM If the formatted string is bigger than the tx-buffer size. + */ +ret_code_t uart_printf(uart_instance_t* uart_instance, const char* format, ...); + + + +/**@brief Function for transmitting data in asynchronous/non-blocking/background mode. + * + * @details This is a non-blocking function. If there is already an ongoing uart transmit operation this function returns NRF_ERROR_BUSY. + * If the tx_data_len is >= 255 the packet is splitted and the remaining bytes will be transmitted via the internal uart interrupt. + * That must be done because nrf_drv_uart-library don't accepts UART TX transfers with size >= 256. (That is very disappointing). + * If the operation was started successfully and terminates, + * the provided uart_handler is called with event: UART_TRANSMIT_DONE or UART_ERROR. + * + * @warning The transmit data must be kept in memory until the operation has terminated. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[in] uart_handler Handler function that should be called if the operation is done, with event: UART_TRANSMIT_DONE or UART_ERROR. Could also be NULL if no handler should be called. + * @param[in] tx_data Pointer to the transmit data. + * @param[in] tx_data_len Size of data to transmit. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing transmit/printf operation. + * @retval NRF_ERROR_INTERNAL If there was an internal error in the lower level uart driver. + * @retval NRF_ERROR_INVALID_ADDR If tx_data does not point to RAM buffer (UARTE only). + */ +ret_code_t uart_transmit_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler, const uint8_t* tx_data, uint32_t tx_data_len); + + + +/**@brief Function for aborting a current data transmission. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_STATE If it is another/false uart_instance that tries to abort the current operation. + */ +ret_code_t uart_transmit_abort_bkgnd(const uart_instance_t* uart_instance); + + + +/**@brief Function for transmitting data in blocking mode. + * + * @details This function uses internally uart_transmit_bkgnd() for the data transmission and + * uart_get_operation() to wait for the termination of the transmit operation. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[in] tx_data Pointer to the transmit data. + * @param[in] tx_data_len Size of data to transmit. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing transmit/printf operation. + * @retval NRF_ERROR_INTERNAL If there was an internal error in the lower level uart driver. + * @retval NRF_ERROR_INVALID_ADDR If tx_data does not point to RAM buffer (UARTE only). + */ +ret_code_t uart_transmit(uart_instance_t* uart_instance, const uint8_t* tx_data, uint32_t tx_data_len); + + + +/**@brief Function for receiving a fixed amount of data in asynchronous/non-blocking/background mode. + * + * @details This is a non-blocking function. The function returns NRF_ERROR_BUSY, + * if there is already a receive or receive-buffer operation ongoing. + * If the uart peripheral received the specified amount of data, + * the uart_handler will be called, with event: UART_RECEIVE_DONE or UART_ERROR. + * + * @warning The receive data must be kept in memory until the operation has terminated. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[in] uart_handler Handler function that should be called if the operation is done, with event: UART_RECEIVE_DONE or UART_ERROR. Could also be NULL if no handler should be called. + * @param[in] rx_data Pointer to the receive data memory. + * @param[in] rx_data_len Size of data to be received. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing receive or receive_buffer operation. + * @retval NRF_ERROR_INTERNAL If there was an internal error in the lower level uart driver. + * @retval NRF_ERROR_INVALID_ADDR If rx_data does not point to RAM buffer (UARTE only). + */ +ret_code_t uart_receive_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler, uint8_t* rx_data, uint32_t rx_data_len); + + + +/**@brief Function for aborting the receiving of the fixed amount of data. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_STATE If it is another/false uart_instance that tries to abort the current operation. + */ +ret_code_t uart_receive_abort_bkgnd(const uart_instance_t* uart_instance); + + +/**@brief Function for receiving a fixed amount of data in blocking mode. + * + * @details This function uses internally uart_receive_bkgnd() for the fixed amount data receive and + * uart_get_operation() to wait for the termination of the receive operation. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[in] rx_data Pointer to the receive data memory. + * @param[in] rx_data_len Size of data to be received. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing receive or receive_buffer operation. + * @retval NRF_ERROR_INTERNAL If there was an internal error in the lower level uart driver. + * @retval NRF_ERROR_INVALID_ADDR If rx_data does not point to RAM buffer (UARTE only). + */ +ret_code_t uart_receive(uart_instance_t* uart_instance, uint8_t* rx_data, uint32_t rx_data_len); + + +/**@brief Function for starting the receive to buffer of an variable length of data in asynchronous/non-blocking/background mode. + * + * @details This is a non-blocking function. The function returns NRF_ERROR_BUSY, + * if there is a receive or receive-buffer operation ongoing. + * If the uart peripheral receives a byte, it is inserted in the circular rx-buffer and + * the specified uart_handler will be called, with events: UART_DATA_AVAILABLE or UART_ERROR. + * The one byte receive operation is rescheduled internally via the interrupt handler. + * The data-bytes of the circular rx-buffer could be read via the uart_receive_buffer_get()-function. + * It is only possible to receive so many bytes at once (without calling uart_receive_buffer_get()) + * as specified during the initialization of the rx-buffer in uart_buffer. + * + * @note This function works only when the uart instance was initialized with UART_BUFFER_INIT() and the specified rx_buf-size is big enough. + * + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[in] uart_handler Handler function that should be called if the operation is done, with event: UART_DATA_AVAILABLE or UART_ERROR. Could also be NULL if no handler should be called. + * + * @retval NRF_SUCCESS If the operation was started successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing receive or receive_buffer operation. + * @retval NRF_ERROR_INTERNAL If there was an internal error in the lower level uart driver. + * @retval NRF_ERROR_INVALID_ADDR If rx_data does not point to RAM buffer (UARTE only). + */ +ret_code_t uart_receive_buffer_bkgnd(uart_instance_t* uart_instance, uart_handler_t uart_handler); + + + +/**@brief Function for aborting the receive buffer operation. + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_STATE If it is another/false uart_instance that tries to abort the current operation. + */ +ret_code_t uart_receive_buffer_abort_bkgnd(const uart_instance_t* uart_instance); + + +/**@brief Function for aborting the receive buffer operation. + * + * @details This functions pops an element from the circular rx-fifo (as long as there are elements in the circular rx-buffer). + * + * @param[in] uart_instance Pointer to an initialized uart instance. + * @param[out] data_byte Pointer to a one byte memory, where the bytes from the circular rx-buffer should be stored to. + * + * @retval NRF_SUCCESS If there is data available. + * @retval NRF_ERROR_NOT_FOUND If no new data is available in the rx-buffer. + */ +ret_code_t uart_receive_buffer_get(uart_instance_t* uart_instance, uint8_t* data_byte); + + +/**@brief Function for retrieving the current uart operation. + * + * @retval UART_NO_OPERATION If there is no uart operation ongoing. + * @retval UART_TRANSMIT_OPERATION If there is an uart transmit operation ongoing. + * @retval UART_RECEIVE_OPERATION If there is an uart receive operation ongoing. + * @retval UART_RECEIVE_BUFFER_OPERATION If there is an uart receive-buffer operation ongoing. + */ +uart_operation_t uart_get_operation(const uart_instance_t* uart_instance); + +#endif diff --git a/firmware/nRF_badge/data_collector/main.c b/firmware/nRF_badge/data_collector/main.c old mode 100644 new mode 100755 index bbe0bb6..ba52a4f --- a/firmware/nRF_badge/data_collector/main.c +++ b/firmware/nRF_badge/data_collector/main.c @@ -1,228 +1,142 @@ - +#include "sdk_config.h" #include #include -#include -#include -#include - -/** - * From Nordic SDK - */ -#include "nordic_common.h" -#include "nrf.h" -#include "nrf51_bitfields.h" - -#include "nrf_drv_rtc.h" //driver abstraction for real-time counter -#include "app_error.h" //error handling -#include "nrf_delay.h" //includes blocking delay functions -#include "nrf_gpio.h" //abstraction for dealing with gpio -#include "ble_flash.h" //for writing to flash - -#include "app_error.h" - -#include "ble_gap.h" //basic ble functions (advertising, scans, connecting) - -#include "debug_log.h" //UART debugging logger -//requires app_fifo, app_uart_fifo.c and retarget.c for printf to work properly - -#include "nrf_drv_config.h" -#include "boards.h" - -/** - * Custom libraries/abstractions - */ -#include "analog.h" //analog inputs, battery reading -#include "battery.h" -//#include "external_flash.h" //for interfacing to external SPI flash -#include "scanner.h" //for performing scans and storing scan data -#include "self_test.h" // for built-in tests - -typedef struct { - bool error_occured; - uint32_t error_code; - uint32_t line_num; - char file_name[32]; -} AppErrorData_t; - -static AppErrorData_t mAppErrorData __attribute((section (".noinit"))); - -void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name) { - mAppErrorData.error_occured = true; - mAppErrorData.error_code = error_code; - mAppErrorData.line_num = line_num; - strncpy(mAppErrorData.file_name, (char *) p_file_name, sizeof(mAppErrorData.file_name)); - - NVIC_SystemReset(); -} - -//=========================== Global function definitions ================================== -//========================================================================================== - -#define SCHED_MAX_EVENT_DATA_SIZE sizeof(uint32_t) -#define SCHED_QUEUE_SIZE 100 +#include + +#include "custom_board.h" +#include "nrf_gpio.h" +#include "nrf_delay.h" +#include "softdevice_handler.h" +#include "debug_lib.h" +#include "app_timer.h" +#include "systick_lib.h" +#include "timeout_lib.h" +#include "ble_lib.h" +#include "advertiser_lib.h" +#include "request_handler_lib_01v1.h" +#include "request_handler_lib_02v1.h" +#include "storer_lib.h" +#include "sampling_lib.h" +#include "app_scheduler.h" +#include "selftest_lib.h" +#include "uart_commands_lib.h" + + +void check_init_error(ret_code_t ret, uint8_t identifier); /** * ============================================== MAIN ==================================================== */ int main(void) { - #if defined(BOARD_PCA10028) //NRF51DK - //If button 4 is pressed on startup, do nothing (mostly so that UART lines are freed on the DK board) - nrf_gpio_cfg_input(BUTTON_4,NRF_GPIO_PIN_PULLUP); //button 4 - if(nrf_gpio_pin_read(BUTTON_4) == 0) //button pressed - { - nrf_gpio_pin_dir_set(LED_4,NRF_GPIO_PIN_DIR_OUTPUT); - nrf_gpio_pin_write(LED_4,LED_ON); - while(1); - } - nrf_gpio_cfg_default(BUTTON_4); - #endif - - debug_log_init(); - debug_log("\r\n\r\n\r\n\r\nUART trace initialized.\r\n\r\n"); - - // TODO: Check the reset reason to make sure our noinit RAM is valid - if (mAppErrorData.error_occured) { - debug_log("CRASH! APP ERROR %lu @ %s:%lu\r\n", mAppErrorData.error_code, - mAppErrorData.file_name, mAppErrorData.line_num); - mAppErrorData.error_occured = false; - } - - debug_log("Name: %.5s\r\n",DEVICE_NAME); - debug_log("Firmware Version: %s, Branch: %s, Commit: %s\r\n", GIT_TAG, GIT_BRANCH, GIT_COMMIT); - - - // Define and set LEDs - nrf_gpio_pin_dir_set(LED_1,NRF_GPIO_PIN_DIR_OUTPUT); //set LED pin to output - nrf_gpio_pin_write(LED_1,LED_ON); //turn on LED - nrf_gpio_pin_dir_set(LED_2,NRF_GPIO_PIN_DIR_OUTPUT); //set LED pin to output - nrf_gpio_pin_write(LED_2,LED_OFF); //turn off LED - - // Initialize - BLE_init(); - sd_power_mode_set(NRF_POWER_MODE_LOWPWR); //set low power sleep mode - adc_config(); - rtc_config(); - spi_init(); - - #if defined(TESTER_ENABLE) // tester mode is enabled - runSelfTests(); - while(1); - #endif // end of self tests - - /* - debug_log("=DEVELOPMENT BADGE. ONLY ERASES EEPROM=\r\n"); - debug_log("=ERASING EEPROM...=\r\n"); - ext_eeprom_wait(); - unsigned char empty[EXT_CHUNK_SIZE + EXT_EEPROM_PADDING]; - memset(empty,0,sizeof(empty)); - for (int i = EXT_FIRST_CHUNK; i <= EXT_LAST_CHUNK; i++) { - ext_eeprom_write(EXT_ADDRESS_OF_CHUNK(i),empty,sizeof(empty)); - if (i % 10 == 0) { - nrf_gpio_pin_toggle(LED_1); - nrf_gpio_pin_toggle(LED_2); - } - ext_eeprom_wait(); - } - debug_log(" done. \r\n"); - nrf_gpio_pin_write(LED_1,LED_ON); - nrf_gpio_pin_write(LED_2,LED_ON); - while (1); - */ - - APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); - - collector_init(); - storer_init(); - sender_init(); - scanner_init(); - BatteryMonitor_init(); - - BLEsetBadgeAssignment(getStoredBadgeAssignment()); - advertising_init(); - - // Blink once on start - nrf_gpio_pin_write(LED_1,LED_OFF); - nrf_delay_ms(100); - nrf_gpio_pin_write(LED_1,LED_ON); - nrf_gpio_pin_write(LED_2,LED_ON); - nrf_delay_ms(100); - for (int i=0; i<=2; i++) { - nrf_gpio_pin_write(LED_1,LED_OFF); - nrf_delay_ms(100); - nrf_gpio_pin_write(LED_1,LED_ON); - nrf_delay_ms(100); - nrf_gpio_pin_write(LED_2,LED_OFF); - nrf_delay_ms(100); - nrf_gpio_pin_write(LED_2,LED_ON); - nrf_delay_ms(100); - } - nrf_gpio_pin_write(LED_1,LED_ON); - nrf_gpio_pin_write(LED_2,LED_ON); - nrf_delay_ms(100); - nrf_gpio_pin_write(LED_1,LED_OFF); - nrf_gpio_pin_write(LED_2,LED_OFF); - - - nrf_delay_ms(1000); - - debug_log("Done with setup. Entering main loop.\r\n\r\n"); - - BLEstartAdvertising(); - - nrf_delay_ms(2); - - while (true) { - app_sched_execute(); - sd_app_evt_wait(); - } + nrf_gpio_pin_dir_set(GREEN_LED, NRF_GPIO_PIN_DIR_OUTPUT); //set LED pin to output + nrf_gpio_pin_dir_set(RED_LED,NRF_GPIO_PIN_DIR_OUTPUT); //set LED pin to output + + // Do some nice pattern here: + nrf_gpio_pin_write(RED_LED, LED_ON); //turn on LED + + nrf_delay_ms(100); + nrf_gpio_pin_write(RED_LED, LED_OFF); //turn off LED + + + ret_code_t ret; + + + debug_init(); + + debug_log("MAIN: Start...\n\r"); + + + nrf_clock_lf_cfg_t clock_lf_cfg = {.source = NRF_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM}; + + + SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL); + APP_SCHED_INIT(4, 100); + APP_TIMER_INIT(0, 60, NULL); + + ret = systick_init(0); + check_init_error(ret, 1); + + ret = timeout_init(); + check_init_error(ret, 2); + + ret = ble_init(); + check_init_error(ret, 3); + + ret = sampling_init(); + check_init_error(ret, 4); + + ret = storer_init(); + check_init_error(ret, 5); + + ret = uart_commands_init(); + check_init_error(ret, 6); + + #ifdef TESTER_ENABLE + + selftest_status_t selftest_status = selftest_test(); + debug_log("MAIN: Ret selftest_test: %u\n\r", selftest_status); + (void) selftest_status; + + ret = storer_clear(); + check_init_error(ret, 7); + debug_log("MAIN: Storer clear: %u\n\r", ret); + + #endif + + uint8_t mac[6]; + ble_get_MAC_address(mac); + debug_log("MAIN: MAC address: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + + advertiser_init(); + + ret = advertiser_start_advertising(); + check_init_error(ret, 8); + + ret = request_handler_init(); + check_init_error(ret, 9); + + + // If initialization was successful, blink the green LED 3 times. + for(uint8_t i = 0; i < 3; i++) { + nrf_gpio_pin_write(GREEN_LED, LED_ON); //turn on LED + nrf_delay_ms(100); + nrf_gpio_pin_write(GREEN_LED, LED_OFF); //turn off LED + nrf_delay_ms(100); + } + + (void) ret; + + + while(1) { + app_sched_execute(); // Executes the events in the scheduler queue + sd_app_evt_wait(); // Sleeps until an event/interrupt occurs + } } -void BLEonConnect() -{ - debug_log("--CONNECTED--\r\n"); - sleep = false; - - // for app development. disable if forgotten in prod. version - #ifdef DEBUG_LOG_ENABLE - nrf_gpio_pin_write(LED_1,LED_ON); - #endif - - ble_timeout_set(CONNECTION_TIMEOUT_MS); -} - -void BLEonDisconnect() -{ - debug_log("--DISCONNECTED--\r\n"); - sleep = false; - - // for app development. disable if forgotten in prod. version - #ifdef DEBUG_LOG_ENABLE - nrf_gpio_pin_write(LED_1,LED_OFF); - #endif - - ble_timeout_cancel(); -} - -static void processPendingCommand(void * p_event_data, uint16_t event_size) { - bool sendOperationsRemaining = updateSender(); - if (sendOperationsRemaining) { - app_sched_event_put(NULL, 0, processPendingCommand); - } -} - -/** Function for handling incoming data from the BLE UART service +/**@brief Function that enters a while-true loop if initialization failed. + * + * @param[in] ret Error code from an initialization function. + * @param[in] identifier Identifier, represents the number of red LED blinks. + * */ -void BLEonReceive(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length) -{ - if(length > 0) - { - pendingCommand = unpackCommand(p_data, length); - app_sched_event_put(NULL, 0, processPendingCommand); - } - - ble_timeout_set(CONNECTION_TIMEOUT_MS); +void check_init_error(ret_code_t ret, uint8_t identifier) { + if(ret == NRF_SUCCESS) + return; + while(1) { + for(uint8_t i = 0; i < identifier; i++) { + nrf_gpio_pin_write(RED_LED, LED_ON); //turn on LED + nrf_delay_ms(200); + nrf_gpio_pin_write(RED_LED, LED_OFF); //turn off LED + nrf_delay_ms(200); + } + nrf_delay_ms(2000); + } } diff --git a/firmware/nRF_badge/data_collector/tinybuf/generator/README.md b/firmware/nRF_badge/data_collector/tinybuf/generator/README.md new file mode 100644 index 0000000..4169469 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/generator/README.md @@ -0,0 +1,73 @@ +Tinybuf is a de-/serialization library that efficiently encodes structured data into a binary representation. +It enables a platform and programming language indpendend data exchange. +Currently C and Python is supported as programming languages. + +The process is splitted into two parts: +- During the development, the structured data are defined in "messages" in a schema-file. This schema-file is parsed by the tinybuf-generator. The generator automatically creates source code for the chosen programming language. +- During the runtime of the system, the automatically generated source code can be used to access the structured data and to de-/serialize it. + + + +Schema-file layout: + +- Messages defines the structured data with different fields: + + message Test_message { + required uint8 a; + optional int8 b; + repeated uint16 c[5]; + fixed_repeated int16 d[10]; + oneof e { + uint32 e (1); + int32 f (2); + } + } + +- Define constants: + + define { + TEST_CONSTANT = 100; + TEST1_CONSTANT = 200; + } + +- Import other automatic generated files: + + import file + +- If you import another file, you need to declare the messages you want/need in the current schema-file: + + extern Extern_message; + + +- Supported field/data-types in messages: + - Primitive types: uint8, int8, uint16, int16, uint32, int32, uint64, int64, float, double (The encoding is done via their binary representations (Big or Little Endian)) + - Other messages: Either extern messages or messages define before the current message, using this message as field type + +- Supported field rules in messages: + - required: A field that has to be set and which is always encoded into the binary representation. + - optional: An optional field needs only to be set if necessary. The application needs to set an additional "has"-entry (in form of one byte) whether the field is present or not. + - repeated: Repeated fields represent "arrays" of the same data type. Only the necessary/specified number of elements is encoded into the binary representation. The number of elements in the array is either an integer numbers or a previoulsy defined constant. It is encoded as "header" in the binary representation. + - In C: An extra struct entry "_count", representing the number of elements, is generated. This variable has to be set by the application. + - In Python: The data-structure used is a list. So Python directly knows how many elements are in the list by calling the len() function. + - fixed_repeated: Similar to repeated fields, but the number of elements is always constant --> no size information has to be provided by the application and is encoded. + - oneof: Represents a set of fields, where only one field can be set at a time. An additional "which"-entry (in form of one byte) has to be set, indicating the tag (e.g. "(1)" means the tag is 1) of the field that is set in the oneof field. None of the other field rules is directly supported as field in oneof fields, but can be represented in an own message. It works similar to Protocol Buffers oneof fields (so for better understanding you can look at the doc of Protocol Buffers). + + +Invoking the generator: + + python tinybuf_generator.py + -c or -python + Path to schema file + Path to output directoy + Name of output file + Parameter for endianness (only effect necessary for Python, not C): -be or -le: Big or Little endian + +- In C a .h and .c file are generated. The messages are represented as C structures. The tinybuf-module has to be used for encoding or decoding. These two functions are generic and uses the information of the "_fields"-array of the generated .c file to encode/decode correctly. +- In Python a .py file is generated. The messages are represented as classes, and are instanciated during the runtime. Each class/object has its own encoding/decoding function. + +Attention: +- In C: If multiple schema-files are in the same project containing the same messages, care must be taken that no compilation conflicts occur --> E.g. by manually inserting additional #ifdef/#endif structures in the automated generated source code + +- In Python: If the developer wants only one file with all messages, either one schema-file has to define all the messages without imports, or the developer has to manually copy all automatically generated python classes into one .py-file (with only one _Istream/_Ostream-class) + + diff --git a/firmware/nRF_badge/data_collector/tinybuf/generator/tinybuf_generator.py b/firmware/nRF_badge/data_collector/tinybuf/generator/tinybuf_generator.py new file mode 100644 index 0000000..85f3048 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/generator/tinybuf_generator.py @@ -0,0 +1,1414 @@ + +import struct +import sys +import os.path +import re +import traceback + + + +SUPPORTED_OUTPUT_FORMATS = ['-c', '-python'] + +BIG_ENDIANNESS = 0 +LITTLE_ENDIANNESS = 1 + +SUPPORTED_ENDIANNESS = ['-be', '-le'] + + + + +FIELD_RULE_REQUIRED = 1 +FIELD_RULE_OPTIONAL = 2 +FIELD_RULE_REPEATED = 4 +FIELD_RULE_FIXED_REPEATED = 8 +FIELD_RULE_ONEOF = 16 +FIELD_TYPE_INT = 32 +FIELD_TYPE_UINT = 64 +FIELD_TYPE_FLOAT = 128 +FIELD_TYPE_DOUBLE = 256 +FIELD_TYPE_MESSAGE = 512 + + +# The supported field types. Messages/extern messages are added to this list, so that subsequent messages can use this message as field type. +SUPPORTED_FIELD_TYPES = ['uint8', 'int8', 'uint16', 'int16', 'uint32', 'int32', 'uint64', 'int64', 'float', 'double'] + + + +Imports = [] # Array of imports +Defines = [] # Array of defines +Messages = [] # Array of messages + + + +class Block: + def __init__(self): + self.lines = [] + self.line_numbers = [] + self.iterator_index = 0 + + def setup_iterator(self): + self.iterator_index = 0 + + def has_next(self): + if(self.iterator_index + 1 > len(self.lines)): + return False + return True + + def get_next(self): + if(not self.has_next()): + raise Exception("get_next() has no next element") + + ret = [self.line_numbers[self.iterator_index], self.lines[self.iterator_index]] + self.iterator_index = self.iterator_index + 1 + + return ret + + def extract_block(self, line_number): # returns a new Block object with this line-number as start, and ends with "}", or None if no Block was found + index = 0 + + for i in range(0, len(self.lines)): + if(self.line_numbers[i] == line_number): + if(not '{' in self.lines[i]): + return None + + index = i + + block = Block() + + bracket_open_counter = 0 + for i in range(index, len(self.lines)): + if('{' in self.lines[i]): + bracket_open_counter = bracket_open_counter + 1 + if('}' in self.lines[i]): + bracket_open_counter = bracket_open_counter - 1 + + + block.lines.append(self.lines[i]) + block.line_numbers.append(self.line_numbers[i]) + + + if(bracket_open_counter == 0): + return block + + + return None + + def remove_lines(self, line_numbers): + + for j in range(0, len(line_numbers)): + for i in range(0, len(self.lines)): + if(line_numbers[j] == self.line_numbers[i]): + self.setup_iterator() + del self.lines[i] + del self.line_numbers[i] + break + + + +class File(Block): + # These special characters should be seperated from other characters + SPECIAL_CHARACTERS = [';', '{', '}', '[' , ']', '(' , ')'] + + # returns [line_number, ['A','B',...]] + def __init__(self, file_name): + self.file_name = file_name + Block.__init__(self) + + + with open(self.file_name) as f: + content = f.readlines() + + # you may also want to remove whitespace characters like `\n` at the end of each line + content = [x.strip() for x in content] + + line_number = 0 + for line in content: + + # prepare the string to split all the special characters: + for c in File.SPECIAL_CHARACTERS: + line = line.replace(c, ' ' + c + ' ') + + # split on white space and other splitting characters + splitted = re.split(' |\n|\r|\t', line) + + # Remove zero length chars + rm = [] + for s in splitted: + if len(s) > 0: + rm.append(s) + + line_number = line_number + 1 + if(len(rm) > 0): + self.lines.append(rm) + self.line_numbers.append(line_number) + + + + +def check_format(line, format, format_required): + if(len(line) != len(format)): + return False + for i in range(0, len(line)): + if(format_required[i]): + if(not format[i] == line[i]): + return False + return True + + +def name_valid(field_name): + # TODO: Add more checks here + if(field_name[0].isdigit()): + return False + return True + + +class Define: + def __init__(self, name, number): + self.name = name + self.number = number + + def __repr__(self): + return "Define" + str(self.__dict__) + + @classmethod + def get_define(cls, line): + format = ["'define_name'", "=", "'Integer'", ";"] + format_required = [0, 1, 0, 1] + + if(not check_format(line, format, format_required)): + raise Exception('Expects required field format ' + str(format) + "\nBut given is: " + str(line)) + + define_name = line[0] + if(not Define.define_name_valid(define_name)): + raise Exception('Unsupported define name ' + define_name) + + define_value = int(line[2]) + + define = Define(define_name, define_value) + + # Check for duplicate define: + + for d in Defines: + if(define.name == d.name): + raise Exception("Duplicate define name " + define.name) + + + + return define + + @classmethod + def define_name_valid(cls, define_name): + return name_valid(define_name) + + + +class Field: + + def __init__(self, name): + self.name = name + + @classmethod + def field_type_supported(cls, field_type): + for f in SUPPORTED_FIELD_TYPES: + if(f == field_type): + return True + return False + + @classmethod + def field_name_valid(cls, field_name): + return name_valid(field_name) + + + @classmethod + def get_integer(cls, integer_str): + # Check if integer_str is a number, or if it is already in a define? + + if(integer_str.isdigit()): + return int(integer_str) + else: + for define in Defines: + if(integer_str == define.name): + return define.number + + return None + + +class RequiredField(Field): + def __init__(self, name, type): + Field.__init__(self, name) + self.type = type + + def get_name(self): + names = [self.name] + return names + + def __repr__(self): + return "RequiredField" + str(self.__dict__) + + @classmethod + def get_field(cls, line): + if(line[0] == 'required'): + + format = ["required", "'field_type'", "'field_name'", ";"] + format_required = [1, 0, 0, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects required field format ' + str(format) + "\nBut given is: " + str(line)) + + field_type = line[1] + if(not Field.field_type_supported(field_type)): + raise Exception('Unsupported field type ' + field_type) + + + field_name = line[2] + if(not Field.field_name_valid(field_name)): + raise Exception('Unsupported field name ' + field_name) + + + + return RequiredField(field_name, field_type) + + else: + return None + + +class RepeatedField(Field): + def __init__(self, name, type, size): + Field.__init__(self, name) + self.type = type + self.size = size + + def get_name(self): + names = [self.name] + return names + + def __repr__(self): + return "RepeatedField" + str(self.__dict__) + + + @classmethod + def get_field(cls, line): + if(line[0] == 'repeated'): + + format = ["repeated", "'field_type'", "'field_name'", "[", "'array_size'", "]", ";"] + format_required = [1, 0, 0, 1, 0, 1, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects repeated field format ' + str(format) + "\nBut given is: " + str(line)) + + field_type = line[1] + if(not Field.field_type_supported(field_type)): + raise Exception('Unsupported field type ' + field_type) + + + field_name = line[2] + if(not Field.field_name_valid(field_name)): + raise Exception('Unsupported field name ' + field_name) + + array_size_str = line[4] + array_size = Field.get_integer(array_size_str) + if(array_size == None): + raise Exception('Unsupported array size ' + array_size_str) + + + return RepeatedField(field_name, field_type, array_size) + + else: + return None + + +class FixedRepeatedField(Field): + def __init__(self, name, type, size): + Field.__init__(self, name) + self.type = type + self.size = size + + def get_name(self): + names = [self.name] + return names + + def __repr__(self): + return "FixedRepeatedField" + str(self.__dict__) + + + @classmethod + def get_field(cls, line): + if(line[0] == 'fixed_repeated'): + + format = ["fixed_repeated", "'field_type'", "'field_name'", "[", "'array_size'", "]", ";"] + format_required = [1, 0, 0, 1, 0, 1, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects fixed repeated field format ' + str(format) + "\nBut given is: " + str(line)) + + field_type = line[1] + if(not Field.field_type_supported(field_type)): + raise Exception('Unsupported field type ' + field_type) + + + field_name = line[2] + if(not Field.field_name_valid(field_name)): + raise Exception('Unsupported field name ' + field_name) + + array_size_str = line[4] + array_size = Field.get_integer(array_size_str) + if(array_size == None): + raise Exception('Unsupported array size ' + array_size_str) + + + return FixedRepeatedField(field_name, field_type, array_size) + + else: + return None + +class OptionalField(Field): + def __init__(self, name, type): + Field.__init__(self, name) + self.type = type + + def get_name(self): + names = [self.name] + return names + + def __repr__(self): + return "OptionalField" + str(self.__dict__) + + @classmethod + def get_field(cls, line): + if(line[0] == 'optional'): + + format = ["optional", "'field_type'", "'field_name'", ";"] + format_required = [1, 0, 0, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects optional field format ' + str(format) + "\nBut given is: " + str(line)) + + field_type = line[1] + if(not Field.field_type_supported(field_type)): + raise Exception('Unsupported field type ' + field_type) + + + field_name = line[2] + if(not Field.field_name_valid(field_name)): + raise Exception('Unsupported field name ' + field_name) + + + + return OptionalField(field_name, field_type) + + else: + return None + +class OneofInnerField(Field): + def __init__(self, name, type, tag): + Field.__init__(self, name) + self.type = type + self.tag = tag + + def __repr__(self): + return "OneofInnerField" + str(self.__dict__) + + @classmethod + def get_field(cls, line): + format = ["'field_type'", "'field_name'", "(", "'oneof_tag'" , ")", ";"] + format_required = [0, 0, 1, 0, 1, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects oneof field format ' + str(format) + "\nBut given is: " + str(line)) + + field_type = line[0] + if(not Field.field_type_supported(field_type)): + raise Exception('Unsupported field type ' + field_type) + + + field_name = line[1] + if(not Field.field_name_valid(field_name)): + raise Exception('Unsupported field name ' + field_name) + + oneof_tag_str = line[3] + oneof_tag = Field.get_integer(oneof_tag_str) + if(oneof_tag == None): + raise Exception('Unsupported oneof tag ' + oneof_tag_str) + + + return OneofInnerField(field_name, field_type, oneof_tag) + + +class OneofField(Field): + def __init__(self, name): + Field.__init__(self, name) + self.inner_fields = [] + + def get_name(self): + names = [] + names.append(self.name) + for f in self.inner_fields: + names.append(f.name) + + return names + + def __repr__(self): + return "OneofField" + str(self.__dict__) + + @classmethod + def is_field(cls, line): + if(line[0] == 'oneof'): + + format = ["oneof", "'field_name'", "{"] + format_required = [1, 0, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects oneof field format ' + str(format) + "\nBut given is: " + str(line)) + + + field_name = line[1] + if(not Field.field_name_valid(field_name)): + raise Exception('Unsupported field name ' + field_name) + + return True + else: + return False + + @classmethod + def get_field(cls, block): # returns a oneof field instance from a block + + if(not OneofField.is_field(block.lines[0])): # Just for safety, check if block starts with a oneof-field declaration + raise Exception('Is no oneof field') + + + oneof_field_name = block.lines[0][1] + oneof_field = OneofField(oneof_field_name) + + + for i in range(1, len(block.lines) - 1): + + line = block.lines[i] + + oneof_inner_field = OneofInnerField.get_field(line) + #print(oneof_inner_field) + + + # Check oneof-field tag duplicates + + for inner_field in oneof_field.inner_fields: + if(inner_field.tag == oneof_inner_field.tag): + raise Exception('Duplicate oneof tag: ' + str(oneof_inner_field.tag)) + + oneof_field.inner_fields.append(oneof_inner_field) + + + return oneof_field + + +class Message: + def __init__(self, name): + self.name = name + self.fields = [] + + def __repr__(self): + return str(self.__dict__) + + @classmethod + def get_message(cls, line): + format = ["message", "'message_name'", "{"] + format_required = [1, 0, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects message format ' + str(format) + "\nBut given is: " + str(line)) + + + message_name = line[1] + if(not name_valid(message_name)): + raise Exception('Unsupported message name ' + message_name) + + message = Message(message_name) + + # Add the message to the SUPPORTED_FIELD_TYPES, check duplicate names + for m in SUPPORTED_FIELD_TYPES: + if(m == message_name): + raise Exception("Duplicate message " + m) + + SUPPORTED_FIELD_TYPES.append(message_name) + + return message + + + def add_field(self, field): + + # Check all field names before appending it + names = [] + for f in self.fields: + for name in f.get_name(): + names.append(name) + + for name in field.get_name(): + if(name in names): + raise Exception("Field name duplicate: " + name) + + self.fields.append(field) + + +class ExternMessage: + def __init__(self, name): + self.name = name + + def __repr__(self): + return str(self.__dict__) + + @classmethod + def get_extern_message(cls, line): + format = ["extern", "'extern_message_name'", ";"] + format_required = [1, 0, 1] + if(not check_format(line, format, format_required)): + raise Exception('Expects extern message format ' + str(format) + "\nBut given is: " + str(line)) + + + extern_message_name = line[1] + if(not name_valid(extern_message_name)): + raise Exception('Unsupported extern message name ' + extern_message_name) + + extern_message = ExternMessage(extern_message_name) + + + # Add the message to the SUPPORTED_FIELD_TYPES, check duplicate names + for m in SUPPORTED_FIELD_TYPES: + if(m == extern_message_name): + raise Exception("Duplicate message " + m) + SUPPORTED_FIELD_TYPES.append(extern_message_name) + + return extern_message + + +class Import: + def __init__(self, name): + self.name = name + + def __repr__(self): + return "Import" + str(self.__dict__) + + @classmethod + def get_import(cls, line): + format = ["import", "'import name'"] + format_required = [1, 0] + if(not check_format(line, format, format_required)): + raise Exception('Expects import format ' + str(format) + "\nBut given is: " + str(line)) + + + import_name = line[1] + if(not name_valid(import_name)): + raise Exception('Unsupported import name ' + import_name) + + imp = Import(import_name) + + + # Add the message to the SUPPORTED_FIELD_TYPES, check duplicate names + for i in Imports: + if(i.name == imp.name): + raise Exception("Duplicate import " + imp.name) + + + return imp + + + + +class Parser: + def __init__(self, file_name): + self.File = File(file_name) + + self.parse_imports() + self.parse_extern_messages() + self.parse_defines() + self.parse_messages() + + + def parse_imports(self): + self.File.setup_iterator() + while(self.File.has_next()): + [line_number, line] = self.File.get_next() + if(line[0] == 'import'): + self.File.remove_lines([line_number]) + + try: + # Return value is not needed.. + imp = Import.get_import(line) + Imports.append(imp) + except Exception as e: + print("Exception at line " + str(line_number) + ":") + print(e) + + def parse_extern_messages(self): + self.File.setup_iterator() + while(self.File.has_next()): + [line_number, line] = self.File.get_next() + if(line[0] == 'extern'): + self.File.remove_lines([line_number]) + try: + # Return value is not needed.. + ExternMessage.get_extern_message(line) + except Exception as e: + print("Exception at line " + str(line_number) + ":") + print(e) + + + + def parse_defines(self): + self.File.setup_iterator() + while(self.File.has_next()): + [line_number, line] = self.File.get_next() + if(line[0] == 'define'): + block = self.File.extract_block(line_number) + if(not block == None): + self.File.remove_lines(block.line_numbers) + + + for i in range(1, len(block.lines) - 1): # ignore lines with "define {" and "}" + try: + # Return value is not needed... + define = Define.get_define(block.lines[i]) + Defines.append(define) + except Exception as e: + print("Exception at line " + str(block.line_numbers[i]) + ":") + print(e) + print(traceback.format_exc()) + + + + else: + raise Exception('Could not parse define at line ' + str(line_number)) + + + def parse_messages(self): + self.File.setup_iterator() + while(self.File.has_next()): + [line_number, line] = self.File.get_next() + if(line[0] == 'message'): + + # Create new message object + message = Message.get_message(line) + + + # Extract message block + block = self.File.extract_block(line_number) + if(not block == None): + self.File.remove_lines(block.line_numbers) + + # iterate through the message block + block.setup_iterator() + while(block.has_next()): + [block_line_number, block_line] = block.get_next() + + try: + # Check different fields + field = RequiredField.get_field(block_line) + if(not field == None): + message.add_field(field) + block.remove_lines([block_line_number]) + continue + + + field = RepeatedField.get_field(block_line) + if(not field == None): + message.add_field(field) + block.remove_lines([block_line_number]) + continue + + field = FixedRepeatedField.get_field(block_line) + if(not field == None): + message.add_field(field) + block.remove_lines([block_line_number]) + continue + + field = OptionalField.get_field(block_line) + if(not field == None): + message.add_field(field) + block.remove_lines([block_line_number]) + continue + + + if(OneofField.is_field(block_line)): # check if oneof-field + oneof_block = block.extract_block(block_line_number) + field = OneofField.get_field(oneof_block) + message.add_field(field) + block.remove_lines(oneof_block.line_numbers) + continue + + + + except Exception as e: + print("Exception at line " + str(block_line_number) + ":") + print(e) + print(traceback.format_exc()) + sys.exit() + + # Add message to Messages + Messages.append(message) + + + else: + raise Exception('Could not parse message at line ' + str(line_number)) + sys.exit() + + +class OutputFile: + def __init__(self, output_file): + self.output_file = output_file + self.file_output = "" + + def append_line(self, s = ""): + self.file_output += s + self.file_output += "\n" + + def write_to_file(self): + with open(self.output_file, "w") as f: + f.write(self.file_output) + + +def search_size_type(size): + size_types = [["uint8", 1], ["uint16", 2], ["uint32", 4], ["uint64", 8]] + + for i in range(0, len(size_types)): + if((((2**8)**size_types[i][1]) > size)): + return [size_types[i][0], size_types[i][1]] + + raise Exception("Not found a valid size-type for size: " + str(size)) + + + +class C_Creator: + def __init__(self, output_path, output_name, imports, defines, messages): + self.output_path = output_path + self.output_name = output_name + self.imports = imports + self.defines = defines + self.messages = messages + + self.create() + + def create(self): + print "Creating C/H-Files..." + + self.c_file = OutputFile(self.output_path + "/" + self.output_name + ".c") + self.h_file = OutputFile(self.output_path + "/" + self.output_name + ".h") + + # First write the include-pattern for header files: + self.h_file.append_line("#ifndef " + "__" + self.output_name.upper() + "_H") + self.h_file.append_line("#define " + "__" + self.output_name.upper() + "_H") + self.h_file.append_line() + + # Then write the imports + self.h_file.append_line("#include ") + self.h_file.append_line('#include "tinybuf.h"') + self.c_file.append_line('#include "tinybuf.h"') + self.c_file.append_line('#include "' + self.output_name + ".h" + '"') + self.c_file.append_line() + + # First create the imports + for imp in self.imports: + self.h_file.append_line('#include "' + imp.name + '.h"') + self.h_file.append_line() + + # Then create the defines + for define in self.defines: + self.h_file.append_line("#define " + define.name + " " + str(define.number)) + self.h_file.append_line() + + # Then create the oneof_tags from the messages + for message in self.messages: + self.create_oneof_tags(message) + self.h_file.append_line() + + # Then create the structs from the messages + for message in self.messages: + self.create_struct(message) + self.h_file.append_line() + + + + # Then create the field-arrays from the messages + for message in self.messages: + self.create_message_fields(message) + self.c_file.append_line() + + # Finally close the header-file with #endif + self.h_file.append_line() + self.h_file.append_line("#endif") + + + #print "H-File:" + #print self.h_file.file_output + + #print "C-File:" + #print self.c_file.file_output + + self.c_file.write_to_file() + self.h_file.write_to_file() + + + def create_oneof_tags(self, message): + for field in message.fields: + if(isinstance(field, OneofField)): # Is oneof field + for inner_field in field.inner_fields: + self.h_file.append_line("#define " + message.name + "_" + inner_field.name + "_tag " + str(inner_field.tag)) + + def get_field_type_mapping(self, field_type): + field_type_mapping = {"uint8": "uint8_t", "int8": "int8_t", "uint16": "uint16_t", "int16": "int16_t", + "uint32": "uint32_t", "int32": "int32_t", "uint64": "uint64_t", "int64": "int64_t", + "float": "float", "double": "double"} + + if field_type in field_type_mapping: + return field_type_mapping[field_type] + else: # For example in the case of message as field type + return field_type + + def get_field_type_identifier(self, field_type): + field_type_identifier ={"uint8": FIELD_TYPE_UINT, "int8": FIELD_TYPE_INT, "uint16": FIELD_TYPE_UINT, "int16": FIELD_TYPE_INT, + "uint32": FIELD_TYPE_UINT, "int32": FIELD_TYPE_INT, "uint64": FIELD_TYPE_UINT, "int64": FIELD_TYPE_INT, + "float": FIELD_TYPE_FLOAT, "double": FIELD_TYPE_DOUBLE} + + if field_type in field_type_identifier: + return field_type_identifier[field_type] + else: # For example in the case of message as field type + return FIELD_TYPE_MESSAGE + + + def create_struct(self, message): + self.h_file.append_line("typedef struct {") + for field in message.fields: + if(isinstance(field, RequiredField)): # Is required field + self.h_file.append_line("\t" + self.get_field_type_mapping(field.type) + " " + field.name + ";") + elif(isinstance(field, OptionalField)): # Is optional field + self.h_file.append_line("\t" + "uint8_t has_" + field.name + ";") + self.h_file.append_line("\t" + self.get_field_type_mapping(field.type) + " " + field.name + ";") + elif(isinstance(field, RepeatedField)): # Is repeated field + [size_type, size_type_byte_number] = search_size_type(field.size) + self.h_file.append_line("\t" + self.get_field_type_mapping(size_type) + " " + field.name + "_count;") + self.h_file.append_line("\t" + self.get_field_type_mapping(field.type) + " " + field.name + "[" + str(field.size) + "];") + elif(isinstance(field, FixedRepeatedField)): # Is fixed repeated field + self.h_file.append_line("\t" + self.get_field_type_mapping(field.type) + " " + field.name + "[" + str(field.size) + "];") + elif(isinstance(field, OneofField)): # Is oneof field + self.h_file.append_line("\t" + "uint8_t which_" + field.name + ";") + self.h_file.append_line("\t" + "union {") + for inner_field in field.inner_fields: + self.h_file.append_line("\t\t" + self.get_field_type_mapping(inner_field.type) + " " + inner_field.name + ";") + self.h_file.append_line("\t" + "} " + field.name + ";") + else: + raise Exception ("Field " + str(field) + " is not supported") + self.h_file.append_line("} " + message.name + ";") + + + def create_message_fields(self, message): + + C_FIELD_TYPE_NAME = "tb_field_t" + C_OFFSETOF_MAKRO_NAME = "tb_offsetof" + C_MEMBERSIZE_MAKRO_NAME = "tb_membersize" + C_DELTA_MAKRO_NAME = "tb_delta" + C_LAST_FIELD_NAME = "TB_LAST_FIELD" + + + + + # Determine the number of fields in the message + num_fields = 0 + for field in message.fields: + if(isinstance(field, OneofField)): # Is oneof field + num_fields = num_fields + len(field.inner_fields) + else: + num_fields = num_fields + 1 + + + # Declare the field-array in the H-file + self.h_file.append_line("extern const " + C_FIELD_TYPE_NAME + " " + message.name + "_fields[" + str(num_fields + 1) + "];") + + + # Create the field-arrays in the C-file + self.c_file.append_line("const " + C_FIELD_TYPE_NAME + " " + message.name + "_fields[" + str(num_fields + 1) + "] = {") + + + for field in message.fields: + if(isinstance(field, RequiredField)): # Is required field + field_identifier = self.get_field_type_identifier(field.type) | FIELD_RULE_REQUIRED + field_ptr = str("&" + field.type + "_fields") if (field_identifier & FIELD_TYPE_MESSAGE) else "NULL" + s = "\t{" + str(field_identifier) + ", "; + s += C_OFFSETOF_MAKRO_NAME + "(" + message.name + ", " + field.name + "), " + s += "0, 0, " + s += C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + "), 0, " + "0, 0, " + field_ptr + "}," + self.c_file.append_line(s) + elif(isinstance(field, OptionalField)): # Is optional field + field_identifier = self.get_field_type_identifier(field.type) | FIELD_RULE_OPTIONAL + field_ptr = str("&" + field.type + "_fields") if (field_identifier & FIELD_TYPE_MESSAGE) else "NULL" + s = "\t{" + str(field_identifier) + ", "; + s += C_OFFSETOF_MAKRO_NAME + "(" + message.name + ", " + field.name + "), " + s += C_DELTA_MAKRO_NAME + "(" + message.name + ", " + "has_" + field.name + ", " + field.name + "), 1, " + s += C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + "), 0, " + "0, 0, " + field_ptr + "}," + self.c_file.append_line(s) + elif(isinstance(field, RepeatedField)): # Is repeated field + [size_type, size_type_byte_number] = search_size_type(field.size) + field_identifier = self.get_field_type_identifier(field.type) | FIELD_RULE_REPEATED + field_ptr = str("&" + field.type + "_fields") if (field_identifier & FIELD_TYPE_MESSAGE) else "NULL" + s = "\t{" + str(field_identifier) + ", "; + s += C_OFFSETOF_MAKRO_NAME + "(" + message.name + ", " + field.name + "), " + s += C_DELTA_MAKRO_NAME + "(" + message.name + ", " + field.name + "_count" + ", " + field.name + "), " + str(size_type_byte_number) + ", " + s += C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + "[0]" + "), " + s += C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + ")/" + C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + "[0]" + "), " + s += "0, 0, " + s += field_ptr + "}," + self.c_file.append_line(s) + elif(isinstance(field, FixedRepeatedField)): # Is fixed repeated field + field_identifier = self.get_field_type_identifier(field.type) | FIELD_RULE_FIXED_REPEATED + field_ptr = str("&" + field.type + "_fields") if (field_identifier & FIELD_TYPE_MESSAGE) else "NULL" + s = "\t{" + str(field_identifier) + ", "; + s += C_OFFSETOF_MAKRO_NAME + "(" + message.name + ", " + field.name + "), " + s += "0, 0, " + s += C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + "[0]" + "), " + s += C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + ")/" + C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + "[0]" + "), " + s += "0, 0, " + s += field_ptr + "}," + self.c_file.append_line(s) + elif(isinstance(field, OneofField)): # Is oneof field + for i in range(0, len(field.inner_fields)): + inner_field = field.inner_fields[i] + inner_field_identifier = self.get_field_type_identifier(inner_field.type) | FIELD_RULE_ONEOF + inner_field_ptr = str("&" + inner_field.type + "_fields") if (inner_field_identifier & FIELD_TYPE_MESSAGE) else "NULL" + s = "\t{" + str(inner_field_identifier) + ", "; + s += C_OFFSETOF_MAKRO_NAME + "(" + message.name + ", " + field.name + "." + inner_field.name + "), " + s += C_DELTA_MAKRO_NAME + "(" + message.name + ", " + "which_" + field.name + ", " + field.name + "." + inner_field.name + "), " + "1" + ", " + s += C_MEMBERSIZE_MAKRO_NAME + "(" + message.name + ", " + field.name + "." + inner_field.name + "), " + s += "0, " + s += str(inner_field.tag) + ", " + s += "1" if i == 0 else "0" + s += ", " + inner_field_ptr + "}," + self.c_file.append_line(s) + else: + raise Exception ("Field " + str(field) + " is not supported") + + self.c_file.append_line("\t" + C_LAST_FIELD_NAME + ",") + self.c_file.append_line("};") + + + +class Python_Creator: + def __init__(self, output_path, output_name, imports, defines, messages, endianness): + self.output_path = output_path + self.output_name = output_name + self.imports = imports + self.defines = defines + self.messages = messages + self.endianness = endianness + + self.create() + + + + + + def create(self): + print "Creating python-file..." + + self.python_file = OutputFile(self.output_path + "/" + self.output_name + ".py") + + self.python_file.append_line("import struct") + + # First create the imports + for imp in self.imports: + self.python_file.append_line("from " + imp.name + " import *") + + self.python_file.append_line() + + # Then create the defines + for define in self.defines: + self.python_file.append_line(define.name + " = " + str(define.number)) + self.python_file.append_line() + + # Then create the oneof_tags from the messages + for message in self.messages: + self.create_oneof_tags(message) + self.python_file.append_line() + + # Create Ostream-class + self.python_file.append_line("class _Ostream:") + self.python_file.append_line("\tdef __init__(self):") + self.python_file.append_line("\t\tself.buf = b''") + self.python_file.append_line("\tdef write(self, data):") + self.python_file.append_line("\t\tself.buf += data") + self.python_file.append_line() + + # Create Istream-class + self.python_file.append_line("class _Istream:") + self.python_file.append_line("\tdef __init__(self, buf):") + self.python_file.append_line("\t\tself.buf = buf") + self.python_file.append_line("\tdef read(self, l):") + self.python_file.append_line("\t\tif(l > len(self.buf)):") + self.python_file.append_line('\t\t\traise Exception("Not enough bytes in Istream to read")') + self.python_file.append_line("\t\tret = self.buf[0:l]") + self.python_file.append_line("\t\tself.buf = self.buf[l:]") + self.python_file.append_line("\t\treturn ret") + self.python_file.append_line() + + + # Then create the python-classes from the messages + for message in self.messages: + self.create_class(message) + self.python_file.append_line() + + #print "Python file:" + #print self.python_file.file_output + + + self.python_file.write_to_file() + + + def create_oneof_tags(self, message): + for field in message.fields: + if(isinstance(field, OneofField)): # Is oneof field + for inner_field in field.inner_fields: + self.python_file.append_line(message.name + "_" + inner_field.name + "_tag = " + str(inner_field.tag)) + + + def get_default_value(self, field_type): + field_type_values = {"uint8": "0", "int8": "0", "uint16": "0", "int16": "0", + "uint32": "0", "int32": "0", "uint64": "0", "int64": "0", + "float": "0", "double": "0"} + + if field_type in field_type_values: + return field_type_values[field_type] + else: # For example in the case of message as field type. Here you can also return an "instance" of the message: field_type + "()" + return "None" + + def get_field_type_mapping(self, field_type): + if(self.endianness == BIG_ENDIANNESS): + field_type_mapping = {"uint8": ">B", "int8": ">b", "uint16": ">H", "int16": ">h", + "uint32": ">I", "int32": ">i", "uint64": ">Q", "int64": ">q", + "float": ">f", "double": ">d"} + else: + field_type_mapping = {"uint8": " +#include "stdio.h" + + +/**@brief Function to retrieve the endianness of the system. + * + * @retval TB_BIG_ENDIAN If the system has big-endian format. + * @retval TB_LITTLE_ENDIAN If the system has little-endian format. + */ +static tb_endian_t test_endianness(void) { + union { + uint16_t shortVar; + uint8_t charVar[2]; + } test_endianness; + + test_endianness.shortVar = 0x8000; // das Most Significant Bit innerhalb von 16 + if (test_endianness.charVar[0] != 0) { + return TB_BIG_ENDIAN; + } else { + return TB_LITTLE_ENDIAN; + } +} + + + +/**@brief Function to converts endiannes of input-data if necessary. + * + * @param[in] data_in Pointer to input-data. + * @param[out] data_out Pointer to output-data. + * @param[in] data_size Size of the data to convert. + * @param[in] desired_endianness The desired endianness of the output-data. + */ +static void convert_endianness(uint8_t* data_in, uint8_t* data_out, uint8_t data_size, tb_endian_t desired_endianness) { + // First check the endiannes, to check whether we have to swap the entries or not + static uint8_t endian_test_done = 0; + static tb_endian_t endianness = TB_LITTLE_ENDIAN; + if(endian_test_done == 0) { + endian_test_done = 1; + endianness = test_endianness(); + } + + if(endianness != desired_endianness) { + // Swap array entries to create array with correct endianness + for(uint8_t k = 0; k < data_size; k++) { + data_out[data_size-k-1] = data_in[k]; + } + } else { + for(uint8_t k = 0; k < data_size; k++) { + data_out[k] = data_in[k]; + } + } +} + + + + + +/**@brief Function to create an output-stream from a buffer. + * + * @param[in] buf Pointer to the buffer that should be used by the output-stream. + * @param[in] buf_size Maximal size of the buffer. + * + * @retval tb_ostream_t Output-stream structure. + */ +tb_ostream_t tb_ostream_from_buffer(uint8_t* buf, uint32_t buf_size) { + tb_ostream_t ostream = {buf, buf_size, 0}; + return ostream; +} + +/**@brief Function to write data to ostream. + * + * @param[in] ostream Pointer to output-stream structure. + * @param[in] data Pointer to data that should be written to the output-stream. + * @param[in] len Size of data to be written to the output-stream. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations. + */ +static uint8_t tb_write_to_ostream(tb_ostream_t* ostream, uint8_t* data, uint32_t len) { + if(ostream->bytes_written + len > ostream->buf_size) + return 0; + + memcpy(&(ostream->buf[ostream->bytes_written]), data, len); + /* + printf("Written %u bytes to ostream: {", len); + for(uint32_t i = ostream->bytes_written; i < ostream->bytes_written+len; i++) + printf("0x%X, ", ostream->buf[i]); + printf("}\n"); + */ + ostream->bytes_written += len; + return 1; +} + + +/**@brief Function to write data to ostream in big-endian format. + * + * @param[in] ostream Pointer to output-stream structure. + * @param[in] data Pointer to data that should be written in big endian format to the output-stream. + * @param[in] data_size Size of one data entry to convert from little- to big-endian. + * @param[in] len Number of data entries to convert from little- to big-endian and write to the output-stream. + * @param[in] output_endianness The desired endianness of the output-data. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations. + */ +static uint8_t tb_write_to_ostream_big_endian(tb_ostream_t* ostream, uint8_t* data, uint8_t data_size, uint32_t len, tb_endian_t ouput_endianness) { + uint8_t tmp[data_size]; + + for(uint32_t i = 0; i < len; i++) { + uint8_t* cur_data = data + (((uint32_t)data_size)*i); + convert_endianness(cur_data, tmp, data_size, ouput_endianness); + if(!tb_write_to_ostream(ostream, tmp, data_size)) + return 0; + } + return 1; +} + + + +/**@brief Function to create an input-stream from a buffer. + * + * @param[in] buf Pointer to the buffer that should be used by the output-stream. + * @param[in] buf_size Size of the input-buffer. + * + * @retval tb_istream_t Input-stream structure. + */ +tb_istream_t tb_istream_from_buffer(uint8_t* buf, uint32_t buf_size) { + tb_istream_t istream = {buf, buf_size, 0}; + return istream; +} + +/**@brief Function to read data from the input-stream. + * + * @param[in] istream Pointer to input-stream structure. + * @param[in] data Pointer to data where the read data should be stored to. + * @param[in] len Size of the data that should be read from the input-stream. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations. + */ +static uint8_t tb_read_from_istream(tb_istream_t* istream, uint8_t* data, uint32_t len) { + if(istream->bytes_read + len > istream->buf_size) + return 0; + + memcpy(data, &(istream->buf[istream->bytes_read]), len); + /* + printf("Read %u bytes from istream: {", len); + for(uint32_t i = istream->bytes_read; i < istream->bytes_read+len; i++) + printf("0x%X, ", istream->buf[i]); + printf("}\n"); + */ + istream->bytes_read += len; + return 1; +} + +/**@brief Function to read data from istream in little-endian format. + * + * @param[in] istream Pointer to input-stream structure. + * @param[in] data Pointer to data where the read data should be stored to (in little-endian format). + * @param[in] data_size Size of one data entry to convert from big- to little-endian. + * @param[in] len Number of data entries to convert from big- to little-endian. + * @param[in] input_endianness The endianness of the input-data. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations. + */ +static uint8_t tb_read_from_istream_little_endian(tb_istream_t* istream, uint8_t* data, uint8_t data_size, uint32_t len, tb_endian_t input_endianness) { + uint8_t tmp[data_size]; + + for(uint32_t i = 0; i < len; i++) { + if(!tb_read_from_istream(istream, tmp, data_size)) + return 0; + uint8_t* cur_data = data + (((uint32_t)data_size)*i); + convert_endianness(tmp, cur_data, data_size, input_endianness); + + } + return 1; +} + + +/**@brief Function to check if an oneof-which tag is valid. + * + * @details This function should only be called at the first field of the oneof-field. + * + * @param[in] fields Pointer to the array of structure-fields. + * @param[in] which_tag The which_field to test. + * @param[in] field_index The first field index of the oneof-field. + + * + * @retval 1 On success. + * @retval 0 On failure, when the which_tag was not found in. + */ +static uint8_t check_oneof_which_tag_validity(const tb_field_t fields[], uint8_t which_tag, uint8_t field_index) { + uint8_t i = field_index; + do { + // Check if the first found field_index has the which_tag: + if(fields[i].oneof_tag == which_tag) + return 1; + i++; + } while((fields[i].type & FIELD_TYPE_ONEOF) && (fields[i].oneof_first == 0)); // Search until the end of the Oneof-field, or until a new Oneof-field starts + + return 0; +} + + + +/**@brief Function to serialize a structure into an output-stream. + * + * @param[in] ostream Pointer to output-stream structure. + * @param[in] fields Pointer to the array of structure-fields. + * @param[in] src_struct Pointer to structure that should be serialized. + * @param[in] output_endianness The desired endianness of the output-data. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations or invalid structure. + */ +uint8_t tb_encode(tb_ostream_t* ostream, const tb_field_t fields[], void* src_struct, tb_endian_t output_endianness) { + uint8_t i = 0; + + while(fields[i].type != 0) { + tb_field_t field = fields[i]; + // All these types are little endian + if(field.type & DATA_TYPE_INT || field.type & DATA_TYPE_UINT || field.type & DATA_TYPE_FLOAT || field.type & DATA_TYPE_DOUBLE) { + void* data_ptr; + data_ptr = ((uint8_t*)src_struct + field.data_offset); + + if(field.type & FIELD_TYPE_REQUIRED) { + if(!tb_write_to_ostream_big_endian(ostream, (uint8_t*) data_ptr, field.data_size, 1, output_endianness)) + return 0; + } else if(field.type & FIELD_TYPE_OPTIONAL) { + + // Check the has ptr-flag + void* has_ptr = ((uint8_t*)data_ptr + field.size_offset); + uint8_t has_flag = *((uint8_t*) has_ptr); + if(!tb_write_to_ostream(ostream, (uint8_t*) has_ptr, field.size_size)) // Could actually also write just 1 byte + return 0; + + //printf("Has flag is: %u!\n", has_flag); + + // Only write the data if has_flag is true + if(has_flag) { + if(!tb_write_to_ostream_big_endian(ostream, (uint8_t*) data_ptr, field.data_size, 1, output_endianness)) + return 0; + } + } else if(field.type & FIELD_TYPE_REPEATED) { + // Read the number of elements: + void* count_ptr = ((uint8_t*)data_ptr + field.size_offset); + uint32_t count_size = field.size_size; + uint32_t count = 0; + if(count_size > sizeof(count)) { + //printf("Error count_size is too big for uint32_t!\n"); + return 0; + } + uint8_t tmp[count_size]; + convert_endianness((uint8_t*)count_ptr, tmp, count_size, TB_BIG_ENDIAN); + // Create the actual count + for(uint8_t k = 0; k < count_size; k++) { + count |= (tmp[count_size-1-k]) << (8*k); + } + //printf("Count: %u\n", count); + + // Check the count: + if(count > field.array_size) { + //printf("Count exceeds array size!\n"); + return 0; + } + if(!tb_write_to_ostream_big_endian(ostream, (uint8_t*)count_ptr, count_size, 1, output_endianness)) + return 0; + + // Now write the actual data of the array + if(!tb_write_to_ostream_big_endian(ostream, (uint8_t*) data_ptr, field.data_size, count, output_endianness)) + return 0; + } else if(field.type & FIELD_TYPE_FIXED_REPEATED) { + // Write the actual data of the array + if(!tb_write_to_ostream_big_endian(ostream, (uint8_t*) data_ptr, field.data_size, field.array_size, output_endianness)) + return 0; + } else if (field.type & FIELD_TYPE_ONEOF) { + void* which_ptr = ((uint8_t*) data_ptr + field.size_offset); + uint8_t which = *((uint8_t*) which_ptr); + + // Only write if this is the first field of the oneof-field + if(field.oneof_first) { + // Check the validity of which-tag + if(!check_oneof_which_tag_validity(fields, which, i)) + return 0; + + if(!tb_write_to_ostream(ostream, (uint8_t*) which_ptr, field.size_size)) // Could actually also write just 1 byte + return 0; + } + + if(which == field.oneof_tag) { + // Here we assume to have a required-field type! + if(!tb_write_to_ostream_big_endian(ostream, (uint8_t*) data_ptr, field.data_size, 1, output_endianness)) + return 0; + } + } else { + //printf("Error no field type specified\n"); + return 0; + } + } else if (field.type & DATA_TYPE_MESSAGE) { + + + void* struct_ptr; + struct_ptr = ((uint8_t*)src_struct + field.data_offset); + + if(field.type & FIELD_TYPE_REQUIRED) { + // Recursive call of encode function + if(!tb_encode(ostream, (tb_field_t*) field.ptr, struct_ptr, output_endianness)) + return 0; + } else if(field.type & FIELD_TYPE_OPTIONAL) { + // Check the has ptr-flag + void* has_ptr = ((uint8_t*)struct_ptr + field.size_offset); + uint8_t has_flag = *((uint8_t*) has_ptr); + if(!tb_write_to_ostream(ostream, (uint8_t*) has_ptr, field.size_size)) // Could actually also write just 1 byte + return 0; + + //printf("Has flag is: %u!\n", has_flag); + + // Only write the data if has_flag is true + if(has_flag) { + // Recursive call of encode function + if(!tb_encode(ostream, (tb_field_t*) field.ptr, struct_ptr, output_endianness)) + return 0; + } + + + } else if(field.type & FIELD_TYPE_REPEATED) { + + // Read the number of elements: + void* count_ptr = ((uint8_t*)struct_ptr + field.size_offset); + uint32_t count_size = field.size_size; + uint32_t count = 0; + if(count_size > sizeof(count)) { + //printf("Error count_size is too big for uint32_t!\n"); + return 0; + } + uint8_t tmp[count_size]; + convert_endianness((uint8_t*)count_ptr, tmp, count_size, TB_BIG_ENDIAN); + // Create the actual count + for(uint8_t k = 0; k < count_size; k++) { + count |= (tmp[count_size-1-k]) << (8*k); + } + //printf("Count: %u\n", count); + // Check the count: + if(count > field.array_size) { + //printf("Count exceeds array size!\n"); + return 0; + } + + if(!tb_write_to_ostream_big_endian(ostream, (uint8_t*)count_ptr, count_size, 1, output_endianness)) + return 0; + + + for(uint32_t k = 0; k < count; k++) { + struct_ptr = ((uint8_t*)src_struct + field.data_offset + k*field.data_size); + // Recursive call of encode function + if(!tb_encode(ostream, (tb_field_t*) field.ptr, struct_ptr, output_endianness)) + return 0; + } + } else if(field.type & FIELD_TYPE_FIXED_REPEATED) { + for(uint32_t k = 0; k < field.array_size; k++) { + struct_ptr = ((uint8_t*)src_struct + field.data_offset + k*field.data_size); + // Recursive call of encode function + if(!tb_encode(ostream, (tb_field_t*) field.ptr, struct_ptr, output_endianness)) + return 0; + } + } else if (field.type & FIELD_TYPE_ONEOF) { + void* which_ptr = ((uint8_t*) struct_ptr + field.size_offset); + uint8_t which = *((uint8_t*) which_ptr); + + // Only write if this is the first field of the oneof-field + if(field.oneof_first) { + // Check the validity of which-tag + if(!check_oneof_which_tag_validity(fields, which, i)) + return 0; + + if(!tb_write_to_ostream(ostream, (uint8_t*) which_ptr, field.size_size)) // Could actually also write just 1 byte + return 0; + } + + if(which == field.oneof_tag) { + // Here we assume to have a required-field type! + // Recursive call of encode function + if(!tb_encode(ostream, (tb_field_t*) field.ptr, struct_ptr, output_endianness)) + return 0; + } + } else { + //printf("Error no field type specified\n"); + return 0; + } + } else { + //printf("Error no data type specified\n"); + return 0; + } + + i++; + } + return 1; +} + + + + +/**@brief Function to deserialize an input-stream to a structure. + * + * @param[in] istream Pointer to input-stream structure. + * @param[in] fields Pointer to the array of structure-fields. + * @param[in] src_struct Pointer to structure where the deserialized data should be stored to. + * @param[in] input_endianness The endianness of the input-data. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations or invalid structure. + */ +uint8_t tb_decode(tb_istream_t* istream, const tb_field_t fields[], void* dst_struct, tb_endian_t input_endianness) { + uint8_t i = 0; + + while(fields[i].type != 0) { + tb_field_t field = fields[i]; + // All these types are little endian + if(field.type & DATA_TYPE_INT || field.type & DATA_TYPE_UINT || field.type & DATA_TYPE_FLOAT || field.type & DATA_TYPE_DOUBLE) { + void* data_ptr; + data_ptr = ((uint8_t*)dst_struct + field.data_offset); + + if(field.type & FIELD_TYPE_REQUIRED) { + if(!tb_read_from_istream_little_endian(istream, (uint8_t*) data_ptr, field.data_size, 1, input_endianness)) + return 0; + } else if(field.type & FIELD_TYPE_OPTIONAL) { + + // Check the has ptr-flag + void* has_ptr = ((uint8_t*)data_ptr + field.size_offset); + if(!tb_read_from_istream(istream, (uint8_t*) has_ptr, field.size_size)) // Could actually also read just 1 byte + return 0; + + uint8_t has_flag = *((uint8_t*) has_ptr); + //printf("Has flag is: %u!\n", has_flag); + + // Only write the data if has_flag is true + if(has_flag) { + if(!tb_read_from_istream_little_endian(istream, (uint8_t*) data_ptr, field.data_size, 1, input_endianness)) + return 0; + } + } else if(field.type & FIELD_TYPE_REPEATED) { + // Read the number of elements: + void* count_ptr = ((uint8_t*)data_ptr + field.size_offset); + uint32_t count_size = field.size_size; + uint32_t count = 0; + if(count_size > sizeof(count)) { + //printf("Error count_size is too big for uint32_t!\n"); + return 0; + } + if(!tb_read_from_istream_little_endian(istream, (uint8_t*) count_ptr, count_size, 1, input_endianness)) + return 0; + + uint8_t tmp[count_size]; + convert_endianness((uint8_t*)count_ptr, tmp, count_size, TB_BIG_ENDIAN); + // Create the actual count + for(uint8_t k = 0; k < count_size; k++) { + count |= (tmp[count_size-1-k]) << (8*k); + } + //printf("Count: %u\n", count); + // Check the count: + if(count > field.array_size) { + //printf("Count exceeds array size!\n"); + return 0; + } + + // Now read the actual data of the array + if(!tb_read_from_istream_little_endian(istream, (uint8_t*) data_ptr, field.data_size, count, input_endianness)) + return 0; + + } else if(field.type & FIELD_TYPE_FIXED_REPEATED) { + // Write the actual data of the array + if(!tb_read_from_istream_little_endian(istream, (uint8_t*) data_ptr, field.data_size, field.array_size, input_endianness)) + return 0; + + } else if (field.type & FIELD_TYPE_ONEOF) { + void* which_ptr = ((uint8_t*) data_ptr + field.size_offset); + + + // Only read if this is the first field of the oneof-field + if(field.oneof_first) + if(!tb_read_from_istream(istream, (uint8_t*) which_ptr, field.size_size)) // Could actually also read just 1 byte + return 0; + + // Here we can just read the which-entry from the structure (the field in the structure should have been written by the first-oneof field) + uint8_t which = *((uint8_t*) which_ptr); + + if(field.oneof_first) + // Check the validity of which-tag + if(!check_oneof_which_tag_validity(fields, which, i)) + return 0; + + if(which == field.oneof_tag) { + // Here we assume to have a required-field type! + if(!tb_read_from_istream_little_endian(istream, (uint8_t*) data_ptr, field.data_size, 1, input_endianness)) + return 0; + } + } else { + //printf("Error no field type specified\n"); + return 0; + } + } else if (field.type & DATA_TYPE_MESSAGE) { + + void* struct_ptr; + struct_ptr = ((uint8_t*)dst_struct + field.data_offset); + + if(field.type & FIELD_TYPE_REQUIRED) { + // Recursive call of decode function + if(!tb_decode(istream, (tb_field_t*) field.ptr, struct_ptr, input_endianness)) + return 0; + } else if(field.type & FIELD_TYPE_OPTIONAL) { + + // Check the has ptr-flag + void* has_ptr = ((uint8_t*)struct_ptr + field.size_offset); + if(!tb_read_from_istream(istream, (uint8_t*) has_ptr, field.size_size)) // Could actually also read just 1 byte + return 0; + + uint8_t has_flag = *((uint8_t*) has_ptr); + //printf("Has flag is: %u!\n", has_flag); + + // Only read the data if has_flag is true + if(has_flag) { + // Recursive call of decode function + if(!tb_decode(istream, (tb_field_t*) field.ptr, struct_ptr, input_endianness)) + return 0; + } + + + } else if(field.type & FIELD_TYPE_REPEATED) { + + // Read the number of elements: + void* count_ptr = ((uint8_t*)struct_ptr + field.size_offset); + uint32_t count_size = field.size_size; + uint32_t count = 0; + if(count_size > sizeof(count)) { + //printf("Error count_size is too big for uint32_t!\n"); + return 0; + } + if(!tb_read_from_istream_little_endian(istream, (uint8_t*) count_ptr, count_size, 1, input_endianness)) + return 0; + + uint8_t tmp[count_size]; + convert_endianness((uint8_t*)count_ptr, tmp, count_size, TB_BIG_ENDIAN); + // Create the actual count + for(uint8_t k = 0; k < count_size; k++) { + count |= (tmp[count_size-1-k]) << (8*k); + } + //printf("Count: %u\n", count); + // Check the count: + if(count > field.array_size) { + //printf("Count exceeds array size!\n"); + return 0; + } + + for(uint32_t k = 0; k < count; k++) { + struct_ptr = ((uint8_t*)dst_struct + field.data_offset + k*field.data_size); + // Recursive call of encode function + if(!tb_decode(istream, (tb_field_t*) field.ptr, struct_ptr, input_endianness)) + return 0; + } + + } else if(field.type & FIELD_TYPE_FIXED_REPEATED) { + + for(uint32_t k = 0; k < field.array_size; k++) { + struct_ptr = ((uint8_t*)dst_struct + field.data_offset + k*field.data_size); + // Recursive call of encode function + if(!tb_decode(istream, (tb_field_t*) field.ptr, struct_ptr, input_endianness)) + return 0; + } + + } else if (field.type & FIELD_TYPE_ONEOF) { + void* which_ptr = ((uint8_t*) struct_ptr + field.size_offset); + + // Only read if this is the first field of the oneof-field + if(field.oneof_first) + if(!tb_read_from_istream(istream, (uint8_t*) which_ptr, field.size_size)) // Could actually also read just 1 byte + return 0; + + // Here we can just read the which-entry from the structure (the field in the structure should have been written by the first-oneof field) + uint8_t which = *((uint8_t*) which_ptr); + + if(field.oneof_first) + // Check the validity of which-tag + if(!check_oneof_which_tag_validity(fields, which, i)) + return 0; + + if(which == field.oneof_tag) { + // Here we assume to have a required-field type! + // Recursive call of encode function + if(!tb_decode(istream, (tb_field_t*) field.ptr, struct_ptr, input_endianness)) + return 0; + } + } else { + //printf("Error no field type specified\n"); + return 0; + } + + } else { + //printf("Error no data type specified\n"); + return 0; + } + i++; + } + return 1; +} + + + + +uint32_t tb_get_max_encoded_len(const tb_field_t fields[]) { + uint8_t i = 0; + uint32_t len = 0; + + while(fields[i].type != 0) { + tb_field_t field = fields[i]; + if(field.type & DATA_TYPE_INT || field.type & DATA_TYPE_UINT || field.type & DATA_TYPE_FLOAT || field.type & DATA_TYPE_DOUBLE) { + if(field.type & FIELD_TYPE_REQUIRED) { + len += field.data_size; + } else if(field.type & FIELD_TYPE_OPTIONAL) { + len += field.size_size; + len += field.data_size; + } else if(field.type & FIELD_TYPE_REPEATED) { + len += field.size_size; + len += ((uint32_t) field.array_size) * field.data_size; + } else if(field.type & FIELD_TYPE_FIXED_REPEATED) { + len += ((uint32_t) field.array_size) * field.data_size; + } else if (field.type & FIELD_TYPE_ONEOF) { + // Here we need to search for the max + len += field.size_size; // The which field + uint32_t tmp_len_max = 0; + do { + uint32_t tmp_len = 0; + if(fields[i].type & DATA_TYPE_MESSAGE) { + tmp_len = tb_get_max_encoded_len((tb_field_t*) fields[i].ptr); + } else { + tmp_len = fields[i].data_size; + } + if(tmp_len > tmp_len_max) { + tmp_len_max = tmp_len; + } + i++; + } while((fields[i].type & FIELD_TYPE_ONEOF) && (fields[i].oneof_first == 0)); // Search until the end of the Oneof-field, or until a new Oneof-field starts + i--; // Decrement one again, because we stepped one too far + len += tmp_len_max; + } + } else if (field.type & DATA_TYPE_MESSAGE) { + if(field.type & FIELD_TYPE_REQUIRED) { + len += tb_get_max_encoded_len((tb_field_t*) field.ptr); + } else if(field.type & FIELD_TYPE_OPTIONAL) { + len += field.size_size; + len += tb_get_max_encoded_len((tb_field_t*) field.ptr); + } else if(field.type & FIELD_TYPE_REPEATED) { + len += field.size_size; + len += ((uint32_t) field.array_size) * tb_get_max_encoded_len((tb_field_t*) field.ptr); + } else if(field.type & FIELD_TYPE_FIXED_REPEATED) { + len += ((uint32_t) field.array_size) * tb_get_max_encoded_len((tb_field_t*) field.ptr); + } else if (field.type & FIELD_TYPE_ONEOF) { + // Here we need to search for the max + len += field.size_size; // The which field + uint32_t tmp_len_max = 0; + do { + uint32_t tmp_len = 0; + if(field.type & DATA_TYPE_MESSAGE) { + tmp_len = tb_get_max_encoded_len((tb_field_t*) field.ptr); + } else { + tmp_len = fields[i].data_size; + } + if(tmp_len > tmp_len_max) { + tmp_len_max = tmp_len; + } + i++; + } while((fields[i].type & FIELD_TYPE_ONEOF) && (fields[i].oneof_first == 0)); // Search until the end of the Oneof-field, or until a new Oneof-field starts + i--; // Decrement one again, because we stepped one too far + len += tmp_len_max; + } + } + i++; + } + return len; +} + + diff --git a/firmware/nRF_badge/data_collector/tinybuf/incl/tinybuf.h b/firmware/nRF_badge/data_collector/tinybuf/incl/tinybuf.h new file mode 100644 index 0000000..f11befb --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/incl/tinybuf.h @@ -0,0 +1,121 @@ +#ifndef __TINYBUF_H +#define __TINYBUF_H + + +#include +#include + +#define tb_membersize(st, m) ((uint32_t)(sizeof ((st*)0)->m)) +#define tb_offsetof(st, m) ((uint32_t) (offsetof(st, m))) +#define tb_delta(st, m1, m2) (((int32_t)tb_offsetof(st, m1)) - ((int32_t)tb_offsetof(st, m2))) + + +typedef enum { + TB_BIG_ENDIAN = 0, + TB_LITTLE_ENDIAN = 1 +} tb_endian_t; + +typedef enum { + FIELD_TYPE_REQUIRED = (1 << 0), + FIELD_TYPE_OPTIONAL = (1 << 1), + FIELD_TYPE_REPEATED = (1 << 2), + FIELD_TYPE_FIXED_REPEATED = (1 << 3), + FIELD_TYPE_ONEOF = (1 << 4), +} tb_field_type_t; + +typedef enum { + DATA_TYPE_INT = (1 << 5), + DATA_TYPE_UINT = (1 << 6), + DATA_TYPE_FLOAT = (1 << 7), + DATA_TYPE_DOUBLE = (1 << 8), + DATA_TYPE_MESSAGE = (1 << 9), +} tb_data_type_t; + + +#define TB_LAST_FIELD {0, 0, 0, 0, 0, 0, 0, 0, NULL} /**< Marker for the last field in a field-array */ + +typedef struct { + uint16_t type; /**< optional/repeated/required. uint, int, float, double, submessage */ + uint32_t data_offset; /**< Offset of data relative to begin of struct */ + int32_t size_offset; /**< Offset to bool flag when optional-field or to size when repeated or to which_ when oneof, relative to field data */ + uint32_t size_size; /**< Size of bool flag or the size of size/count of an array or the which_-field of oneof-field */ + uint32_t data_size; /**< Size of one data entry (1,2,4,8,submessage_size) */ + uint32_t array_size; /**< Size of array. 0 if it is not an array */ + uint8_t oneof_tag; /**< The tag-number/identifier of a oneof-field. 0 if not an oneof-field. */ + uint8_t oneof_first; /**< Flag if the field is the first oneof-entry. */ + const void* ptr; /**< Pointer to submessage description, otherwise NULL */ +} tb_field_t; + + + +typedef struct { + uint8_t* buf; + uint32_t buf_size; + uint32_t bytes_written; +} tb_ostream_t; + +typedef struct { + uint8_t* buf; + uint32_t buf_size; + uint32_t bytes_read; +} tb_istream_t; + + +/**@brief Function to create an output-stream from a buffer. + * + * @param[in] buf Pointer to the buffer that should be used by the output-stream. + * @param[in] buf_size Maximal size of the buffer. + * + * @retval tb_ostream_t Output-stream structure. + */ +tb_ostream_t tb_ostream_from_buffer(uint8_t* buf, uint32_t buf_size); + + +/**@brief Function to create an input-stream from a buffer. + * + * @param[in] buf Pointer to the buffer that should be used by the output-stream. + * @param[in] buf_size Size of the input-buffer. + * + * @retval tb_istream_t Input-stream structure. + */ +tb_istream_t tb_istream_from_buffer(uint8_t* buf, uint32_t buf_size); + + + +/**@brief Function to serialize a structure into an output-stream. + * + * @param[in] ostream Pointer to output-stream structure. + * @param[in] fields Pointer to the array of structure-fields. + * @param[in] src_struct Pointer to structure that should be serialized. + * @param[in] output_endianness The desired endianness of the output-data. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations or invalid structure. + */ +uint8_t tb_encode(tb_ostream_t* ostream, const tb_field_t fields[], void* src_struct, tb_endian_t output_endianness); + + +/**@brief Function to deserialize an input-stream to a structure. + * + * @param[in] istream Pointer to input-stream structure. + * @param[in] fields Pointer to the array of structure-fields. + * @param[in] src_struct Pointer to structure where the deserialized data should be stored to. + * @param[in] input_endianness The endianness of the input-data. + * + * @retval 1 On success. + * @retval 0 On failure, due to buffer limitations or invalid structure. + */ +uint8_t tb_decode(tb_istream_t* istream, const tb_field_t fields[], void* dst_struct, tb_endian_t input_endianness); + + +/**@brief Function to retrieve the max encoded length of a message. + * + * @param[in] fields Pointer to the array of structure-fields. + * + * @retval Maximum encoded length. + */ +uint32_t tb_get_max_encoded_len(const tb_field_t fields[]); + + +#endif + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/Makefile b/firmware/nRF_badge/data_collector/tinybuf/tests/Makefile new file mode 100644 index 0000000..d816960 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/Makefile @@ -0,0 +1,109 @@ + + +# Where to find source code to test +SOURCE_DIR = incl + +# Where to find test code. +TEST_DIR = . + +# Where the builded files will be stored to. +BUILD_DIR = _build + + +# The pathes to tinybuf-file-dir, to tinybuf-include-dir, to tinybuf-generator-dir and to output-dir +TINYBUF_DIR := .. +TINYBUF_SRC_DIR := $(TINYBUF_DIR)/incl + + +# Points to the root of Google Test, relative to where this file is. +# Remember to tweak this if you move this file. +GTEST_DIR = googletest + + +# If you want no cmd-line print outs of the cmds +NO_ECHO := @ + +# All tests produced by this Makefile. Remember to add new tests you +# created to the list. +TESTS = tester + + +SOURCE_INC_DIR += -I$(SOURCE_DIR) +TINYBUF_INC_DIR += -I$(TINYBUF_SRC_DIR) + +#$(info SOURCE_INC_DIR=${SOURCE_INC_DIR}) +#$(info TINYBUF_INC_DIR=${TINYBUF_INC_DIR}) + + + +CFLAGS += -Wall -Werror -Wextra + + +# Add the CFLAGS to the CXXFLAGS +CXXFLAGS += $(CFLAGS) + + + +SOURCE_FILES = $(notdir $(wildcard $(SOURCE_DIR)/*.c)) +SOURCE_OBJECTS += $(addprefix $(BUILD_DIR)/,$(SOURCE_FILES:.c=.o)) + +TESTS_OBJECTS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o, $(TESTS))) + + + +TINYBUF_FILES = $(notdir $(wildcard $(TINYBUF_SRC_DIR)/*.c)) +TINYBUF_OBJECTS += $(addprefix $(BUILD_DIR)/,$(TINYBUF_FILES:.c=.o)) +#$(info TINYBUF_SRC_DIR=${TINYBUF_SRC_DIR}) +#$(info TINYBUF_FILES=${TINYBUF_FILES}) +#$(info TINYBUF_OBJECTS=${TINYBUF_OBJECTS}) + + +RUN_TESTS = $(addprefix run_, $(TESTS)) + + +all : clean $(TESTS) + + +clean : + $(NO_ECHO)rm -f -r $(BUILD_DIR) + $(NO_ECHO)mkdir $(BUILD_DIR) + + +# Target for running all unit tests +run_all : $(RUN_TESTS) + +# Make the run_..-targets Phony. +.PHONY: $(RUN_TESTS) + + +# Target for running one specific unit test +$(RUN_TESTS) : + @echo + @echo ==================================================================== + @echo Run test: $@ + @echo ==================================================================== + $(NO_ECHO)(cd $(BUILD_DIR)/ ; ./$@) + @echo ==================================================================== + + + +$(SOURCE_OBJECTS): + @echo Compiling $(notdir $@) + $(NO_ECHO) $(CXX) $(CPPFLAGS) $(TINYBUF_INC_DIR) $(SOURCE_INC_DIR) $(CXXFLAGS) -o $@ -c $(SOURCE_DIR)/$(basename $(notdir $@)).c* + +$(TINYBUF_OBJECTS): + @echo Compiling $(notdir $@) + $(NO_ECHO) $(CXX) $(CPPFLAGS) $(TINYBUF_INC_DIR) $(SOURCE_INC_DIR) $(CXXFLAGS) -o $@ -c $(TINYBUF_SRC_DIR)/$(basename $(notdir $@)).c* + +$(TESTS_OBJECTS): $(TINYBUF_OBJECTS) $(SOURCE_OBJECTS) + @echo Compiling $(notdir $@) +# @echo Test object $(basename $@) + $(NO_ECHO)$(CXX) $(CPPFLAGS) $(TINYBUF_INC_DIR) $(SOURCE_INC_DIR) $(CXXFLAGS) -o $@ -c $(TEST_DIR)/$(basename $(notdir $@)).c* + +#https://stackoverflow.com/questions/16262344/pass-a-target-name-to-dependency-list-in-makefile +$(TESTS): % : $(BUILD_DIR)/%.o + @echo Linking $(notdir $@) + $(NO_ECHO)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(TINYBUF_INC_DIR) $(SOURCE_INC_DIR) $(BUILD_DIR)/$@.o $(TINYBUF_OBJECTS) $(SOURCE_OBJECTS) -o $(BUILD_DIR)/run_$@ + + + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_parent_protocol.c b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_parent_protocol.c new file mode 100644 index 0000000..303e3d5 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_parent_protocol.c @@ -0,0 +1,7 @@ +#include "tinybuf.h" +#include "test_parent_protocol.h" + +const tb_field_t Empty_message_fields[1] = { + TB_LAST_FIELD, +}; + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_parent_protocol.h b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_parent_protocol.h new file mode 100644 index 0000000..1c8bf81 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_parent_protocol.h @@ -0,0 +1,14 @@ +#ifndef __TEST_PARENT_PROTOCOL_H +#define __TEST_PARENT_PROTOCOL_H + +#include +#include "tinybuf.h" + + + +typedef struct { +} Empty_message; + +extern const tb_field_t Empty_message_fields[1]; + +#endif diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_protocol.c b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_protocol.c new file mode 100644 index 0000000..442c9d7 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_protocol.c @@ -0,0 +1,31 @@ +#include "tinybuf.h" +#include "test_protocol.h" + +const tb_field_t Embedded_message1_fields[2] = { + {66, tb_offsetof(Embedded_message1, e), tb_delta(Embedded_message1, has_e, e), 1, tb_membersize(Embedded_message1, e), 0, 0, 0, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t Embedded_message_fields[4] = { + {65, tb_offsetof(Embedded_message, f), 0, 0, tb_membersize(Embedded_message, f), 0, 0, 0, NULL}, + {520, tb_offsetof(Embedded_message, embedded_message1), 0, 0, tb_membersize(Embedded_message, embedded_message1[0]), tb_membersize(Embedded_message, embedded_message1)/tb_membersize(Embedded_message, embedded_message1[0]), 0, 0, &Embedded_message1_fields}, + {80, tb_offsetof(Embedded_message, embedded_payload.g), tb_delta(Embedded_message, which_embedded_payload, embedded_payload.g), 1, tb_membersize(Embedded_message, embedded_payload.g), 0, 100, 1, NULL}, + TB_LAST_FIELD, +}; + +const tb_field_t Test_message_fields[13] = { + {72, tb_offsetof(Test_message, fixed_array), 0, 0, tb_membersize(Test_message, fixed_array[0]), tb_membersize(Test_message, fixed_array)/tb_membersize(Test_message, fixed_array[0]), 0, 0, NULL}, + {66, tb_offsetof(Test_message, a), tb_delta(Test_message, has_a, a), 1, tb_membersize(Test_message, a), 0, 0, 0, NULL}, + {33, tb_offsetof(Test_message, b), 0, 0, tb_membersize(Test_message, b), 0, 0, 0, NULL}, + {68, tb_offsetof(Test_message, uint16_array), tb_delta(Test_message, uint16_array_count, uint16_array), 1, tb_membersize(Test_message, uint16_array[0]), tb_membersize(Test_message, uint16_array)/tb_membersize(Test_message, uint16_array[0]), 0, 0, NULL}, + {516, tb_offsetof(Test_message, embedded_messages), tb_delta(Test_message, embedded_messages_count, embedded_messages), 1, tb_membersize(Test_message, embedded_messages[0]), tb_membersize(Test_message, embedded_messages)/tb_membersize(Test_message, embedded_messages[0]), 0, 0, &Embedded_message_fields}, + {514, tb_offsetof(Test_message, embedded_message1), tb_delta(Test_message, has_embedded_message1, embedded_message1), 1, tb_membersize(Test_message, embedded_message1), 0, 0, 0, &Embedded_message1_fields}, + {513, tb_offsetof(Test_message, empty_message), 0, 0, tb_membersize(Test_message, empty_message), 0, 0, 0, &Empty_message_fields}, + {68, tb_offsetof(Test_message, uint8_array), tb_delta(Test_message, uint8_array_count, uint8_array), 2, tb_membersize(Test_message, uint8_array[0]), tb_membersize(Test_message, uint8_array)/tb_membersize(Test_message, uint8_array[0]), 0, 0, NULL}, + {258, tb_offsetof(Test_message, c), tb_delta(Test_message, has_c, c), 1, tb_membersize(Test_message, c), 0, 0, 0, NULL}, + {129, tb_offsetof(Test_message, d), 0, 0, tb_membersize(Test_message, d), 0, 0, 0, NULL}, + {80, tb_offsetof(Test_message, payload.x), tb_delta(Test_message, which_payload, payload.x), 1, tb_membersize(Test_message, payload.x), 0, 1, 1, NULL}, + {528, tb_offsetof(Test_message, payload.embedded_message_oneof), tb_delta(Test_message, which_payload, payload.embedded_message_oneof), 1, tb_membersize(Test_message, payload.embedded_message_oneof), 0, 2, 0, &Embedded_message_fields}, + TB_LAST_FIELD, +}; + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_protocol.h b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_protocol.h new file mode 100644 index 0000000..c7d562f --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/incl/test_protocol.h @@ -0,0 +1,58 @@ +#ifndef __TEST_PROTOCOL_H +#define __TEST_PROTOCOL_H + +#include +#include "tinybuf.h" +#include "test_parent_protocol.h" + +#define TEST1 10 +#define TEST2 12 +#define TEST3 1000 + +#define Embedded_message_g_tag 100 +#define Test_message_x_tag 1 +#define Test_message_embedded_message_oneof_tag 2 + +typedef struct { + uint8_t has_e; + uint64_t e; +} Embedded_message1; + +typedef struct { + uint8_t f; + Embedded_message1 embedded_message1[2]; + uint8_t which_embedded_payload; + union { + uint8_t g; + } embedded_payload; +} Embedded_message; + +typedef struct { + uint32_t fixed_array[4]; + uint8_t has_a; + uint16_t a; + int32_t b; + uint8_t uint16_array_count; + uint16_t uint16_array[10]; + uint8_t embedded_messages_count; + Embedded_message embedded_messages[12]; + uint8_t has_embedded_message1; + Embedded_message1 embedded_message1; + Empty_message empty_message; + uint16_t uint8_array_count; + uint8_t uint8_array[1000]; + uint8_t has_c; + double c; + float d; + uint8_t which_payload; + union { + uint8_t x; + Embedded_message embedded_message_oneof; + } payload; +} Test_message; + +extern const tb_field_t Embedded_message1_fields[2]; +extern const tb_field_t Embedded_message_fields[4]; +extern const tb_field_t Test_message_fields[13]; + +#endif diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/test_parent_protocol.py b/firmware/nRF_badge/data_collector/tinybuf/tests/test_parent_protocol.py new file mode 100644 index 0000000..f22de99 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/test_parent_protocol.py @@ -0,0 +1,48 @@ +import struct + + + +class _Ostream: + def __init__(self): + self.buf = b'' + def write(self, data): + self.buf += data + +class _Istream: + def __init__(self, buf): + self.buf = buf + def read(self, l): + if(l > len(self.buf)): + raise Exception("Not enough bytes in Istream to read") + ret = self.buf[0:l] + self.buf = self.buf[l:] + return ret + +class Empty_message: + + def __init__(self): + self.reset() + + def reset(self): + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + pass + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + pass + + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/test_parent_protocol.tb b/firmware/nRF_badge/data_collector/tinybuf/tests/test_parent_protocol.tb new file mode 100644 index 0000000..7f69369 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/test_parent_protocol.tb @@ -0,0 +1,5 @@ + +message Empty_message { +} + + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/test_protocol.py b/firmware/nRF_badge/data_collector/tinybuf/tests/test_protocol.py new file mode 100644 index 0000000..c9fde3e --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/test_protocol.py @@ -0,0 +1,372 @@ +import struct +from test_parent_protocol import * + +TEST1 = 10 +TEST2 = 12 +TEST3 = 1000 + +Embedded_message_g_tag = 100 +Test_message_x_tag = 1 +Test_message_embedded_message_oneof_tag = 2 + +class _Ostream: + def __init__(self): + self.buf = b'' + def write(self, data): + self.buf += data + +class _Istream: + def __init__(self, buf): + self.buf = buf + def read(self, l): + if(l > len(self.buf)): + raise Exception("Not enough bytes in Istream to read") + ret = self.buf[0:l] + self.buf = self.buf[l:] + return ret + +class Embedded_message1: + + def __init__(self): + self.reset() + + def reset(self): + self.has_e = 0 + self.e = 0 + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_e(ostream) + pass + + def encode_e(self, ostream): + ostream.write(struct.pack('>B', self.has_e)) + if self.has_e: + ostream.write(struct.pack('>Q', self.e)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_e(istream) + pass + + def decode_e(self, istream): + self.has_e= struct.unpack('>B', istream.read(1))[0] + if self.has_e: + self.e= struct.unpack('>Q', istream.read(8))[0] + + +class Embedded_message: + + def __init__(self): + self.reset() + + def reset(self): + self.f = 0 + self.embedded_message1 = [] + self.embedded_payload = self._embedded_payload() + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_f(ostream) + self.encode_embedded_message1(ostream) + self.embedded_payload.encode_internal(ostream) + pass + + def encode_f(self, ostream): + ostream.write(struct.pack('>B', self.f)) + + def encode_embedded_message1(self, ostream): + count = 2 + for i in range(0, count): + self.embedded_message1[i].encode_internal(ostream) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_f(istream) + self.decode_embedded_message1(istream) + self.embedded_payload.decode_internal(istream) + pass + + def decode_f(self, istream): + self.f= struct.unpack('>B', istream.read(1))[0] + + def decode_embedded_message1(self, istream): + count = 2 + for i in range(0, count): + tmp = Embedded_message1() + tmp.decode_internal(istream) + self.embedded_message1.append(tmp) + + class _embedded_payload: + + def __init__(self): + self.reset() + + def reset(self): + self.which = 0 + self.g = 0 + pass + + def encode_internal(self, ostream): + ostream.write(struct.pack('>B', self.which)) + options = { + 100: self.encode_g, + } + options[self.which](ostream) + pass + + def encode_g(self, ostream): + ostream.write(struct.pack('>B', self.g)) + + + def decode_internal(self, istream): + self.reset() + self.which= struct.unpack('>B', istream.read(1))[0] + options = { + 100: self.decode_g, + } + options[self.which](istream) + pass + + def decode_g(self, istream): + self.g= struct.unpack('>B', istream.read(1))[0] + + +class Test_message: + + def __init__(self): + self.reset() + + def reset(self): + self.fixed_array = [] + self.has_a = 0 + self.a = 0 + self.b = 0 + self.uint16_array = [] + self.embedded_messages = [] + self.has_embedded_message1 = 0 + self.embedded_message1 = None + self.empty_message = None + self.uint8_array = [] + self.has_c = 0 + self.c = 0 + self.d = 0 + self.payload = self._payload() + pass + + def encode(self): + ostream = _Ostream() + self.encode_internal(ostream) + return ostream.buf + + def encode_internal(self, ostream): + self.encode_fixed_array(ostream) + self.encode_a(ostream) + self.encode_b(ostream) + self.encode_uint16_array(ostream) + self.encode_embedded_messages(ostream) + self.encode_embedded_message1(ostream) + self.encode_empty_message(ostream) + self.encode_uint8_array(ostream) + self.encode_c(ostream) + self.encode_d(ostream) + self.payload.encode_internal(ostream) + pass + + def encode_fixed_array(self, ostream): + count = 4 + for i in range(0, count): + ostream.write(struct.pack('>I', self.fixed_array[i])) + + def encode_a(self, ostream): + ostream.write(struct.pack('>B', self.has_a)) + if self.has_a: + ostream.write(struct.pack('>H', self.a)) + + def encode_b(self, ostream): + ostream.write(struct.pack('>i', self.b)) + + def encode_uint16_array(self, ostream): + count = len(self.uint16_array) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + ostream.write(struct.pack('>H', self.uint16_array[i])) + + def encode_embedded_messages(self, ostream): + count = len(self.embedded_messages) + ostream.write(struct.pack('>B', count)) + for i in range(0, count): + self.embedded_messages[i].encode_internal(ostream) + + def encode_embedded_message1(self, ostream): + ostream.write(struct.pack('>B', self.has_embedded_message1)) + if self.has_embedded_message1: + self.embedded_message1.encode_internal(ostream) + + def encode_empty_message(self, ostream): + self.empty_message.encode_internal(ostream) + + def encode_uint8_array(self, ostream): + count = len(self.uint8_array) + ostream.write(struct.pack('>H', count)) + for i in range(0, count): + ostream.write(struct.pack('>B', self.uint8_array[i])) + + def encode_c(self, ostream): + ostream.write(struct.pack('>B', self.has_c)) + if self.has_c: + ostream.write(struct.pack('>d', self.c)) + + def encode_d(self, ostream): + ostream.write(struct.pack('>f', self.d)) + + + @classmethod + def decode(cls, buf): + obj = cls() + obj.decode_internal(_Istream(buf)) + return obj + + def decode_internal(self, istream): + self.reset() + self.decode_fixed_array(istream) + self.decode_a(istream) + self.decode_b(istream) + self.decode_uint16_array(istream) + self.decode_embedded_messages(istream) + self.decode_embedded_message1(istream) + self.decode_empty_message(istream) + self.decode_uint8_array(istream) + self.decode_c(istream) + self.decode_d(istream) + self.payload.decode_internal(istream) + pass + + def decode_fixed_array(self, istream): + count = 4 + for i in range(0, count): + self.fixed_array.append(struct.unpack('>I', istream.read(4))[0]) + + def decode_a(self, istream): + self.has_a= struct.unpack('>B', istream.read(1))[0] + if self.has_a: + self.a= struct.unpack('>H', istream.read(2))[0] + + def decode_b(self, istream): + self.b= struct.unpack('>i', istream.read(4))[0] + + def decode_uint16_array(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + self.uint16_array.append(struct.unpack('>H', istream.read(2))[0]) + + def decode_embedded_messages(self, istream): + count = struct.unpack('>B', istream.read(1))[0] + for i in range(0, count): + tmp = Embedded_message() + tmp.decode_internal(istream) + self.embedded_messages.append(tmp) + + def decode_embedded_message1(self, istream): + self.has_embedded_message1= struct.unpack('>B', istream.read(1))[0] + if self.has_embedded_message1: + self.embedded_message1 = Embedded_message1() + self.embedded_message1.decode_internal(istream) + + def decode_empty_message(self, istream): + self.empty_message = Empty_message() + self.empty_message.decode_internal(istream) + + def decode_uint8_array(self, istream): + count = struct.unpack('>H', istream.read(2))[0] + for i in range(0, count): + self.uint8_array.append(struct.unpack('>B', istream.read(1))[0]) + + def decode_c(self, istream): + self.has_c= struct.unpack('>B', istream.read(1))[0] + if self.has_c: + self.c= struct.unpack('>d', istream.read(8))[0] + + def decode_d(self, istream): + self.d= struct.unpack('>f', istream.read(4))[0] + + def get_uint8_array_as_bytestring(self): + bytestring = b'' + for b in self.uint8_array: + bytestring += struct.pack('>B', b) + return bytestring + + def set_uint8_array_as_bytestring(self, bytestring): + self.uint8_array = [] + for b in bytestring: + self.uint8_array.append(struct.unpack('>B', b)[0]) + + class _payload: + + def __init__(self): + self.reset() + + def reset(self): + self.which = 0 + self.x = 0 + self.embedded_message_oneof = None + pass + + def encode_internal(self, ostream): + ostream.write(struct.pack('>B', self.which)) + options = { + 1: self.encode_x, + 2: self.encode_embedded_message_oneof, + } + options[self.which](ostream) + pass + + def encode_x(self, ostream): + ostream.write(struct.pack('>B', self.x)) + + def encode_embedded_message_oneof(self, ostream): + self.embedded_message_oneof.encode_internal(ostream) + + + def decode_internal(self, istream): + self.reset() + self.which= struct.unpack('>B', istream.read(1))[0] + options = { + 1: self.decode_x, + 2: self.decode_embedded_message_oneof, + } + options[self.which](istream) + pass + + def decode_x(self, istream): + self.x= struct.unpack('>B', istream.read(1))[0] + + def decode_embedded_message_oneof(self, istream): + self.embedded_message_oneof = Embedded_message() + self.embedded_message_oneof.decode_internal(istream) + + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/test_protocol.tb b/firmware/nRF_badge/data_collector/tinybuf/tests/test_protocol.tb new file mode 100644 index 0000000..ffe468c --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/test_protocol.tb @@ -0,0 +1,41 @@ +import test_parent_protocol +extern Empty_message; + +define { + TEST1 = 10; + TEST2 = 12; + TEST3 = 1000; +} + + +message Embedded_message1 { + optional uint64 e; +} + +message Embedded_message { + required uint8 f; + fixed_repeated Embedded_message1 embedded_message1[2]; + oneof embedded_payload { + uint8 g(100); + } +} + + + +message Test_message { + fixed_repeated uint32 fixed_array[4]; + optional uint16 a; + required int32 b; + repeated uint16 uint16_array[TEST1]; + repeated Embedded_message embedded_messages[TEST2]; + optional Embedded_message1 embedded_message1; + required Empty_message empty_message; + repeated uint8 uint8_array[TEST3]; + optional double c; + required float d; + oneof payload { + uint8 x (1); + Embedded_message embedded_message_oneof (2); + } +} + diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/tester.cc b/firmware/nRF_badge/data_collector/tinybuf/tests/tester.cc new file mode 100644 index 0000000..1a5cefd --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/tester.cc @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include + +#include "test_protocol.h" + +#define EXPECT_ARRAY_EQ(A, B, len) {for(uint32_t i = 0; i < len; i++) {EXPECT_EQ(A[i], B[i])}} +#define EXPECT_EQ(A, B) {if(A != B) { printf("Error EXPECT_EQ at line %u\n", __LINE__); return 0;}} + +char output_file_name[] = "/output_file.bin"; +char input_file_name[] = "/input_file.bin"; + +uint8_t create_test_message(uint8_t* buf, uint32_t max_size, uint32_t* len) { + + tb_ostream_t ostream = tb_ostream_from_buffer(buf, max_size); + + Test_message test_message; + memset(&test_message, 0, sizeof(test_message)); + + test_message.fixed_array[0] = 100; + test_message.fixed_array[1] = 101; + test_message.fixed_array[2] = 102; + test_message.fixed_array[3] = 103; + + + test_message.has_a = 0; + test_message.a = 10000; + + test_message.b = -100000; + + test_message.uint16_array_count = 3; + test_message.uint16_array[0] = TEST1; + test_message.uint16_array[1] = TEST2; + test_message.uint16_array[2] = TEST3; + + + test_message.embedded_messages_count = 2; + test_message.embedded_messages[0].f = 100; + test_message.embedded_messages[0].embedded_message1[0].has_e = 1; + test_message.embedded_messages[0].embedded_message1[0].e = 0xDEADBEEF; + test_message.embedded_messages[0].embedded_message1[1].has_e = 0; + test_message.embedded_messages[0].which_embedded_payload = Embedded_message_g_tag; + test_message.embedded_messages[0].embedded_payload.g = 100; + test_message.embedded_messages[1].f = 101; + test_message.embedded_messages[1].embedded_message1[0].has_e = 0; + test_message.embedded_messages[1].embedded_message1[1].has_e = 1; + test_message.embedded_messages[1].embedded_message1[1].e = 0xBEEFDEAD; + test_message.embedded_messages[1].which_embedded_payload = Embedded_message_g_tag; + test_message.embedded_messages[1].embedded_payload.g = 200; + + test_message.has_embedded_message1 = 1; + test_message.embedded_message1.has_e = 1; + test_message.embedded_message1.e = 0xBAADFEED; + + test_message.uint8_array_count = 5; + test_message.uint8_array[0] = 200; + test_message.uint8_array[1] = 201; + test_message.uint8_array[2] = 202; + + test_message.has_c = 1; + test_message.c = 1.123; + + test_message.d = 11.23f; + + test_message.which_payload = Test_message_embedded_message_oneof_tag; + test_message.payload.embedded_message_oneof.f = 11; + test_message.payload.embedded_message_oneof.embedded_message1[0].has_e = 1; + test_message.payload.embedded_message_oneof.embedded_message1[0].e = 0xFF; + test_message.payload.embedded_message_oneof.embedded_message1[1].has_e = 1; + test_message.payload.embedded_message_oneof.embedded_message1[1].e = 0xAA; + test_message.payload.embedded_message_oneof.which_embedded_payload = Embedded_message_g_tag; + test_message.payload.embedded_message_oneof.embedded_payload.g = 100; + + + + uint8_t encode_status = tb_encode(&ostream, Test_message_fields, &test_message, TB_BIG_ENDIAN); + *len = ostream.bytes_written; + printf("Encode status: %u, Len: %u\n", encode_status, *len); + + + return encode_status; + +} + +uint8_t check_test_message(uint8_t* buf, uint32_t len) { + tb_istream_t istream = tb_istream_from_buffer(buf, len); + + Test_message test_message; + memset(&test_message, 0, sizeof(test_message)); + + uint8_t decode_status = tb_decode(&istream, Test_message_fields, &test_message, TB_BIG_ENDIAN); + printf("Decode status: %u\n", decode_status); + EXPECT_EQ(decode_status, 1); + + + EXPECT_EQ(test_message.fixed_array[0], 100); + EXPECT_EQ(test_message.fixed_array[1], 101); + EXPECT_EQ(test_message.fixed_array[2], 102); + EXPECT_EQ(test_message.fixed_array[3], 103); + + EXPECT_EQ(test_message.has_a, 0); + EXPECT_EQ(test_message.a, 0); + + EXPECT_EQ(test_message.b, -100000); + + + + EXPECT_EQ(test_message.uint16_array_count, 3); + EXPECT_EQ(test_message.uint16_array[0], TEST1); + EXPECT_EQ(test_message.uint16_array[1], TEST2); + EXPECT_EQ(test_message.uint16_array[2], TEST3); + + + EXPECT_EQ(test_message.embedded_messages_count, 2); + EXPECT_EQ(test_message.embedded_messages[0].f, 100); + EXPECT_EQ(test_message.embedded_messages[0].embedded_message1[0].has_e, 1); + EXPECT_EQ(test_message.embedded_messages[0].embedded_message1[0].e, 0xDEADBEEF); + EXPECT_EQ(test_message.embedded_messages[0].embedded_message1[1].has_e, 0); + EXPECT_EQ(test_message.embedded_messages[0].which_embedded_payload, Embedded_message_g_tag); + EXPECT_EQ(test_message.embedded_messages[0].embedded_payload.g, 100); + EXPECT_EQ(test_message.embedded_messages[1].f, 101); + EXPECT_EQ(test_message.embedded_messages[1].embedded_message1[0].has_e, 0); + EXPECT_EQ(test_message.embedded_messages[1].embedded_message1[1].has_e, 1); + EXPECT_EQ(test_message.embedded_messages[1].embedded_message1[1].e, 0xBEEFDEAD); + EXPECT_EQ(test_message.embedded_messages[1].which_embedded_payload, Embedded_message_g_tag); + EXPECT_EQ(test_message.embedded_messages[1].embedded_payload.g, 200); + + EXPECT_EQ(test_message.has_embedded_message1, 1); + EXPECT_EQ(test_message.embedded_message1.has_e, 1); + EXPECT_EQ(test_message.embedded_message1.e, 0xBAADFEED); + + EXPECT_EQ(test_message.uint8_array_count, 5); + EXPECT_EQ(test_message.uint8_array[0], 200); + EXPECT_EQ(test_message.uint8_array[1], 201); + EXPECT_EQ(test_message.uint8_array[2], 202); + EXPECT_EQ(test_message.uint8_array[3], 0); + EXPECT_EQ(test_message.uint8_array[4], 0); + + EXPECT_EQ(test_message.has_c, 1); + EXPECT_EQ(test_message.c, 1.123); + + EXPECT_EQ(test_message.d, 11.23f); + + + EXPECT_EQ(test_message.which_payload, Test_message_embedded_message_oneof_tag); + EXPECT_EQ(test_message.payload.embedded_message_oneof.f, 11); + EXPECT_EQ(test_message.payload.embedded_message_oneof.embedded_message1[0].has_e, 1); + EXPECT_EQ(test_message.payload.embedded_message_oneof.embedded_message1[0].e, 0xFF); + EXPECT_EQ(test_message.payload.embedded_message_oneof.embedded_message1[1].has_e, 1); + EXPECT_EQ(test_message.payload.embedded_message_oneof.embedded_message1[1].e, 0xAA); + EXPECT_EQ(test_message.payload.embedded_message_oneof.which_embedded_payload, Embedded_message_g_tag); + EXPECT_EQ(test_message.payload.embedded_message_oneof.embedded_payload.g, 100); + + + return decode_status; +} + +void write_to_file(char* file_name, const uint8_t* buf, uint32_t len) { + FILE *output_file; + output_file = fopen(file_name, "wb"); + if(output_file == NULL) + return; + + fwrite(buf, len, 1, output_file); + fclose(output_file); +} + +void read_from_file(char* file_name, uint8_t* buf, uint32_t* len) { + FILE *input_file; + input_file = fopen(file_name, "rb"); + if(input_file == NULL) + return; + uint32_t l = 0; + while(fread(&buf[l], 1, 1, input_file)) { + l++; + } + *len = l; + fclose(input_file); +} + + + +int main(void) { + + char cwd[PATH_MAX]; + if(getcwd(cwd, sizeof(cwd)) == NULL) + return 0; + + + char output_file_path[PATH_MAX]; + char input_file_path[PATH_MAX]; + strcpy(output_file_path, cwd); + strcpy(&output_file_path[strlen(cwd)], output_file_name); + strcpy(input_file_path, cwd); + strcpy(&input_file_path[strlen(cwd)], input_file_name); + + + printf("Path: %s\n", output_file_path); + printf("Path: %s\n", input_file_path); + + uint32_t Empty_message_expected_size = 0; + uint32_t Embedded_message1_expected_size = 1 + 8; + uint32_t Embedded_message_expected_size = 1 + 2*Embedded_message1_expected_size + (1+1); + uint32_t Test_message_expected_size = 4*4 + (1+2) + 4 + (1+2*10) + (1+12*Embedded_message_expected_size) + + (1+Embedded_message1_expected_size) + Empty_message_expected_size + + (2+1000) + (1+8) + 4 + (1+Embedded_message_expected_size); + EXPECT_EQ(tb_get_max_encoded_len(Empty_message_fields), Empty_message_expected_size); + EXPECT_EQ(tb_get_max_encoded_len(Embedded_message1_fields), Embedded_message1_expected_size); + EXPECT_EQ(tb_get_max_encoded_len(Embedded_message_fields), Embedded_message_expected_size); + EXPECT_EQ(tb_get_max_encoded_len(Test_message_fields), Test_message_expected_size); + + + uint8_t buf[1000]; + uint32_t len = 0; + uint8_t ret = create_test_message(buf, sizeof(buf), &len); + EXPECT_EQ(ret, 1); + + + write_to_file(output_file_path, buf, len); + + + printf("Calling python script\n\n\n############# PYTHON ##############\n\n"); + char command[1024]; + strcpy(command, "python ../tester.py "); + strcpy(&command[strlen(command)], output_file_path); + strcpy(&command[strlen(command)], " " ); + strcpy(&command[strlen(command)], input_file_path); + system(command); + + + printf("\n############# PYTHON ##############\n\n\nChecking python script output\n"); + + uint8_t buf1[1000]; + uint32_t len1 = 0; + read_from_file(input_file_path, buf1, &len1); + + // Check if length and the buffers are the same + EXPECT_EQ(len, len1); + EXPECT_ARRAY_EQ(buf, buf1, len); + + ret = check_test_message(buf1, len1); + EXPECT_EQ(ret, 1); + + printf("\nTest was successful!\n"); + +} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/tinybuf/tests/tester.py b/firmware/nRF_badge/data_collector/tinybuf/tests/tester.py new file mode 100644 index 0000000..26ea3f7 --- /dev/null +++ b/firmware/nRF_badge/data_collector/tinybuf/tests/tester.py @@ -0,0 +1,43 @@ +import sys +import test_protocol + + + +def read_bytes(file): + bytes_read = b'' + with open(file, "rb") as f: + bytes_read = f.read() + + return bytes_read + +def write_bytes(file, byte_write): + with open(file, "wb") as f: + f.write(bytes_write) + + +if __name__ == '__main__': + if(len(sys.argv) < 3): + raise Exception("Too less arguments") + + input_file = sys.argv[1] + output_file = sys.argv[2] + + bytes_read = read_bytes(input_file) + + test_message = test_protocol.Test_message.decode(bytes_read) + + + print "Decoded test_message" + print test_message.__dict__ + print test_message.payload.__dict__ + + + + bytes_write = test_message.encode() + + + print "Encoded test_message: Len: " + str(len(bytes_write)) + print repr(bytes_write) + + write_bytes(output_file, bytes_write) + \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/unit_test/Makefile b/firmware/nRF_badge/data_collector/unit_test/Makefile new file mode 100644 index 0000000..b344834 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/Makefile @@ -0,0 +1,513 @@ +#TODO: Clean it up +# +# A sample Makefile for building Google Test and using it in user +# tests. Please tweak it to suit your environment and project. You +# may want to move it to your project's root directory. +# +# SYNOPSIS: +# +# make [all] - makes everything. +# make TARGET - makes the given target. +# make clean - removes all files generated by make. + +# Please tweak the following variable definitions as needed by your +# project, except GTEST_HEADERS, which you can use in your own targets +# but shouldn't modify. + + +#GTest Assert/Expect: https://github.com/google/googletest/blob/master/googletest/docs/primer.md or http://cheezyworld.com/wp-content/uploads/2010/12/PlainGoogleQuickTestReferenceGuide1.pdf + + + + +# Where to find firmware code. +FIRMWARE_DIR = .. + +# Where to find test code. +TEST_DIR = tests + +# Where to find mocking code. +MOCK_DIR = mock/incl + +# Where the builded files will be stored to. +BUILD_DIR = _build +BUILD_C_DIR = $(BUILD_DIR)/C +BUILD_CC_DIR = $(BUILD_DIR)/CC + +LCOV_DIR = $(BUILD_DIR)/LCOV + +# The tinybuf directories +TINYBUF_PATH := ../tinybuf +TINYBUF_SRC_PATH := $(TINYBUF_PATH)/incl + + +SDK_PATH := $(NRF_SDK_PATH)/nRF5_SDK_12.3.0_d7731ad + +# Points to the root of Google Test, relative to where this file is. +# Remember to tweak this if you move this file. +GTEST_DIR = googletest + + +# If you want no cmd-line print outs of the cmds +NO_ECHO := @ + +#function for removing duplicates in a list +remduplicates = $(strip $(if $1,$(firstword $1) $(call remduplicates,$(filter-out $(firstword $1),$1)))) + +# All tests produced by this Makefile. Remember to add new tests you +# created to the list. +TESTS = flash_lib_mock_unittest \ + eeprom_lib_mock_unittest \ + storage1_lib_unittest \ + storage2_lib_unittest \ + storage_lib_unittest \ + filesystem_lib_unittest \ + timer_lib_unittest \ + app_timer_mock_unittest \ + app_scheduler_mock_unittest \ + callback_generator_lib_unittest \ + data_generator_lib_unittest \ + accel_lib_mock_unittest \ + chunk_fifo_lib_unittest \ + ble_lib_mock_unittest \ + circular_fifo_lib_unittest \ + timeout_lib_unittest \ + scan_integration_unittest \ + +FIRMWARE_SRCS = $(FIRMWARE_DIR)/incl/storage1_lib.c \ + $(FIRMWARE_DIR)/incl/storage2_lib.c \ + $(FIRMWARE_DIR)/incl/storage_lib.c \ + $(FIRMWARE_DIR)/incl/filesystem_lib.c \ + $(FIRMWARE_DIR)/incl/chunk_fifo_lib.c \ + $(FIRMWARE_DIR)/incl/systick_lib.c \ + $(FIRMWARE_DIR)/incl/circular_fifo_lib.c \ + $(FIRMWARE_DIR)/incl/timeout_lib.c \ + $(SDK_PATH)/components/libraries/fifo/app_fifo.c \ + $(FIRMWARE_DIR)/incl/sender_lib.c \ + $(FIRMWARE_DIR)/incl/request_handler_lib_01v1.c \ + $(FIRMWARE_DIR)/incl/request_handler_lib_02v1.c \ + $(FIRMWARE_DIR)/incl/protocol_messages_01v1.c \ + $(FIRMWARE_DIR)/incl/protocol_messages_02v1.c \ + $(FIRMWARE_DIR)/incl/common_messages.c \ + $(FIRMWARE_DIR)/incl/chunk_messages.c \ + $(FIRMWARE_DIR)/incl/stream_messages.c \ + $(FIRMWARE_DIR)/incl/storer_lib.c \ + $(FIRMWARE_DIR)/incl/advertiser_lib.c \ + $(FIRMWARE_DIR)/incl/scanner_lib.c \ + $(FIRMWARE_DIR)/incl/sampling_lib.c \ + $(FIRMWARE_DIR)/incl/processing_lib.c \ + + + + + + + + + + +FIRMWARE_INC_PATH += -I$(FIRMWARE_DIR)/incl +FIRMWARE_INC_PATH += -I$(FIRMWARE_DIR)/config +FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/libraries/util +FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/drivers_nrf/nrf_soc_nosd +FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/device +FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/libraries/timer +FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/libraries/scheduler +FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/libraries/fifo +FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/softdevice/s130/headers + + + +#FIRMWARE_INC_PATH += -I$(FIRMWARE_DIR)/incl +#FIRMWARE_INC_PATH += -I$(FIRMWARE_DIR)/config +#FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/libraries/util --> For sdk_errors.h +#FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/drivers_nrf/nrf_soc_nosd --> For nrf_error.h +#FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/device --> For compiler_abstractions.h +#FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/libraries/timer --> For app_timer.h +#FIRMWARE_INC_PATH += -I$(SDK_PATH)/components/libraries/scheduler --> For app_scheduler.h + + +MOCK_INC_PATH += -I$(MOCK_DIR) + +TINYBUF_INC_PATH += -I$(TINYBUF_SRC_PATH) + + + + + + + +# Flags passed to the preprocessor. +# Set Google Test's header directory as a system directory, such that +# the compiler doesn't generate warnings in Google Test headers. +CPPFLAGS += -isystem $(GTEST_DIR)/include + +# Flags passed to the C++ compiler (no Wextra, because warning in gtest..) +#CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -pthread + + + +# All Google Test headers. Usually you shouldn't change this +# definition. +GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \ + $(GTEST_DIR)/include/gtest/internal/*.h + +# House-keeping build targets. + +#target for printing all targets +help: + @echo + @echo "The following targets are available:" + @echo " - Info: help helpDetailed" + @echo + @echo " - Compile options: " + @echo " NRF51DK badge_03 badge_03_noDebug" + @echo " badge_03v2_rigado badge_03v2_dynastream" + @echo " badge_03v2_rigado_noDebug badge_03v2_rigado_tester" + @echo " badge_03v4 badge_03v4_noDebug badge_03v4_tester" + @echo " badge_03v6 badge_03v6_noDebug badge_03v6_tester" + @echo + @echo " - Compile tests: " + @$(foreach test,$(TESTS),echo " ${test}";) + @echo " all (compiles all the above tests)" + + @echo + @echo " - Run tests (after compilation):" + @$(foreach run_test,$(RUN_TESTS),echo " ${run_test}";) + @echo " run_all (runs all the above tests)" + @echo + @echo " - LCOV: Enable with LCOV=TRUE as make parameter (for compiling and running tests)" + @echo + + +helpDetailed: + @echo + @echo "The following targets are available:" + @echo " - NRF51DK: compile for NRF51 devkit board." + @echo " - badge_03: compile for Badge 03 board (square blue badge)" + @echo " - badge_03_noDebug: compile for Badge 03, UART disabled" + @echo " -----" + @echo " - badge_03v2_[rigado|dynastream]: compile for Badge 03v2" + @echo " (with corresponding BLE module)" + @echo " - badge_03v2_rigado_noDebug: compile for Badge 03v2, UART disabled" + @echo " - badge_03v2_rigado_tester: compile 03v2 Rigado self tester" + @echo " -----" + @echo " - badge_03v4: compile for Badge 03v4 board" + @echo " - badge_03v4_noDebug: compile for Badge 03v4 board, UART disabled" + @echo " - badge_03v4_tester: compile for Badge 03v4 board, UART disabled" + @echo " -----" + @echo " - badge_03v46: compile for Badge 03v6 board" + @echo " - badge_03v6_noDebug: compile for Badge 03v6 board, UART disabled" + @echo " - badge_03v6_tester: compile for Badge 03v6 board, UART disabled" + @echo " -----" + @echo + + + + + + + +#======= Target-dependent compiler flags +NRF51DK: CFLAGS += -DBOARD_PCA10028 + +badge_03: CFLAGS += -DBOARD_CUSTOM +badge_03: CFLAGS += -DBOARD_BADGE_03 +badge_03_noDebug: CFLAGS += -DBOARD_CUSTOM +badge_03_noDebug: CFLAGS += -DBOARD_BADGE_03 + +badge_03v2_rigado: CFLAGS += -DBOARD_CUSTOM +badge_03v2_rigado: CFLAGS += -DBOARD_BADGE_03V2_RIGADO +badge_03v2_rigado_noDebug: CFLAGS += -DBOARD_CUSTOM +badge_03v2_rigado_noDebug: CFLAGS += -DBOARD_BADGE_03V2_RIGADO +badge_03v2_rigado_tester: CFLAGS += -DBOARD_CUSTOM +badge_03v2_rigado_tester: CFLAGS += -DBOARD_BADGE_03V2_RIGADO +badge_03v2_rigado_tester: CFLAGS += -DTESTER_ENABLE #enables tester mode +badge_03v2_dynastream: CFLAGS += -DBOARD_CUSTOM +badge_03v2_dynastream: CFLAGS += -DBOARD_BADGE_03V2_DYNASTREAM + +badge_03v4: CFLAGS += -DBOARD_CUSTOM +badge_03v4: CFLAGS += -DBOARD_BADGE_03V4 +badge_03v4_noDebug: CFLAGS += -DBOARD_CUSTOM +badge_03v4_noDebug: CFLAGS += -DBOARD_BADGE_03V4 +badge_03v4_tester: CFLAGS += -DBOARD_CUSTOM +badge_03v4_tester: CFLAGS += -DBOARD_BADGE_03V4 +badge_03v4_tester: CFLAGS += -DTESTER_ENABLE #enables tester mode + +badge_03v6: CFLAGS += -DBOARD_CUSTOM +badge_03v6: CFLAGS += -DBOARD_BADGE_03V6 #Cambiar a DBOARD_BADGE_03V6 +badge_03v6_noDebug: CFLAGS += -DBOARD_CUSTOM +badge_03v6_noDebug: CFLAGS += -DBOARD_BADGE_03V6 +badge_03v6_tester: CFLAGS += -DBOARD_CUSTOM +badge_03v6_tester: CFLAGS += -DBOARD_BADGE_03V6 +badge_03v6_tester: CFLAGS += -DTESTER_ENABLE #enables tester mode + + +CFLAGS += -DDEBUG_LOG_ENABLE #enable UART debug logger +badge_03_noDebug: CFLAGS += -UDEBUG_LOG_ENABLE #disable if noDebug target specified +badge_03v2_rigado_noDebug: CFLAGS += -UDEBUG_LOG_ENABLE +badge_03v4_noDebug: CFLAGS += -UDEBUG_LOG_ENABLE +badge_03v6_noDebug: CFLAGS += -UDEBUG_LOG_ENABLE + +CFLAGS += -DSOFTDEVICE_PRESENT +CFLAGS += -DNRF51 +CFLAGS += -DNRF51822 + +CFLAGS += -Wall -Werror +# keep every function in separate section. This will allow linker to dump unused functions +#CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing +#CFLAGS += -fno-builtin --short-enums + + +# Add the CFLAGS to the CXXFLAGS +CXXFLAGS += $(CFLAGS) +CXXFLAGS += -DUNIT_TEST +CXXFLAGS += -DDEBUG_LOG_ENABLE +CXXFLAGS += -DPROTOCOL_02v1 + + + +#$(info LCOV=${LCOV}. To enable LCOV, call make with parameter LCOV=TRUE.) + +TESTS_OBJECTS_CC = $(addprefix $(BUILD_CC_DIR)/, $(addsuffix .o, $(TESTS))) +TESTS_FILES_CC_PATH = $(addsuffix .cc, $(addprefix $(TEST_DIR)/, $(TESTS))) +#$(info TESTS_FILES=${TESTS_FILES}) + +MOCK_SRCS += $(shell find $(MOCK_DIR) -name '*.c*') +MOCK_FILES_C = $(notdir $(shell find $(MOCK_DIR) -name '*.c')) +MOCK_FILES_CC = $(notdir $(shell find $(MOCK_DIR) -name '*.cc')) +MOCK_OBJECTS_C += $(addprefix $(BUILD_C_DIR)/,$(MOCK_FILES_C:.c=.o)) +MOCK_OBJECTS_CC += $(addprefix $(BUILD_CC_DIR)/,$(MOCK_FILES_CC:.cc=.o)) +MOCK_FILES_C_PATH += $(addprefix $(MOCK_DIR)/, $(MOCK_FILES_C)) +MOCK_FILES_CC_PATH += $(addprefix $(MOCK_DIR)/, $(MOCK_FILES_CC)) +#$(info MOCK_FILES_PATH=${MOCK_FILES_PATH}) + +FIRMWARE_FILES = $(notdir $(FIRMWARE_SRCS)) +FIRMWARE_OBJECTS_C += $(addprefix $(BUILD_C_DIR)/,$(FIRMWARE_FILES:.c=.o)) +FIRMWARE_FILES_C_PATH += $(FIRMWARE_SRCS) +#$(info FIRMWARE_FILES_PATH=${FIRMWARE_FILES_PATH}) + +TINYBUF_FILES = $(notdir $(wildcard $(TINYBUF_SRC_PATH)/*.c)) +TINYBUF_OBJECTS_C += $(addprefix $(BUILD_C_DIR)/,$(TINYBUF_FILES:.c=.o)) +TINYBUF_FILES_C_PATH += $(addprefix $(TINYBUF_SRC_PATH)/,$(TINYBUF_FILES)) +#$(info TINYBUF_FILES_PATH=${TINYBUF_FILES_PATH}) +#$(info TINYBUF_FILES=${TINYBUF_FILES}) +#$(info TINYBUF_OBJECTS=${TINYBUF_OBJECTS}) + + + +C_OBJECTS = $(MOCK_OBJECTS_C) $(FIRMWARE_OBJECTS_C) $(TINYBUF_OBJECTS_C) +CC_OBJECTS = $(MOCK_OBJECTS_CC) +SRC_FILES_C_PATHS += $(call remduplicates, $(dir $(MOCK_FILES_C_PATH) ) ) +SRC_FILES_C_PATHS += $(call remduplicates, $(dir $(FIRMWARE_FILES_C_PATH) ) ) +SRC_FILES_C_PATHS += $(call remduplicates, $(dir $(TINYBUF_FILES_C_PATH) ) ) +SRC_FILES_CC_PATHS += $(call remduplicates, $(dir $(TESTS_FILES_CC_PATH) ) ) +SRC_FILES_CC_PATHS += $(call remduplicates, $(dir $(MOCK_FILES_CC_PATH) ) ) + +#$(info C_OBJECTS=${C_OBJECTS}) +#$(info CC_OBJECTS=${CC_OBJECTS}) +#$(info SRC_FILES_C_PATHS=${SRC_FILES_C_PATHS}) +#$(info SRC_FILES_CC_PATHS=${SRC_FILES_CC_PATHS}) + +vpath %.c $(SRC_FILES_C_PATHS) +vpath %.cc $(SRC_FILES_CC_PATHS) + + +RUN_TESTS = $(addprefix run_, $(TESTS)) + + + +all : $(TESTS) + + +clean : + $(NO_ECHO)rm -f $(addprefix $(BUILD_DIR)/, $(addprefix run_, $(TESTS))) gtest.a gtest_main.a *.o *.gcda *.gcno *.gcov *.info + $(NO_ECHO)rm -f -r $(BUILD_DIR) + $(NO_ECHO)mkdir $(BUILD_DIR) + $(NO_ECHO)mkdir $(BUILD_C_DIR) + $(NO_ECHO)mkdir $(BUILD_CC_DIR) + $(NO_ECHO)rm -f -r $(LCOV_DIR) + $(NO_ECHO)mkdir $(LCOV_DIR) + + +# Target for running all unit tests +run_all : $(RUN_TESTS) + +# Make the run_..-targets Phony. +.PHONY: $(RUN_TESTS) + +THIS_FILE := $(lastword $(MAKEFILE_LIST)) + +# Target for running one specific unit test +$(RUN_TESTS) : +ifeq ($(LCOV), TRUE) + $(NO_ECHO)cp -f $(LCOV_DIR)/*.gcno . +endif + @echo + @echo ==================================================================== + @echo Run test: $@ + @echo ==================================================================== + $(NO_ECHO)(cd $(BUILD_DIR)/ ; ./$@) + @echo ==================================================================== +ifeq ($(LCOV), TRUE) + @echo Creating LCOV data and removing zero called files... +# https://stackoverflow.com/questions/5377297/how-to-manually-call-another-target-from-a-make-target/5378242?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa +# Call the lcov_genhtml-target with parameter LCOV_DIR_CUR + $(NO_ECHO)-mv $(BUILD_DIR)/*.gcda . 2>/dev/null || : + $(NO_ECHO)-mv $(BUILD_C_DIR)/*.gcda . 2>/dev/null || : + $(NO_ECHO)-mv $(BUILD_CC_DIR)/*.gcda . 2>/dev/null || : + $(NO_ECHO)$(MAKE) -f $(THIS_FILE) lcov_genhtml LCOV_DIR_CUR=$(LCOV_DIR)/$@ > /dev/null +else + +# Remove all the gcda-files + $(NO_ECHO)rm -f $(BUILD_DIR)/*.gcda $(BUILD_C_DIR)/*.gcda $(BUILD_CC_DIR)/*.gcda ./*.gcda +endif + + + +print_lcov_help: +ifeq ($(LCOV), TRUE) +else + @echo To enable LCOV, call make with parameter LCOV=TRUE. +endif + + +#Adding coverage information flags to compiler +lcov_flags : +ifeq ($(LCOV), TRUE) + @echo Compiling with lcov/gcov flags.. + $(eval CXXFLAGS += -fprofile-arcs -ftest-coverage) +endif + +# Generating the +lcov_genhtml : +ifeq ($(LCOV), TRUE) + @echo Generating LCOV HTML file... + gcov $(TESTS) 2> /dev/null + rm -f -r $(LCOV_DIR_CUR) + mkdir $(LCOV_DIR_CUR) + + lcov -c -d . -o $(LCOV_DIR_CUR)/coverage.info 2> /dev/null + lcov --remove $(LCOV_DIR_CUR)/coverage.info "/usr*" "*googletest*" "*_build*" "*callback_generator_lib.*" "*data_generator_lib.*" -o $(LCOV_DIR_CUR)/coverage.info 2> /dev/null + +## Call the lcov_remove_zero_called-target with parameter LCOV_DIR_CUR + $(NO_ECHO)$(MAKE) -f $(THIS_FILE) lcov_remove_zero_called LCOV_DIR_CUR=$(LCOV_DIR_CUR) > /dev/null +# genhtml $(LCOV_DIR_CUR)/coverage.info -o $(LCOV_DIR_CUR) --prefix=/app/firmware/nRF_badge/data_collector > /dev/null + genhtml $(LCOV_DIR_CUR)/coverage.info -o $(LCOV_DIR_CUR) --prefix=/opt/nrf/sdk/nrf_sdk_12_3/nrf5_12.3.0_d7731ad/nRF5_SDK_12.3.0_d7731ad > /dev/null +## Delete the gcda and gcno data again + rm -f *.gcda *.gcno +endif + + + +# https://github.com/s-oravec/quilt/wiki/LCOV-Trace-File +lcov_remove_zero_called : +ifeq ($(LCOV), TRUE) + @echo Removing zero called files... + $(NO_ECHO)cp $(LCOV_DIR_CUR)/coverage.info $(LCOV_DIR_CUR)/coverage.info.tmp + @while read line; \ + do \ + case "$$line" in \ + *SF:*) \ + cur_src_file="$${line#*:}";;\ + *FNH:0*) \ + echo "$$cur_src_file";\ + lcov --remove $(LCOV_DIR_CUR)/coverage.info "$$cur_src_file" -o $(LCOV_DIR_CUR)/coverage.info;; \ + esac \ + done <$(LCOV_DIR_CUR)/coverage.info.tmp + $(NO_ECHO)rm -f $(LCOV_DIR_CUR)/coverage.info.tmp +endif + + +# Builds gtest.a and gtest_main.a. + +# Usually you shouldn't tweak such internal variables, indicated by a +# trailing _. +GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS) + +# For simplicity and to avoid depending on Google Test's +# implementation details, the dependencies specified below are +# conservative and not optimized. This is fine as Google Test +# compiles fast and for ordinary users its source rarely changes. +$(BUILD_DIR)/gtest-all.o : $(GTEST_SRCS_) + $(NO_ECHO)$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -o $(BUILD_DIR)/gtest-all.o -c \ + $(GTEST_DIR)/src/gtest-all.cc + +$(BUILD_DIR)/gtest_main.o : $(GTEST_SRCS_) + $(NO_ECHO)$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -o $(BUILD_DIR)/gtest_main.o -c \ + $(GTEST_DIR)/src/gtest_main.cc + +$(BUILD_DIR)/gtest.a : $(BUILD_DIR)/gtest-all.o + $(NO_ECHO)$(AR) $(ARFLAGS) $@ $^ + +$(BUILD_DIR)/gtest_main.a : $(BUILD_DIR)/gtest-all.o $(BUILD_DIR)/gtest_main.o + $(NO_ECHO)$(AR) $(ARFLAGS) $@ $^ + + + + + + + + + +#============================== Board option rules =================================== +print_info: print_lcov_help +# @echo "You can run tests with \"make [${RUN_TESTS}]\" or \"make run_all\"" + + + + +compile: clean lcov_flags print_info + +NRF51DK: compile + +badge_03: compile +badge_03_noDebug: compile + +badge_03v2_rigado: compile +badge_03v2_rigado_noDebug: compile +badge_03v2_rigado_tester: compile +badge_03v2_dynastream: compile + +badge_03v4: compile +badge_03v4_noDebug: compile +badge_03v4_tester: compile + +badge_03v6: compile +badge_03v6_noDebug: compile +badge_03v6_tester: compile + + +$(BUILD_C_DIR)/%.o : %.c + @echo Compiling $(notdir $@) + $(NO_ECHO) $(CXX) $(CPPFLAGS) $(TINYBUF_INC_PATH) $(MOCK_INC_PATH) $(FIRMWARE_INC_PATH) $(CXXFLAGS) -o $@ -c $< + +$(BUILD_CC_DIR)/%.o : %.cc + @echo Compiling $(notdir $@) + $(NO_ECHO) $(CXX) $(CPPFLAGS) $(TINYBUF_INC_PATH) $(MOCK_INC_PATH) $(FIRMWARE_INC_PATH) $(CXXFLAGS) -o $@ -c $< + + +# Builds a sample test. A test should link with either gtest.a or +# gtest_main.a, depending on whether it defines its own main() +# function. + +$(TESTS_OBJECTS_CC): $(C_OBJECTS) $(CC_OBJECTS) + @echo Compiling $(notdir $@) + $(NO_ECHO)$(CXX) $(CPPFLAGS) $(TINYBUF_INC_PATH) $(MOCK_INC_PATH) $(FIRMWARE_INC_PATH) $(CXXFLAGS) -o $@ -c $(TEST_DIR)/$(basename $(notdir $@)).cc + +#https://stackoverflow.com/questions/16262344/pass-a-target-name-to-dependency-list-in-makefile +$(TESTS): % : $(BUILD_DIR)/gtest_main.a $(BUILD_CC_DIR)/%.o + @echo Linking $(notdir $@) +# $(NO_ECHO)-mv $(BUILD_DIR)/*.o $(BUILD_DIR)/*.a . 2>/dev/null || : + $(NO_ECHO)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(TINYBUF_INC_PATH) $(MOCK_INC_PATH) $(FIRMWARE_INC_PATH) -lpthread $(BUILD_CC_DIR)/$@.o $(C_OBJECTS) $(CC_OBJECTS) $(BUILD_DIR)/gtest_main.a -o $(BUILD_DIR)/run_$@ -lrt +# $(NO_ECHO)-mv *.o *.a $(BUILD_DIR) 2>/dev/null || : +ifeq ($(LCOV), TRUE) +#move .gcno files (only if exist) and don't raise an error + $(NO_ECHO)-mv $(BUILD_DIR)/*.gcno $(LCOV_DIR) 2>/dev/null || : + $(NO_ECHO)-mv $(BUILD_C_DIR)/*.gcno $(LCOV_DIR) 2>/dev/null || : + $(NO_ECHO)-mv $(BUILD_CC_DIR)/*.gcno $(LCOV_DIR) 2>/dev/null || : +endif + + diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-death-test.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-death-test.h new file mode 100644 index 0000000..53f11d4 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-death-test.h @@ -0,0 +1,342 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include "gtest/internal/gtest-death-test-internal.h" + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows or Mac), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// +// TODO(wan@google.com): make thread-safe death tests search the PATH. + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + bool operator()(int exit_status) const; + private: + // No implementation - assignment is unsupported. + void operator=(const ExitedWithCode& other); + + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on +// systems that support death tests. This allows one to write such a macro +// on a system that does not support death tests and be sure that it will +// compile on a death-test supporting system. It is exposed publicly so that +// systems that have death-tests with stricter requirements than +// GTEST_HAS_DEATH_TEST can write their own equivalent of +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter iff EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) +#endif + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-message.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-message.h new file mode 100644 index 0000000..d7266ba --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-message.h @@ -0,0 +1,249 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include + +#include "gtest/internal/gtest-port.h" + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + +#if GTEST_OS_SYMBIAN + // Streams a value (either a pointer or not) to this object. + template + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer::type(), value); + return *this; + } +#else + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } +#endif // GTEST_OS_SYMBIAN + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: +#if GTEST_OS_SYMBIAN + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template + inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + } + template + inline void StreamHelper(internal::false_type /*is_pointer*/, + const T& value) { + // See the comments in Message& operator <<(const T&) above for why + // we need this using statement. + using ::operator <<; + *ss_ << value; + } +#endif // GTEST_OS_SYMBIAN + + // We'll hold the text streamed to this object here. + const internal::scoped_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-param-test.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-param-test.h new file mode 100644 index 0000000..8993c1c --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-param-test.h @@ -0,0 +1,1438 @@ +// This file was GENERATED by command: +// pump.py gtest-param-test.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include "gtest/internal/gtest-port.h" + +#if !GTEST_OS_SYMBIAN +# include +#endif + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-param-util-generated.h" + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test case FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test case StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_CASE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits + ::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to 50 parameters. +// +template +internal::ValueArray1 Values(T1 v1) { + return internal::ValueArray1(v1); +} + +template +internal::ValueArray2 Values(T1 v1, T2 v2) { + return internal::ValueArray2(v1, v2); +} + +template +internal::ValueArray3 Values(T1 v1, T2 v2, T3 v3) { + return internal::ValueArray3(v1, v2, v3); +} + +template +internal::ValueArray4 Values(T1 v1, T2 v2, T3 v3, T4 v4) { + return internal::ValueArray4(v1, v2, v3, v4); +} + +template +internal::ValueArray5 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5) { + return internal::ValueArray5(v1, v2, v3, v4, v5); +} + +template +internal::ValueArray6 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6) { + return internal::ValueArray6(v1, v2, v3, v4, v5, v6); +} + +template +internal::ValueArray7 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7) { + return internal::ValueArray7(v1, v2, v3, v4, v5, + v6, v7); +} + +template +internal::ValueArray8 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { + return internal::ValueArray8(v1, v2, v3, v4, + v5, v6, v7, v8); +} + +template +internal::ValueArray9 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { + return internal::ValueArray9(v1, v2, v3, + v4, v5, v6, v7, v8, v9); +} + +template +internal::ValueArray10 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { + return internal::ValueArray10(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10); +} + +template +internal::ValueArray11 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) { + return internal::ValueArray11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); +} + +template +internal::ValueArray12 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) { + return internal::ValueArray12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); +} + +template +internal::ValueArray13 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) { + return internal::ValueArray13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); +} + +template +internal::ValueArray14 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { + return internal::ValueArray14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14); +} + +template +internal::ValueArray15 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { + return internal::ValueArray15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); +} + +template +internal::ValueArray16 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16) { + return internal::ValueArray16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16); +} + +template +internal::ValueArray17 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17) { + return internal::ValueArray17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17); +} + +template +internal::ValueArray18 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18) { + return internal::ValueArray18(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18); +} + +template +internal::ValueArray19 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { + return internal::ValueArray19(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); +} + +template +internal::ValueArray20 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { + return internal::ValueArray20(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); +} + +template +internal::ValueArray21 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { + return internal::ValueArray21(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); +} + +template +internal::ValueArray22 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22) { + return internal::ValueArray22(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22); +} + +template +internal::ValueArray23 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23) { + return internal::ValueArray23(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23); +} + +template +internal::ValueArray24 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24) { + return internal::ValueArray24(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24); +} + +template +internal::ValueArray25 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { + return internal::ValueArray25(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25); +} + +template +internal::ValueArray26 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) { + return internal::ValueArray26(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); +} + +template +internal::ValueArray27 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) { + return internal::ValueArray27(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); +} + +template +internal::ValueArray28 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) { + return internal::ValueArray28(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28); +} + +template +internal::ValueArray29 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) { + return internal::ValueArray29(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29); +} + +template +internal::ValueArray30 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { + return internal::ValueArray30(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30); +} + +template +internal::ValueArray31 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { + return internal::ValueArray31(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31); +} + +template +internal::ValueArray32 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32) { + return internal::ValueArray32(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32); +} + +template +internal::ValueArray33 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33) { + return internal::ValueArray33(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); +} + +template +internal::ValueArray34 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34) { + return internal::ValueArray34(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); +} + +template +internal::ValueArray35 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { + return internal::ValueArray35(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); +} + +template +internal::ValueArray36 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { + return internal::ValueArray36(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36); +} + +template +internal::ValueArray37 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37) { + return internal::ValueArray37(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37); +} + +template +internal::ValueArray38 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38) { + return internal::ValueArray38(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, + v33, v34, v35, v36, v37, v38); +} + +template +internal::ValueArray39 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38, T39 v39) { + return internal::ValueArray39(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, + v32, v33, v34, v35, v36, v37, v38, v39); +} + +template +internal::ValueArray40 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, + T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, + T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { + return internal::ValueArray40(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, + v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); +} + +template +internal::ValueArray41 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { + return internal::ValueArray41(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); +} + +template +internal::ValueArray42 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) { + return internal::ValueArray42(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, + v42); +} + +template +internal::ValueArray43 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) { + return internal::ValueArray43(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, + v41, v42, v43); +} + +template +internal::ValueArray44 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) { + return internal::ValueArray44(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, + v40, v41, v42, v43, v44); +} + +template +internal::ValueArray45 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { + return internal::ValueArray45(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, + v39, v40, v41, v42, v43, v44, v45); +} + +template +internal::ValueArray46 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { + return internal::ValueArray46(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46); +} + +template +internal::ValueArray47 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { + return internal::ValueArray47(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); +} + +template +internal::ValueArray48 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, + T48 v48) { + return internal::ValueArray48(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, + v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); +} + +template +internal::ValueArray49 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, + T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, + T47 v47, T48 v48, T49 v49) { + return internal::ValueArray49(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, + v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); +} + +template +internal::ValueArray50 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, + T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, + T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { + return internal::ValueArray50(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, + v48, v49, v50); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to 10 arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder2 Combine( + const Generator1& g1, const Generator2& g2) { + return internal::CartesianProductHolder2( + g1, g2); +} + +template +internal::CartesianProductHolder3 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3) { + return internal::CartesianProductHolder3( + g1, g2, g3); +} + +template +internal::CartesianProductHolder4 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4) { + return internal::CartesianProductHolder4( + g1, g2, g3, g4); +} + +template +internal::CartesianProductHolder5 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5) { + return internal::CartesianProductHolder5( + g1, g2, g3, g4, g5); +} + +template +internal::CartesianProductHolder6 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6) { + return internal::CartesianProductHolder6( + g1, g2, g3, g4, g5, g6); +} + +template +internal::CartesianProductHolder7 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7) { + return internal::CartesianProductHolder7( + g1, g2, g3, g4, g5, g6, g7); +} + +template +internal::CartesianProductHolder8 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8) { + return internal::CartesianProductHolder8( + g1, g2, g3, g4, g5, g6, g7, g8); +} + +template +internal::CartesianProductHolder9 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9) { + return internal::CartesianProductHolder9( + g1, g2, g3, g4, g5, g6, g7, g8, g9); +} + +template +internal::CartesianProductHolder10 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9, + const Generator10& g10) { + return internal::CartesianProductHolder10( + g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); +} +# endif // GTEST_HAS_COMBINE + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, \ + ::testing::internal::CodeLocation(\ + __FILE__, __LINE__))->AddTestPattern(\ + GTEST_STRINGIFY_(test_case_name), \ + GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(\ + test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +// The optional last argument to INSTANTIATE_TEST_CASE_P allows the user +// to specify a function or functor that generates custom test name suffixes +// based on the test parameters. The function should accept one argument of +// type testing::TestParamInfo, and return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +#define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \ + static ::testing::internal::ParamGenerator \ + gtest_##prefix##test_case_name##_EvalGenerator_() { \ + return generator; \ + } \ + static ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo& info) { \ + return ::testing::internal::GetParamNameGen( \ + __VA_ARGS__)(info); \ + } \ + static int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestCasePatternHolder( \ + #test_case_name, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestCaseInstantiation( \ + #prefix, >est_##prefix##test_case_name##_EvalGenerator_, \ + >est_##prefix##test_case_name##_EvalGenerateName_, __FILE__, \ + __LINE__) + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-param-test.h.pump b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-param-test.h.pump new file mode 100644 index 0000000..7b7243f --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-param-test.h.pump @@ -0,0 +1,501 @@ +$$ -*- mode: c++; -*- +$var n = 50 $$ Maximum length of Values arguments we want to support. +$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support. +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include "gtest/internal/gtest-port.h" + +#if !GTEST_OS_SYMBIAN +# include +#endif + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-param-util-generated.h" + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test case FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test case StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_CASE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits + ::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to $n parameters. +// +$range i 1..n +$for i [[ +$range j 1..i + +template <$for j, [[typename T$j]]> +internal::ValueArray$i<$for j, [[T$j]]> Values($for j, [[T$j v$j]]) { + return internal::ValueArray$i<$for j, [[T$j]]>($for j, [[v$j]]); +} + +]] + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to $maxtuple arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +$range i 2..maxtuple +$for i [[ +$range j 1..i + +template <$for j, [[typename Generator$j]]> +internal::CartesianProductHolder$i<$for j, [[Generator$j]]> Combine( + $for j, [[const Generator$j& g$j]]) { + return internal::CartesianProductHolder$i<$for j, [[Generator$j]]>( + $for j, [[g$j]]); +} + +]] +# endif // GTEST_HAS_COMBINE + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, \ + ::testing::internal::CodeLocation(\ + __FILE__, __LINE__))->AddTestPattern(\ + GTEST_STRINGIFY_(test_case_name), \ + GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(\ + test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +// The optional last argument to INSTANTIATE_TEST_CASE_P allows the user +// to specify a function or functor that generates custom test name suffixes +// based on the test parameters. The function should accept one argument of +// type testing::TestParamInfo, and return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \ + static ::testing::internal::ParamGenerator \ + gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ + static ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo& info) { \ + return ::testing::internal::GetParamNameGen \ + (__VA_ARGS__)(info); \ + } \ + static int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, \ + ::testing::internal::CodeLocation(\ + __FILE__, __LINE__))->AddTestCaseInstantiation(\ + #prefix, \ + >est_##prefix##test_case_name##_EvalGenerator_, \ + >est_##prefix##test_case_name##_EvalGenerateName_, \ + __FILE__, __LINE__) + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-printers.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-printers.h new file mode 100644 index 0000000..373946b --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-printers.h @@ -0,0 +1,1082 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include // NOLINT +#include +#include +#include +#include +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-internal.h" + +#if GTEST_HAS_STD_TUPLE_ +# include +#endif + +#if GTEST_HAS_ABSL +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#endif // GTEST_HAS_ABSL + +namespace testing { + +// Definitions in the 'internal' and 'internal2' name spaces are +// subject to change without notice. DO NOT USE THEM IN USER CODE! +namespace internal2 { + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); + +// For selecting which printer to use when a given type has neither << +// nor PrintTo(). +enum TypeKind { + kProtobuf, // a protobuf type + kConvertibleToInteger, // a type implicitly convertible to BiggestInt + // (e.g. a named or unnamed enum type) +#if GTEST_HAS_ABSL + kConvertibleToStringView, // a type implicitly convertible to + // absl::string_view +#endif + kOtherType // anything else +}; + +// TypeWithoutFormatter::PrintValue(value, os) is called +// by the universal printer to print a value of type T when neither +// operator<< nor PrintTo() is defined for T, where kTypeKind is the +// "kind" of T as defined by enum TypeKind. +template +class TypeWithoutFormatter { + public: + // This default version is called when kTypeKind is kOtherType. + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo(static_cast( + reinterpret_cast(&value)), + sizeof(value), os); + } +}; + +// We print a protobuf using its ShortDebugString() when the string +// doesn't exceed this many characters; otherwise we print it using +// DebugString() for better readability. +const size_t kProtobufOneLinerMaxLength = 50; + +template +class TypeWithoutFormatter { + public: + static void PrintValue(const T& value, ::std::ostream* os) { + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } + *os << ("<" + pretty_str + ">"); + } +}; + +template +class TypeWithoutFormatter { + public: + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(const T& value, ::std::ostream* os) { + const internal::BiggestInt kBigInt = value; + *os << kBigInt; + } +}; + +#if GTEST_HAS_ABSL +template +class TypeWithoutFormatter { + public: + // Since T has neither operator<< nor PrintTo() but can be implicitly + // converted to absl::string_view, we print it as a absl::string_view. + // + // Note: the implementation is further below, as it depends on + // internal::PrintTo symbol which is defined later in the file. + static void PrintValue(const T& value, ::std::ostream* os); +}; +#endif + +// Prints the given value to the given ostream. If the value is a +// protocol message, its debug string is printed; if it's an enum or +// of a type implicitly convertible to BiggestInt, it's printed as an +// integer; otherwise the bytes in the value are printed. This is +// what UniversalPrinter::Print() does when it knows nothing about +// type T and T has neither << operator nor PrintTo(). +// +// A user can override this behavior for a class type Foo by defining +// a << operator in the namespace where Foo is defined. +// +// We put this operator in namespace 'internal2' instead of 'internal' +// to simplify the implementation, as much code in 'internal' needs to +// use << in STL, which would conflict with our own << were it defined +// in 'internal'. +// +// Note that this operator<< takes a generic std::basic_ostream type instead of the more restricted std::ostream. If +// we define it to take an std::ostream instead, we'll get an +// "ambiguous overloads" compiler error when trying to print a type +// Foo that supports streaming to std::basic_ostream, as the compiler cannot tell whether +// operator<<(std::ostream&, const T&) or +// operator<<(std::basic_stream, const Foo&) is more +// specific. +template +::std::basic_ostream& operator<<( + ::std::basic_ostream& os, const T& x) { + TypeWithoutFormatter::value + ? kProtobuf + : internal::ImplicitlyConvertible< + const T&, internal::BiggestInt>::value + ? kConvertibleToInteger + : +#if GTEST_HAS_ABSL + internal::ImplicitlyConvertible< + const T&, absl::string_view>::value + ? kConvertibleToStringView + : +#endif + kOtherType)>::PrintValue(x, &os); + return os; +} + +} // namespace internal2 +} // namespace testing + +// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up +// magic needed for implementing UniversalPrinter won't work. +namespace testing_internal { + +// Used to print a value that is not an STL-style container when the +// user doesn't define PrintTo() for it. +template +void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { + // With the following statement, during unqualified name lookup, + // testing::internal2::operator<< appears as if it was declared in + // the nearest enclosing namespace that contains both + // ::testing_internal and ::testing::internal2, i.e. the global + // namespace. For more details, refer to the C++ Standard section + // 7.3.4-1 [namespace.udir]. This allows us to fall back onto + // testing::internal2::operator<< in case T doesn't come with a << + // operator. + // + // We cannot write 'using ::testing::internal2::operator<<;', which + // gcc 3.3 fails to compile due to a compiler bug. + using namespace ::testing::internal2; // NOLINT + + // Assuming T is defined in namespace foo, in the next statement, + // the compiler will consider all of: + // + // 1. foo::operator<< (thanks to Koenig look-up), + // 2. ::operator<< (as the current namespace is enclosed in ::), + // 3. testing::internal2::operator<< (thanks to the using statement above). + // + // The operator<< whose type matches T best will be picked. + // + // We deliberately allow #2 to be a candidate, as sometimes it's + // impossible to define #1 (e.g. when foo is ::std, defining + // anything in it is undefined behavior unless you are a compiler + // vendor.). + *os << value; +} + +} // namespace testing_internal + +namespace testing { +namespace internal { + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); + +#if GTEST_HAS_GLOBAL_STRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string); +#endif + +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring); +#endif + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +enum DefaultPrinterType { + kPrintContainer, + kPrintPointer, + kPrintFunctionPointer, + kPrintOther, +}; +template struct WrapPrinterType {}; + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +template +void DefaultPrintTo(WrapPrinterType /* dummy */, + const C& container, ::std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (typename C::const_iterator it = container.begin(); + it != container.end(); ++it, ++count) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; +} + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +template +void DefaultPrintTo(WrapPrinterType /* dummy */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } +} +template +void DefaultPrintTo(WrapPrinterType /* dummy */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast(p); + } +} + +// Used to print a non-container, non-pointer value when the user +// doesn't define PrintTo() for it. +template +void DefaultPrintTo(WrapPrinterType /* dummy */, + const T& value, ::std::ostream* os) { + ::testing_internal::DefaultPrintNonContainerTo(value, os); +} + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + // DefaultPrintTo() is overloaded. The type of its first argument + // determines which version will be picked. + // + // Note that we check for container types here, prior to we check + // for protocol message types in our operator<<. The rationale is: + // + // For protocol messages, we want to give people a chance to + // override Google Mock's format by defining a PrintTo() or + // operator<<. For STL containers, other formats can be + // incompatible with Google Mock's format for the container + // elements; therefore we check for container types here to ensure + // that our format is used. + // + // Note that MSVC and clang-cl do allow an implicit conversion from + // pointer-to-function to pointer-to-object, but clang-cl warns on it. + // So don't use ImplicitlyConvertible if it can be helped since it will + // cause this warning, and use a separate overload of DefaultPrintTo for + // function pointers so that the `*os << p` in the object pointer overload + // doesn't cause that warning either. + DefaultPrintTo( + WrapPrinterType < + (sizeof(IsContainerTest(0)) == sizeof(IsContainer)) && + !IsRecursiveContainer::value + ? kPrintContainer + : !is_pointer::value + ? kPrintOther +#if GTEST_LANG_CXX11 + : std::is_function::type>::value +#else + : !internal::ImplicitlyConvertible::value +#endif + ? kPrintFunctionPointer + : kPrintPointer > (), + value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::string and ::std::string. +#if GTEST_HAS_GLOBAL_STRING +GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); +inline void PrintTo(const ::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::wstring and ::std::wstring. +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_ABSL +// Overload for absl::string_view. +inline void PrintTo(absl::string_view sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_HAS_ABSL + +#if GTEST_LANG_CXX11 +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } +#endif // GTEST_LANG_CXX11 + +#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os); +#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ + +#if GTEST_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. + +// Overloaded PrintTo() for tuples of various arities. We support +// tuples of up-to 10 fields. The following implementation works +// regardless of whether tr1::tuple is implemented using the +// non-standard variadic template feature or not. + +inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo( + const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_TR1_TUPLE + +#if GTEST_HAS_STD_TUPLE_ +template +void PrintTo(const ::std::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_STD_TUPLE_ + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +#if GTEST_HAS_ABSL + +// Printer for absl::optional + +template +class UniversalPrinter<::absl::optional> { + public: + static void Print(const ::absl::optional& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +#endif // GTEST_HAS_ABSL + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + // TODO(wan@google.com): let the user control the threshold using a flag. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(std::string(str), os); + } + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(char* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +typedef ::std::vector< ::std::string> Strings; + +// TuplePolicy must provide: +// - tuple_size +// size of tuple TupleT. +// - get(const TupleT& t) +// static function extracting element I of tuple TupleT. +// - tuple_element::type +// type of element I of tuple TupleT. +template +struct TuplePolicy; + +#if GTEST_HAS_TR1_TUPLE +template +struct TuplePolicy { + typedef TupleT Tuple; + static const size_t tuple_size = ::std::tr1::tuple_size::value; + + template + struct tuple_element : ::std::tr1::tuple_element {}; + + template + static typename AddReference< + const typename ::std::tr1::tuple_element::type>::type get( + const Tuple& tuple) { + return ::std::tr1::get(tuple); + } +}; +template +const size_t TuplePolicy::tuple_size; +#endif // GTEST_HAS_TR1_TUPLE + +#if GTEST_HAS_STD_TUPLE_ +template +struct TuplePolicy< ::std::tuple > { + typedef ::std::tuple Tuple; + static const size_t tuple_size = ::std::tuple_size::value; + + template + struct tuple_element : ::std::tuple_element {}; + + template + static const typename ::std::tuple_element::type& get( + const Tuple& tuple) { + return ::std::get(tuple); + } +}; +template +const size_t TuplePolicy< ::std::tuple >::tuple_size; +#endif // GTEST_HAS_STD_TUPLE_ + +#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ +// This helper template allows PrintTo() for tuples and +// UniversalTersePrintTupleFieldsToStrings() to be defined by +// induction on the number of tuple fields. The idea is that +// TuplePrefixPrinter::PrintPrefixTo(t, os) prints the first N +// fields in tuple t, and can be defined in terms of +// TuplePrefixPrinter. +// +// The inductive case. +template +struct TuplePrefixPrinter { + // Prints the first N fields of a tuple. + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + TuplePrefixPrinter::PrintPrefixTo(t, os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (N > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter< + typename TuplePolicy::template tuple_element::type> + ::Print(TuplePolicy::template get(t), os); + } + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + TuplePrefixPrinter::TersePrintPrefixToStrings(t, strings); + ::std::stringstream ss; + UniversalTersePrint(TuplePolicy::template get(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Base case. +template <> +struct TuplePrefixPrinter<0> { + template + static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} + + template + static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} +}; + +// Helper function for printing a tuple. +// Tuple must be either std::tr1::tuple or std::tuple type. +template +void PrintTupleTo(const Tuple& t, ::std::ostream* os) { + *os << "("; + TuplePrefixPrinter::tuple_size>::PrintPrefixTo(t, os); + *os << ")"; +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TuplePrefixPrinter::tuple_size>:: + TersePrintPrefixToStrings(value, &result); + return result; +} +#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ + +} // namespace internal + +#if GTEST_HAS_ABSL +namespace internal2 { +template +void TypeWithoutFormatter::PrintValue( + const T& value, ::std::ostream* os) { + internal::PrintTo(absl::string_view(value), os); +} +} // namespace internal2 +#endif + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +// Include any custom printer added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +#include "gtest/internal/custom/gtest-printers.h" + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-spi.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-spi.h new file mode 100644 index 0000000..0e5c10c --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-spi.h @@ -0,0 +1,231 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include "gtest/gtest.h" + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-test-part.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-test-part.h new file mode 100644 index 0000000..77eb844 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-test-part.h @@ -0,0 +1,179 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure // Failed and the test should be terminated. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, + const char* a_file_name, + int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == NULL ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) { + } + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? NULL : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true iff the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != kSuccess; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + virtual ~HasNewFatalFailureHelper(); + virtual void ReportTestPartResult(const TestPartResult& result); + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-typed-test.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-typed-test.h new file mode 100644 index 0000000..759d1db --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest-typed-test.h @@ -0,0 +1,264 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test case, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_CASE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_CASE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test case as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + // Since we are inside a derived class template, C++ requires use to + // visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test case +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_CASE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test case as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test case name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_CASE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test case name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); + +#endif // 0 + +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-type-util.h" + +// Implements typed tests. + +#if GTEST_HAS_TYPED_TEST + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test case. +# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define TYPED_TEST_CASE(CaseName, Types) \ + typedef ::testing::internal::TypeList< Types >::type \ + GTEST_TYPE_PARAMS_(CaseName) + +# define TYPED_TEST(CaseName, TestName) \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel< \ + GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ + GTEST_TYPE_PARAMS_(CaseName)>::Register(\ + "", ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + #CaseName, #TestName, 0); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() + +#endif // GTEST_HAS_TYPED_TEST + +// Implements type-parameterized tests. + +#if GTEST_HAS_TYPED_TEST_P + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test case are defined in. The exact +// name of the namespace is subject to change without notice. +# define GTEST_CASE_NAMESPACE_(TestCaseName) \ + gtest_case_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test case. +# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ + gtest_typed_test_case_p_state_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test case. +# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ + gtest_registered_test_names_##TestCaseName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +# define TYPED_TEST_CASE_P(CaseName) \ + static ::testing::internal::TypedTestCasePState \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) + +# define TYPED_TEST_P(CaseName, TestName) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + template \ + class TestName : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ + __FILE__, __LINE__, #CaseName, #TestName); \ + } \ + template \ + void GTEST_CASE_NAMESPACE_(CaseName)::TestName::TestBody() + +# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) \ + GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames( \ + __FILE__, __LINE__, #__VA_ARGS__) + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ + bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestCase::type>::Register(\ + #Prefix, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_CASE_P_STATE_(CaseName), \ + #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) + +#endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest.h new file mode 100644 index 0000000..138537c --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest.h @@ -0,0 +1,2342 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest-message.h" +#include "gtest/gtest-param-test.h" +#include "gtest/gtest-printers.h" +#include "gtest/gtest_prod.h" +#include "gtest/gtest-test-part.h" +#include "gtest/gtest-typed-test.h" + +// Depending on the platform, different string classes are available. +// On Linux, in addition to ::std::string, Google also makes use of +// class ::string, which has the same interface as ::std::string, but +// has a different implementation. +// +// You can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that +// ::string is available AND is a distinct type to ::std::string, or +// define it to 0 to indicate otherwise. +// +// If ::std::string and ::string are the same class on your platform +// due to aliasing, you should define GTEST_HAS_GLOBAL_STRING to 0. +// +// If you do not define GTEST_HAS_GLOBAL_STRING, it is defined +// heuristically. + +namespace testing { + +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4805) +# pragma warning(disable:4100) +#endif + + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. For use with an external test framework. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class FuchsiaDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestCase; +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + +#if defined(_MSC_VER) && _MSC_VER < 1910 + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif + + // Used in the EXPECT_TRUE/FALSE(bool_expression). + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template + explicit AssertionResult( + const T& success, + typename internal::EnableIf< + !internal::ImplicitlyConvertible::value>::type* + /*enabler*/ = NULL) + : success_(success) {} + +#if defined(_MSC_VER) && _MSC_VER < 1910 + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } + + // Returns true iff the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != NULL ? message_->c_str() : ""; + } + // TODO(vladl@google.com): Remove this after making sure no clients use it. + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == NULL) + message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + internal::scoped_ptr< ::std::string> message_; +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +} // namespace testing + +// Includes the auto-generated header that implements a family of generic +// predicate assertion macros. This include comes late because it relies on +// APIs declared above. +#include "gtest/gtest_pred_impl.h" + +namespace testing { + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true iff the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true iff the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestCase or TearDownTestCase are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + const internal::scoped_ptr< GTEST_FLAG_SAVER_ > gtest_flag_saver_; + + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. + // + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const; + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns true iff the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestCase; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // TODO(russr): Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properites_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != NULL) + return value_param_->c_str(); + return NULL; + } + + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } + + // Returns the line where this test is defined. + int line() const { return location_.line; } + + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true iff this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestCase; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + internal::CodeLocation code_location, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_case_name, + const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test case, which consists of a vector of TestInfos. +// +// TestCase is not copyable. +class GTEST_API_ TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestCase and TearDownTestCase. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestCase. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestCase. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true iff the test is disabled and will be reported in the XML + // report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true iff this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test case. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test case. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test case starts. + virtual void OnTestCaseStart(const TestCase& test_case) = 0; + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test case ends. + virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} + virtual void OnTestStart(const TestInfo& /*test_info*/) {} + virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} + virtual void OnTestEnd(const TestInfo& /*test_info*/) {} + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} + virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestCase; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestCases. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + + // Returns the ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const; + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const; + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test cases. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestCase's ad_hoc_test_result_ when invoked + // from SetUpTestCase or TearDownTestCase, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and functions are friends as they need to access private + // members of UnitTest. + friend class ScopedTrace; + friend class Test; + friend class internal::AssertHelper; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +namespace internal { + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template +AssertionResult CmpHelperEQFailure(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, const T2& rhs) { + return EqFailure(lhs_expression, + rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), + false); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, + const T2& rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +// With this overloaded version, we allow anonymous enums to be used +// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums +// can be implicitly cast to BiggestInt. +GTEST_API_ AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + BiggestInt lhs, + BiggestInt rhs); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template +class EqHelper { + public: + // This templatized version is for the general case. + template + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, + const T2& rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, + BiggestInt lhs, + BiggestInt rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal, like NULL, false, or 0. +template <> +class EqHelper { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template + static AssertionResult Compare( + const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, + const T2& rhs, + // The following line prevents this overload from being considered if T2 + // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) + // expands to Compare("", "", NULL, my_ptr), which requires a conversion + // to match the Secret* in the other overload, which would otherwise make + // this template match better. + typename EnableIf::value>::type* = 0) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // This version will be picked when the second argument to ASSERT_EQ() is a + // pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template + static AssertionResult Compare( + const char* lhs_expression, + const char* rhs_expression, + // We used to have a second template parameter instead of Secret*. That + // template parameter would deduce to 'long', making this a better match + // than the first overload even without the first overload's EnableIf. + // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to + // non-pointer argument" (even a deduced integral argument), so the old + // implementation caused warnings in user code. + Secret* /* lhs (NULL) */, + T* rhs) { + // We already know that 'lhs' is a null pointer. + return CmpHelperEQ(lhs_expression, rhs_expression, + static_cast(NULL), rhs); + } +}; + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\ + }\ +}\ +GTEST_API_ AssertionResult CmpHelper##op_name(\ + const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=); +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=); +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <); +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=); +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >); + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression, + const char* rhs_expression, + RawType lhs_value, + RawType rhs_value) { + const FloatingPoint lhs(lhs_value), rhs(rhs_value); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream lhs_ss; + lhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << lhs_value; + + ::std::stringstream rhs_ss; + rhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << rhs_value; + + return EqFailure(lhs_expression, + rhs_expression, + StringStreamToString(&lhs_ss), + StringStreamToString(&rhs_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// virtual ~FooTest() { +// // Can use GetParam() here. +// } +// virtual void SetUp() { +// // Can use GetParam() here. +// } +// virtual void TearDown { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. This member function is non-static, even though it only + // references static data, to reduce the opportunity for incorrect uses + // like writing 'WithParamInterface::GetParam()' for a test that + // uses a fixture whose parameter type is int. + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = NULL; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface { +}; + +// Macros for indicating success/failure in test code. + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2 +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to +// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + val1, val2) +#define EXPECT_NE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + val1, val2) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_DOUBLE_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_FLOAT_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_DOUBLE_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + +#if GTEST_HAS_GLOBAL_STRING + ScopedTrace(const char* file, int line, const ::string& message) { + PushTrace(file, line, message); + } +#endif + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. +#define SCOPED_TRACE(message) \ + ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, (message)) + + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles iff type1 and type2 are +// the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +bool StaticAssertTypeEq() { + (void)internal::StaticAssertTypeEqHelper(); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// Test code should appear between braces after an invocation of +// this macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_case_name, test_name)\ + GTEST_TEST_(test_case_name, test_name, \ + ::testing::Test, ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// the test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) + +// Returns a path to temporary directory. +// Tries to determine an appropriate directory for the platform. +GTEST_API_ std::string TempDir(); + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} + +#define EXPECT_ARRAY_EQ(array1, array2, num) { \ + for(uint32_t i = 0; i < num; i++) { \ + EXPECT_EQ(array1[i], array2[i]); \ + if(array1[i] != array2[i]) { \ + printf(" at index i=%u\n", i); \ + } \ + } \ +} + + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest_pred_impl.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest_pred_impl.h new file mode 100644 index 0000000..c8be230 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest_pred_impl.h @@ -0,0 +1,357 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 01/02/2018 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +#include "gtest/gtest.h" + +namespace testing { + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest_prod.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest_prod.h new file mode 100644 index 0000000..89314f4 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/gtest_prod.h @@ -0,0 +1,61 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing and Mocking Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void PrivateMethod(); +// FRIEND_TEST(MyClassTest, PrivateMethodWorks); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, PrivateMethodWorks) { +// // Can call MyClass::PrivateMethod() here. +// } +// +// Note: The test class must be in the same namespace as the class being tested. +// For example, putting MyClassTest in an anonymous namespace will not work. + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest-port.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest-port.h new file mode 100644 index 0000000..94884c1 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest-port.h @@ -0,0 +1,75 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. +// The following macros can be defined: +// +// Flag related macros: +// GTEST_FLAG(flag_name) +// GTEST_USE_OWN_FLAGFILE_FLAG_ - Define to 0 when the system provides its +// own flagfile flag parsing. +// GTEST_DECLARE_bool_(name) +// GTEST_DECLARE_int32_(name) +// GTEST_DECLARE_string_(name) +// GTEST_DEFINE_bool_(name, default_val, doc) +// GTEST_DEFINE_int32_(name, default_val, doc) +// GTEST_DEFINE_string_(name, default_val, doc) +// +// Test filtering: +// GTEST_TEST_FILTER_ENV_VAR_ - The name of an environment variable that +// will be used if --GTEST_FLAG(test_filter) +// is not provided. +// +// Logging: +// GTEST_LOG_(severity) +// GTEST_CHECK_(condition) +// Functions LogToStderr() and FlushInfoLog() have to be provided too. +// +// Threading: +// GTEST_HAS_NOTIFICATION_ - Enabled if Notification is already provided. +// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Enabled if Mutex and ThreadLocal are +// already provided. +// Must also provide GTEST_DECLARE_STATIC_MUTEX_(mutex) and +// GTEST_DEFINE_STATIC_MUTEX_(mutex) +// +// GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +// GTEST_LOCK_EXCLUDED_(locks) +// +// Underlying library support features: +// GTEST_HAS_CXXABI_H_ +// +// Exporting API symbols: +// GTEST_API_ - Specifier for exported symbols. +// +// ** Custom implementation starts here ** + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest-printers.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest-printers.h new file mode 100644 index 0000000..60c1ea0 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest-printers.h @@ -0,0 +1,42 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file provides an injection point for custom printers in a local +// installation of gTest. +// It will be included from gtest-printers.h and the overrides in this file +// will be visible to everyone. +// See documentation at gtest/gtest-printers.h for details on how to define a +// custom printer. +// +// ** Custom implementation starts here ** + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest.h new file mode 100644 index 0000000..6f7c5e4 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/custom/gtest.h @@ -0,0 +1,45 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. +// The following macros can be defined: +// +// GTEST_OS_STACK_TRACE_GETTER_ - The name of an implementation of +// OsStackTraceGetterInterface. +// +// GTEST_CUSTOM_TEMPDIR_FUNCTION_ - An override for testing::TempDir(). +// See testing::TempDir for semantics and +// signature. +// +// ** Custom implementation starts here ** + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-death-test-internal.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 0000000..949c429 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,275 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include "gtest/internal/gtest-internal.h" + +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ + gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel \ + gtest_sentinel(gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ + fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + static_cast(gtest_regex); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-filepath.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-filepath.h new file mode 100644 index 0000000..406597a --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-filepath.h @@ -0,0 +1,205 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#include "gtest/internal/gtest-string.h" + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true iff the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-internal.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-internal.h new file mode 100644 index 0000000..fc65b1f --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-internal.h @@ -0,0 +1,1277 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#include "gtest/internal/gtest-port.h" + +#if GTEST_OS_LINUX +# include +# include +# include +# include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-type-util.h" + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +// Stringifies its argument. +#define GTEST_STRINGIFY_(name) #name + +class ProtocolMessage; +namespace proto2 { class Message; } + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test cases. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef GTEST_ELLIPSIS_NEEDS_POD_ +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_IS_NULL_LITERAL_(x) false +#else +# define GTEST_IS_NULL_LITERAL_(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // GTEST_ELLIPSIS_NEEDS_POD_ + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +#endif // GTEST_HAS_EXCEPTIONS + +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner-Fischer algorithm. +// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, const std::vector& right); + +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right); + +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context = 2); + +} // namespace edit_distance + +// Calculate the diff between 'left' and 'right' and return it in unified diff +// format. +// If not null, stores in 'total_line_count' the total number of lines found +// in left + right. +GTEST_API_ std::string DiffStrings(const std::string& left, + const std::string& right, + size_t* total_line_count); + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const size_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test case, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + virtual Test* CreateTest() { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestCase() and TearDownTestCase() functions. +typedef void (*SetUpTestCaseFunc)(); +typedef void (*TearDownTestCaseFunc)(); + +struct CodeLocation { + CodeLocation(const std::string& a_file, int a_line) + : file(a_file), line(a_line) {} + + std::string file; + int line; +}; + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + CodeLocation code_location, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// State of the definition of a type-parameterized test case. +class GTEST_API_ TypedTestCasePState { + public: + TypedTestCasePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test case hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + registered_tests_.insert( + ::std::make_pair(test_name, CodeLocation(file, line))); + return true; + } + + bool TestExists(const std::string& test_name) const { + return registered_tests_.count(test_name) > 0; + } + + const CodeLocation& GetCodeLocation(const std::string& test_name) const { + RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name); + GTEST_CHECK_(it != registered_tests_.end()); + return it->second; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests); + + private: + typedef ::std::map RegisteredTestsMap; + + bool registered_; + RegisteredTestsMap registered_tests_; +}; + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == NULL) { + return NULL; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == NULL ? str : std::string(str, comma); +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. +void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest); + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, + const CodeLocation& code_location, + const char* case_name, const char* test_names, + int index) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" + + StreamableToString(index)).c_str(), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), + GetTypeName().c_str(), + NULL, // No value parameter. + code_location, + GetTypeId(), + TestClass::SetUpTestCase, + TestClass::TearDownTestCase, + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest + ::Register(prefix, code_location, case_name, test_names, index + 1); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const char* /*case_name*/, const char* /*test_names*/, + int /*index*/) { + return true; + } +}; + +// TypeParameterizedTestCase::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, CodeLocation code_location, + const TypedTestCasePState* state, + const char* case_name, const char* test_names) { + std::string test_name = StripTrailingSpaces( + GetPrefixUntilComma(test_names)); + if (!state->TestExists(test_name)) { + fprintf(stderr, "Failed to get code location for test %s.%s at %s.", + case_name, test_name.c_str(), + FormatFileLocation(code_location.file.c_str(), + code_location.line).c_str()); + fflush(stderr); + posix::Abort(); + } + const CodeLocation& test_location = state->GetCodeLocation(test_name); + + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, test_location, case_name, test_names, 0); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestCase + ::Register(prefix, code_location, state, + case_name, SkipComma(test_names)); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const TypedTestCasePState* /*state*/, + const char* /*case_name*/, const char* /*test_names*/) { + return true; + } +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const UInt32 kMaxRange = 1u << 31; + + explicit Random(UInt32 seed) : state_(seed) {} + + void Reseed(UInt32 seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + UInt32 Generate(UInt32 range); + + private: + UInt32 state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Defining a variable of type CompileAssertTypesEqual will cause a +// compiler error iff T1 and T2 are different types. +template +struct CompileAssertTypesEqual; + +template +struct CompileAssertTypesEqual { +}; + +// Removes the reference from a type if it is a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::remove_reference, which is not widely available yet. +template +struct RemoveReference { typedef T type; }; // NOLINT +template +struct RemoveReference { typedef T type; }; // NOLINT + +// A handy wrapper around RemoveReference that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_REFERENCE_(T) \ + typename ::testing::internal::RemoveReference::type + +// Removes const from a type if it is a const type, otherwise leaves +// it unchanged. This is the same as tr1::remove_const, which is not +// widely available yet. +template +struct RemoveConst { typedef T type; }; // NOLINT +template +struct RemoveConst { typedef T type; }; // NOLINT + +// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above +// definition to fail to remove the const in 'const int[3]' and 'const +// char[3][4]'. The following specialization works around the bug. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; + +#if defined(_MSC_VER) && _MSC_VER < 1400 +// This is the only specialization that allows VC++ 7.1 to remove const in +// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC +// and thus needs to be conditionally compiled. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; +#endif + +// A handy wrapper around RemoveConst that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_CONST_(T) \ + typename ::testing::internal::RemoveConst::type + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) + +// ImplicitlyConvertible::value is a compile-time bool +// constant that's true iff type From can be implicitly converted to +// type To. +template +class ImplicitlyConvertible { + private: + // We need the following helper functions only for their types. + // They have no implementations. + + // MakeFrom() is an expression whose type is From. We cannot simply + // use From(), as the type From may not have a public default + // constructor. + static typename AddReference::type MakeFrom(); + + // These two functions are overloaded. Given an expression + // Helper(x), the compiler will pick the first version if x can be + // implicitly converted to type To; otherwise it will pick the + // second version. + // + // The first version returns a value of size 1, and the second + // version returns a value of size 2. Therefore, by checking the + // size of Helper(x), which can be done at compile time, we can tell + // which version of Helper() is used, and hence whether x can be + // implicitly converted to type To. + static char Helper(To); + static char (&Helper(...))[2]; // NOLINT + + // We have to put the 'public' section after the 'private' section, + // or MSVC refuses to compile the code. + public: +#if defined(__BORLANDC__) + // C++Builder cannot use member overload resolution during template + // instantiation. The simplest workaround is to use its C++0x type traits + // functions (C++Builder 2009 and above only). + static const bool value = __is_convertible(From, To); +#else + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4244) + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif // __BORLANDC__ +}; +template +const bool ImplicitlyConvertible::value; + +// IsAProtocolMessage::value is a compile-time bool constant that's +// true iff T is type ProtocolMessage, proto2::Message, or a subclass +// of those. +template +struct IsAProtocolMessage + : public bool_constant< + ImplicitlyConvertible::value || + ImplicitlyConvertible::value> { +}; + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +#if GTEST_LANG_CXX11 +template ().begin()), + class = decltype(::std::declval().end()), + class = decltype(++::std::declval()), + class = decltype(*::std::declval()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} +#else +template +IsContainer IsContainerTest(int /* dummy */, + typename C::iterator* /* it */ = NULL, + typename C::const_iterator* /* const_it */ = NULL) { + return 0; +} +#endif // GTEST_LANG_CXX11 + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template +struct IsHashTable { + private: + template + static char test(typename U::hasher*, typename U::reverse_iterator*); + template + static int test(typename U::hasher*, ...); + template + static char test(...); + + public: + static const bool value = sizeof(test(0, 0)) == sizeof(int); +}; + +template +const bool IsHashTable::value; + +template +struct VoidT { + typedef void value_type; +}; + +template +struct HasValueType : false_type {}; +template +struct HasValueType > : true_type { +}; + +template (0)) == sizeof(IsContainer), + bool = HasValueType::value> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template +struct IsRecursiveContainerImpl : public false_type {}; + +template +struct IsRecursiveContainerImpl { + #if GTEST_LANG_CXX11 + typedef typename IteratorTraits::value_type + value_type; +#else + typedef typename IteratorTraits::value_type value_type; +#endif + typedef is_same type; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + +// EnableIf::type is void when 'Cond' is true, and +// undefined when 'Cond' is false. To use SFINAE to make a function +// overload only apply when a particular expression is true, add +// "typename EnableIf::type* = 0" as the last parameter. +template struct EnableIf; +template<> struct EnableIf { typedef void type; }; // NOLINT + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + (this->*rhs.clone_)(rhs.array_, rhs.size_); + } + + ~NativeArray() { + if (clone_ != &NativeArray::InitRef) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + enum { + kCheckTypeIsNotConstOrAReference = StaticAssertTypeEqHelper< + Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value + }; + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + size_ = a_size; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; + } + + const Element* array_; + size_t size_; + void (NativeArray::*clone_)(const Element*, size_t); + + GTEST_DISALLOW_ASSIGN_(NativeArray); +}; + +} // namespace internal +} // namespace testing + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +// Suppress MSVC warning 4702 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { statement; } + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::ConstCharPtr gtest_msg = "") { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + catch (...) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ + fail(gtest_msg.value) + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: it throws.") + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// represenation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + test_case_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ + public:\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ +};\ +\ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, NULL, NULL, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + (parent_id), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + new ::testing::internal::TestFactoryImpl<\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-linked_ptr.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-linked_ptr.h new file mode 100644 index 0000000..3602942 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-linked_ptr.h @@ -0,0 +1,243 @@ +// Copyright 2003 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Dan Egnor (egnor@google.com) +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is assigned, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Bill Gibbons suggested we use something like this. +// +// Thread Safety: +// Unlike other linked_ptr implementations, in this implementation +// a linked_ptr object is thread-safe in the sense that: +// - it's safe to copy linked_ptr objects concurrently, +// - it's safe to copy *from* a linked_ptr and read its underlying +// raw pointer (e.g. via get()) concurrently, and +// - it's safe to write to two linked_ptrs that point to the same +// shared object concurrently. +// TODO(wan@google.com): rename this to safe_linked_ptr to avoid +// confusion with normal linked_ptr. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ + +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// Protects copying of all linked_ptr objects. +GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr(obj) vs linked_ptr(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Many linked_ptr operations may change p.link_ for some linked_ptr + // variable p in the same circle as this object. Therefore we need + // to prevent two such operations from occurring concurrently. + // + // Note that different types of linked_ptr objects can coexist in a + // circle (e.g. linked_ptr, linked_ptr, and + // linked_ptr). Therefore we must use a single mutex to + // protect all linked_ptr objects. This can create serious + // contention in production code, but is acceptable in a testing + // framework. + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + linked_ptr_internal const* p = ptr; + while (p->next_ != ptr) { + assert(p->next_ != this && + "Trying to join() a linked ring we are already in. " + "Is GMock thread safety enabled?"); + p = p->next_; + } + p->next_ = this; + next_ = ptr; + } + + // Leave whatever circle we're part of. Returns true if we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) { + assert(p->next_ != next_ && + "Trying to depart() a linked ring we are not in. " + "Is GMock thread safety enabled?"); + p = p->next_; + } + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { // NOLINT + assert(&ptr != this); + copy(&ptr); + } + + // Assignment releases the old value and acquires the new. + template linked_ptr& operator=(linked_ptr const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { + depart(); + capture(ptr); + } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + + bool operator==(T* p) const { return value_ == p; } + bool operator!=(T* p) const { return value_ != p; } + template + bool operator==(linked_ptr const& ptr) const { + return value_ == ptr.get(); + } + template + bool operator!=(linked_ptr const& ptr) const { + return value_ != ptr.get(); + } + + private: + template + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template void copy(linked_ptr const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template inline +bool operator==(T* ptr, const linked_ptr& x) { + return ptr == x.get(); +} + +template inline +bool operator!=(T* ptr, const linked_ptr& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr +// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation +// for linked_ptr >(new FooBarBaz(arg)) +template +linked_ptr make_linked_ptr(T* ptr) { + return linked_ptr(ptr); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util-generated.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util-generated.h new file mode 100644 index 0000000..dcf90c2 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util-generated.h @@ -0,0 +1,5139 @@ +// This file was GENERATED by command: +// pump.py gtest-param-util-generated.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most 50 arguments in Values, +// and at most 10 arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tuple which is +// currently set at 10. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]); + +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +template +class ValueArray1 { + public: + explicit ValueArray1(T1 v1) : v1_(v1) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray1& other); + + const T1 v1_; +}; + +template +class ValueArray2 { + public: + ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray2& other); + + const T1 v1_; + const T2 v2_; +}; + +template +class ValueArray3 { + public: + ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray3& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; +}; + +template +class ValueArray4 { + public: + ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray4& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; +}; + +template +class ValueArray5 { + public: + ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray5& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; +}; + +template +class ValueArray6 { + public: + ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray6& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; +}; + +template +class ValueArray7 { + public: + ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray7& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; +}; + +template +class ValueArray8 { + public: + ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray8& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; +}; + +template +class ValueArray9 { + public: + ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray9& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; +}; + +template +class ValueArray10 { + public: + ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray10& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; +}; + +template +class ValueArray11 { + public: + ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray11& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; +}; + +template +class ValueArray12 { + public: + ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray12& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; +}; + +template +class ValueArray13 { + public: + ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray13& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; +}; + +template +class ValueArray14 { + public: + ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray14& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; +}; + +template +class ValueArray15 { + public: + ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray15& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; +}; + +template +class ValueArray16 { + public: + ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray16& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; +}; + +template +class ValueArray17 { + public: + ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray17& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; +}; + +template +class ValueArray18 { + public: + ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray18& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; +}; + +template +class ValueArray19 { + public: + ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray19& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; +}; + +template +class ValueArray20 { + public: + ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray20& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; +}; + +template +class ValueArray21 { + public: + ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray21& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; +}; + +template +class ValueArray22 { + public: + ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray22& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; +}; + +template +class ValueArray23 { + public: + ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray23& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; +}; + +template +class ValueArray24 { + public: + ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray24& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; +}; + +template +class ValueArray25 { + public: + ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray25& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; +}; + +template +class ValueArray26 { + public: + ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray26& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; +}; + +template +class ValueArray27 { + public: + ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray27& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; +}; + +template +class ValueArray28 { + public: + ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray28& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; +}; + +template +class ValueArray29 { + public: + ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray29& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; +}; + +template +class ValueArray30 { + public: + ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray30& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; +}; + +template +class ValueArray31 { + public: + ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray31& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; +}; + +template +class ValueArray32 { + public: + ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray32& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; +}; + +template +class ValueArray33 { + public: + ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray33& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; +}; + +template +class ValueArray34 { + public: + ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray34& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; +}; + +template +class ValueArray35 { + public: + ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray35& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; +}; + +template +class ValueArray36 { + public: + ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray36& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; +}; + +template +class ValueArray37 { + public: + ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray37& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; +}; + +template +class ValueArray38 { + public: + ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray38& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; +}; + +template +class ValueArray39 { + public: + ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray39& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; +}; + +template +class ValueArray40 { + public: + ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray40& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; +}; + +template +class ValueArray41 { + public: + ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray41& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; +}; + +template +class ValueArray42 { + public: + ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray42& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; +}; + +template +class ValueArray43 { + public: + ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), + v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray43& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; +}; + +template +class ValueArray44 { + public: + ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), + v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), + v43_(v43), v44_(v44) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray44& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; +}; + +template +class ValueArray45 { + public: + ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), + v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray45& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; +}; + +template +class ValueArray46 { + public: + ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray46& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; +}; + +template +class ValueArray47 { + public: + ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), + v47_(v47) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray47& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; +}; + +template +class ValueArray48 { + public: + ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), + v46_(v46), v47_(v47), v48_(v48) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray48& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; +}; + +template +class ValueArray49 { + public: + ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, + T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray49& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; +}; + +template +class ValueArray50 { + public: + ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, + T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_), static_cast(v50_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray50& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; + const T50 v50_; +}; + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +template +class CartesianProductGenerator2 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator2(const ParamGenerator& g1, + const ParamGenerator& g2) + : g1_(g1), g2_(g2) {} + virtual ~CartesianProductGenerator2() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current2_; + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + linked_ptr current_value_; + }; // class CartesianProductGenerator2::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator2& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; +}; // class CartesianProductGenerator2 + + +template +class CartesianProductGenerator3 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator3(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + virtual ~CartesianProductGenerator3() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current3_; + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + linked_ptr current_value_; + }; // class CartesianProductGenerator3::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator3& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; +}; // class CartesianProductGenerator3 + + +template +class CartesianProductGenerator4 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator4(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + virtual ~CartesianProductGenerator4() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current4_; + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + linked_ptr current_value_; + }; // class CartesianProductGenerator4::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator4& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; +}; // class CartesianProductGenerator4 + + +template +class CartesianProductGenerator5 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator5(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + virtual ~CartesianProductGenerator5() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current5_; + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + linked_ptr current_value_; + }; // class CartesianProductGenerator5::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator5& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; +}; // class CartesianProductGenerator5 + + +template +class CartesianProductGenerator6 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator6(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + virtual ~CartesianProductGenerator6() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current6_; + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + linked_ptr current_value_; + }; // class CartesianProductGenerator6::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator6& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; +}; // class CartesianProductGenerator6 + + +template +class CartesianProductGenerator7 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator7(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + virtual ~CartesianProductGenerator7() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current7_; + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + linked_ptr current_value_; + }; // class CartesianProductGenerator7::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator7& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; +}; // class CartesianProductGenerator7 + + +template +class CartesianProductGenerator8 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator8(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + virtual ~CartesianProductGenerator8() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current8_; + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + linked_ptr current_value_; + }; // class CartesianProductGenerator8::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator8& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; +}; // class CartesianProductGenerator8 + + +template +class CartesianProductGenerator9 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator9(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + virtual ~CartesianProductGenerator9() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current9_; + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + linked_ptr current_value_; + }; // class CartesianProductGenerator9::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator9& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; +}; // class CartesianProductGenerator9 + + +template +class CartesianProductGenerator10 + : public ParamGeneratorInterface< ::testing::tuple > { + public: + typedef ::testing::tuple ParamType; + + CartesianProductGenerator10(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9, + const ParamGenerator& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + virtual ~CartesianProductGenerator10() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end(), g10_, g10_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9, + const ParamGenerator& g10, + const typename ParamGenerator::iterator& current10) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9), + begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current10_; + if (current10_ == end10_) { + current10_ = begin10_; + ++current9_; + } + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_ && + current10_ == typed_other->current10_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_), + begin10_(other.begin10_), + end10_(other.end10_), + current10_(other.current10_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_, *current10_)); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_ || + current10_ == end10_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + const typename ParamGenerator::iterator begin10_; + const typename ParamGenerator::iterator end10_; + typename ParamGenerator::iterator current10_; + linked_ptr current_value_; + }; // class CartesianProductGenerator10::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator10& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; + const ParamGenerator g10_; +}; // class CartesianProductGenerator10 + + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN to ParamGenerator if T is +// convertible to U. +// +template +class CartesianProductHolder2 { + public: +CartesianProductHolder2(const Generator1& g1, const Generator2& g2) + : g1_(g1), g2_(g2) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator2( + static_cast >(g1_), + static_cast >(g2_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder2& other); + + const Generator1 g1_; + const Generator2 g2_; +}; // class CartesianProductHolder2 + +template +class CartesianProductHolder3 { + public: +CartesianProductHolder3(const Generator1& g1, const Generator2& g2, + const Generator3& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator3( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder3& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; +}; // class CartesianProductHolder3 + +template +class CartesianProductHolder4 { + public: +CartesianProductHolder4(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator4( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder4& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; +}; // class CartesianProductHolder4 + +template +class CartesianProductHolder5 { + public: +CartesianProductHolder5(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator5( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder5& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; +}; // class CartesianProductHolder5 + +template +class CartesianProductHolder6 { + public: +CartesianProductHolder6(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator6( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder6& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; +}; // class CartesianProductHolder6 + +template +class CartesianProductHolder7 { + public: +CartesianProductHolder7(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator7( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder7& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; +}; // class CartesianProductHolder7 + +template +class CartesianProductHolder8 { + public: +CartesianProductHolder8(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator8( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder8& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; +}; // class CartesianProductHolder8 + +template +class CartesianProductHolder9 { + public: +CartesianProductHolder9(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator9( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder9& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; +}; // class CartesianProductHolder9 + +template +class CartesianProductHolder10 { + public: +CartesianProductHolder10(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9, const Generator10& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + template + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( + new CartesianProductGenerator10( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_), + static_cast >(g10_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder10& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; + const Generator10 g10_; +}; // class CartesianProductHolder10 + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util-generated.h.pump b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util-generated.h.pump new file mode 100644 index 0000000..d65086a --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util-generated.h.pump @@ -0,0 +1,279 @@ +$$ -*- mode: c++; -*- +$var n = 50 $$ Maximum length of Values arguments we want to support. +$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support. +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most $n arguments in Values, +// and at most $maxtuple arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tuple which is +// currently set at $maxtuple. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]); + +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +$range i 1..n +$for i [[ +$range j 1..i + +template <$for j, [[typename T$j]]> +class ValueArray$i { + public: + $if i==1 [[explicit ]]ValueArray$i($for j, [[T$j v$j]]) : $for j, [[v$(j)_(v$j)]] {} + + template + operator ParamGenerator() const { + const T array[] = {$for j, [[static_cast(v$(j)_)]]}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray$i& other); + +$for j [[ + + const T$j v$(j)_; +]] + +}; + +]] + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +$range i 2..maxtuple +$for i [[ +$range j 1..i +$range k 2..i + +template <$for j, [[typename T$j]]> +class CartesianProductGenerator$i + : public ParamGeneratorInterface< ::testing::tuple<$for j, [[T$j]]> > { + public: + typedef ::testing::tuple<$for j, [[T$j]]> ParamType; + + CartesianProductGenerator$i($for j, [[const ParamGenerator& g$j]]) + : $for j, [[g$(j)_(g$j)]] {} + virtual ~CartesianProductGenerator$i() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, $for j, [[g$(j)_, g$(j)_.begin()]]); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, $for j, [[g$(j)_, g$(j)_.end()]]); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, $for j, [[ + + const ParamGenerator& g$j, + const typename ParamGenerator::iterator& current$(j)]]) + : base_(base), +$for j, [[ + + begin$(j)_(g$j.begin()), end$(j)_(g$j.end()), current$(j)_(current$j) +]] { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current$(i)_; + +$for k [[ + if (current$(i+2-k)_ == end$(i+2-k)_) { + current$(i+2-k)_ = begin$(i+2-k)_; + ++current$(i+2-k-1)_; + } + +]] + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return current_value_.get(); } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ($for j && [[ + + current$(j)_ == typed_other->current$(j)_ +]]); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), $for j, [[ + + begin$(j)_(other.begin$(j)_), + end$(j)_(other.end$(j)_), + current$(j)_(other.current$(j)_) +]] { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_.reset(new ParamType($for j, [[*current$(j)_]])); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return +$for j || [[ + + current$(j)_ == end$(j)_ +]]; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. +$for j [[ + + const typename ParamGenerator::iterator begin$(j)_; + const typename ParamGenerator::iterator end$(j)_; + typename ParamGenerator::iterator current$(j)_; +]] + + linked_ptr current_value_; + }; // class CartesianProductGenerator$i::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator$i& other); + + +$for j [[ + const ParamGenerator g$(j)_; + +]] +}; // class CartesianProductGenerator$i + + +]] + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN to ParamGenerator if T is +// convertible to U. +// +$range i 2..maxtuple +$for i [[ +$range j 1..i + +template <$for j, [[class Generator$j]]> +class CartesianProductHolder$i { + public: +CartesianProductHolder$i($for j, [[const Generator$j& g$j]]) + : $for j, [[g$(j)_(g$j)]] {} + template <$for j, [[typename T$j]]> + operator ParamGenerator< ::testing::tuple<$for j, [[T$j]]> >() const { + return ParamGenerator< ::testing::tuple<$for j, [[T$j]]> >( + new CartesianProductGenerator$i<$for j, [[T$j]]>( +$for j,[[ + + static_cast >(g$(j)_) +]])); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder$i& other); + + +$for j [[ + const Generator$j g$(j)_; + +]] +}; // class CartesianProductHolder$i + +]] + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util.h new file mode 100644 index 0000000..3c80863 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-param-util.h @@ -0,0 +1,723 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include + +#include +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-linked_ptr.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/gtest-printers.h" + +namespace testing { + +// Input to a parameterized test name generator, describing a test parameter. +// Consists of the parameter value and the integer parameter index. +template +struct TestParamInfo { + TestParamInfo(const ParamType& a_param, size_t an_index) : + param(a_param), + index(an_index) {} + ParamType param; + size_t index; +}; + +// A builtin parameterized test name generator which returns the result of +// testing::PrintToString. +struct PrintToStringParamName { + template + std::string operator()(const TestParamInfo& info) const { + return PrintToString(info.param); + } +}; + +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Outputs a message explaining invalid registration of different +// fixture class for the same test case. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, + CodeLocation code_location); + +template class ParamGeneratorInterface; +template class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + scoped_ptr > impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + linked_ptr > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + virtual ~RangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, begin_, 0, step_); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + value_ = static_cast(value_ + step_); + index_++; + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const T* Current() const { return &value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = static_cast(i + step)) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + virtual ~ValuesInIteratorRangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, container_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + ++iterator_; + value_.reset(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + virtual const T* Current() const { + if (value_.get() == NULL) + value_.reset(new T(*iterator_)); + return value_.get(); + } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of scoped_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable scoped_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Default parameterized test name generator, returns a string containing the +// integer test parameter index. +template +std::string DefaultParamName(const TestParamInfo& info) { + Message name_stream; + name_stream << info.index; + return name_stream.GetString(); +} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Parameterized test name overload helpers, which help the +// INSTANTIATE_TEST_CASE_P macro choose between the default parameterized +// test name generator and user param name generator. +template +ParamNameGenFunctor GetParamNameGen(ParamNameGenFunctor func) { + return func; +} + +template +struct ParamNameGenFunc { + typedef std::string Type(const TestParamInfo&); +}; + +template +typename ParamNameGenFunc::Type *GetParamNameGen() { + return DefaultParamName; +} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + virtual Test* CreateTest() { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestCaseInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + typedef typename TestCase::ParamType ParamType; + + TestMetaFactory() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { + return new ParameterizedTestFactory(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfoBase is a generic interface +// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestCaseRegistry class holds +// a collection of pointers to the ParameterizedTestCaseInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestCaseInfoBase { + public: + virtual ~ParameterizedTestCaseInfoBase() {} + + // Base part of test case name for display purposes. + virtual const std::string& GetTestCaseName() const = 0; + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test case right before running them in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestCaseInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test case and generators +// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that +// test case. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestCaseInstantiation(). + typedef typename TestCase::ParamType ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + typedef typename ParamNameGenFunc::Type ParamNameGeneratorFunc; + + explicit ParameterizedTestCaseInfo( + const char* name, CodeLocation code_location) + : test_case_name_(name), code_location_(code_location) {} + + // Test case base name for display purposes. + virtual const std::string& GetTestCaseName() const { return test_case_name_; } + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_case_name is the base name of the test case (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test case base name and DoBar is test base name. + void AddTestPattern(const char* test_case_name, + const char* test_base_name, + TestMetaFactoryBase* meta_factory) { + tests_.push_back(linked_ptr(new TestInfo(test_case_name, + test_base_name, + meta_factory))); + } + // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestCaseInstantiation(const std::string& instantiation_name, + GeneratorCreationFunc* func, + ParamNameGeneratorFunc* name_func, + const char* file, int line) { + instantiations_.push_back( + InstantiationInfo(instantiation_name, func, name_func, file, line)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test case + // test cases right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more then once. + virtual void RegisterTests() { + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + linked_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const std::string& instantiation_name = gen_it->name; + ParamGenerator generator((*gen_it->generator)()); + ParamNameGeneratorFunc* name_func = gen_it->name_func; + const char* file = gen_it->file; + int line = gen_it->line; + + std::string test_case_name; + if ( !instantiation_name.empty() ) + test_case_name = instantiation_name + "/"; + test_case_name += test_info->test_case_base_name; + + size_t i = 0; + std::set test_param_names; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + Message test_name_stream; + + std::string param_name = name_func( + TestParamInfo(*param_it, i)); + + GTEST_CHECK_(IsValidParamName(param_name)) + << "Parameterized test name '" << param_name + << "' is invalid, in " << file + << " line " << line << std::endl; + + GTEST_CHECK_(test_param_names.count(param_name) == 0) + << "Duplicate parameterized test name '" << param_name + << "', in " << file << " line " << line << std::endl; + + test_param_names.insert(param_name); + + test_name_stream << test_info->test_base_name << "/" << param_name; + MakeAndRegisterTestInfo( + test_case_name.c_str(), + test_name_stream.GetString().c_str(), + NULL, // No type parameter. + PrintToString(*param_it).c_str(), + code_location_, + GetTestCaseTypeId(), + TestCase::SetUpTestCase, + TestCase::TearDownTestCase, + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_case_base_name, + const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory) : + test_case_base_name(a_test_case_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory) {} + + const std::string test_case_base_name; + const std::string test_base_name; + const scoped_ptr > test_meta_factory; + }; + typedef ::std::vector > TestInfoContainer; + // Records data received from INSTANTIATE_TEST_CASE_P macros: + // + struct InstantiationInfo { + InstantiationInfo(const std::string &name_in, + GeneratorCreationFunc* generator_in, + ParamNameGeneratorFunc* name_func_in, + const char* file_in, + int line_in) + : name(name_in), + generator(generator_in), + name_func(name_func_in), + file(file_in), + line(line_in) {} + + std::string name; + GeneratorCreationFunc* generator; + ParamNameGeneratorFunc* name_func; + const char* file; + int line; + }; + typedef ::std::vector InstantiationContainer; + + static bool IsValidParamName(const std::string& name) { + // Check for empty string + if (name.empty()) + return false; + + // Check for invalid characters + for (std::string::size_type index = 0; index < name.size(); ++index) { + if (!isalnum(name[index]) && name[index] != '_') + return false; + } + + return true; + } + + const std::string test_case_name_; + CodeLocation code_location_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); +}; // class ParameterizedTestCaseInfo + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase +// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P +// macros use it to locate their corresponding ParameterizedTestCaseInfo +// descriptors. +class ParameterizedTestCaseRegistry { + public: + ParameterizedTestCaseRegistry() {} + ~ParameterizedTestCaseRegistry() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + delete *it; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test case. + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, + CodeLocation code_location) { + ParameterizedTestCaseInfo* typed_test_info = NULL; + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + if ((*it)->GetTestCaseName() == test_case_name) { + if ((*it)->GetTestCaseTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test case setup and tear-down in this case. + ReportInvalidTestCaseType(test_case_name, code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestCaseInfo >(*it); + } + break; + } + } + if (typed_test_info == NULL) { + typed_test_info = new ParameterizedTestCaseInfo( + test_case_name, code_location); + test_case_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + (*it)->RegisterTests(); + } + } + + private: + typedef ::std::vector TestCaseInfoContainer; + + TestCaseInfoContainer test_case_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); +}; + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-port-arch.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-port-arch.h new file mode 100644 index 0000000..f83700e --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-port-arch.h @@ -0,0 +1,100 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the GTEST_OS_* macro. +// It is separate from gtest-port.h so that custom/gtest-port.h can include it. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +#elif defined __SYMBIAN32__ +# define GTEST_OS_SYMBIAN 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(__MINGW__) || defined(__MINGW32__) +# define GTEST_OS_WINDOWS_MINGW 1 +# elif defined(WINAPI_FAMILY) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GTEST_OS_WINDOWS_DESKTOP 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +# define GTEST_OS_WINDOWS_PHONE 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +# define GTEST_OS_WINDOWS_RT 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +# define GTEST_OS_WINDOWS_PHONE 1 +# define GTEST_OS_WINDOWS_TV_TITLE 1 +# else + // WINAPI_FAMILY defined but no known partition matched. + // Default to desktop. +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# endif +#elif defined __FreeBSD__ +# define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +# define GTEST_OS_FUCHSIA 1 +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# if defined __ANDROID__ +# define GTEST_OS_LINUX_ANDROID 1 +# endif +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +# define GTEST_OS_NETBSD 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#endif // __CYGWIN__ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-port.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-port.h new file mode 100644 index 0000000..437a4ed --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-port.h @@ -0,0 +1,2687 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available +// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::wstring +// is/isn't available +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple +// is/isn't available. +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google +// Test's own tr1 tuple implementation should be +// used. Unused when the user sets +// GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test +// is building in C++11/C++98 mode. +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. + +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_SYMBIAN - Symbian +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #if +// which controls that code. For example: +// +// #if GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif +// +// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized +// tests) +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above RE\b(s) are mutually exclusive. +// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// +// C++11 feature wrappers: +// +// testing::internal::forward - portability wrapper for std::forward. +// testing::internal::move - portability wrapper for std::move. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. +// IteratorTraits - partial implementation of std::iterator_traits, which +// is not available in libCstd when compiled with Sun C++. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like platforms +// or a reduced regular exception syntax on other +// platforms, including Windows. +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an Int32 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include +#ifndef _WIN32_WCE +# include +# include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include +# include +#endif + +// Brings in the definition of HAS_GLOBAL_STRING. This must be done +// BEFORE we test HAS_GLOBAL_STRING. +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include +#include // NOLINT + +#include "gtest/internal/gtest-port-arch.h" +#include "gtest/internal/custom/gtest-port.h" + +#if !defined(GTEST_DEV_EMAIL_) +# define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +# define GTEST_FLAG_PREFIX_ "gtest_" +# define GTEST_FLAG_PREFIX_DASH_ "gtest-" +# define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +# define GTEST_NAME_ "Google Test" +# define GTEST_PROJECT_URL_ "https://github.com/google/googletest/" +#endif // !defined(GTEST_DEV_EMAIL_) + +#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_) +# define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest" +#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_) + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if _MSC_VER >= 1500 +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) \ + __pragma(warning(disable: warnings)) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() \ + __pragma(warning(pop)) +#else +// Older versions of MSVC don't have __pragma. +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +#ifndef GTEST_LANG_CXX11 +// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when +// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a +// value for __cplusplus, and recent versions of clang, gcc, and +// probably other compilers set that too in C++11 mode. +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L || _MSC_VER >= 1900 +// Compiling in at least C++11 mode. +# define GTEST_LANG_CXX11 1 +# else +# define GTEST_LANG_CXX11 0 +# endif +#endif + +// Distinct from C++11 language support, some environments don't provide +// proper C++11 library support. Notably, it's possible to build in +// C++11 mode when targeting Mac OS X 10.6, which has an old libstdc++ +// with no C++11 support. +// +// libstdc++ has sufficient C++11 support as of GCC 4.6.0, __GLIBCXX__ +// 20110325, but maintenance releases in the 4.4 and 4.5 series followed +// this date, so check for those versions by their date stamps. +// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning +#if GTEST_LANG_CXX11 && \ + (!defined(__GLIBCXX__) || ( \ + __GLIBCXX__ >= 20110325ul && /* GCC >= 4.6.0 */ \ + /* Blacklist of patch releases of older branches: */ \ + __GLIBCXX__ != 20110416ul && /* GCC 4.4.6 */ \ + __GLIBCXX__ != 20120313ul && /* GCC 4.4.7 */ \ + __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \ + __GLIBCXX__ != 20120702ul)) /* GCC 4.5.4 */ +# define GTEST_STDLIB_CXX11 1 +#endif + +// Only use C++11 library features if the library provides them. +#if GTEST_STDLIB_CXX11 +# define GTEST_HAS_STD_BEGIN_AND_END_ 1 +# define GTEST_HAS_STD_FORWARD_LIST_ 1 +# if !defined(_MSC_VER) || (_MSC_FULL_VER >= 190023824) +// works only with VS2015U2 and better +# define GTEST_HAS_STD_FUNCTION_ 1 +# endif +# define GTEST_HAS_STD_INITIALIZER_LIST_ 1 +# define GTEST_HAS_STD_MOVE_ 1 +# define GTEST_HAS_STD_UNIQUE_PTR_ 1 +# define GTEST_HAS_STD_SHARED_PTR_ 1 +# define GTEST_HAS_UNORDERED_MAP_ 1 +# define GTEST_HAS_UNORDERED_SET_ 1 +#endif + +// C++11 specifies that provides std::tuple. +// Some platforms still might not have it, however. +#if GTEST_LANG_CXX11 +# define GTEST_HAS_STD_TUPLE_ 1 +# if defined(__clang__) +// Inspired by http://clang.llvm.org/docs/LanguageExtensions.html#__has_include +# if defined(__has_include) && !__has_include() +# undef GTEST_HAS_STD_TUPLE_ +# endif +# elif defined(_MSC_VER) +// Inspired by boost/config/stdlib/dinkumware.hpp +# if defined(_CPPLIB_VER) && _CPPLIB_VER < 520 +# undef GTEST_HAS_STD_TUPLE_ +# endif +# elif defined(__GLIBCXX__) +// Inspired by boost/config/stdlib/libstdcpp3.hpp, +// http://gcc.gnu.org/gcc-4.2/changes.html and +// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2) +# undef GTEST_HAS_STD_TUPLE_ +# endif +# endif +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS_MOBILE +# include +# include +# endif +// In order to avoid having to include , use forward declaration +#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif +#else +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include +# include +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + +// Defines this to true iff Google Test can use POSIX regular expressions. +#ifndef GTEST_HAS_POSIX_RE +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# endif +#endif + +#if GTEST_USES_PCRE +// The appropriate headers have already been included. + +#elif GTEST_HAS_POSIX_RE + +// On some platforms, needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included , which is guaranteed to define size_t through +// . +# include // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_USES_PCRE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__clang__) +// clang defines __EXCEPTIONS iff exceptions are enabled before clang 220714, +// but iff cleanups are enabled after that. In Obj-C++ files, there can be +// cleanups for ObjC exceptions which also need cleanups, even if C++ exceptions +// are disabled. clang has __has_feature(cxx_exceptions) which checks for C++ +// exceptions starting at clang r206352, but which checked for cleanups prior to +// that. To reliably check for C++ exception availability with clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +# define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions)) +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#if !defined(GTEST_HAS_STD_STRING) +// Even though we don't use this macro any longer, we keep it in case +// some clients still depend on it. +# define GTEST_HAS_STD_STRING 1 +#elif !GTEST_HAS_STD_STRING +// The user told us that ::std::string isn't available. +# error "::std::string isn't available." +#endif // !defined(GTEST_HAS_STD_STRING) + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +# define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring +// is available. + +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +# define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS)) + +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_GLOBAL_WSTRING +// The user didn't tell us whether ::wstring is available, so we need +// to figure it out. +# define GTEST_HAS_GLOBAL_WSTRING \ + (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) + +# ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +#define GTEST_HAS_PTHREAD \ + (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +# include // NOLINT + +// For timespec and nanosleep, used below. +# include // NOLINT +#endif + +// Determines if hash_map/hash_set are available. +// Only used for testing against those containers. +#if !defined(GTEST_HAS_HASH_MAP_) +# if defined(_MSC_VER) && (_MSC_VER < 1900) +# define GTEST_HAS_HASH_MAP_ 1 // Indicates that hash_map is available. +# define GTEST_HAS_HASH_SET_ 1 // Indicates that hash_set is available. +# endif // _MSC_VER +#endif // !defined(GTEST_HAS_HASH_MAP_) + +// Determines whether Google Test can use tr1/tuple. You can define +// this macro to 0 to prevent Google Test from using tuple (any +// feature depending on tuple with be disabled in this mode). +#ifndef GTEST_HAS_TR1_TUPLE +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) +// STLport, provided with the Android NDK, has neither or . +# define GTEST_HAS_TR1_TUPLE 0 +# elif defined(_MSC_VER) && (_MSC_VER >= 1910) +// Prevent `warning C4996: 'std::tr1': warning STL4002: +// The non-Standard std::tr1 namespace and TR1-only machinery +// are deprecated and will be REMOVED.` +# define GTEST_HAS_TR1_TUPLE 0 +# elif GTEST_LANG_CXX11 && defined(_LIBCPP_VERSION) +// libc++ doesn't support TR1. +# define GTEST_HAS_TR1_TUPLE 0 +# else +// The user didn't tell us not to do it, so we assume it's OK. +# define GTEST_HAS_TR1_TUPLE 1 +# endif +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether Google Test's own tr1 tuple implementation +// should be used. +#ifndef GTEST_USE_OWN_TR1_TUPLE +// We use our own tuple implementation on Symbian. +# if GTEST_OS_SYMBIAN +# define GTEST_USE_OWN_TR1_TUPLE 1 +# else +// The user didn't tell us, so we need to figure it out. + +// We use our own TR1 tuple if we aren't sure the user has an +// implementation of it already. At this time, libstdc++ 4.0.0+ and +// MSVC 2010 are the only mainstream standard libraries that come +// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler +// pretends to be GCC by defining __GNUC__ and friends, but cannot +// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1 +// tuple in a 323 MB Feature Pack download, which we cannot assume the +// user has. QNX's QCC compiler is a modified GCC but it doesn't +// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, +// and it can be used with some compilers that define __GNUC__. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) \ + || (_MSC_VER >= 1600 && _MSC_VER < 1900) +# define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# endif + +// C++11 specifies that provides std::tuple. Use that if gtest is used +// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6 +// can build with clang but need to use gcc4.2's libstdc++). +# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) +# define GTEST_ENV_HAS_STD_TUPLE_ 1 +# endif + +# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ +# define GTEST_USE_OWN_TR1_TUPLE 0 +# else +# define GTEST_USE_OWN_TR1_TUPLE 1 +# endif +# endif // GTEST_OS_SYMBIAN +#endif // GTEST_USE_OWN_TR1_TUPLE + +// To avoid conditional compilation we make it gtest-port.h's responsibility +// to #include the header implementing tuple. +#if GTEST_HAS_STD_TUPLE_ +# include // IWYU pragma: export +# define GTEST_TUPLE_NAMESPACE_ ::std +#endif // GTEST_HAS_STD_TUPLE_ + +// We include tr1::tuple even if std::tuple is available to define printers for +// them. +#if GTEST_HAS_TR1_TUPLE +# ifndef GTEST_TUPLE_NAMESPACE_ +# define GTEST_TUPLE_NAMESPACE_ ::std::tr1 +# endif // GTEST_TUPLE_NAMESPACE_ + +# if GTEST_USE_OWN_TR1_TUPLE +# include "gtest/internal/gtest-tuple.h" // IWYU pragma: export // NOLINT +# elif GTEST_OS_SYMBIAN + +// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to +// use STLport's tuple implementation, which unfortunately doesn't +// work as the copy of STLport distributed with Symbian is incomplete. +// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to +// use its own tuple implementation. +# ifdef BOOST_HAS_TR1_TUPLE +# undef BOOST_HAS_TR1_TUPLE +# endif // BOOST_HAS_TR1_TUPLE + +// This prevents , which defines +// BOOST_HAS_TR1_TUPLE, from being #included by Boost's . +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# include // IWYU pragma: export // NOLINT + +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) +// GCC 4.0+ implements tr1/tuple in the header. This does +// not conform to the TR1 spec, which requires the header to be . + +# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +// Until version 4.3.2, gcc has a bug that causes , +// which is #included by , to not compile when RTTI is +// disabled. _TR1_FUNCTIONAL is the header guard for +// . Hence the following #define is a hack to prevent +// from being included. +# define _TR1_FUNCTIONAL 1 +# include +# undef _TR1_FUNCTIONAL // Allows the user to #include + // if they choose to. +# else +# include // NOLINT +# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 + +// VS 2010 now has tr1 support. +# elif _MSC_VER >= 1600 +# include // IWYU pragma: export // NOLINT + +# else // GTEST_USE_OWN_TR1_TUPLE +# include // IWYU pragma: export // NOLINT +# endif // GTEST_USE_OWN_TR1_TUPLE + +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +# if defined(__LP64__) || \ + (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || \ + GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// Google Test does not support death tests for VC 7.1 and earlier as +// abort() in a VC 7.1 application compiled as GUI in debug config +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ + GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD || \ + GTEST_OS_NETBSD || GTEST_OS_FUCHSIA) +# define GTEST_HAS_DEATH_TEST 1 +#endif + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether to support Combine(). This only makes sense when +// value-parameterized tests are enabled. The implementation doesn't +// work on Sun Studio since it doesn't understand templated conversion +// operators. +#if (GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_) && !defined(__SUNPRO_CC) +# define GTEST_HAS_COMBINE 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#elif defined(__clang__) +# if __has_attribute(unused) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +# endif +#endif +#ifndef GTEST_ATTRIBUTE_UNUSED_ +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +#if GTEST_LANG_CXX11 +# define GTEST_CXX11_EQUALS_DELETE_ = delete +#else // GTEST_LANG_CXX11 +# define GTEST_CXX11_EQUALS_DELETE_ +#endif // GTEST_LANG_CXX11 + +// Use this annotation before a function that takes a printf format string. +#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC) +# if defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \ + first_to_check))) +# else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +# endif +#else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + + +// A macro to disallow operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type) \ + void operator=(type const &) GTEST_CXX11_EQUALS_DELETE_ + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \ + type(type const &) GTEST_CXX11_EQUALS_DELETE_; \ + GTEST_DISALLOW_ASSIGN_(type) + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC + +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +# define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +# define GTEST_INTENTIONAL_CONST_COND_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#define GTEST_IS_THREADSAFE \ + (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ \ + || (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) \ + || GTEST_HAS_PTHREAD) + +#endif // GTEST_HAS_SEH + +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + +#ifdef _MSC_VER +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif +#elif __GNUC__ >= 4 || defined(__clang__) +# define GTEST_API_ __attribute__((visibility ("default"))) +#endif // _MSC_VER + +#endif // GTEST_API_ + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +# define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if !defined(GTEST_HAS_CXXABI_H_) +# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +# define GTEST_HAS_CXXABI_H_ 1 +# else +# define GTEST_HAS_CXXABI_H_ 0 +# endif +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if defined(__clang__) +# if __has_feature(memory_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ \ + __attribute__((no_sanitize_memory)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +# endif // __has_feature(memory_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif // __clang__ + +// A function level attribute to disable AddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(address_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +# endif // __has_feature(address_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif // __clang__ + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(thread_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ \ + __attribute__((no_sanitize_thread)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +# endif // __has_feature(thread_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif // __clang__ + +namespace testing { + +class Message; + +#if defined(GTEST_TUPLE_NAMESPACE_) +// Import tuple and friends into the ::testing namespace. +// It is part of our interface, having them in ::testing allows us to change +// their types as needed. +using GTEST_TUPLE_NAMESPACE_::get; +using GTEST_TUPLE_NAMESPACE_::make_tuple; +using GTEST_TUPLE_NAMESPACE_::tuple; +using GTEST_TUPLE_NAMESPACE_::tuple_size; +using GTEST_TUPLE_NAMESPACE_::tuple_element; +#endif // defined(GTEST_TUPLE_NAMESPACE_) + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES, +// names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +#if GTEST_LANG_CXX11 +# define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg) +#else // !GTEST_LANG_CXX11 +template + struct CompileAssert { +}; + +# define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(static_cast(expr))> \ + msg[static_cast(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_ +#endif // !GTEST_LANG_CXX11 + +// Implementation details of GTEST_COMPILE_ASSERT_: +// +// (In C++11, we simply use static_assert instead of the following) +// +// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// GTEST_COMPILE_ASSERT_(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. +// +// This template is declared, but intentionally undefined. +template +struct StaticAssertTypeEqHelper; + +template +struct StaticAssertTypeEqHelper { + enum { value = true }; +}; + +// Same as std::is_same<>. +template +struct IsSame { + enum { value = false }; +}; +template +struct IsSame { + enum { value = true }; +}; + +// Evaluates to the number of elements in 'array'. +#define GTEST_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0])) + +#if GTEST_HAS_GLOBAL_STRING +typedef ::string string; +#else +typedef ::std::string string; +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING +typedef ::wstring wstring; +#elif GTEST_HAS_STD_WSTRING +typedef ::std::wstring wstring; +#endif // GTEST_HAS_GLOBAL_WSTRING + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template +class scoped_ptr { + public: + typedef T element_type; + + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + + friend void swap(scoped_ptr& a, scoped_ptr& b) { + using std::swap; + swap(a.ptr_, b.ptr_); + } + + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); +}; + +// Defines RE. + +#if GTEST_USES_PCRE +using ::RE; +#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + +# if GTEST_HAS_GLOBAL_STRING + + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT + +# endif // GTEST_HAS_GLOBAL_STRING + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true iff regular expression re matches + // the entire str. + // PartialMatch(str, re) returns true iff regular expression re + // matches a substring of str (including str itself). + // + // TODO(wan@google.com): make FullMatch() and PartialMatch() work + // when str contains NUL characters. + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +# if GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const ::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +# endif // GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of an std::string, as Google Test used to be + // used where std::string is not available. TODO(wan@google.com): change to + // std::string. + const char* pattern_; + bool is_valid_; + +# if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +# else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +# endif + + GTEST_DISALLOW_ASSIGN_(RE); +}; + +#endif // GTEST_USES_PCRE + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#if !defined(GTEST_LOG_) + +# define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +#endif // !defined(GTEST_LOG_) + +#if !defined(GTEST_CHECK_) +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +# define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " +#endif // !defined(GTEST_CHECK_) + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template +struct AddReference { typedef T& type; }; // NOLINT +template +struct AddReference { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference::type + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template +struct ConstRef { typedef const T& type; }; +template +struct ConstRef { typedef T& type; }; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef::type + +#if GTEST_HAS_STD_MOVE_ +using std::forward; +using std::move; + +template +struct RvalueRef { + typedef T&& type; +}; +#else // GTEST_HAS_STD_MOVE_ +template +const T& move(const T& t) { + return t; +} +template +GTEST_ADD_REFERENCE_(T) forward(GTEST_ADD_REFERENCE_(T) t) { return t; } + +template +struct RvalueRef { + typedef const T& type; +}; +#endif // GTEST_HAS_STD_MOVE_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template // use like this: DownCast_(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (false) { + GTEST_INTENTIONAL_CONST_COND_POP_() + const To to = NULL; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == NULL || dynamic_cast(f) != NULL); +#endif + return static_cast(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); +#endif + +#if GTEST_HAS_DOWNCAST_ + return ::down_cast(base); +#elif GTEST_HAS_RTTI + return dynamic_cast(base); // NOLINT +#else + return static_cast(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION +// Returns the size (in bytes) of a file. +GTEST_API_ size_t GetFileSize(FILE* file); + +// Reads the entire content of a file as a string. +GTEST_API_ std::string ReadEntireFile(FILE* file); + +// All command line arguments. +GTEST_API_ std::vector GetArgvs(); + +#if GTEST_HAS_DEATH_TEST + +std::vector GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector* new_argvs); +void SetInjectableArgvs(const std::vector& new_argvs); +#if GTEST_HAS_GLOBAL_STRING +void SetInjectableArgvs(const std::vector< ::string>& new_argvs); +#endif // GTEST_HAS_GLOBAL_STRING +void ClearInjectableArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. +#if GTEST_IS_THREADSAFE +# if GTEST_HAS_PTHREAD +// Sleeps for (roughly) n milliseconds. This function is only for testing +// Google Test's own constructs. Don't use it in user tests, either +// directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, NULL); +} +# endif // GTEST_HAS_PTHREAD + +# if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_HAS_PTHREAD +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; + SleepMilliseconds(10); + } + } + + private: + pthread_mutex_t mutex_; + bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +GTEST_API_ void SleepMilliseconds(int n); + +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including in this header file. Including is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true iff the handle is a valid handle object that can be closed. + bool IsCloseable() const; + + Handle handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class GTEST_API_ Notification { + public: + Notification(); + void Notify(); + void WaitForNotification(); + + private: + AutoHandle event_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; +# endif // GTEST_HAS_NOTIFICATION_ + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return NULL; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); + finished_ = true; + } + } + + virtual void Run() { + if (thread_can_start_ != NULL) + thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + UserThreadFunc* const func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true iff we know that the thread function has finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; +# endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD || + // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +# if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +// Mutex and ThreadLocal have already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. +// +// A static Mutex *must* be defined or declared using one of the following +// macros: +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + GTEST_CRITICAL_SECTION* critical_section_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder object holding a default value passed to + // this ThreadLocal's constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase); +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) { + } + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) + : func_(func), + param_(param) { + } + virtual ~RunnableImpl() {} + virtual void Run() { + func_(param_); + } + + private: + UserThreadFunc* const func_; + const T param_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl); + }; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + + T* GetOrCreateValue() const { + return static_cast( + ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer(); + } + + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const { + return default_factory_->MakeNewHolder(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + virtual ValueHolder* MakeNewHolder() const { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + scoped_ptr default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false } + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + has_owner_ = false; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() + : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : key_(CreateKey()), + default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != NULL) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = default_factory_->MakeNewHolder(); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + virtual ValueHolder* MakeNewHolder() const { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + scoped_ptr default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#else // GTEST_IS_THREADSAFE + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +#endif // GTEST_IS_THREADSAFE + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +// Passing non-POD classes through ellipsis (...) crashes the ARM +// compiler and generates a warning in Sun Studio before 12u4. The Nokia Symbian +// and the IBM XL C/C++ compiler try to instantiate a copy constructor +// for objects passed through ellipsis (...), failing for uncopyable +// objects. We define this to ensure that only POD is passed through +// ellipsis on these systems. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x5130) +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_ELLIPSIS_NEEDS_POD_ 1 +#else +# define GTEST_CAN_COMPARE_NULL 1 +#endif + +// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between +// const T& and const T* in a function template. These compilers +// _can_ decide between class template specializations for T and T*, +// so a tr1::type_traits-like is_pointer works. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) +# define GTEST_NEEDS_IS_POINTER_ 1 +#endif + +template +struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; +}; +template const bool bool_constant::value; + +typedef bool_constant false_type; +typedef bool_constant true_type; + +template +struct is_same : public false_type {}; + +template +struct is_same : public true_type {}; + + +template +struct is_pointer : public false_type {}; + +template +struct is_pointer : public true_type {}; + +template +struct IteratorTraits { + typedef typename Iterator::value_type value_type; +}; + + +template +struct IteratorTraits { + typedef T value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +// The biggest signed integer type the compiler supports. +typedef __int64 BiggestInt; +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +typedef long long BiggestInt; // NOLINT +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) + it = str.erase(it); + return str; +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int IsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int IsATTY(int /* fd */) { return 0; } +# else +inline int IsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int IsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +// Functions deprecated by MSVC 8.0. + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996 /* deprecated function */) + +inline const char* StrNCpy(char* dest, const char* src, size_t n) { + return strncpy(dest, src, n); +} + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { + return fopen(path, mode); +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT + // We are on Windows CE, which has no environment variables. + static_cast(name); // To prevent 'unused argument' warning. + return NULL; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != NULL && env[0] != '\0') ? env : NULL; +#else + return getenv(name); +#endif +} + +GTEST_DISABLE_MSC_WARNINGS_POP_() + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +void Abort(); +#else +inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't +// complain about _snprintf. +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: +#if GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#if !defined(GTEST_FLAG) +# define GTEST_FLAG(name) FLAGS_gtest_##name +#endif // !defined(GTEST_FLAG) + +#if !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) +# define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 +#endif // !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) + +#if !defined(GTEST_DECLARE_bool_) +# define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver + +// Macros for declaring flags. +# define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +# define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) +# define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::std::string GTEST_FLAG(name) + +// Macros for defining flags. +# define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +#endif // !defined(GTEST_DECLARE_bool_) + +// Thread annotations +#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) +# define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +# define GTEST_LOCK_EXCLUDED_(locks) +#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-string.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-string.h new file mode 100644 index 0000000..71eb840 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-string.h @@ -0,0 +1,167 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by +// gtest/internal/gtest-internal.h. +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true iff the given string ends with the given suffix, ignoring + // case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-tuple.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-tuple.h new file mode 100644 index 0000000..e9b4053 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-tuple.h @@ -0,0 +1,1020 @@ +// This file was GENERATED by command: +// pump.py gtest-tuple.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2009 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template friend class tuple; \ + private: +#endif + +// Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that conflict +// with our own definitions. Therefore using our own tuple does not work on +// those compilers. +#if defined(_MSC_VER) && _MSC_VER >= 1600 /* 1600 is Visual Studio 2010 */ +# error "gtest's tuple doesn't compile on Visual Studio 2010 or later. \ +GTEST_USE_OWN_TR1_TUPLE must be set to 0 on those compilers." +#endif + +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> +#define GTEST_1_TUPLE_(T) tuple +#define GTEST_2_TUPLE_(T) tuple +#define GTEST_3_TUPLE_(T) tuple +#define GTEST_4_TUPLE_(T) tuple +#define GTEST_5_TUPLE_(T) tuple +#define GTEST_6_TUPLE_(T) tuple +#define GTEST_7_TUPLE_(T) tuple +#define GTEST_8_TUPLE_(T) tuple +#define GTEST_9_TUPLE_(T) tuple +#define GTEST_10_TUPLE_(T) tuple + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. +#define GTEST_0_TYPENAMES_(T) +#define GTEST_1_TYPENAMES_(T) typename T##0 +#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 +#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 +#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3 +#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4 +#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5 +#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6 +#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 +#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8 +#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8, typename T##9 + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef::type is T if T is a reference; otherwise it's const T&. +template +struct ByRef { typedef const T& type; }; // NOLINT +template +struct ByRef { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type + +// AddRef::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference::type. +template +struct AddRef { typedef T& type; }; // NOLINT +template +struct AddRef { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type + +// A helper for implementing get(). +template class Get; + +// A helper for implementing tuple_element. kIndexValid is true +// iff k < the number of fields in tuple type T. +template +struct TupleElement; + +template +struct TupleElement { + typedef T0 type; +}; + +template +struct TupleElement { + typedef T1 type; +}; + +template +struct TupleElement { + typedef T2 type; +}; + +template +struct TupleElement { + typedef T3 type; +}; + +template +struct TupleElement { + typedef T4 type; +}; + +template +struct TupleElement { + typedef T5 type; +}; + +template +struct TupleElement { + typedef T6 type; +}; + +template +struct TupleElement { + typedef T7 type; +}; + +template +struct TupleElement { + typedef T8 type; +}; + +template +struct TupleElement { + typedef T9 type; +}; + +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + +template +class GTEST_1_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} + + tuple(const tuple& t) : f0_(t.f0_) {} + + template + tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_1_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { + f0_ = t.f0_; + return *this; + } + + T0 f0_; +}; + +template +class GTEST_2_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), + f1_(f1) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} + + template + tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} + template + tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_2_TUPLE_(U)& t) { + return CopyFrom(t); + } + template + tuple& operator=(const ::std::pair& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + return *this; + } + + T0 f0_; + T1 f1_; +}; + +template +class GTEST_3_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + template + tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_3_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; +}; + +template +class GTEST_4_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} + + template + tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_4_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; +}; + +template +class GTEST_5_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, + GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_) {} + + template + tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_5_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; +}; + +template +class GTEST_6_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_) {} + + template + tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_6_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; +}; + +template +class GTEST_7_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + template + tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_7_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; +}; + +template +class GTEST_8_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, + GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + template + tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_8_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; +}; + +template +class GTEST_9_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + template + tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_9_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; +}; + +template +class tuple { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), + f9_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} + + template + tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), + f9_(t.f9_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_10_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + f9_ = t.f9_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; + T9 f9_; +}; + +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +template +inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { + return GTEST_1_TUPLE_(T)(f0); +} + +template +inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { + return GTEST_2_TUPLE_(T)(f0, f1); +} + +template +inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { + return GTEST_3_TUPLE_(T)(f0, f1, f2); +} + +template +inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3) { + return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); +} + +template +inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4) { + return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); +} + +template +inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5) { + return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); +} + +template +inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6) { + return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); +} + +template +inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { + return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); +} + +template +inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8) { + return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); +} + +template +inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8, const T9& f9) { + return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); +} + +// 6.1.3.3 Tuple helper classes. + +template struct tuple_size; + +template +struct tuple_size { + static const int value = 0; +}; + +template +struct tuple_size { + static const int value = 1; +}; + +template +struct tuple_size { + static const int value = 2; +}; + +template +struct tuple_size { + static const int value = 3; +}; + +template +struct tuple_size { + static const int value = 4; +}; + +template +struct tuple_size { + static const int value = 5; +}; + +template +struct tuple_size { + static const int value = 6; +}; + +template +struct tuple_size { + static const int value = 7; +}; + +template +struct tuple_size { + static const int value = 8; +}; + +template +struct tuple_size { + static const int value = 9; +}; + +template +struct tuple_size { + static const int value = 10; +}; + +template +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + +template <> +class Get<0> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + Field(Tuple& t) { return t.f0_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + ConstField(const Tuple& t) { return t.f0_; } +}; + +template <> +class Get<1> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + Field(Tuple& t) { return t.f1_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + ConstField(const Tuple& t) { return t.f1_; } +}; + +template <> +class Get<2> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + Field(Tuple& t) { return t.f2_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + ConstField(const Tuple& t) { return t.f2_; } +}; + +template <> +class Get<3> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + Field(Tuple& t) { return t.f3_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + ConstField(const Tuple& t) { return t.f3_; } +}; + +template <> +class Get<4> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + Field(Tuple& t) { return t.f4_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + ConstField(const Tuple& t) { return t.f4_; } +}; + +template <> +class Get<5> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + Field(Tuple& t) { return t.f5_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + ConstField(const Tuple& t) { return t.f5_; } +}; + +template <> +class Get<6> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + Field(Tuple& t) { return t.f6_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + ConstField(const Tuple& t) { return t.f6_; } +}; + +template <> +class Get<7> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + Field(Tuple& t) { return t.f7_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + ConstField(const Tuple& t) { return t.f7_; } +}; + +template <> +class Get<8> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + Field(Tuple& t) { return t.f8_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + ConstField(const Tuple& t) { return t.f8_; } +}; + +template <> +class Get<9> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + Field(Tuple& t) { return t.f9_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + ConstField(const Tuple& t) { return t.f9_; } +}; + +} // namespace gtest_internal + +template +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::Field(t); +} + +template +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(const GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template +struct SameSizeTuplePrefixComparator { + template + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator::Eq(t1, t2) && + ::std::tr1::get(t1) == ::std::tr1::get(t2); + } +}; + +} // namespace gtest_internal + +template +inline bool operator==(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size::value, + tuple_size::value>::Eq(t, u); +} + +template +inline bool operator!=(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + +#undef GTEST_0_TUPLE_ +#undef GTEST_1_TUPLE_ +#undef GTEST_2_TUPLE_ +#undef GTEST_3_TUPLE_ +#undef GTEST_4_TUPLE_ +#undef GTEST_5_TUPLE_ +#undef GTEST_6_TUPLE_ +#undef GTEST_7_TUPLE_ +#undef GTEST_8_TUPLE_ +#undef GTEST_9_TUPLE_ +#undef GTEST_10_TUPLE_ + +#undef GTEST_0_TYPENAMES_ +#undef GTEST_1_TYPENAMES_ +#undef GTEST_2_TYPENAMES_ +#undef GTEST_3_TYPENAMES_ +#undef GTEST_4_TYPENAMES_ +#undef GTEST_5_TYPENAMES_ +#undef GTEST_6_TYPENAMES_ +#undef GTEST_7_TYPENAMES_ +#undef GTEST_8_TYPENAMES_ +#undef GTEST_9_TYPENAMES_ +#undef GTEST_10_TYPENAMES_ + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-tuple.h.pump b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-tuple.h.pump new file mode 100644 index 0000000..429ddfe --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-tuple.h.pump @@ -0,0 +1,347 @@ +$$ -*- mode: c++; -*- +$var n = 10 $$ Maximum number of tuple fields we want to support. +$$ This meta comment fixes auto-indentation in Emacs. }} +// Copyright 2009 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template friend class tuple; \ + private: +#endif + +// Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that conflict +// with our own definitions. Therefore using our own tuple does not work on +// those compilers. +#if defined(_MSC_VER) && _MSC_VER >= 1600 /* 1600 is Visual Studio 2010 */ +# error "gtest's tuple doesn't compile on Visual Studio 2010 or later. \ +GTEST_USE_OWN_TR1_TUPLE must be set to 0 on those compilers." +#endif + + +$range i 0..n-1 +$range j 0..n +$range k 1..n +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> + +$for k [[ +$range m 0..k-1 +$range m2 k..n-1 +#define GTEST_$(k)_TUPLE_(T) tuple<$for m, [[T##$m]]$for m2 [[, void]]> + +]] + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. + +$for j [[ +$range m 0..j-1 +#define GTEST_$(j)_TYPENAMES_(T) $for m, [[typename T##$m]] + + +]] + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template <$for i, [[typename T$i = void]]> +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef::type is T if T is a reference; otherwise it's const T&. +template +struct ByRef { typedef const T& type; }; // NOLINT +template +struct ByRef { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type + +// AddRef::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference::type. +template +struct AddRef { typedef T& type; }; // NOLINT +template +struct AddRef { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type + +// A helper for implementing get(). +template class Get; + +// A helper for implementing tuple_element. kIndexValid is true +// iff k < the number of fields in tuple type T. +template +struct TupleElement; + + +$for i [[ +template +struct TupleElement { + typedef T$i type; +}; + + +]] +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + + +$for k [[ +$range m 0..k-1 +template +class $if k < n [[GTEST_$(k)_TUPLE_(T)]] $else [[tuple]] { + public: + template friend class gtest_internal::Get; + + tuple() : $for m, [[f$(m)_()]] {} + + explicit tuple($for m, [[GTEST_BY_REF_(T$m) f$m]]) : [[]] +$for m, [[f$(m)_(f$m)]] {} + + tuple(const tuple& t) : $for m, [[f$(m)_(t.f$(m)_)]] {} + + template + tuple(const GTEST_$(k)_TUPLE_(U)& t) : $for m, [[f$(m)_(t.f$(m)_)]] {} + +$if k == 2 [[ + template + tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} + +]] + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_$(k)_TUPLE_(U)& t) { + return CopyFrom(t); + } + +$if k == 2 [[ + template + tuple& operator=(const ::std::pair& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + +]] + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_$(k)_TUPLE_(U)& t) { + +$for m [[ + f$(m)_ = t.f$(m)_; + +]] + return *this; + } + + +$for m [[ + T$m f$(m)_; + +]] +}; + + +]] +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +$for k [[ +$range m 0..k-1 + +template +inline GTEST_$(k)_TUPLE_(T) make_tuple($for m, [[const T$m& f$m]]) { + return GTEST_$(k)_TUPLE_(T)($for m, [[f$m]]); +} + +]] + +// 6.1.3.3 Tuple helper classes. + +template struct tuple_size; + + +$for j [[ +template +struct tuple_size { + static const int value = $j; +}; + + +]] +template +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + + +$for i [[ +template <> +class Get<$i> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple)) + Field(Tuple& t) { return t.f$(i)_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple)) + ConstField(const Tuple& t) { return t.f$(i)_; } +}; + + +]] +} // namespace gtest_internal + +template +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T))) +get(GTEST_$(n)_TUPLE_(T)& t) { + return gtest_internal::Get::Field(t); +} + +template +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T))) +get(const GTEST_$(n)_TUPLE_(T)& t) { + return gtest_internal::Get::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template +struct SameSizeTuplePrefixComparator { + template + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator::Eq(t1, t2) && + ::std::tr1::get(t1) == ::std::tr1::get(t2); + } +}; + +} // namespace gtest_internal + +template +inline bool operator==(const GTEST_$(n)_TUPLE_(T)& t, + const GTEST_$(n)_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size::value, + tuple_size::value>::Eq(t, u); +} + +template +inline bool operator!=(const GTEST_$(n)_TUPLE_(T)& t, + const GTEST_$(n)_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + + +$for j [[ +#undef GTEST_$(j)_TUPLE_ + +]] + + +$for j [[ +#undef GTEST_$(j)_TYPENAMES_ + +]] + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-type-util.h b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-type-util.h new file mode 100644 index 0000000..282b81f --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-type-util.h @@ -0,0 +1,3347 @@ +// This file was GENERATED by command: +// pump.py gtest-type-util.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most 50 types in a list, and at most 50 +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + return s; +} + +// GetTypeName() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +# if GTEST_HAS_RTTI + + const char* const name = typeid(T).name(); +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +# if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +# endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +# else + return name; +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC + +# else + + return ""; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template +struct AssertTypeEq; + +template +struct AssertTypeEq { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types, Type, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; +template +struct Types2 { + typedef T1 Head; + typedef Types1 Tail; +}; + +template +struct Types3 { + typedef T1 Head; + typedef Types2 Tail; +}; + +template +struct Types4 { + typedef T1 Head; + typedef Types3 Tail; +}; + +template +struct Types5 { + typedef T1 Head; + typedef Types4 Tail; +}; + +template +struct Types6 { + typedef T1 Head; + typedef Types5 Tail; +}; + +template +struct Types7 { + typedef T1 Head; + typedef Types6 Tail; +}; + +template +struct Types8 { + typedef T1 Head; + typedef Types7 Tail; +}; + +template +struct Types9 { + typedef T1 Head; + typedef Types8 Tail; +}; + +template +struct Types10 { + typedef T1 Head; + typedef Types9 Tail; +}; + +template +struct Types11 { + typedef T1 Head; + typedef Types10 Tail; +}; + +template +struct Types12 { + typedef T1 Head; + typedef Types11 Tail; +}; + +template +struct Types13 { + typedef T1 Head; + typedef Types12 Tail; +}; + +template +struct Types14 { + typedef T1 Head; + typedef Types13 Tail; +}; + +template +struct Types15 { + typedef T1 Head; + typedef Types14 Tail; +}; + +template +struct Types16 { + typedef T1 Head; + typedef Types15 Tail; +}; + +template +struct Types17 { + typedef T1 Head; + typedef Types16 Tail; +}; + +template +struct Types18 { + typedef T1 Head; + typedef Types17 Tail; +}; + +template +struct Types19 { + typedef T1 Head; + typedef Types18 Tail; +}; + +template +struct Types20 { + typedef T1 Head; + typedef Types19 Tail; +}; + +template +struct Types21 { + typedef T1 Head; + typedef Types20 Tail; +}; + +template +struct Types22 { + typedef T1 Head; + typedef Types21 Tail; +}; + +template +struct Types23 { + typedef T1 Head; + typedef Types22 Tail; +}; + +template +struct Types24 { + typedef T1 Head; + typedef Types23 Tail; +}; + +template +struct Types25 { + typedef T1 Head; + typedef Types24 Tail; +}; + +template +struct Types26 { + typedef T1 Head; + typedef Types25 Tail; +}; + +template +struct Types27 { + typedef T1 Head; + typedef Types26 Tail; +}; + +template +struct Types28 { + typedef T1 Head; + typedef Types27 Tail; +}; + +template +struct Types29 { + typedef T1 Head; + typedef Types28 Tail; +}; + +template +struct Types30 { + typedef T1 Head; + typedef Types29 Tail; +}; + +template +struct Types31 { + typedef T1 Head; + typedef Types30 Tail; +}; + +template +struct Types32 { + typedef T1 Head; + typedef Types31 Tail; +}; + +template +struct Types33 { + typedef T1 Head; + typedef Types32 Tail; +}; + +template +struct Types34 { + typedef T1 Head; + typedef Types33 Tail; +}; + +template +struct Types35 { + typedef T1 Head; + typedef Types34 Tail; +}; + +template +struct Types36 { + typedef T1 Head; + typedef Types35 Tail; +}; + +template +struct Types37 { + typedef T1 Head; + typedef Types36 Tail; +}; + +template +struct Types38 { + typedef T1 Head; + typedef Types37 Tail; +}; + +template +struct Types39 { + typedef T1 Head; + typedef Types38 Tail; +}; + +template +struct Types40 { + typedef T1 Head; + typedef Types39 Tail; +}; + +template +struct Types41 { + typedef T1 Head; + typedef Types40 Tail; +}; + +template +struct Types42 { + typedef T1 Head; + typedef Types41 Tail; +}; + +template +struct Types43 { + typedef T1 Head; + typedef Types42 Tail; +}; + +template +struct Types44 { + typedef T1 Head; + typedef Types43 Tail; +}; + +template +struct Types45 { + typedef T1 Head; + typedef Types44 Tail; +}; + +template +struct Types46 { + typedef T1 Head; + typedef Types45 Tail; +}; + +template +struct Types47 { + typedef T1 Head; + typedef Types46 Tail; +}; + +template +struct Types48 { + typedef T1 Head; + typedef Types47 Tail; +}; + +template +struct Types49 { + typedef T1 Head; + typedef Types48 Tail; +}; + +template +struct Types50 { + typedef T1 Head; + typedef Types49 Tail; +}; + + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types +// will appear as Types in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types, and Google Test will translate +// that to TypesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. +template +struct Types { + typedef internal::Types50 type; +}; + +template <> +struct Types { + typedef internal::Types0 type; +}; +template +struct Types { + typedef internal::Types1 type; +}; +template +struct Types { + typedef internal::Types2 type; +}; +template +struct Types { + typedef internal::Types3 type; +}; +template +struct Types { + typedef internal::Types4 type; +}; +template +struct Types { + typedef internal::Types5 type; +}; +template +struct Types { + typedef internal::Types6 type; +}; +template +struct Types { + typedef internal::Types7 type; +}; +template +struct Types { + typedef internal::Types8 type; +}; +template +struct Types { + typedef internal::Types9 type; +}; +template +struct Types { + typedef internal::Types10 type; +}; +template +struct Types { + typedef internal::Types11 type; +}; +template +struct Types { + typedef internal::Types12 type; +}; +template +struct Types { + typedef internal::Types13 type; +}; +template +struct Types { + typedef internal::Types14 type; +}; +template +struct Types { + typedef internal::Types15 type; +}; +template +struct Types { + typedef internal::Types16 type; +}; +template +struct Types { + typedef internal::Types17 type; +}; +template +struct Types { + typedef internal::Types18 type; +}; +template +struct Types { + typedef internal::Types19 type; +}; +template +struct Types { + typedef internal::Types20 type; +}; +template +struct Types { + typedef internal::Types21 type; +}; +template +struct Types { + typedef internal::Types22 type; +}; +template +struct Types { + typedef internal::Types23 type; +}; +template +struct Types { + typedef internal::Types24 type; +}; +template +struct Types { + typedef internal::Types25 type; +}; +template +struct Types { + typedef internal::Types26 type; +}; +template +struct Types { + typedef internal::Types27 type; +}; +template +struct Types { + typedef internal::Types28 type; +}; +template +struct Types { + typedef internal::Types29 type; +}; +template +struct Types { + typedef internal::Types30 type; +}; +template +struct Types { + typedef internal::Types31 type; +}; +template +struct Types { + typedef internal::Types32 type; +}; +template +struct Types { + typedef internal::Types33 type; +}; +template +struct Types { + typedef internal::Types34 type; +}; +template +struct Types { + typedef internal::Types35 type; +}; +template +struct Types { + typedef internal::Types36 type; +}; +template +struct Types { + typedef internal::Types37 type; +}; +template +struct Types { + typedef internal::Types38 type; +}; +template +struct Types { + typedef internal::Types39 type; +}; +template +struct Types { + typedef internal::Types40 type; +}; +template +struct Types { + typedef internal::Types41 type; +}; +template +struct Types { + typedef internal::Types42 type; +}; +template +struct Types { + typedef internal::Types43 type; +}; +template +struct Types { + typedef internal::Types44 type; +}; +template +struct Types { + typedef internal::Types45 type; +}; +template +struct Types { + typedef internal::Types46 type; +}; +template +struct Types { + typedef internal::Types47 type; +}; +template +struct Types { + typedef internal::Types48 type; +}; +template +struct Types { + typedef internal::Types49 type; +}; + +namespace internal { + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates, Templates, +// and etc), which C++ doesn't support directly. +template +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template +struct Templates1 { + typedef TemplateSel Head; + typedef Templates0 Tail; +}; +template +struct Templates2 { + typedef TemplateSel Head; + typedef Templates1 Tail; +}; + +template +struct Templates3 { + typedef TemplateSel Head; + typedef Templates2 Tail; +}; + +template +struct Templates4 { + typedef TemplateSel Head; + typedef Templates3 Tail; +}; + +template +struct Templates5 { + typedef TemplateSel Head; + typedef Templates4 Tail; +}; + +template +struct Templates6 { + typedef TemplateSel Head; + typedef Templates5 Tail; +}; + +template +struct Templates7 { + typedef TemplateSel Head; + typedef Templates6 Tail; +}; + +template +struct Templates8 { + typedef TemplateSel Head; + typedef Templates7 Tail; +}; + +template +struct Templates9 { + typedef TemplateSel Head; + typedef Templates8 Tail; +}; + +template +struct Templates10 { + typedef TemplateSel Head; + typedef Templates9 Tail; +}; + +template +struct Templates11 { + typedef TemplateSel Head; + typedef Templates10 Tail; +}; + +template +struct Templates12 { + typedef TemplateSel Head; + typedef Templates11 Tail; +}; + +template +struct Templates13 { + typedef TemplateSel Head; + typedef Templates12 Tail; +}; + +template +struct Templates14 { + typedef TemplateSel Head; + typedef Templates13 Tail; +}; + +template +struct Templates15 { + typedef TemplateSel Head; + typedef Templates14 Tail; +}; + +template +struct Templates16 { + typedef TemplateSel Head; + typedef Templates15 Tail; +}; + +template +struct Templates17 { + typedef TemplateSel Head; + typedef Templates16 Tail; +}; + +template +struct Templates18 { + typedef TemplateSel Head; + typedef Templates17 Tail; +}; + +template +struct Templates19 { + typedef TemplateSel Head; + typedef Templates18 Tail; +}; + +template +struct Templates20 { + typedef TemplateSel Head; + typedef Templates19 Tail; +}; + +template +struct Templates21 { + typedef TemplateSel Head; + typedef Templates20 Tail; +}; + +template +struct Templates22 { + typedef TemplateSel Head; + typedef Templates21 Tail; +}; + +template +struct Templates23 { + typedef TemplateSel Head; + typedef Templates22 Tail; +}; + +template +struct Templates24 { + typedef TemplateSel Head; + typedef Templates23 Tail; +}; + +template +struct Templates25 { + typedef TemplateSel Head; + typedef Templates24 Tail; +}; + +template +struct Templates26 { + typedef TemplateSel Head; + typedef Templates25 Tail; +}; + +template +struct Templates27 { + typedef TemplateSel Head; + typedef Templates26 Tail; +}; + +template +struct Templates28 { + typedef TemplateSel Head; + typedef Templates27 Tail; +}; + +template +struct Templates29 { + typedef TemplateSel Head; + typedef Templates28 Tail; +}; + +template +struct Templates30 { + typedef TemplateSel Head; + typedef Templates29 Tail; +}; + +template +struct Templates31 { + typedef TemplateSel Head; + typedef Templates30 Tail; +}; + +template +struct Templates32 { + typedef TemplateSel Head; + typedef Templates31 Tail; +}; + +template +struct Templates33 { + typedef TemplateSel Head; + typedef Templates32 Tail; +}; + +template +struct Templates34 { + typedef TemplateSel Head; + typedef Templates33 Tail; +}; + +template +struct Templates35 { + typedef TemplateSel Head; + typedef Templates34 Tail; +}; + +template +struct Templates36 { + typedef TemplateSel Head; + typedef Templates35 Tail; +}; + +template +struct Templates37 { + typedef TemplateSel Head; + typedef Templates36 Tail; +}; + +template +struct Templates38 { + typedef TemplateSel Head; + typedef Templates37 Tail; +}; + +template +struct Templates39 { + typedef TemplateSel Head; + typedef Templates38 Tail; +}; + +template +struct Templates40 { + typedef TemplateSel Head; + typedef Templates39 Tail; +}; + +template +struct Templates41 { + typedef TemplateSel Head; + typedef Templates40 Tail; +}; + +template +struct Templates42 { + typedef TemplateSel Head; + typedef Templates41 Tail; +}; + +template +struct Templates43 { + typedef TemplateSel Head; + typedef Templates42 Tail; +}; + +template +struct Templates44 { + typedef TemplateSel Head; + typedef Templates43 Tail; +}; + +template +struct Templates45 { + typedef TemplateSel Head; + typedef Templates44 Tail; +}; + +template +struct Templates46 { + typedef TemplateSel Head; + typedef Templates45 Tail; +}; + +template +struct Templates47 { + typedef TemplateSel Head; + typedef Templates46 Tail; +}; + +template +struct Templates48 { + typedef TemplateSel Head; + typedef Templates47 Tail; +}; + +template +struct Templates49 { + typedef TemplateSel Head; + typedef Templates48 Tail; +}; + +template +struct Templates50 { + typedef TemplateSel Head; + typedef Templates49 Tail; +}; + + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates +// will appear as Templates in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates, and Google Test will translate +// that to TemplatesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. +template +struct Templates { + typedef Templates50 type; +}; + +template <> +struct Templates { + typedef Templates0 type; +}; +template +struct Templates { + typedef Templates1 type; +}; +template +struct Templates { + typedef Templates2 type; +}; +template +struct Templates { + typedef Templates3 type; +}; +template +struct Templates { + typedef Templates4 type; +}; +template +struct Templates { + typedef Templates5 type; +}; +template +struct Templates { + typedef Templates6 type; +}; +template +struct Templates { + typedef Templates7 type; +}; +template +struct Templates { + typedef Templates8 type; +}; +template +struct Templates { + typedef Templates9 type; +}; +template +struct Templates { + typedef Templates10 type; +}; +template +struct Templates { + typedef Templates11 type; +}; +template +struct Templates { + typedef Templates12 type; +}; +template +struct Templates { + typedef Templates13 type; +}; +template +struct Templates { + typedef Templates14 type; +}; +template +struct Templates { + typedef Templates15 type; +}; +template +struct Templates { + typedef Templates16 type; +}; +template +struct Templates { + typedef Templates17 type; +}; +template +struct Templates { + typedef Templates18 type; +}; +template +struct Templates { + typedef Templates19 type; +}; +template +struct Templates { + typedef Templates20 type; +}; +template +struct Templates { + typedef Templates21 type; +}; +template +struct Templates { + typedef Templates22 type; +}; +template +struct Templates { + typedef Templates23 type; +}; +template +struct Templates { + typedef Templates24 type; +}; +template +struct Templates { + typedef Templates25 type; +}; +template +struct Templates { + typedef Templates26 type; +}; +template +struct Templates { + typedef Templates27 type; +}; +template +struct Templates { + typedef Templates28 type; +}; +template +struct Templates { + typedef Templates29 type; +}; +template +struct Templates { + typedef Templates30 type; +}; +template +struct Templates { + typedef Templates31 type; +}; +template +struct Templates { + typedef Templates32 type; +}; +template +struct Templates { + typedef Templates33 type; +}; +template +struct Templates { + typedef Templates34 type; +}; +template +struct Templates { + typedef Templates35 type; +}; +template +struct Templates { + typedef Templates36 type; +}; +template +struct Templates { + typedef Templates37 type; +}; +template +struct Templates { + typedef Templates38 type; +}; +template +struct Templates { + typedef Templates39 type; +}; +template +struct Templates { + typedef Templates40 type; +}; +template +struct Templates { + typedef Templates41 type; +}; +template +struct Templates { + typedef Templates42 type; +}; +template +struct Templates { + typedef Templates43 type; +}; +template +struct Templates { + typedef Templates44 type; +}; +template +struct Templates { + typedef Templates45 type; +}; +template +struct Templates { + typedef Templates46 type; +}; +template +struct Templates { + typedef Templates47 type; +}; +template +struct Templates { + typedef Templates48 type; +}; +template +struct Templates { + typedef Templates49 type; +}; + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template +struct TypeList { + typedef Types1 type; +}; + +template +struct TypeList > { + typedef typename Types::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-type-util.h.pump b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-type-util.h.pump new file mode 100644 index 0000000..eaa8880 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/include/gtest/internal/gtest-type-util.h.pump @@ -0,0 +1,313 @@ +$$ -*- mode: c++; -*- +$var n = 50 $$ Maximum length of type lists we want to support. +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most $n types in a list, and at most $n +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + return s; +} + +// GetTypeName() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +# if GTEST_HAS_RTTI + + const char* const name = typeid(T).name(); +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +# if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +# endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +# else + return name; +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC + +# else + + return ""; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template +struct AssertTypeEq; + +template +struct AssertTypeEq { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types, Type, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; + +$range i 2..n + +$for i [[ +$range j 1..i +$range k 2..i +template <$for j, [[typename T$j]]> +struct Types$i { + typedef T1 Head; + typedef Types$(i-1)<$for k, [[T$k]]> Tail; +}; + + +]] + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types +// will appear as Types in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types, and Google Test will translate +// that to TypesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. + +$range i 1..n +template <$for i, [[typename T$i = internal::None]]> +struct Types { + typedef internal::Types$n<$for i, [[T$i]]> type; +}; + +template <> +struct Types<$for i, [[internal::None]]> { + typedef internal::Types0 type; +}; + +$range i 1..n-1 +$for i [[ +$range j 1..i +$range k i+1..n +template <$for j, [[typename T$j]]> +struct Types<$for j, [[T$j]]$for k[[, internal::None]]> { + typedef internal::Types$i<$for j, [[T$j]]> type; +}; + +]] + +namespace internal { + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates, Templates, +// and etc), which C++ doesn't support directly. +template +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template +struct Templates1 { + typedef TemplateSel Head; + typedef Templates0 Tail; +}; + +$range i 2..n + +$for i [[ +$range j 1..i +$range k 2..i +template <$for j, [[GTEST_TEMPLATE_ T$j]]> +struct Templates$i { + typedef TemplateSel Head; + typedef Templates$(i-1)<$for k, [[T$k]]> Tail; +}; + + +]] + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates +// will appear as Templates in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates, and Google Test will translate +// that to TemplatesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. + +$range i 1..n +template <$for i, [[GTEST_TEMPLATE_ T$i = NoneT]]> +struct Templates { + typedef Templates$n<$for i, [[T$i]]> type; +}; + +template <> +struct Templates<$for i, [[NoneT]]> { + typedef Templates0 type; +}; + +$range i 1..n-1 +$for i [[ +$range j 1..i +$range k i+1..n +template <$for j, [[GTEST_TEMPLATE_ T$j]]> +struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> { + typedef Templates$i<$for j, [[T$j]]> type; +}; + +]] + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template +struct TypeList { + typedef Types1 type; +}; + + +$range i 1..n +template <$for i, [[typename T$i]]> +struct TypeList > { + typedef typename Types<$for i, [[T$i]]>::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-all.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-all.cc new file mode 100644 index 0000000..5872a2e --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-all.cc @@ -0,0 +1,48 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// Google C++ Testing and Mocking Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +#include "src/gtest.cc" +#include "src/gtest-death-test.cc" +#include "src/gtest-filepath.cc" +#include "src/gtest-port.cc" +#include "src/gtest-printers.cc" +#include "src/gtest-test-part.cc" +#include "src/gtest-typed-test.cc" diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-death-test.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-death-test.cc new file mode 100644 index 0000000..e5b4e09 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-death-test.cc @@ -0,0 +1,1536 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) +// +// This file implements death tests. + +#include "gtest/gtest-death-test.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/custom/gtest.h" + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include +# endif // GTEST_OS_MAC + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include + +# if GTEST_OS_WINDOWS +# include +# else +# include +# include +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + +# if GTEST_OS_FUCHSIA +# include +# include +# include +# include +# endif // GTEST_OS_FUCHSIA + +#endif // GTEST_HAS_DEATH_TEST + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-string.h" +#include "src/gtest-internal-inl.h" + +namespace testing { + +// Constants. + +// The default death test style. +// +// This is defined in internal/gtest-port.h as "fast", but can be overridden by +// a definition in internal/custom/gtest-port.h. The recommended value, which is +// used internally at Google, is "threadsafe". +static const char kDefaultDeathTestStyle[] = GTEST_DEFAULT_DEATH_TEST_STYLE; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +static bool g_in_fast_death_test_child = false; +# endif + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + // On Windows and Fuchsia, death tests are thread-safe regardless of the value + // of the death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA +} + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { +# if defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_) + { + bool result; + if (GTEST_KILLED_BY_SIGNAL_OVERRIDE_(signum_, exit_status, &result)) { + return result; + } + } +# endif // defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_) + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +#if GTEST_OS_FUCHSIA + +// File descriptor used for the pipe in the child process. +static const int kFuchsiaReadPipeFd = 3; + +#endif + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +static void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != NULL) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, const RE* a_regex) + : statement_(a_statement), + regex_(a_regex), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason); + virtual bool Passed(bool status_ok); + + const char* statement() const { return statement_; } + const RE* regex() const { return regex_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // The regular expression which test output must match. DeathTestImpl + // doesn't own this object and should not attempt to delete it. + const RE* const regex_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// regex: A regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetCapturedStderr(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { +# if GTEST_USES_PCRE + // PCRE regexes support embedded NULs. + // GTEST_USES_PCRE is defined only in google3 mode + const bool matched = RE::PartialMatch(error_message, *regex()); +# else + const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); +# endif // GTEST_USES_PCRE + if (matched) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex()->pattern() << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = { + sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + NULL)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_( + _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_(::CreateProcessA( + executable_path, + const_cast(command_line.c_str()), + NULL, // Retuned process handle is not inheritable. + NULL, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + NULL, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), + &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} + +# elif GTEST_OS_FUCHSIA + +class FuchsiaDeathTest : public DeathTestImpl { + public: + FuchsiaDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + + zx_handle_t child_process_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + int size() { + return args_.size() - 1; + } + + private: + std::vector args_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int FuchsiaDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait for child process to terminate. + zx_status_t status_zx; + zx_signals_t signals; + status_zx = zx_object_wait_one( + child_process_, + ZX_PROCESS_TERMINATED, + ZX_TIME_INFINITE, + &signals); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + ReadAndInterpretStatusByte(); + + zx_info_process_t buffer; + status_zx = zx_object_get_info( + child_process_, + ZX_INFO_PROCESS, + &buffer, + sizeof(buffer), + nullptr, + nullptr); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + GTEST_DEATH_TEST_CHECK_(buffer.exited); + set_status(buffer.return_code); + return status(); +} + +// The AssumeRole process for a Fuchsia death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole FuchsiaDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(kFuchsiaReadPipeFd); + return EXECUTE_TEST; + } + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // Build the child process command line. + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + + StreamableToString(line_) + "|" + + StreamableToString(death_test_index); + Arguments args; + args.AddArguments(GetInjectableArgvs()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + // Build the pipe for communication with the child. + zx_status_t status; + zx_handle_t child_pipe_handle; + uint32_t type; + status = fdio_pipe_half(&child_pipe_handle, &type); + GTEST_DEATH_TEST_CHECK_(status >= 0); + set_read_fd(status); + + // Set the pipe handle for the child. + fdio_spawn_action_t add_handle_action = { + .action = FDIO_SPAWN_ACTION_ADD_HANDLE, + .h = { + .id = PA_HND(type, kFuchsiaReadPipeFd), + .handle = child_pipe_handle + } + }; + + // Spawn the child process. + status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, + args.Argv()[0], args.Argv(), nullptr, 1, + &add_handle_action, &child_process_, nullptr); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + set_spawned(true); + return OVERSEE_TEST; +} + +#else // We are neither on Windows, nor on Fuchsia. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) + : DeathTestImpl(a_statement, a_regex), + child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, const RE* a_regex) : + ForkingDeathTest(a_statement, a_regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, const RE* a_regex, + const char* file, int line) : + ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + static ::std::vector GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); +# if defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) + ::std::vector extra_args = + GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_(); + args.insert(args.end(), extra_args.begin(), extra_args.end()); +# endif // defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_MAC +inline char** GetEnviron() { + // When Google Test is built as a framework on MacOS X, the environ variable + // is unavailable. Apple's documentation (man environ) recommends using + // _NSGetEnviron() instead. + return *_NSGetEnviron(); +} +# else +// Some POSIX platforms expect you to declare environ. extern "C" makes +// it reside in the global namespace. +extern "C" char** environ; +inline char** GetEnviron() { return environ; } +# endif // GTEST_OS_MAC + +# if !GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execve() as it's a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execve() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execve(args->argv[0], args->argv, GetEnviron()); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // !GTEST_OS_QNX + +# if GTEST_HAS_CLONE +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +static void StackLowerThanAddress(const void* ptr, + bool* result) GTEST_NO_INLINE_; +static void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy; + *result = (&dummy < ptr); +} + +// Make sure AddressSanitizer does not tamper with the stack here. +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +static bool StackGrowsDown() { + int dummy; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} +# endif // GTEST_HAS_CLONE + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = NULL; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, regex, file, line); + } + +# elif GTEST_OS_FUCHSIA + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new FuchsiaDeathTest(statement, regex, file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +static int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + // TODO(vladl@google.com): Replace the following check with a + // compile-time assertion when available. + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); + +# elif GTEST_OS_FUCHSIA + + if (fields.size() != 3 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-filepath.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-filepath.cc new file mode 100644 index 0000000..6b76ea0 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-filepath.cc @@ -0,0 +1,385 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/internal/gtest-filepath.h" + +#include +#include "gtest/internal/gtest-port.h" +#include "gtest/gtest-message.h" + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +# include +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h +# include +#else +# include +# include // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + +#include "gtest/internal/gtest-string.h" + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT + // Windows CE doesn't have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + char* result = getcwd(cwd, sizeof(cwd)); +# if GTEST_OS_NACL + // getcwd will likely fail in NaCl due to the sandbox, so return something + // reasonable. The user may have provided a shim implementation for getcwd, + // however, so fallback only when failure is detected. + return FilePath(result == NULL ? kCurrentDirectoryString : cwd); +# endif // GTEST_OS_NACL + return FilePath(result == NULL ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurrence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != NULL && + (last_sep == NULL || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), last_sep + 1 - c_str()); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + // TODO(wan@google.com): on Windows a network share like + // \\server\share can be a root directory, although it cannot be the + // current directory. Handle this properly. + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, NULL) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +void FilePath::Normalize() { + if (pathname_.c_str() == NULL) { + pathname_ = ""; + return; + } + const char* src = pathname_.c_str(); + char* const dest = new char[pathname_.length() + 1]; + char* dest_ptr = dest; + memset(dest_ptr, 0, pathname_.length() + 1); + + while (*src != '\0') { + *dest_ptr = *src; + if (!IsPathSeparator(*src)) { + src++; + } else { +#if GTEST_HAS_ALT_PATH_SEP_ + if (*dest_ptr == kAlternatePathSeparator) { + *dest_ptr = kPathSeparator; + } +#endif + while (IsPathSeparator(*src)) + src++; + } + dest_ptr++; + } + *dest_ptr = '\0'; + pathname_ = dest; + delete[] dest; +} + +} // namespace internal +} // namespace testing diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-internal-inl.h b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-internal-inl.h new file mode 100644 index 0000000..e77c8b6 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-internal-inl.h @@ -0,0 +1,1175 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +#ifndef _WIN32_WCE +# include +#endif // !_WIN32_WCE +#include +#include // For strtoll/_strtoul64/malloc/free. +#include // For memmove. + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS + +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kPrintTimeFlag[] = "print_time"; +const char kPrintUTF8Flag[] = "print_utf8"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; +const char kFlagfileFlag[] = "flagfile"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true iff Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, Int32* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast(GetTimeInMillis()) : + static_cast(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast((raw_seed - 1U) % + static_cast(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + print_time_ = GTEST_FLAG(print_time); + print_utf8_ = GTEST_FLAG(print_utf8); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(print_utf8) = print_utf8_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool print_time_; + bool print_utf8_; + internal::Int32 random_seed_; + internal::Int32 repeat_; + bool shuffle_; + internal::Int32 stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template +inline E GetElementOr(const std::vector& v, int i, E default_value) { + return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector* v) { + const int size = static_cast(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = begin + random->Generate(range_width); + std::swap((*v)[selected], (*v)[last_in_range]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template +inline void Shuffle(internal::Random* random, std::vector* v) { + ShuffleRange(random, 0, static_cast(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual std::string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() {} + + virtual std::string CurrentStackTrace(int max_depth, int skip_count); + virtual void UponLeavingGTest(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[i]; + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i) { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[index]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as an std::string. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo* test_info) { + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } + + GetTestCase(test_info->test_case_name(), + test_info->type_param(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + + // Returns ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* a_current_test_case) { + current_test_case_ = a_current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test cases, and the tests within each test case, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test cases and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector environments_; + + // The vector of TestCases in their original order. It owns the + // elements in the vector. + std::vector test_cases_; + + // Provides a level of indirection for the test case list to allow + // easy shuffling and restoring the test case order. The i-th + // element of this vector is the index of the i-th test case in the + // shuffled order. + std::vector test_case_indices_; + + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestCaseRegistry parameterized_test_registry_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; + + // Index of the last death test case registered. Initially -1. + int last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True iff PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + + // MSVC and C++ Builder define __int64 instead of the standard long long. + typedef unsigned __int64 BiggestConvertible; + const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); + +# else + + typedef unsigned long long BiggestConvertible; // NOLINT + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + + const bool parse_success = *end == '\0' && errno == 0; + + // TODO(vladl@google.com): Convert this to compile time assertion when it is + // available. + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast(parsed); + if (parse_success && static_cast(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const std::string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const std::string& message) { Send(message + "\n"); } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const std::string& host, const std::string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const std::string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const std::string host_name_; + const std::string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static std::string UrlEncode(const char* str); + + StreamingListener(const std::string& host, const std::string& port) + : socket_writer_(new SocketWriter(host, port)) { + Start(); + } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const std::string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + std::string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-port.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-port.cc new file mode 100644 index 0000000..f8a0ad6 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-port.cc @@ -0,0 +1,1277 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/internal/gtest-port.h" + +#include +#include +#include +#include +#include + +#if GTEST_OS_WINDOWS +# include +# include +# include +# include // Used in ThreadLocal. +#else +# include +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_MAC +# include +# include +# include +#endif // GTEST_OS_MAC + +#if GTEST_OS_QNX +# include +# include +# include +#endif // GTEST_OS_QNX + +#if GTEST_OS_AIX +# include +# include +#endif // GTEST_OS_AIX + +#if GTEST_OS_FUCHSIA +# include +# include +#endif // GTEST_OS_FUCHSIA + +#include "gtest/gtest-spi.h" +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" +#include "src/gtest-internal-inl.h" + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_LINUX + +namespace { +template +T ReadProcFileField(const std::string& filename, int field) { + std::string dummy; + std::ifstream file(filename.c_str()); + while (field-- > 0) { + file >> dummy; + } + T output = 0; + file >> output; + return output; +} +} // namespace + +// Returns the number of active threads, or 0 when there is an error. +size_t GetThreadCount() { + const std::string filename = + (Message() << "/proc/" << getpid() << "/stat").GetString(); + return ReadProcFileField(filename, 19); +} + +#elif GTEST_OS_MAC + +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + +#elif GTEST_OS_AIX + +size_t GetThreadCount() { + struct procentry64 entry; + pid_t pid = getpid(); + int status = getprocs64(&entry, sizeof(entry), NULL, 0, &pid, 1); + if (status == 1) { + return entry.pi_thcount; + } else { + return 0; + } +} + +#elif GTEST_OS_FUCHSIA + +size_t GetThreadCount() { + int dummy_buffer; + size_t avail; + zx_status_t status = zx_object_get_info( + zx_process_self(), + ZX_INFO_PROCESS_THREADS, + &dummy_buffer, + 0, + nullptr, + &avail); + if (status == ZX_OK) { + return avail; + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_LINUX + +#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + +void SleepMilliseconds(int n) { + ::Sleep(n); +} + +AutoHandle::AutoHandle() + : handle_(INVALID_HANDLE_VALUE) {} + +AutoHandle::AutoHandle(Handle handle) + : handle_(handle) {} + +AutoHandle::~AutoHandle() { + Reset(); +} + +AutoHandle::Handle AutoHandle::Get() const { + return handle_; +} + +void AutoHandle::Reset() { + Reset(INVALID_HANDLE_VALUE); +} + +void AutoHandle::Reset(HANDLE handle) { + // Resetting with the same handle we already own is invalid. + if (handle_ != handle) { + if (IsCloseable()) { + ::CloseHandle(handle_); + } + handle_ = handle; + } else { + GTEST_CHECK_(!IsCloseable()) + << "Resetting a valid handle to itself is likely a programmer error " + "and thus not allowed."; + } +} + +bool AutoHandle::IsCloseable() const { + // Different Windows APIs may use either of these values to represent an + // invalid handle. + return handle_ != NULL && handle_ != INVALID_HANDLE_VALUE; +} + +Notification::Notification() + : event_(::CreateEvent(NULL, // Default security attributes. + TRUE, // Do not reset automatically. + FALSE, // Initially unset. + NULL)) { // Anonymous event. + GTEST_CHECK_(event_.Get() != NULL); +} + +void Notification::Notify() { + GTEST_CHECK_(::SetEvent(event_.Get()) != FALSE); +} + +void Notification::WaitForNotification() { + GTEST_CHECK_( + ::WaitForSingleObject(event_.Get(), INFINITE) == WAIT_OBJECT_0); +} + +Mutex::Mutex() + : owner_thread_id_(0), + type_(kDynamic), + critical_section_init_phase_(0), + critical_section_(new CRITICAL_SECTION) { + ::InitializeCriticalSection(critical_section_); +} + +Mutex::~Mutex() { + // Static mutexes are leaked intentionally. It is not thread-safe to try + // to clean them up. + // TODO(yukawa): Switch to Slim Reader/Writer (SRW) Locks, which requires + // nothing to clean it up but is available only on Vista and later. + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx + if (type_ == kDynamic) { + ::DeleteCriticalSection(critical_section_); + delete critical_section_; + critical_section_ = NULL; + } +} + +void Mutex::Lock() { + ThreadSafeLazyInit(); + ::EnterCriticalSection(critical_section_); + owner_thread_id_ = ::GetCurrentThreadId(); +} + +void Mutex::Unlock() { + ThreadSafeLazyInit(); + // We don't protect writing to owner_thread_id_ here, as it's the + // caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + owner_thread_id_ = 0; + ::LeaveCriticalSection(critical_section_); +} + +// Does nothing if the current thread holds the mutex. Otherwise, crashes +// with high probability. +void Mutex::AssertHeld() { + ThreadSafeLazyInit(); + GTEST_CHECK_(owner_thread_id_ == ::GetCurrentThreadId()) + << "The current thread is not holding the mutex @" << this; +} + +// Initializes owner_thread_id_ and critical_section_ in static mutexes. +void Mutex::ThreadSafeLazyInit() { + // Dynamic mutexes are initialized in the constructor. + if (type_ == kStatic) { + switch ( + ::InterlockedCompareExchange(&critical_section_init_phase_, 1L, 0L)) { + case 0: + // If critical_section_init_phase_ was 0 before the exchange, we + // are the first to test it and need to perform the initialization. + owner_thread_id_ = 0; + critical_section_ = new CRITICAL_SECTION; + ::InitializeCriticalSection(critical_section_); + // Updates the critical_section_init_phase_ to 2 to signal + // initialization complete. + GTEST_CHECK_(::InterlockedCompareExchange( + &critical_section_init_phase_, 2L, 1L) == + 1L); + break; + case 1: + // Somebody else is already initializing the mutex; spin until they + // are done. + while (::InterlockedCompareExchange(&critical_section_init_phase_, + 2L, + 2L) != 2L) { + // Possibly yields the rest of the thread's time slice to other + // threads. + ::Sleep(0); + } + break; + + case 2: + break; // The mutex is already initialized and ready for use. + + default: + GTEST_CHECK_(false) + << "Unexpected value of critical_section_init_phase_ " + << "while initializing a static mutex."; + } + } +} + +namespace { + +class ThreadWithParamSupport : public ThreadWithParamBase { + public: + static HANDLE CreateThread(Runnable* runnable, + Notification* thread_can_start) { + ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start); + DWORD thread_id; + // TODO(yukawa): Consider to use _beginthreadex instead. + HANDLE thread_handle = ::CreateThread( + NULL, // Default security. + 0, // Default stack size. + &ThreadWithParamSupport::ThreadMain, + param, // Parameter to ThreadMainStatic + 0x0, // Default creation flags. + &thread_id); // Need a valid pointer for the call to work under Win98. + GTEST_CHECK_(thread_handle != NULL) << "CreateThread failed with error " + << ::GetLastError() << "."; + if (thread_handle == NULL) { + delete param; + } + return thread_handle; + } + + private: + struct ThreadMainParam { + ThreadMainParam(Runnable* runnable, Notification* thread_can_start) + : runnable_(runnable), + thread_can_start_(thread_can_start) { + } + scoped_ptr runnable_; + // Does not own. + Notification* thread_can_start_; + }; + + static DWORD WINAPI ThreadMain(void* ptr) { + // Transfers ownership. + scoped_ptr param(static_cast(ptr)); + if (param->thread_can_start_ != NULL) + param->thread_can_start_->WaitForNotification(); + param->runnable_->Run(); + return 0; + } + + // Prohibit instantiation. + ThreadWithParamSupport(); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParamSupport); +}; + +} // namespace + +ThreadWithParamBase::ThreadWithParamBase(Runnable *runnable, + Notification* thread_can_start) + : thread_(ThreadWithParamSupport::CreateThread(runnable, + thread_can_start)) { +} + +ThreadWithParamBase::~ThreadWithParamBase() { + Join(); +} + +void ThreadWithParamBase::Join() { + GTEST_CHECK_(::WaitForSingleObject(thread_.Get(), INFINITE) == WAIT_OBJECT_0) + << "Failed to join the thread with error " << ::GetLastError() << "."; +} + +// Maps a thread to a set of ThreadIdToThreadLocals that have values +// instantiated on that thread and notifies them when the thread exits. A +// ThreadLocal instance is expected to persist until all threads it has +// values on have terminated. +class ThreadLocalRegistryImpl { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { + DWORD current_thread = ::GetCurrentThreadId(); + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(current_thread); + if (thread_local_pos == thread_to_thread_locals->end()) { + thread_local_pos = thread_to_thread_locals->insert( + std::make_pair(current_thread, ThreadLocalValues())).first; + StartWatcherThreadFor(current_thread); + } + ThreadLocalValues& thread_local_values = thread_local_pos->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos == thread_local_values.end()) { + value_pos = + thread_local_values + .insert(std::make_pair( + thread_local_instance, + linked_ptr( + thread_local_instance->NewValueForCurrentThread()))) + .first; + } + return value_pos->second.get(); + } + + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + std::vector > value_holders; + // Clean up the ThreadLocalValues data structure while holding the lock, but + // defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + for (ThreadIdToThreadLocals::iterator it = + thread_to_thread_locals->begin(); + it != thread_to_thread_locals->end(); + ++it) { + ThreadLocalValues& thread_local_values = it->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos != thread_local_values.end()) { + value_holders.push_back(value_pos->second); + thread_local_values.erase(value_pos); + // This 'if' can only be successful at most once, so theoretically we + // could break out of the loop here, but we don't bother doing so. + } + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + static void OnThreadExit(DWORD thread_id) { + GTEST_CHECK_(thread_id != 0) << ::GetLastError(); + std::vector > value_holders; + // Clean up the ThreadIdToThreadLocals data structure while holding the + // lock, but defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(thread_id); + if (thread_local_pos != thread_to_thread_locals->end()) { + ThreadLocalValues& thread_local_values = thread_local_pos->second; + for (ThreadLocalValues::iterator value_pos = + thread_local_values.begin(); + value_pos != thread_local_values.end(); + ++value_pos) { + value_holders.push_back(value_pos->second); + } + thread_to_thread_locals->erase(thread_local_pos); + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + private: + // In a particular thread, maps a ThreadLocal object to its value. + typedef std::map > ThreadLocalValues; + // Stores all ThreadIdToThreadLocals having values in a thread, indexed by + // thread's ID. + typedef std::map ThreadIdToThreadLocals; + + // Holds the thread id and thread handle that we pass from + // StartWatcherThreadFor to WatcherThreadFunc. + typedef std::pair ThreadIdAndHandle; + + static void StartWatcherThreadFor(DWORD thread_id) { + // The returned handle will be kept in thread_map and closed by + // watcher_thread in WatcherThreadFunc. + HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, + FALSE, + thread_id); + GTEST_CHECK_(thread != NULL); + // We need to pass a valid thread ID pointer into CreateThread for it + // to work correctly under Win98. + DWORD watcher_thread_id; + HANDLE watcher_thread = ::CreateThread( + NULL, // Default security. + 0, // Default stack size + &ThreadLocalRegistryImpl::WatcherThreadFunc, + reinterpret_cast(new ThreadIdAndHandle(thread_id, thread)), + CREATE_SUSPENDED, + &watcher_thread_id); + GTEST_CHECK_(watcher_thread != NULL); + // Give the watcher thread the same priority as ours to avoid being + // blocked by it. + ::SetThreadPriority(watcher_thread, + ::GetThreadPriority(::GetCurrentThread())); + ::ResumeThread(watcher_thread); + ::CloseHandle(watcher_thread); + } + + // Monitors exit from a given thread and notifies those + // ThreadIdToThreadLocals about thread termination. + static DWORD WINAPI WatcherThreadFunc(LPVOID param) { + const ThreadIdAndHandle* tah = + reinterpret_cast(param); + GTEST_CHECK_( + ::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0); + OnThreadExit(tah->first); + ::CloseHandle(tah->second); + delete tah; + return 0; + } + + // Returns map of thread local instances. + static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() { + mutex_.AssertHeld(); + static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals; + return map; + } + + // Protects access to GetThreadLocalsMapLocked() and its return value. + static Mutex mutex_; + // Protects access to GetThreadMapLocked() and its return value. + static Mutex thread_map_mutex_; +}; + +Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex); +Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex); + +ThreadLocalValueHolderBase* ThreadLocalRegistry::GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { + return ThreadLocalRegistryImpl::GetValueOnCurrentThread( + thread_local_instance); +} + +void ThreadLocalRegistry::OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + ThreadLocalRegistryImpl::OnThreadLocalDestroyed(thread_local_instance); +} + +#endif // GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast(pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true iff ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != NULL; +} + +// Returns true iff ch belongs to the given classification. Unlike +// similar functions in , these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true iff "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true iff the given atom (specified by escaped and pattern) +// matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +static std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == NULL) { + // TODO(wan@google.com): fix the source file location in the + // assertion failures to match where the regex is used in user + // code. + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True iff ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true iff regex matches a prefix of str. regex must be a +// valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true iff regex matches any substring of str. regex must be +// a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == NULL || str == NULL) + return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast(pattern_)); + free(const_cast(full_pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = NULL; + if (regex != NULL) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} + +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else + char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID + const int captured_fd = mkstemp(name_template); + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(NULL); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() + +static CapturedStream* g_captured_stderr = NULL; +static CapturedStream* g_captured_stdout = NULL; + +// Starts capturing an output stream (stdout/stderr). +static void CaptureStream(int fd, const char* stream_name, + CapturedStream** stream) { + if (*stream != NULL) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +static std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = NULL; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + + + + + +size_t GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +std::string ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +#if GTEST_HAS_DEATH_TEST +static const std::vector* g_injected_test_argvs = NULL; // Owned. + +std::vector GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return GetArgvs(); +} + +void SetInjectableArgvs(const std::vector* new_argvs) { + if (g_injected_test_argvs != new_argvs) delete g_injected_test_argvs; + g_injected_test_argvs = new_argvs; +} + +void SetInjectableArgvs(const std::vector& new_argvs) { + SetInjectableArgvs( + new std::vector(new_argvs.begin(), new_argvs.end())); +} + +#if GTEST_HAS_GLOBAL_STRING +void SetInjectableArgvs(const std::vector< ::string>& new_argvs) { + SetInjectableArgvs( + new std::vector(new_argvs.begin(), new_argvs.end())); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void ClearInjectableArgvs() { + delete g_injected_test_argvs; + g_injected_test_argvs = NULL; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { +#if defined(GTEST_GET_BOOL_FROM_ENV_) + return GTEST_GET_BOOL_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +#endif // defined(GTEST_GET_BOOL_FROM_ENV_) +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { +#if defined(GTEST_GET_INT32_FROM_ENV_) + return GTEST_GET_INT32_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +#endif // defined(GTEST_GET_INT32_FROM_ENV_) +} + +// As a special case for the 'output' flag, if GTEST_OUTPUT is not +// set, we look for XML_OUTPUT_FILE, which is set by the Bazel build +// system. The value of XML_OUTPUT_FILE is a filename without the +// "xml:" prefix of GTEST_OUTPUT. +// Note that this is meant to be called at the call site so it does +// not check that the flag is 'output' +// In essence this checks an env variable called XML_OUTPUT_FILE +// and if it is set we prepend "xml:" to its value, if it not set we return "" +std::string OutputFlagAlsoCheckEnvVar(){ + std::string default_value_for_output_flag = ""; + const char* xml_output_file_env = posix::GetEnv("XML_OUTPUT_FILE"); + if (NULL != xml_output_file_env) { + default_value_for_output_flag = std::string("xml:") + xml_output_file_env; + } + return default_value_for_output_flag; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { +#if defined(GTEST_GET_STRING_FROM_ENV_) + return GTEST_GET_STRING_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +#endif // defined(GTEST_GET_STRING_FROM_ENV_) +} + +} // namespace internal +} // namespace testing diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-printers.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-printers.cc new file mode 100644 index 0000000..eeba17f --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-printers.cc @@ -0,0 +1,458 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include "gtest/gtest-printers.h" +#include +#include +#include +#include // NOLINT +#include +#include "gtest/internal/gtest-port.h" +#include "src/gtest-internal-inl.h" + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexadecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast(c); + return kAsIs; + } else { + ostream::fmtflags flags = os->flags(); + *os << "\\x" << std::hex << std::uppercase + << static_cast(static_cast(c)); + os->flags(flags); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a wchar_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast(c); + + // For more convenience, we print c's code again in hexadecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo(wc, os); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static CharFormat PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; + bool is_previous_hex = false; + CharFormat print_format = kAsIs; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << kQuoteBegin; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + // Remember if any characters required hex escaping. + if (is_previous_hex) { + print_format = kHexEscape; + } + } + *os << "\""; + return print_format; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, std::wcslen(s), os); + } +} +#endif // wchar_t is native + +namespace { + +bool ContainsUnprintableControlCodes(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast(str); + + for (size_t i = 0; i < length; i++) { + unsigned char ch = *s++; + if (std::iscntrl(ch)) { + switch (ch) { + case '\t': + case '\n': + case '\r': + break; + default: + return true; + } + } + } + return false; +} + +bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t<= 0xbf; } + +bool IsValidUTF8(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast(str); + + for (size_t i = 0; i < length;) { + unsigned char lead = s[i++]; + + if (lead <= 0x7f) { + continue; // single-byte character (ASCII) 0..7F + } + if (lead < 0xc2) { + return false; // trail byte or non-shortest form + } else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) { + ++i; // 2-byte character + } else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + // check for non-shortest form and surrogate + (lead != 0xe0 || s[i] >= 0xa0) && + (lead != 0xed || s[i] < 0xa0)) { + i += 2; // 3-byte character + } else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + IsUTF8TrailByte(s[i + 2]) && + // check for non-shortest form + (lead != 0xf0 || s[i] >= 0x90) && + (lead != 0xf4 || s[i] < 0x90)) { + i += 3; // 4-byte character + } else { + return false; + } + } + return true; +} + +void ConditionalPrintAsText(const char* str, size_t length, ostream* os) { + if (!ContainsUnprintableControlCodes(str, length) && + IsValidUTF8(str, length)) { + *os << "\n As Text: \"" << str << "\""; + } +} + +} // anonymous namespace + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) { + if (GTEST_FLAG(print_utf8)) { + ConditionalPrintAsText(s.data(), s.size(), os); + } + } +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) { + if (GTEST_FLAG(print_utf8)) { + ConditionalPrintAsText(s.data(), s.size(), os); + } + } +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-test-part.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-test-part.cc new file mode 100644 index 0000000..4c5f2e3 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-test-part.cc @@ -0,0 +1,102 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// The Google C++ Testing and Mocking Framework (Google Test) + +#include "gtest/gtest-test-part.h" +#include "src/gtest-internal-inl.h" + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == NULL ? message : + std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os + << result.file_name() << ":" << result.line_number() << ": " + << (result.type() == TestPartResult::kSuccess ? "Success" : + result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[index]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-typed-test.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-typed-test.cc new file mode 100644 index 0000000..b358243 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest-typed-test.cc @@ -0,0 +1,119 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/gtest-typed-test.h" + +#include "gtest/gtest.h" + +namespace testing { +namespace internal { + +#if GTEST_HAS_TYPED_TEST_P + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +static std::vector SplitIntoTestNames(const char* src) { + std::vector name_vec; + src = SkipSpaces(src); + for (; src != NULL; src = SkipComma(src)) { + name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src))); + } + return name_vec; +} + +// Verifies that registered_tests match the test names in +// registered_tests_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestCasePState::VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests) { + typedef RegisteredTestsMap::const_iterator RegisteredTestIter; + registered_ = true; + + std::vector name_vec = SplitIntoTestNames(registered_tests); + + Message errors; + + std::set tests; + for (std::vector::const_iterator name_it = name_vec.begin(); + name_it != name_vec.end(); ++name_it) { + const std::string& name = *name_it; + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + bool found = false; + for (RegisteredTestIter it = registered_tests_.begin(); + it != registered_tests_.end(); + ++it) { + if (name == it->first) { + found = true; + break; + } + } + + if (found) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test case.\n"; + } + } + + for (RegisteredTestIter it = registered_tests_.begin(); + it != registered_tests_.end(); + ++it) { + if (tests.count(it->first) == 0) { + errors << "You forgot to list test " << it->first << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +#endif // GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest.cc new file mode 100644 index 0000000..5406392 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest.cc @@ -0,0 +1,5845 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing and Mocking Framework (Google Test) + +#include "gtest/gtest.h" +#include "gtest/internal/custom/gtest.h" +#include "gtest/gtest-spi.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include // NOLINT +#include +#include + +#if GTEST_OS_LINUX + +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +# include // NOLINT +# include // NOLINT +# include // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include + +#elif GTEST_OS_SYMBIAN +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +#elif GTEST_OS_ZOS +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include // NOLINT +# undef min + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +// MinGW has gettimeofday() but not _ftime64(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +// TODO(kenton@google.com): There are other ways to get the time on +// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW +// supports these. consider using them instead. +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# undef min + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# include // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +#endif + +#include "src/gtest-internal-inl.h" + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test case name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test case whose name matches this filter is considered a death +// test case and will be run before test cases whose name doesn't +// match this filter. +static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output format. +static const char kDefaultOutputFormat[] = "xml"; +// The default output file. +static const char kDefaultOutputFile[] = "test_detail"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +bool g_help_flag = false; + +} // namespace internal + +static const char* GetDefaultFilter() { +#ifdef GTEST_TEST_FILTER_ENV_VAR_ + const char* const testbridge_test_only = getenv(GTEST_TEST_FILTER_ENV_VAR_); + if (testbridge_test_only != NULL) { + return testbridge_test_only; + } +#endif // GTEST_TEST_FILTER_ENV_VAR_ + return kUniversalFilter; +} + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool_( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True iff " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +// The net priority order after flag processing is thus: +// --gtest_output command line flag +// GTEST_OUTPUT environment variable +// XML_OUTPUT_FILE environment variable +// '' +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", + internal::OutputFlagAlsoCheckEnvVar().c_str()), + "A format (defaults to \"xml\" but can be specified to be \"json\"), " + "optionally followed by a colon and an output file name or directory. " + "A directory is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + print_time, + internal::BoolFromGTestEnv("print_time", true), + "True iff " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_bool_( + print_utf8, + internal::BoolFromGTestEnv("print_utf8", true), + "True iff " GTEST_NAME_ + " prints UTF8 characters as text."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_( + show_internal_stack_frames, false, + "True iff " GTEST_NAME_ " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_( + shuffle, + internal::BoolFromGTestEnv("shuffle", false), + "True iff " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise. For use with an external test framework."); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DEFINE_string_( + flagfile, + internal::StringFromGTestEnv("flagfile", ""), + "This flag specifies the flagfile to read command-line flags from."); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + // Use wider types than necessary to prevent unsigned overflow diagnostics. + state_ = static_cast(1103515245ULL*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +static bool GTestIsInitialized() { return GetArgvs().size() > 0; } + +// Iterates over a vector of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const std::vector& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Mutex for linked pointers. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// A copy of all command line arguments. Set by InitGoogleTest(). +::std::vector g_argvs; + +::std::vector GetArgvs() { +#if defined(GTEST_CUSTOM_GET_ARGVS_) + // GTEST_CUSTOM_GET_ARGVS_() may return a container of std::string or + // ::string. This code converts it to the appropriate type. + const auto& custom = GTEST_CUSTOM_GET_ARGVS_(); + return ::std::vector(custom.begin(), custom.end()); +#else // defined(GTEST_CUSTOM_GET_ARGVS_) + return g_argvs; +#endif // defined(GTEST_CUSTOM_GET_ARGVS_) +} + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS + result.Set(FilePath(GetArgvs()[0]).RemoveExtension("exe")); +#else + result.Set(FilePath(GetArgvs()[0])); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return std::string(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return ""; + + std::string format = GetOutputFormat(); + if (format.empty()) + format = std::string(kDefaultOutputFormat); + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return internal::FilePath::MakeFileName( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile), 0, + format.c_str()).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + // TODO(wan@google.com): on Windows \some\path is not an absolute + // path (as its meaning depends on the current drive), yet the + // following logic for turning it into an absolute path is wrong. + // Fix it. + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { + const char *cur_pattern = filter; + for (;;) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +static AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const std::string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker::SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const std::string& substr) + : results_(results), type_(type), substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return CountIf(test_cases_, TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return CountIf(test_cases_, TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return static_cast(test_cases_.size()); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return CountIf(test_cases_, ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + return os_stack_trace_getter()->CurrentStackTrace( + static_cast(GTEST_FLAG(stack_trace_depth)), + skip_count + 1 + // Skips the user-specified number of frames plus this function + // itself. + ); // NOLINT +} + +// Returns the current time in milliseconds. +TimeInMillis GetTimeInMillis() { +#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) + // Difference between 1970-01-01 and 1601-01-01 in milliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = + static_cast(116444736UL) * 100000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton@google.com): Shouldn't this just use + // GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ + __timeb64 now; + + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton@google.com): Use GetTickCount()? Or use + // SystemTimeToFileTime() + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) + _ftime64(&now); + GTEST_DISABLE_MSC_WARNINGS_POP_() + + return static_cast(now.time) * 1000 + now.millitm; +#elif GTEST_HAS_GETTIMEOFDAY_ + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +# error "Don't know how to get the current time on your system." +#endif +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return NULL; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, + NULL, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return NULL; + const int ansi_length = + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + NULL, 0, NULL, NULL); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + ansi, ansi_length, NULL, NULL); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != NULL ? + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { +} + +// Swaps two AssertionResults. +void AssertionResult::swap(AssertionResult& other) { + using std::swap; + swap(success_, other.success_); + swap(message_, other.message_); +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != NULL) + negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +namespace edit_distance { +std::vector CalculateOptimalEdits(const std::vector& left, + const std::vector& right) { + std::vector > costs( + left.size() + 1, std::vector(right.size() + 1)); + std::vector > best_move( + left.size() + 1, std::vector(right.size() + 1)); + + // Populate for empty right. + for (size_t l_i = 0; l_i < costs.size(); ++l_i) { + costs[l_i][0] = static_cast(l_i); + best_move[l_i][0] = kRemove; + } + // Populate for empty left. + for (size_t r_i = 1; r_i < costs[0].size(); ++r_i) { + costs[0][r_i] = static_cast(r_i); + best_move[0][r_i] = kAdd; + } + + for (size_t l_i = 0; l_i < left.size(); ++l_i) { + for (size_t r_i = 0; r_i < right.size(); ++r_i) { + if (left[l_i] == right[r_i]) { + // Found a match. Consume it. + costs[l_i + 1][r_i + 1] = costs[l_i][r_i]; + best_move[l_i + 1][r_i + 1] = kMatch; + continue; + } + + const double add = costs[l_i + 1][r_i]; + const double remove = costs[l_i][r_i + 1]; + const double replace = costs[l_i][r_i]; + if (add < remove && add < replace) { + costs[l_i + 1][r_i + 1] = add + 1; + best_move[l_i + 1][r_i + 1] = kAdd; + } else if (remove < add && remove < replace) { + costs[l_i + 1][r_i + 1] = remove + 1; + best_move[l_i + 1][r_i + 1] = kRemove; + } else { + // We make replace a little more expensive than add/remove to lower + // their priority. + costs[l_i + 1][r_i + 1] = replace + 1.00001; + best_move[l_i + 1][r_i + 1] = kReplace; + } + } + } + + // Reconstruct the best path. We do it in reverse order. + std::vector best_path; + for (size_t l_i = left.size(), r_i = right.size(); l_i > 0 || r_i > 0;) { + EditType move = best_move[l_i][r_i]; + best_path.push_back(move); + l_i -= move != kAdd; + r_i -= move != kRemove; + } + std::reverse(best_path.begin(), best_path.end()); + return best_path; +} + +namespace { + +// Helper class to convert string into ids with deduplication. +class InternalStrings { + public: + size_t GetId(const std::string& str) { + IdMap::iterator it = ids_.find(str); + if (it != ids_.end()) return it->second; + size_t id = ids_.size(); + return ids_[str] = id; + } + + private: + typedef std::map IdMap; + IdMap ids_; +}; + +} // namespace + +std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right) { + std::vector left_ids, right_ids; + { + InternalStrings intern_table; + for (size_t i = 0; i < left.size(); ++i) { + left_ids.push_back(intern_table.GetId(left[i])); + } + for (size_t i = 0; i < right.size(); ++i) { + right_ids.push_back(intern_table.GetId(right[i])); + } + } + return CalculateOptimalEdits(left_ids, right_ids); +} + +namespace { + +// Helper class that holds the state for one hunk and prints it out to the +// stream. +// It reorders adds/removes when possible to group all removes before all +// adds. It also adds the hunk header before printint into the stream. +class Hunk { + public: + Hunk(size_t left_start, size_t right_start) + : left_start_(left_start), + right_start_(right_start), + adds_(), + removes_(), + common_() {} + + void PushLine(char edit, const char* line) { + switch (edit) { + case ' ': + ++common_; + FlushEdits(); + hunk_.push_back(std::make_pair(' ', line)); + break; + case '-': + ++removes_; + hunk_removes_.push_back(std::make_pair('-', line)); + break; + case '+': + ++adds_; + hunk_adds_.push_back(std::make_pair('+', line)); + break; + } + } + + void PrintTo(std::ostream* os) { + PrintHeader(os); + FlushEdits(); + for (std::list >::const_iterator it = + hunk_.begin(); + it != hunk_.end(); ++it) { + *os << it->first << it->second << "\n"; + } + } + + bool has_edits() const { return adds_ || removes_; } + + private: + void FlushEdits() { + hunk_.splice(hunk_.end(), hunk_removes_); + hunk_.splice(hunk_.end(), hunk_adds_); + } + + // Print a unified diff header for one hunk. + // The format is + // "@@ -, +, @@" + // where the left/right parts are omitted if unnecessary. + void PrintHeader(std::ostream* ss) const { + *ss << "@@ "; + if (removes_) { + *ss << "-" << left_start_ << "," << (removes_ + common_); + } + if (removes_ && adds_) { + *ss << " "; + } + if (adds_) { + *ss << "+" << right_start_ << "," << (adds_ + common_); + } + *ss << " @@\n"; + } + + size_t left_start_, right_start_; + size_t adds_, removes_, common_; + std::list > hunk_, hunk_adds_, hunk_removes_; +}; + +} // namespace + +// Create a list of diff hunks in Unified diff format. +// Each hunk has a header generated by PrintHeader above plus a body with +// lines prefixed with ' ' for no change, '-' for deletion and '+' for +// addition. +// 'context' represents the desired unchanged prefix/suffix around the diff. +// If two hunks are close enough that their contexts overlap, then they are +// joined into one hunk. +std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context) { + const std::vector edits = CalculateOptimalEdits(left, right); + + size_t l_i = 0, r_i = 0, edit_i = 0; + std::stringstream ss; + while (edit_i < edits.size()) { + // Find first edit. + while (edit_i < edits.size() && edits[edit_i] == kMatch) { + ++l_i; + ++r_i; + ++edit_i; + } + + // Find the first line to include in the hunk. + const size_t prefix_context = std::min(l_i, context); + Hunk hunk(l_i - prefix_context + 1, r_i - prefix_context + 1); + for (size_t i = prefix_context; i > 0; --i) { + hunk.PushLine(' ', left[l_i - i].c_str()); + } + + // Iterate the edits until we found enough suffix for the hunk or the input + // is over. + size_t n_suffix = 0; + for (; edit_i < edits.size(); ++edit_i) { + if (n_suffix >= context) { + // Continue only if the next hunk is very close. + std::vector::const_iterator it = edits.begin() + edit_i; + while (it != edits.end() && *it == kMatch) ++it; + if (it == edits.end() || (it - edits.begin()) - edit_i >= context) { + // There is no next edit or it is too far away. + break; + } + } + + EditType edit = edits[edit_i]; + // Reset count when a non match is found. + n_suffix = edit == kMatch ? n_suffix + 1 : 0; + + if (edit == kMatch || edit == kRemove || edit == kReplace) { + hunk.PushLine(edit == kMatch ? ' ' : '-', left[l_i].c_str()); + } + if (edit == kAdd || edit == kReplace) { + hunk.PushLine('+', right[r_i].c_str()); + } + + // Advance indices, depending on edit type. + l_i += edit != kAdd; + r_i += edit != kRemove; + } + + if (!hunk.has_edits()) { + // We are done. We don't want this hunk. + break; + } + + hunk.PrintTo(&ss); + } + return ss.str(); +} + +} // namespace edit_distance + +namespace { + +// The string representation of the values received in EqFailure() are already +// escaped. Split them on escaped '\n' boundaries. Leave all other escaped +// characters the same. +std::vector SplitEscapedString(const std::string& str) { + std::vector lines; + size_t start = 0, end = str.size(); + if (end > 2 && str[0] == '"' && str[end - 1] == '"') { + ++start; + --end; + } + bool escaped = false; + for (size_t i = start; i + 1 < end; ++i) { + if (escaped) { + escaped = false; + if (str[i] == 'n') { + lines.push_back(str.substr(start, i - start - 1)); + start = i + 1; + } + } else { + escaped = str[i] == '\\'; + } + } + lines.push_back(str.substr(start, end - start)); + return lines; +} + +} // namespace + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// lhs_expression: "foo" +// rhs_expression: "bar" +// lhs_value: "5" +// rhs_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string "Ignoring case" will +// be inserted into the message. +AssertionResult EqFailure(const char* lhs_expression, + const char* rhs_expression, + const std::string& lhs_value, + const std::string& rhs_value, + bool ignoring_case) { + Message msg; + msg << "Expected equality of these values:"; + msg << "\n " << lhs_expression; + if (lhs_value != lhs_expression) { + msg << "\n Which is: " << lhs_value; + } + msg << "\n " << rhs_expression; + if (rhs_value != rhs_expression) { + msg << "\n Which is: " << rhs_value; + } + + if (ignoring_case) { + msg << "\nIgnoring case"; + } + + if (!lhs_value.empty() && !rhs_value.empty()) { + const std::vector lhs_lines = + SplitEscapedString(lhs_value); + const std::vector rhs_lines = + SplitEscapedString(rhs_value); + if (lhs_lines.size() > 1 || rhs_lines.size() > 1) { + msg << "\nWith diff:\n" + << edit_distance::CreateUnifiedDiff(lhs_lines, rhs_lines); + } + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + BiggestInt lhs, + BiggestInt rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* lhs_expression, + const char* rhs_expression, + const char* lhs, + const char* rhs) { + if (String::CStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* lhs_expression, + const char* rhs_expression, + const char* lhs, + const char* rhs) { + if (String::CaseInsensitiveCStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_TV_TITLE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have up to 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const UInt32 mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) ? + (((first & mask) << 10) | (second & mask)) + 0x10000 : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + static_cast(first); +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + UInt32 unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* lhs_expression, + const char* rhs_expression, + const wchar_t* lhs, + const wchar_t* rhs) { + if (String::WideCStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == NULL) + return rhs == NULL; + if (rhs == NULL) + return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(*lhs++); + right = towlower(*rhs++); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(2 * (end - start)); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(i); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(i); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properites_mutex_); + const std::vector::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +static bool ValidateTestPropertyName( + const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true iff the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true iff the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the states of all flags. +Test::Test() + : gtest_flag_saver_(new GTEST_FLAG_SAVER_) { +} + +// The d'tor restores the states of all flags. The actual work is +// done by the d'tor of the gtest_flag_saver_ field, and thus not +// visible here. +Test::~Test() { +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + NULL, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // Both TEST and TEST_F appear in same test case, which is incorrect. + // Tell the user how to fix this. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // Two fixture classes with the same name appear in two different + // namespaces, which is not allowed. Tell the user how to fix this. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const AssertionException&) { // NOLINT + // This failure was reported already. + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); + } + return static_cast(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true iff the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, + const char* a_type_param, + const char* a_value_param, + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + location_(a_code_location), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + CodeLocation code_location, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, type_param, value_param, + code_location, fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +void ReportInvalidTestCaseType(const char* test_case_name, + CodeLocation code_location) { + Message errors; + errors + << "Attempted redefinition of test case " << test_case_name << ".\n" + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " << test_case_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test cases."; + + GTEST_LOG_(ERROR) << FormatFileLocation(code_location.file.c_str(), + code_location.line) + << " " << errors.GetString(); +} +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && test_info->name() == name_; + } + + private: + std::string name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + parameterized_tests_registered_ = true; + } +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TimeInMillis start = internal::GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + + result_.set_elapsed_time(internal::GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. +int TestCase::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return static_cast(test_info_list_.size()); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* a_name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestCase::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestCase::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast(test_indices_.size())); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + repeater->OnTestCaseStart(*this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + } + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + + repeater->OnTestCaseEnd(*this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test case. +void TestCase::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestCase::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static std::string FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter + +enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW + +// Returns the character attribute for the given color. +static WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +static int GetBitOffset(WORD color_mask) { + if (color_mask == 0) return 0; + + int bitOffset = 0; + while ((color_mask & 1) == 0) { + color_mask >>= 1; + ++bitOffset; + } + return bitOffset; +} + +static WORD GetNewColor(GTestColor color, WORD old_color_attrs) { + // Let's reuse the BG + static const WORD background_mask = BACKGROUND_BLUE | BACKGROUND_GREEN | + BACKGROUND_RED | BACKGROUND_INTENSITY; + static const WORD foreground_mask = FOREGROUND_BLUE | FOREGROUND_GREEN | + FOREGROUND_RED | FOREGROUND_INTENSITY; + const WORD existing_bg = old_color_attrs & background_mask; + + WORD new_color = + GetColorAttribute(color) | existing_bg | FOREGROUND_INTENSITY; + static const int bg_bitOffset = GetBitOffset(background_mask); + static const int fg_bitOffset = GetBitOffset(foreground_mask); + + if (((new_color & background_mask) >> bg_bitOffset) == + ((new_color & foreground_mask) >> fg_bitOffset)) { + new_color ^= FOREGROUND_INTENSITY; // invert intensity + } + return new_color; +} + +#else + +// Returns the ANSI color code for the given color. COLOR_DEFAULT is +// an invalid input. +static const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + default: return NULL; + }; +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "tmux") || + String::CStringEquals(term, "tmux-256color") || + String::CStringEquals(term, "rxvt-unicode") || + String::CStringEquals(term, "rxvt-unicode-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +static void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || \ + GTEST_OS_IOS || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT + const bool use_color = AlwaysFalse(); +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != COLOR_DEFAULT); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + const WORD new_color = GetNewColor(color, old_color_attrs); + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, new_color); + + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gtest_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +static void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the TestEventListener class. + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME_, filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %d of %s.\n", + static_cast(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(COLOR_YELLOW, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == NULL) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_info.test_case_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + // If the test part succeeded, we don't need to do anything. + if (result.type() == TestPartResult::kSuccess) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_info.test_case_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s (%s ms total)\n\n", + counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + if (!test_info.should_run() || test_info.result()->Passed()) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s", test_case.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + int num_failures = unit_test.failed_test_count(); + if (!unit_test.Passed()) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + PrintFailedTests(unit_test); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + virtual ~TestEventRepeater(); + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + virtual void OnTestProgramStart(const UnitTest& unit_test); + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& unit_test); + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + i); + return listener; + } + } + + return NULL; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { + listeners_[i]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // Streams an XML representation of the test properties of a TestResult + // object. + static void OutputXmlTestProperties(std::ostream* stream, + const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + GTEST_LOG_(FATAL) << "XML output file may not be null"; + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + xmlout = posix::FOpen(output_file_.c_str(), "w"); + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + + GTEST_LOG_(FATAL) << "Unable to open file \"" << output_file_ << "\""; + } + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// ... +// ... +// ... +// <-- individual assertion failures +// +// +// + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << (static_cast(ms) * 1e-3); + return ss.str(); +} + +static bool PortableLocaltime(time_t seconds, struct tm* out) { +#if defined(_MSC_VER) + return localtime_s(out, &seconds) == 0; +#elif defined(__MINGW32__) || defined(__MINGW64__) + // MINGW provides neither localtime_r nor localtime_s, but uses + // Windows' localtime(), which has a thread-local tm buffer. + struct tm* tm_ptr = localtime(&seconds); // NOLINT + if (tm_ptr == NULL) + return false; + *out = *tm_ptr; + return true; +#else + return localtime_r(&seconds, out) != NULL; +#endif +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << ""); + if (next_segment != NULL) { + stream->write( + segment, static_cast(next_segment - segment)); + *stream << "]]>]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + + if (test_info.is_in_another_shard()) { + return; + } + + *stream << " \n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string summary = location + "\n" + part.summary(); + *stream << " "; + const std::string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } + } + + if (failures == 0 && result.test_property_count() == 0) { + *stream << " />\n"; + } else { + if (failures == 0) { + *stream << ">\n"; + } + OutputXmlTestProperties(stream, result); + *stream << " \n"; + } +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; + + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + *stream << " \n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +void XmlUnitTestResultPrinter::OutputXmlTestProperties( + std::ostream* stream, const TestResult& result) { + const std::string kProperties = "properties"; + const std::string kProperty = "property"; + + if (result.test_property_count() <= 0) { + return; + } + + *stream << "<" << kProperties << ">\n"; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + *stream << "<" << kProperty; + *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\""; + *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\""; + *stream << "/>\n"; + } + *stream << "\n"; +} + +// End XmlUnitTestResultPrinter + + +// This class generates an JSON output file. +class JsonUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit JsonUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Returns an JSON-escaped copy of the input string str. + static std::string EscapeJson(const std::string& str); + + //// Verifies that the given attribute belongs to the given element and + //// streams the attribute as JSON. + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma = true); + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma = true); + + // Streams a JSON representation of a TestInfo object. + static void OutputJsonTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints a JSON representation of a TestCase object + static void PrintJsonTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints a JSON summary of unit_test to output stream out. + static void PrintJsonUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as + // a JSON dictionary. + static std::string TestPropertiesAsJson(const TestResult& result, + const std::string& indent); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(JsonUnitTestResultPrinter); +}; + +// Creates a new JsonUnitTestResultPrinter. +JsonUnitTestResultPrinter::JsonUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.empty()) { + GTEST_LOG_(FATAL) << "JSON output file may not be null"; + } +} + +void JsonUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* jsonout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + jsonout = posix::FOpen(output_file_.c_str(), "w"); + } + if (jsonout == NULL) { + // TODO(phosek): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + GTEST_LOG_(FATAL) << "Unable to open file \"" + << output_file_ << "\""; + } + std::stringstream stream; + PrintJsonUnitTest(&stream, unit_test); + fprintf(jsonout, "%s", StringStreamToString(&stream).c_str()); + fclose(jsonout); +} + +// Returns an JSON-escaped copy of the input string str. +std::string JsonUnitTestResultPrinter::EscapeJson(const std::string& str) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '\\': + case '"': + case '/': + m << '\\' << ch; + break; + case '\b': + m << "\\b"; + break; + case '\t': + m << "\\t"; + break; + case '\n': + m << "\\n"; + break; + case '\f': + m << "\\f"; + break; + case '\r': + m << "\\r"; + break; + default: + if (ch < ' ') { + m << "\\u00" << String::FormatByte(static_cast(ch)); + } else { + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// The following routines generate an JSON representation of a UnitTest +// object. + +// Formats the given time in milliseconds as seconds. +static std::string FormatTimeInMillisAsDuration(TimeInMillis ms) { + ::std::stringstream ss; + ss << (static_cast(ms) * 1e-3) << "s"; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the +// RFC3339 format, without the timezone information. +static std::string FormatEpochTimeInMillisAsRFC3339(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec) + "Z"; +} + +static inline std::string Indent(int width) { + return std::string(width, ' '); +} + +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; + + *stream << indent << "\"" << name << "\": \"" << EscapeJson(value) << "\""; + if (comma) + *stream << ",\n"; +} + +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; + + *stream << indent << "\"" << name << "\": " << StreamableToString(value); + if (comma) + *stream << ",\n"; +} + +// Prints a JSON representation of a TestInfo object. +void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + const std::string kIndent = Indent(10); + + *stream << Indent(8) << "{\n"; + OutputJsonKey(stream, kTestcase, "name", test_info.name(), kIndent); + + if (test_info.value_param() != NULL) { + OutputJsonKey(stream, kTestcase, "value_param", + test_info.value_param(), kIndent); + } + if (test_info.type_param() != NULL) { + OutputJsonKey(stream, kTestcase, "type_param", test_info.type_param(), + kIndent); + } + + OutputJsonKey(stream, kTestcase, "status", + test_info.should_run() ? "RUN" : "NOTRUN", kIndent); + OutputJsonKey(stream, kTestcase, "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), kIndent); + OutputJsonKey(stream, kTestcase, "classname", test_case_name, kIndent, false); + *stream << TestPropertiesAsJson(result, kIndent); + + int failures = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + *stream << ",\n"; + if (++failures == 1) { + *stream << kIndent << "\"" << "failures" << "\": [\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string message = EscapeJson(location + "\n" + part.message()); + *stream << kIndent << " {\n" + << kIndent << " \"failure\": \"" << message << "\",\n" + << kIndent << " \"type\": \"\"\n" + << kIndent << " }"; + } + } + + if (failures > 0) + *stream << "\n" << kIndent << "]"; + *stream << "\n" << Indent(8) << "}"; +} + +// Prints an JSON representation of a TestCase object +void JsonUnitTestResultPrinter::PrintJsonTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + const std::string kIndent = Indent(6); + + *stream << Indent(4) << "{\n"; + OutputJsonKey(stream, kTestsuite, "name", test_case.name(), kIndent); + OutputJsonKey(stream, kTestsuite, "tests", test_case.reportable_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuite, "failures", test_case.failed_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuite, "disabled", + test_case.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuite, "errors", 0, kIndent); + OutputJsonKey(stream, kTestsuite, "time", + FormatTimeInMillisAsDuration(test_case.elapsed_time()), kIndent, + false); + *stream << TestPropertiesAsJson(test_case.ad_hoc_test_result(), kIndent) + << ",\n"; + + *stream << kIndent << "\"" << kTestsuite << "\": [\n"; + + bool comma = false; + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + OutputJsonTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + } + *stream << "\n" << kIndent << "]\n" << Indent(4) << "}"; +} + +// Prints a JSON summary of unit_test to output stream out. +void JsonUnitTestResultPrinter::PrintJsonUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + const std::string kIndent = Indent(2); + *stream << "{\n"; + + OutputJsonKey(stream, kTestsuites, "tests", unit_test.reportable_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "failures", unit_test.failed_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "disabled", + unit_test.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuites, "errors", 0, kIndent); + if (GTEST_FLAG(shuffle)) { + OutputJsonKey(stream, kTestsuites, "random_seed", unit_test.random_seed(), + kIndent); + } + OutputJsonKey(stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsRFC3339(unit_test.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuites, "time", + FormatTimeInMillisAsDuration(unit_test.elapsed_time()), kIndent, + false); + + *stream << TestPropertiesAsJson(unit_test.ad_hoc_test_result(), kIndent) + << ",\n"; + + OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent); + *stream << kIndent << "\"" << kTestsuites << "\": [\n"; + + bool comma = false; + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + PrintJsonTestCase(stream, *unit_test.GetTestCase(i)); + } + } + + *stream << "\n" << kIndent << "]\n" << "}\n"; +} + +// Produces a string representing the test properties in a result as +// a JSON dictionary. +std::string JsonUnitTestResultPrinter::TestPropertiesAsJson( + const TestResult& result, const std::string& indent) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << ",\n" << indent << "\"" << property.key() << "\": " + << "\"" << EscapeJson(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End JsonUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +std::string StreamingListener::UrlEncode(const char* str) { + std::string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// class OsStackTraceGetter + +const char* const OsStackTraceGetterInterface::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +std::string OsStackTraceGetter::CurrentStackTrace(int /*max_depth*/, + int /*skip_count*/) { + return ""; +} + +void OsStackTraceGetter::UponLeavingGTest() {} + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath ? + premature_exit_filepath : "") { + // If a path to the premature-exit file is specified... + if (!premature_exit_filepath_.empty()) { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (!premature_exit_filepath_.empty()) { + int retval = remove(premature_exit_filepath_.c_str()); + if (retval) { + GTEST_LOG_(ERROR) << "Failed to remove premature exit filepath \"" + << premature_exit_filepath_ << "\" with error " + << retval; + } + } + } + + private: + const std::string premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(NULL), + default_xml_generator_(NULL) { +} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = NULL; + else if (listener == default_xml_generator_) + default_xml_generator_ = NULL; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. + + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +} + +// Gets the number of successful test cases. +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_case_count(); +} + +// Gets the number of failed test cases. +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_case_count(); +} + +// Gets the number of all test cases. +int UnitTest::total_test_case_count() const { + return impl()->total_test_case_count(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTest::test_case_to_run_count() const { + return impl()->test_case_to_run_count(); +} + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true iff the unit test passed (i.e. all test cases passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true iff the unit test failed (i.e. some test case failed +// or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +TestCase* UnitTest::GetMutableTestCase(int i) { + return impl()->GetMutableTestCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (int i = static_cast(impl_->gtest_trace_stack().size()); + i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#elif (!defined(__native_client__)) && \ + ((defined(__clang__) || defined(__GNUC__)) && \ + (defined(__x86_64__) || defined(__i386__))) + // with clang/gcc we can achieve the same effect on x86 by invoking int3 + asm("int3"); +#else + // Dereference NULL through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: Symbian doesn't implement abort() well, and some debuggers + // don't correctly trap abort(). + *static_cast(NULL) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_HAS_SEH + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + // + // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. + // Users of prior VC versions shall suffer the agony and pain of + // clicking through the countless debug dialogs. + // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // debug mode when compiled with VC 7.1 or lower. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. +# endif + } +#endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +// Returns ParameterizedTestCaseRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestCaseRegistry& + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */) + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), + GTEST_DISABLE_MSC_WARNINGS_POP_() + global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), + parameterized_test_registry_(), + parameterized_tests_registered_(false), + last_death_test_case_(-1), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + os_stack_trace_getter_(NULL), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + ForEach(test_cases_, internal::Delete); + + // Deletes every Environment. + ForEach(environments_, internal::Delete); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != NULL) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format == "json") { + listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + GTEST_LOG_(WARNING) << "WARNING: unrecognized output format \"" + << output_format << "\" ignored."; + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + GTEST_LOG_(WARNING) << "unrecognized streaming target \"" << target + << "\" ignored."; + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) + // Register to send notifications about key process state changes. + listeners()->Append(new GTEST_CUSTOM_TEST_EVENT_LISTENER_()); +#endif // defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + } +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const std::string& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + const std::vector::const_iterator test_case = + std::find_if(test_cases_.begin(), test_cases_.end(), + TestCaseNameIs(test_case_name)); + + if (test_case != test_cases_.end()) + return *test_case; + + // No. Let's create one. + TestCase* const new_test_case = + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (internal::UnitTestOptions::MatchesFilter(test_case_name, + kDeathTestCaseFilter)) { + // Yes. Inserts the test case after the last death test case + // defined so far. This only works when the test cases haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_case_; + test_cases_.insert(test_cases_.begin() + last_death_test_case_, + new_test_case); + } else { + // No. Appends to the end of the list. + test_cases_.push_back(new_test_case); + } + + test_case_indices_.push_back(static_cast(test_case_indices_.size())); + return new_test_case; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // True iff Google Test is initialized before RUN_ALL_TESTS() is called. + const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized(); + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_) + if (in_subprocess_for_death_test) { + GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_(); + } +# endif // defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_) +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True iff at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + const TimeInMillis start = GetTimeInMillis(); + + // Shuffles test cases and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(random_seed_); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_case_count(); + test_index++) { + GetMutableTestCase(test_index)->Run(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + if (!gtest_is_initialized_before_run_all_tests) { + ColoredPrintf( + COLOR_RED, + "\nIMPORTANT NOTICE - DO NOT IGNORE:\n" + "This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_ + "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_ + " will start to enforce the valid usage. " + "Please fix it ASAP, or IT WILL START TO FAIL.\n"); // NOLINT +#if GTEST_FOR_GOOGLE_ + ColoredPrintf(COLOR_RED, + "For more details, see http://wiki/Main/ValidGUnitMain.\n"); +#endif // GTEST_FOR_GOOGLE_ + } + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md +// . Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (size_t i = 0; i < test_cases_.size(); i++) { + TestCase* const test_case = test_cases_[i]; + const std::string &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + TestInfo* const test_info = test_case->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestFilter. + const bool is_disabled = + internal::UnitTestOptions::MatchesFilter(test_case_name, + kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter(test_name, + kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_in_another_shard = + shard_tests != IGNORE_SHARDING_PROTOCOL && + !ShouldRunTestOnShard(total_shards, shard_index, num_runnable_tests); + test_info->is_in_another_shard_ = is_in_another_shard; + const bool is_selected = is_runnable && !is_in_another_shard; + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_case->set_should_run(test_case->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (size_t i = 0; i < test_cases_.size(); i++) { + const TestCase* const test_case = test_cases_[i]; + bool printed_test_case_name = false; + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + const TestInfo* const test_info = + test_case->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_case_name) { + printed_test_case_name = true; + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { +#ifdef GTEST_OS_STACK_TRACE_GETTER_ + os_stack_trace_getter_ = new GTEST_OS_STACK_TRACE_GETTER_; +#else + os_stack_trace_getter_ = new OsStackTraceGetter; +#endif // GTEST_OS_STACK_TRACE_GETTER_ + } + + return os_stack_trace_getter_; +} + +// Returns the most specific TestResult currently running. +TestResult* UnitTestImpl::current_test_result() { + if (current_test_info_ != NULL) { + return ¤t_test_info_->result_; + } + if (current_test_case_ != NULL) { + return ¤t_test_case_->ad_hoc_test_result_; + } + return &ad_hoc_test_result_; +} + +// Shuffles all test cases, and the tests within each test case, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test cases. + ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); + + // Shuffles the non-death test cases. + ShuffleRange(random(), last_death_test_case_ + 1, + static_cast(test_cases_.size()), &test_case_indices_); + + // Shuffles the tests inside each test case. + for (size_t i = 0; i < test_cases_.size(); i++) { + test_cases_[i]->ShuffleTests(random()); + } +} + +// Restores the test cases and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_cases_.size(); i++) { + // Unshuffles the tests in each test case. + test_cases_[i]->UnshuffleTests(); + // Resets the index of each test case. + test_case_indices_[i] = static_cast(i); + } +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +static const char* ParseFlagValue(const char* str, const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +template +static bool ParseStringFlag(const char* str, const char* flag, String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +// TODO(wan@google.com): Write tests for this once we add stdout +// capturing to Google Test. +static void PrintColorEncoded(const char* str) { + GTestColor color = COLOR_DEFAULT; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == NULL) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = COLOR_DEFAULT; + } else if (ch == 'R') { + color = COLOR_RED; + } else if (ch == 'G') { + color = COLOR_GREEN; + } else if (ch == 'Y') { + color = COLOR_YELLOW; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = +"This program contains tests written using " GTEST_NAME_ ". You can use the\n" +"following command line flags to control its behavior:\n" +"\n" +"Test Selection:\n" +" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" +" List the names of all tests instead of running them. The name of\n" +" TEST(Foo, Bar) is \"Foo.Bar\".\n" +" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" +" Run all disabled tests too.\n" +"\n" +"Test Execution:\n" +" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" +" Run the tests repeatedly; use a negative count to repeat forever.\n" +" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" +" Randomize tests' orders on every iteration.\n" +" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" +" Random number seed to use for shuffling test orders (between 1 and\n" +" 99999, or 0 to use a seed based on the current time).\n" +"\n" +"Test Output:\n" +" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" +" Enable/disable colored output. The default is @Gauto@D.\n" +" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" +" Don't print the elapsed time of each test.\n" +" @G--" GTEST_FLAG_PREFIX_ "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G" + GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" +" Generate a JSON or XML report in the given directory or with the given\n" +" file name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +# if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +# endif // GTEST_CAN_STREAM_RESULTS_ +"\n" +"Assertion Behavior:\n" +# if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" +" Set the default death test style.\n" +# endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" +" Turn assertion failures into debugger break-points.\n" +" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" +" Turn assertion failures into C++ exceptions for use by an external\n" +" test framework.\n" +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" +"\n" +"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " + "the corresponding\n" +"environment variable of a flag (all letters in upper-case). For example, to\n" +"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" +"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" +"\n" +"For more information, please read the " GTEST_NAME_ " documentation at\n" +"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" +"(not one in your own code or tests), please report it to\n" +"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +static bool ParseGoogleTestFlag(const char* const arg) { + return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseBoolFlag(arg, kPrintUTF8Flag, >EST_FLAG(print_utf8)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)); +} + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +static void LoadFlagsFromFile(const std::string& path) { + FILE* flagfile = posix::FOpen(path.c_str(), "r"); + if (!flagfile) { + GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG(flagfile) + << "\""; + } + std::string contents(ReadEntireFile(flagfile)); + posix::FClose(flagfile); + std::vector lines; + SplitString(contents, '\n', &lines); + for (size_t i = 0; i < lines.size(); ++i) { + if (lines[i].empty()) + continue; + if (!ParseGoogleTestFlag(lines[i].c_str())) + g_help_flag = true; + } +} +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + bool remove_flag = false; + if (ParseGoogleTestFlag(arg)) { + remove_flag = true; +#if GTEST_USE_OWN_FLAGFILE_FLAG_ + } else if (ParseStringFlag(arg, kFlagfileFlag, >EST_FLAG(flagfile))) { + LoadFlagsFromFile(GTEST_FLAG(flagfile)); + remove_flag = true; +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + + if (remove_flag) { + // Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleTestImpl(int* argc, CharType** argv) { + // We don't want to run the initialization code twice. + if (GTestIsInitialized()) return; + + if (*argc <= 0) return; + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +std::string TempDir() { +#if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) + return GTEST_CUSTOM_TEMPDIR_FUNCTION_(); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + return "\\temp\\"; +#elif GTEST_OS_WINDOWS + const char* temp_dir = internal::posix::GetEnv("TEMP"); + if (temp_dir == NULL || temp_dir[0] == '\0') + return "\\temp\\"; + else if (temp_dir[strlen(temp_dir) - 1] == '\\') + return temp_dir; + else + return std::string(temp_dir) + "\\"; +#elif GTEST_OS_LINUX_ANDROID + return "/sdcard/"; +#else + return "/tmp/"; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +void ScopedTrace::PushTrace(const char* file, int line, std::string message) { + internal::TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message.swap(message); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + +} // namespace testing diff --git a/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest_main.cc b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest_main.cc new file mode 100644 index 0000000..5e9c94c --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/googletest/src/gtest_main.cc @@ -0,0 +1,38 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include "gtest/gtest.h" + +GTEST_API_ int main(int argc, char **argv) { + printf("Running main() from gtest_main.cc\n"); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/accel_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/accel_lib_mock.c new file mode 100644 index 0000000..95106a0 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/accel_lib_mock.c @@ -0,0 +1,145 @@ +#include "accel_lib.h" + + +#include "callback_generator_lib.h" +#include "data_generator_lib.h" + + +// TODO: retrieve Pin-numbers from the custom_board-file! +#define ACCEL_INT1_PIN 25 + +/**< The default configuration parameters for the accelerometer */ +#define ACCEL_INTERRUPT_EVENT_DEFAULT ACCEL_NO_INTERRUPT + + + +static volatile accel_interrupt_event_t interrupt_event = ACCEL_INTERRUPT_EVENT_DEFAULT; /**< The current interrupt event that is generated, when the interrupt-pin switches from low to high */ +static volatile accel_interrupt_handler_t interrupt_handler = NULL; /**< The current interrupt handler callback function */ + + + + +/**@brief The interrupt handler function of the INT1-interrupt pin. + * + * @param[in] pin The gpio-pin where the event/interrupt was detected. + * @param[in] action The action of the pin (e.g. NRF_GPIOTE_POLARITY_LOTOHI) + * + */ +static void accel_int1_event_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { + if(pin == ACCEL_INT1_PIN && action == NRF_GPIOTE_POLARITY_LOTOHI) { + if(interrupt_event != ACCEL_NO_INTERRUPT) { + + if(interrupt_handler != NULL) { + accel_interrupt_event_t evt = interrupt_event; // A temporary copy, so the called handler can't change the internal interrupt_event + interrupt_handler(&evt); + } + } + + } +} + + +ret_code_t accel_init(void) { + + + callback_generator_ACCEL_INT1_init(); + callback_generator_ACCEL_INT1_set_handler(accel_int1_event_handler); + + return NRF_SUCCESS; +} + + + +ret_code_t accel_set_datarate(accel_datarate_t accel_datarate) { + + + return NRF_SUCCESS; +} + + +ret_code_t accel_set_operating_mode(accel_operating_mode_t accel_operating_mode) { + + + + return NRF_SUCCESS; +} + +ret_code_t accel_set_axis(accel_axis_t accel_axis) { + + + return NRF_SUCCESS; +} + + + +ret_code_t accel_set_full_scale(accel_full_scale_t accel_full_scale) { + + + return NRF_SUCCESS; + +} + +ret_code_t accel_set_HP_filter(accel_HP_filter_t accel_HP_filter) { + + return NRF_SUCCESS; +} + + +void accel_set_interrupt_handler(accel_interrupt_handler_t accel_interrupt_handler) { + interrupt_handler = accel_interrupt_handler; +} + + + +ret_code_t accel_set_motion_interrupt_parameters(uint16_t threshold_milli_gauss, uint16_t minimal_duration_ms) { + + + return NRF_SUCCESS; + +} + + + +ret_code_t accel_set_interrupt(accel_interrupt_event_t accel_interrupt_event) { + interrupt_event = accel_interrupt_event; + + if(accel_interrupt_event == ACCEL_MOTION_INTERRUPT) { + callback_generator_ACCEL_INT1_trigger(); + } + + return NRF_SUCCESS; +} + +ret_code_t accel_reset_interrupt(void) { + // Every time accel_reset_interrupt is called, the trigger should be called + if(interrupt_event == ACCEL_MOTION_INTERRUPT) { + callback_generator_ACCEL_INT1_trigger(); + } + return NRF_SUCCESS; +} + + + +ret_code_t accel_set_fifo(accel_fifo_t accel_fifo) { + + + + return NRF_SUCCESS; +} + + +ret_code_t accel_read_acceleration(int16_t* accel_x, int16_t* accel_y, int16_t* accel_z, uint8_t* num_samples, uint32_t max_num_samples) { + + return data_generator_accel_read_acceleration(accel_x, accel_y, accel_z, num_samples, max_num_samples); + +} + + + +bool accel_selftest(void) { + + + return 1; +} + + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_error.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_error.h new file mode 100644 index 0000000..0da09eb --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_error.h @@ -0,0 +1,19 @@ +/** @file + * @brief Simulation of the common application error handler and macros for utilizing a common error handler. + */ +#ifndef APP_ERROR_H__ +#define APP_ERROR_H__ + +#include +#include +#include +#include "sdk_errors.h" + + +#define APP_ERROR_CHECK(ERR_CODE) \ + do \ + { \ + (void) ERR_CODE; \ + } while (0) + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_scheduler.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_scheduler.h new file mode 100644 index 0000000..f22f0d3 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_scheduler.h @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2012 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup app_scheduler Scheduler + * @{ + * @ingroup app_common + * + * @brief The scheduler is used for transferring execution from the interrupt context to the main + * context. + * + * @details See @ref seq_diagrams_sched for sequence diagrams illustrating the flow of events + * when using the Scheduler. + * + * @section app_scheduler_req Requirements: + * + * @subsection main_context_logic Logic in main context: + * + * - Define an event handler for each type of event expected. + * - Initialize the scheduler by calling the APP_SCHED_INIT() macro before entering the + * application main loop. + * - Call app_sched_execute() from the main loop each time the application wakes up because of an + * event (typically when sd_app_evt_wait() returns). + * + * @subsection int_context_logic Logic in interrupt context: + * + * - In the interrupt handler, call app_sched_event_put() + * with the appropriate data and event handler. This will insert an event into the + * scheduler's queue. The app_sched_execute() function will pull this event and call its + * handler in the main context. + * + * @if (PERIPHERAL) + * For an example usage of the scheduler, see the implementations of + * @ref ble_sdk_app_hids_mouse and @ref ble_sdk_app_hids_keyboard. + * @endif + * + * @image html scheduler_working.svg The high level design of the scheduler + */ + +#ifndef APP_SCHEDULER_H__ +#define APP_SCHEDULER_H__ + +#include "sdk_config.h" +#include +#include "app_error.h" +#include "app_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define APP_SCHED_EVENT_HEADER_SIZE 16 /**< Size of app_scheduler.event_header_t (only for use inside APP_SCHED_BUF_SIZE()). Changed it to 16, because of other word alignement! */ + +/**@brief Compute number of bytes required to hold the scheduler buffer. + * + * @param[in] EVENT_SIZE Maximum size of events to be passed through the scheduler. + * @param[in] QUEUE_SIZE Number of entries in scheduler queue (i.e. the maximum number of events + * that can be scheduled for execution). + * + * @return Required scheduler buffer size (in bytes). + */ +#define APP_SCHED_BUF_SIZE(EVENT_SIZE, QUEUE_SIZE) \ + (((EVENT_SIZE) + APP_SCHED_EVENT_HEADER_SIZE) * ((QUEUE_SIZE) + 1)) + +/**@brief Scheduler event handler type. */ +typedef void (*app_sched_event_handler_t)(void * p_event_data, uint16_t event_size); + +/**@brief Macro for initializing the event scheduler. + * + * @details It will also handle dimensioning and allocation of the memory buffer required by the + * scheduler, making sure the buffer is correctly aligned. + * + * @param[in] EVENT_SIZE Maximum size of events to be passed through the scheduler. + * @param[in] QUEUE_SIZE Number of entries in scheduler queue (i.e. the maximum number of events + * that can be scheduled for execution). + * + * @note Since this macro allocates a buffer, it must only be called once (it is OK to call it + * several times as long as it is from the same location, e.g. to do a reinitialization). + */ +#define APP_SCHED_INIT(EVENT_SIZE, QUEUE_SIZE) \ + do \ + { \ + static uint32_t APP_SCHED_BUF[CEIL_DIV(APP_SCHED_BUF_SIZE((EVENT_SIZE), (QUEUE_SIZE)), \ + sizeof(uint32_t))]; \ + uint32_t ERR_CODE = app_sched_init((EVENT_SIZE), (QUEUE_SIZE), APP_SCHED_BUF); \ + APP_ERROR_CHECK(ERR_CODE); \ + } while (0) + +/**@brief Function for initializing the Scheduler. + * + * @details It must be called before entering the main loop. + * + * @param[in] max_event_size Maximum size of events to be passed through the scheduler. + * @param[in] queue_size Number of entries in scheduler queue (i.e. the maximum number of + * events that can be scheduled for execution). + * @param[in] p_evt_buffer Pointer to memory buffer for holding the scheduler queue. It must + * be dimensioned using the APP_SCHED_BUFFER_SIZE() macro. The buffer + * must be aligned to a 4 byte boundary. + * + * @note Normally initialization should be done using the APP_SCHED_INIT() macro, as that will both + * allocate the scheduler buffer, and also align the buffer correctly. + * + * @retval NRF_SUCCESS Successful initialization. + * @retval NRF_ERROR_INVALID_PARAM Invalid parameter (buffer not aligned to a 4 byte + * boundary). + */ +uint32_t app_sched_init(uint16_t max_event_size, uint16_t queue_size, void * p_evt_buffer); + +/**@brief Function for executing all scheduled events. + * + * @details This function must be called from within the main loop. It will execute all events + * scheduled since the last time it was called. + */ +void app_sched_execute(void); + +/**@brief Function for scheduling an event. + * + * @details Puts an event into the event queue. + * + * @param[in] p_event_data Pointer to event data to be scheduled. + * @param[in] event_size Size of event data to be scheduled. + * @param[in] handler Event handler to receive the event. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +uint32_t app_sched_event_put(void * p_event_data, + uint16_t event_size, + app_sched_event_handler_t handler); + +/**@brief Function for getting the maximum observed queue utilization. + * + * Function for tuning the module and determining QUEUE_SIZE value and thus module RAM usage. + * + * @note @ref APP_SCHEDULER_WITH_PROFILER must be enabled to use this functionality. + * + * @return Maximum number of events in queue observed so far. + */ +uint16_t app_sched_queue_utilization_get(void); + +/**@brief Function for getting the current amount of free space in the queue. + * + * @details The real amount of free space may be less if entries are being added from an interrupt. + * To get the sxact value, this function should be called from the critical section. + * + * @return Amount of free space in the queue. + */ +uint16_t app_sched_queue_space_get(void); + +/**@brief A function to pause the scheduler. + * + * @details When the scheduler is paused events are not pulled from the scheduler queue for + * processing. The function can be called multiple times. To unblock the scheduler the + * function @ref app_sched_resume has to be called the same number of times. + * + * @note @ref APP_SCHEDULER_WITH_PAUSE must be enabled to use this functionality. + */ +void app_sched_pause(void); + +/**@brief A function to resume a scheduler. + * + * @details To unblock the scheduler this function has to be called the same number of times as + * @ref app_sched_pause function. + * + * @note @ref APP_SCHEDULER_WITH_PAUSE must be enabled to use this functionality. + */ +void app_sched_resume(void); + +#ifdef __cplusplus +} +#endif + +#endif // APP_SCHEDULER_H__ + +/** @} */ diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_scheduler_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_scheduler_mock.c new file mode 100644 index 0000000..263f4aa --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_scheduler_mock.c @@ -0,0 +1,216 @@ +/** @file + * @brief Simulation of the scheduler that is used for transferring execution from the interrupt context to the main + * context. + * + * @details The implementation is nearly the same as in the SDK. Only the critical section stuff is changed and is + * saved by a pthread-mutex. + * The following functions are implemented: + * app_sched_init, app_sched_queue_space_get, app_sched_event_put, app_sched_execute + * + * + */ +#include "app_scheduler.h" +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(APP_SCHEDULER) +#include +#include +#include + +#include "pthread.h" + + +static pthread_mutex_t critical_section_mutex; + +/**@brief Function for entering a critical section by locking the mutex. + */ +static void enter_critical_section(void) { + pthread_mutex_lock(&critical_section_mutex); +} + +/**@brief Function for exiting a critical section by locking the mutex. + */ +static void exit_critical_section(void) { + pthread_mutex_unlock(&critical_section_mutex); +} + +/**@brief Function for checking if a pointer value is aligned to a 4 byte boundary. + * + * @param[in] p Pointer value to be checked. + * + * @return TRUE if pointer is aligned to a 4 byte boundary, FALSE otherwise. + */ +static bool is_word_aligned(void const* p) +{ + return (((uintptr_t)p & 0x03) == 0); +} + + + +/**@brief Structure for holding a scheduled event header. */ +typedef struct +{ + app_sched_event_handler_t handler; /**< Pointer to event handler to receive the event. */ + uint16_t event_data_size; /**< Size of event data. */ +} event_header_t; + + + +static event_header_t * m_queue_event_headers; /**< Array for holding the queue event headers. */ +static uint8_t * m_queue_event_data; /**< Array for holding the queue event data. */ +static volatile uint8_t m_queue_start_index; /**< Index of queue entry at the start of the queue. */ +static volatile uint8_t m_queue_end_index; /**< Index of queue entry at the end of the queue. */ +static uint16_t m_queue_event_size; /**< Maximum event size in queue. */ +static uint16_t m_queue_size; /**< Number of queue entries. */ + + +/**@brief Function for incrementing a queue index, and handle wrap-around. + * + * @param[in] index Old index. + * + * @return New (incremented) index. + */ +static uint8_t next_index(uint8_t index) +{ + return (index < m_queue_size) ? (index + 1) : 0; +} + + +static uint8_t app_sched_queue_full() +{ + uint8_t tmp = m_queue_start_index; + return next_index(m_queue_end_index) == tmp; +} + + + +static uint8_t app_sched_queue_empty() +{ + uint8_t tmp = m_queue_start_index; + return m_queue_end_index == tmp; +} + + +uint32_t app_sched_init(uint16_t event_size, uint16_t queue_size, void * p_event_buffer) +{ + + // Init the critical section mutex + pthread_mutex_init (&critical_section_mutex, NULL); + + + uint16_t data_start_index = (queue_size + 1) * sizeof(event_header_t); + + // Check that buffer is correctly aligned + if (!is_word_aligned(p_event_buffer)) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Initialize event scheduler + m_queue_event_headers = (event_header_t*)p_event_buffer; + m_queue_event_data = &((uint8_t *)p_event_buffer)[data_start_index]; + m_queue_end_index = 0; + m_queue_start_index = 0; + m_queue_event_size = event_size; + m_queue_size = queue_size; + + return NRF_SUCCESS; +} + + +uint16_t app_sched_queue_space_get() +{ + uint16_t start = m_queue_start_index; + uint16_t end = m_queue_end_index; + uint16_t free_space = m_queue_size - ((end >= start) ? + (end - start) : (m_queue_size + 1 - start + end)); + return free_space; +} + + +uint32_t app_sched_event_put(void * p_event_data, + uint16_t event_data_size, + app_sched_event_handler_t handler) +{ + uint32_t err_code; + + if (event_data_size <= m_queue_event_size) + { + uint16_t event_index = 0xFFFF; + + enter_critical_section(); + + if (!app_sched_queue_full()) + { + event_index = m_queue_end_index; + // Don't increment here, because app_sched_execute() could interrupt this, and will execute a non inserted event-handler! + // m_queue_end_index = next_index(m_queue_end_index); + + } + + + + if (event_index != 0xFFFF) + { + + m_queue_event_headers[event_index].handler = handler; + if ((p_event_data != NULL) && (event_data_size > 0)) + { + memcpy(&m_queue_event_data[event_index * m_queue_event_size], + p_event_data, + event_data_size); + m_queue_event_headers[event_index].event_data_size = event_data_size; + } + else + { + m_queue_event_headers[event_index].event_data_size = 0; + } + + // Increment it iff the header was actually added to the queue! + m_queue_end_index = next_index(m_queue_end_index); + err_code = NRF_SUCCESS; + } + else + { + err_code = NRF_ERROR_NO_MEM; + } + + exit_critical_section(); + } + else + { + err_code = NRF_ERROR_INVALID_LENGTH; + } + + return err_code; +} + + + + + +void app_sched_execute(void) +{ + while (!app_sched_queue_empty()) + { + // Since this function is only called from the main loop, there is no + // need for a critical region here, however a special care must be taken + // regarding update of the queue start index (see the end of the loop). + uint16_t event_index = m_queue_start_index; + + void * p_event_data; + uint16_t event_data_size; + app_sched_event_handler_t event_handler; + + p_event_data = &m_queue_event_data[event_index * m_queue_event_size]; + event_data_size = m_queue_event_headers[event_index].event_data_size; + event_handler = m_queue_event_headers[event_index].handler; + + event_handler(p_event_data, event_data_size); + + // Event processed, now it is safe to move the queue start index, + // so the queue entry occupied by this event can be used to store + // a next one. + m_queue_start_index = next_index(m_queue_start_index); + } +} +#endif //NRF_MODULE_ENABLED(APP_SCHEDULER) diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_timer_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_timer_mock.c new file mode 100644 index 0000000..40dea8c --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_timer_mock.c @@ -0,0 +1,206 @@ +/** @file + * + * @details This mock-module enables the application to create multiple timer instances based on the + * the timer module that is implemented as a thread. It simulates the RTC-based app-timer + * through the transformation of RTC-ticks and milliseconds. + * The app-timer must be enabled in the sdk_config.h-file (APP_TIMER_ENABLED 1). + * + * @note Only a prescaler value of 0 is supported for the app_timer-mock module. + * The following functions are supported: + * app_timer_init, app_timer_create, app_timer_start, app_timer_stop, app_timer_stop_all, + * app_timer_cnt_get, app_timer_cnt_diff_compute. + */ + +#include "app_timer.h" +#include "sdk_common.h" + +#if NRF_MODULE_ENABLED(APP_TIMER) + +#include "timer_lib.h" + + +#define MAX_NUMBER_OF_APP_TIMERS MAX_NUMBER_OF_TIMERS /**< Maximum number of app timers, is equal to the max number of timers of the timer-module */ + + +#define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */ + +/**< Macro for transforming RTC-ticks to milliseconds. */ +#define APP_TIMER_TICKS_TO_MS(TICKS, PRESCALER) \ + ((uint32_t)((TICKS) * ((PRESCALER) + 1) * 1000)/ (uint64_t)APP_TIMER_CLOCK_FREQ) + +#define APP_TIMER_US_TO_TICKS(US, PRESCALER) \ + ((uint64_t)ROUNDED_DIV(((uint64_t) US) * ((uint64_t)APP_TIMER_CLOCK_FREQ), ((PRESCALER) + 1) * ((uint64_t)1000000))) + + +uint32_t timer_ids[MAX_NUMBER_OF_APP_TIMERS]; /**< The mapping between app_timer-index and timer-index/timer-id. */ + +uint32_t number_of_app_timers = 0; /**< The number of created app timers. */ + + +/**@brief Function for initializing the timer module. + * + * Normally, initialization should be done using the APP_TIMER_INIT() macro, because that macro will both + * allocate the buffers needed by the timer module (including aligning the buffers correctly) + * and take care of connecting the timer module to the scheduler (if specified). + * + * @param[in] prescaler Value of the RTC1 PRESCALER register. Set to 0 for no prescaling. Only 0 is supported. + * @param[in] op_queue_size Size of the queue holding timer operations that are pending + * execution. Note that due to the queue implementation, this size must + * be one more than the size that is actually needed. + * @param[in] p_buffer Pointer to memory buffer for internal use in the app_timer + * module. The size of the buffer can be computed using the + * APP_TIMER_BUF_SIZE() macro. The buffer must be aligned to a + * 4 byte boundary. + * @param[in] evt_schedule_func Function for passing time-out events to the scheduler. Point to + * app_timer_evt_schedule() to connect to the scheduler. Set to NULL + * to make the timer module call the time-out handler directly from + * the timer interrupt handler. + * + * @retval NRF_SUCCESS If the module was initialized successfully. + * @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid (buffer NULL, op_queue_size > MAX_NUMBER_OF_APP_TIMERS or prescaler != 0). + */ +uint32_t app_timer_init(uint32_t prescaler, + uint8_t op_queue_size, + void * p_buffer, + app_timer_evt_schedule_func_t evt_schedule_func) +{ + if(op_queue_size > MAX_NUMBER_OF_APP_TIMERS || p_buffer == NULL || prescaler != 0) { + return NRF_ERROR_INVALID_PARAM; + } + + timer_init(); + + number_of_app_timers = 0; + + return NRF_SUCCESS; +} + +/**@brief Function for creating a timer instance. + * + * @param[in] p_timer_id Pointer to timer identifier. + * @param[in] mode Timer mode. + * @param[in] timeout_handler Function to be executed when the timer expires. + * + * @retval NRF_SUCCESS If the timer was successfully created. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or + * the timer is running. + * + * @note This function does the timer allocation in the caller's context. It is also not protected + * by a critical region. Therefore care must be taken not to call it from several interrupt + * levels simultaneously. + */ +uint32_t app_timer_create(app_timer_id_t const * p_timer_id, + app_timer_mode_t mode, + app_timer_timeout_handler_t timeout_handler) +{ + + + uint32_t app_timer_id = number_of_app_timers; + (*p_timer_id)->data[0] = app_timer_id; + + uint32_t t_id; + timer_mode_t timer_mode = (mode == APP_TIMER_MODE_SINGLE_SHOT) ? TIMER_MODE_SINGLE_SHOT : TIMER_MODE_REPEATED; + uint8_t ret = timer_create_timer(&t_id, timer_mode, (timer_timeout_handler_t) timeout_handler, 0); + if(ret == 0) + return NRF_ERROR_INVALID_STATE; + + + timer_ids[app_timer_id] = t_id; + + + number_of_app_timers++; + + + return NRF_SUCCESS; +} + + +/**@brief Function for starting a timer. + * + * @param[in] timer_id Timer identifier. + * @param[in] timeout_ticks Number of ticks (of RTC1, including prescaling) to time-out event. + * @param[in] p_context General purpose pointer. Will be passed to the time-out handler when + * the timer expires. + * + * @retval NRF_SUCCESS If the timer was successfully stopped. + */ +uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context) { + uint32_t app_timer_id = (*timer_id).data[0]; + uint32_t t_id = timer_ids[app_timer_id]; + + uint64_t timeout_microseconds = ((uint64_t)APP_TIMER_TICKS_TO_MS(timeout_ticks, 0)) * 1000; + + uint8_t ret = timer_start_timer(t_id, timeout_microseconds, p_context); + + if(ret == 0) + return NRF_ERROR_INVALID_STATE; + return NRF_SUCCESS; +} + +/**@brief Function for stopping the specified timer. + * + * @param[in] timer_id Timer identifier. + * + * @retval NRF_SUCCESS If the timer was successfully stopped. + */ +uint32_t app_timer_stop(app_timer_id_t timer_id) { + uint32_t app_timer_id = (*timer_id).data[0]; + uint32_t t_id = timer_ids[app_timer_id]; + + timer_stop_timer(t_id); + + return NRF_SUCCESS; +} + +/**@brief Function for stopping all running timers. + * + * @retval NRF_SUCCESS If all timers were successfully stopped. + */ +uint32_t app_timer_stop_all(void) { + for(uint8_t i = 0; i < number_of_app_timers; i++) { + uint32_t t_id = timer_ids[i]; + timer_stop_timer(t_id); + } + return NRF_SUCCESS; +} + +/**@brief Function for returning the current value of the simulated RTC1 counter (ticks). + * + * @return Current value of the simulated RTC1 counter. + */ +uint32_t app_timer_cnt_get(void) { + uint64_t us = timer_get_microseconds_since_start(); + uint64_t ticks = APP_TIMER_US_TO_TICKS(us, 0); + + // Map to 0 - MAX_RTC_COUNTER_VAL + ticks = ticks % (MAX_RTC_COUNTER_VAL + 1); + + return (uint32_t) ticks; +} + + +/**@brief Function for computing the difference between two RTC1 counter values. + * + * @return Number of ticks elapsed from ticks_old to ticks_now. + */ +static uint32_t ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old) +{ + return ((ticks_now - ticks_old) & MAX_RTC_COUNTER_VAL); +} + +/**@brief Function for computing the difference between two RTC1 counter values. + * + * @param[in] ticks_to Value returned by app_timer_cnt_get(). + * @param[in] ticks_from Value returned by app_timer_cnt_get(). + * @param[out] p_ticks_diff Number of ticks from ticks_from to ticks_to. + * + * @retval NRF_SUCCESS If the counter difference was successfully computed. + */ +uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to, + uint32_t ticks_from, + uint32_t * p_ticks_diff) +{ + *p_ticks_diff = ticks_diff_get(ticks_to, ticks_from); + return NRF_SUCCESS; +} +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_util.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_util.h new file mode 100644 index 0000000..7d1256b --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_util.h @@ -0,0 +1,44 @@ +/** @file + * @brief Simulaton of various types and definitions available to all applications. + */ + +#ifndef APP_UTIL_H__ +#define APP_UTIL_H__ + +#include +#include + +/**@brief Macro for performing integer division, making sure the result is rounded up. + * + * @details One typical use for this is to compute the number of objects with size B is needed to + * hold A number of bytes. + * + * @param[in] A Numerator. + * @param[in] B Denominator. + * + * @return Integer result of dividing A by B, rounded up. + */ +#define CEIL_DIV(A, B) \ + (((A) + (B) - 1) / (B)) + + +/**@brief Macro for performing rounded integer division (as opposed to truncating the result). + * + * @param[in] A Numerator. + * @param[in] B Denominator. + * + * @return Rounded (integer) result of dividing A by B. + */ +#define ROUNDED_DIV(A, B) (((A) + ((B) / 2)) / (B)) + +/**@brief Macro for checking if an integer is a power of two. + * + * @param[in] A Number to be tested. + * + * @return true if value is power of two. + * @return false if value not power of two. + */ +#define IS_POWER_OF_TWO(A) ( ((A) != 0) && ((((A) - 1) & (A)) == 0) ) + + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_util_platform.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_util_platform.h new file mode 100644 index 0000000..be7d2d4 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/app_util_platform.h @@ -0,0 +1,107 @@ +/** @file + * @brief Simulaton of various types and definitions available to all applications. + */ + +#ifndef APP_UTIL_PLATFORM_H__ +#define APP_UTIL_PLATFORM_H__ + +#include +#include +#include "timer_lib.h" // Needed for the Enter/Exit Critical Section-function + + +#ifdef __cplusplus +extern "C" { +#endif + +// This is extracted from nrf.h and nrf5X.h (But we need it for the priority declaration) +#if defined (NRF51) + #define __CORTEX_M (0x00U) /*!< Cortex-M Core */ +#elif defined (NRF52840_XXAA) + #define __CORTEX_M (0x04U) /*!< Cortex-M Core */ +#elif defined (NRF52832_XXAA) + #define __CORTEX_M (0x04U) /*!< Cortex-M Core */ +#else + #error "Device must be defined. See nrf.h." +#endif /* NRF51, NRF52832_XXAA, NRF52840_XXAA */ + + + +#if __CORTEX_M == (0x00U) +#define _PRIO_SD_HIGH 0 +#define _PRIO_APP_HIGH 1 +#define _PRIO_APP_MID 1 +#define _PRIO_SD_LOW 2 +#define _PRIO_APP_LOW 3 +#define _PRIO_APP_LOWEST 3 +#define _PRIO_THREAD 4 +#elif __CORTEX_M == (0x04U) +#define _PRIO_SD_HIGH 0 +#define _PRIO_SD_MID 1 +#define _PRIO_APP_HIGH 2 +#define _PRIO_APP_MID 3 +#define _PRIO_SD_LOW 4 +#define _PRIO_SD_LOWEST 5 +#define _PRIO_APP_LOW 6 +#define _PRIO_APP_LOWEST 7 +#define _PRIO_THREAD 15 +#else + #error "No platform defined" +#endif + + +//lint -save -e113 -e452 +/**@brief The interrupt priorities available to the application while the SoftDevice is active. */ +typedef enum +{ +#ifndef SOFTDEVICE_PRESENT + APP_IRQ_PRIORITY_HIGHEST = _PRIO_SD_HIGH, +#else + APP_IRQ_PRIORITY_HIGHEST = _PRIO_APP_HIGH, +#endif + APP_IRQ_PRIORITY_HIGH = _PRIO_APP_HIGH, +#ifndef SOFTDEVICE_PRESENT + APP_IRQ_PRIORITY_MID = _PRIO_SD_LOW, +#else + APP_IRQ_PRIORITY_MID = _PRIO_APP_MID, +#endif + APP_IRQ_PRIORITY_LOW = _PRIO_APP_LOW, + APP_IRQ_PRIORITY_LOWEST = _PRIO_APP_LOWEST, + APP_IRQ_PRIORITY_THREAD = _PRIO_THREAD /**< "Interrupt level" when running in Thread Mode. */ +} app_irq_priority_t; + + +/**@brief Macro for entering a critical region. + * + * @note Due to implementation details, there must exist one and only one call to + * CRITICAL_REGION_EXIT() for each call to CRITICAL_REGION_ENTER(), and they must be located + * in the same scope. + */ +#ifdef SOFTDEVICE_PRESENT +#define CRITICAL_REGION_ENTER() \ + { \ + timer_enter_critical_section(); +#else +#define CRITICAL_REGION_ENTER() timer_enter_critical_section() +#endif + +/**@brief Macro for leaving a critical region. + * + * @note Due to implementation details, there must exist one and only one call to + * CRITICAL_REGION_EXIT() for each call to CRITICAL_REGION_ENTER(), and they must be located + * in the same scope. + */ +#ifdef SOFTDEVICE_PRESENT +#define CRITICAL_REGION_EXIT() \ + timer_exit_critical_section(); \ + } +#else +#define CRITICAL_REGION_EXIT() timer_exit_critical_section() +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/battery_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/battery_lib_mock.c new file mode 100644 index 0000000..52c133b --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/battery_lib_mock.c @@ -0,0 +1,22 @@ +#include "battery_lib.h" + + +#include "callback_generator_lib.h" +#include "data_generator_lib.h" + + +ret_code_t battery_init(void) { + return NRF_SUCCESS; +} + +float battery_get_voltage(void) { + return 3.0; +} + + + +bool battery_selftest(void) { + + + return 1; +} diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/ble_gap.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/ble_gap.h new file mode 100644 index 0000000..f12e45b --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/ble_gap.h @@ -0,0 +1,956 @@ +/**@file + @details Partial copy of the ble_gap.h library + */ + +#ifndef BLE_GAP_H__ +#define BLE_GAP_H__ + +#include "ble_types.h" +#include "ble_ranges.h" +//#include "nrf_svc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@addtogroup BLE_GAP_ENUMERATIONS Enumerations + * @{ */ + +/**@brief GAP API SVC numbers. + */ +enum BLE_GAP_SVCS +{ + SD_BLE_GAP_ADDRESS_SET = BLE_GAP_SVC_BASE, /**< Set own Bluetooth Address. */ + SD_BLE_GAP_ADDRESS_GET, /**< Get own Bluetooth Address. */ + SD_BLE_GAP_ADV_DATA_SET, /**< Set Advertising Data. */ + SD_BLE_GAP_ADV_START, /**< Start Advertising. */ + SD_BLE_GAP_ADV_STOP, /**< Stop Advertising. */ + SD_BLE_GAP_CONN_PARAM_UPDATE, /**< Connection Parameter Update. */ + SD_BLE_GAP_DISCONNECT, /**< Disconnect. */ + SD_BLE_GAP_TX_POWER_SET, /**< Set TX Power. */ + SD_BLE_GAP_APPEARANCE_SET, /**< Set Appearance. */ + SD_BLE_GAP_APPEARANCE_GET, /**< Get Appearance. */ + SD_BLE_GAP_PPCP_SET, /**< Set PPCP. */ + SD_BLE_GAP_PPCP_GET, /**< Get PPCP. */ + SD_BLE_GAP_DEVICE_NAME_SET, /**< Set Device Name. */ + SD_BLE_GAP_DEVICE_NAME_GET, /**< Get Device Name. */ + SD_BLE_GAP_AUTHENTICATE, /**< Initiate Pairing/Bonding. */ + SD_BLE_GAP_SEC_PARAMS_REPLY, /**< Reply with Security Parameters. */ + SD_BLE_GAP_AUTH_KEY_REPLY, /**< Reply with an authentication key. */ + SD_BLE_GAP_LESC_DHKEY_REPLY, /**< Reply with an LE Secure Connections DHKey. */ + SD_BLE_GAP_KEYPRESS_NOTIFY, /**< Notify of a keypress during an authentication procedure. */ + SD_BLE_GAP_LESC_OOB_DATA_GET, /**< Get the local LE Secure Connections OOB data. */ + SD_BLE_GAP_LESC_OOB_DATA_SET, /**< Set the remote LE Secure Connections OOB data. */ + SD_BLE_GAP_ENCRYPT, /**< Initiate encryption procedure. */ + SD_BLE_GAP_SEC_INFO_REPLY, /**< Reply with Security Information. */ + SD_BLE_GAP_CONN_SEC_GET, /**< Obtain connection security level. */ + SD_BLE_GAP_RSSI_START, /**< Start reporting of changes in RSSI. */ + SD_BLE_GAP_RSSI_STOP, /**< Stop reporting of changes in RSSI. */ + SD_BLE_GAP_SCAN_START, /**< Start Scanning. */ + SD_BLE_GAP_SCAN_STOP, /**< Stop Scanning. */ + SD_BLE_GAP_CONNECT, /**< Connect. */ + SD_BLE_GAP_CONNECT_CANCEL, /**< Cancel ongoing connection procedure. */ + SD_BLE_GAP_RSSI_GET, /**< Get the last RSSI sample. */ +}; + +/**@brief GAP Event IDs. + * IDs that uniquely identify an event coming from the stack to the application. + */ +enum BLE_GAP_EVTS +{ + BLE_GAP_EVT_CONNECTED = BLE_GAP_EVT_BASE, /**< Connection established. \n See @ref ble_gap_evt_connected_t. */ + BLE_GAP_EVT_DISCONNECTED, /**< Disconnected from peer. \n See @ref ble_gap_evt_disconnected_t. */ + BLE_GAP_EVT_CONN_PARAM_UPDATE, /**< Connection Parameters updated. \n See @ref ble_gap_evt_conn_param_update_t. */ + BLE_GAP_EVT_SEC_PARAMS_REQUEST, /**< Request to provide security parameters. \n Reply with @ref sd_ble_gap_sec_params_reply. \n See @ref ble_gap_evt_sec_params_request_t. */ + BLE_GAP_EVT_SEC_INFO_REQUEST, /**< Request to provide security information. \n Reply with @ref sd_ble_gap_sec_info_reply. \n See @ref ble_gap_evt_sec_info_request_t. */ + BLE_GAP_EVT_PASSKEY_DISPLAY, /**< Request to display a passkey to the user. \n In LESC Numeric Comparison, reply with @ref sd_ble_gap_auth_key_reply. \n See @ref ble_gap_evt_passkey_display_t. */ + BLE_GAP_EVT_KEY_PRESSED, /**< Notification of a keypress on the remote device.\n See @ref ble_gap_evt_key_pressed_t */ + BLE_GAP_EVT_AUTH_KEY_REQUEST, /**< Request to provide an authentication key. \n Reply with @ref sd_ble_gap_auth_key_reply. \n See @ref ble_gap_evt_auth_key_request_t. */ + BLE_GAP_EVT_LESC_DHKEY_REQUEST, /**< Request to calculate an LE Secure Connections DHKey. \n Reply with @ref sd_ble_gap_lesc_dhkey_reply. \n See @ref ble_gap_evt_lesc_dhkey_request_t */ + BLE_GAP_EVT_AUTH_STATUS, /**< Authentication procedure completed with status. \n See @ref ble_gap_evt_auth_status_t. */ + BLE_GAP_EVT_CONN_SEC_UPDATE, /**< Connection security updated. \n See @ref ble_gap_evt_conn_sec_update_t. */ + BLE_GAP_EVT_TIMEOUT, /**< Timeout expired. \n See @ref ble_gap_evt_timeout_t. */ + BLE_GAP_EVT_RSSI_CHANGED, /**< RSSI report. \n See @ref ble_gap_evt_rssi_changed_t. */ + BLE_GAP_EVT_ADV_REPORT, /**< Advertising report. \n See @ref ble_gap_evt_adv_report_t. */ + BLE_GAP_EVT_SEC_REQUEST, /**< Security Request. \n See @ref ble_gap_evt_sec_request_t. */ + BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST, /**< Connection Parameter Update Request. \n Reply with @ref sd_ble_gap_conn_param_update. \n See @ref ble_gap_evt_conn_param_update_request_t. */ + BLE_GAP_EVT_SCAN_REQ_REPORT, /**< Scan request report. \n See @ref ble_gap_evt_scan_req_report_t. */ +}; + +/**@brief GAP Option IDs. + * IDs that uniquely identify a GAP option. + */ +enum BLE_GAP_OPTS +{ + BLE_GAP_OPT_CH_MAP = BLE_GAP_OPT_BASE, /**< Channel Map. @ref ble_gap_opt_ch_map_t */ + BLE_GAP_OPT_LOCAL_CONN_LATENCY, /**< Local connection latency. @ref ble_gap_opt_local_conn_latency_t */ + BLE_GAP_OPT_PASSKEY, /**< Set passkey. @ref ble_gap_opt_passkey_t */ + BLE_GAP_OPT_PRIVACY, /**< Custom privacy. @ref ble_gap_opt_privacy_t */ + BLE_GAP_OPT_SCAN_REQ_REPORT, /**< Scan request report. @ref ble_gap_opt_scan_req_report_t */ + BLE_GAP_OPT_COMPAT_MODE /**< Compatibility mode. @ref ble_gap_opt_compat_mode_t */ +}; + +/** @} */ + +/**@addtogroup BLE_GAP_DEFINES Defines + * @{ */ + +/**@defgroup BLE_ERRORS_GAP SVC return values specific to GAP + * @{ */ +#define BLE_ERROR_GAP_UUID_LIST_MISMATCH (NRF_GAP_ERR_BASE + 0x000) /**< UUID list does not contain an integral number of UUIDs. */ +#define BLE_ERROR_GAP_DISCOVERABLE_WITH_WHITELIST (NRF_GAP_ERR_BASE + 0x001) /**< Use of Whitelist not permitted with discoverable advertising. */ +#define BLE_ERROR_GAP_INVALID_BLE_ADDR (NRF_GAP_ERR_BASE + 0x002) /**< The upper two bits of the address do not correspond to the specified address type. */ +#define BLE_ERROR_GAP_WHITELIST_IN_USE (NRF_GAP_ERR_BASE + 0x003) /**< Attempt to overwrite the whitelist while already in use by another operation. */ +/**@} */ + + +/**@defgroup BLE_GAP_ROLES GAP Roles + * @note Not explicitly used in peripheral API, but will be relevant for central API. + * @{ */ +#define BLE_GAP_ROLE_INVALID 0x0 /**< Invalid Role. */ +#define BLE_GAP_ROLE_PERIPH 0x1 /**< Peripheral Role. */ +#define BLE_GAP_ROLE_CENTRAL 0x2 /**< Central Role. */ +/**@} */ + + +/**@defgroup BLE_GAP_TIMEOUT_SOURCES GAP Timeout sources + * @{ */ +#define BLE_GAP_TIMEOUT_SRC_ADVERTISING 0x00 /**< Advertising timeout. */ +#define BLE_GAP_TIMEOUT_SRC_SECURITY_REQUEST 0x01 /**< Security request timeout. */ +#define BLE_GAP_TIMEOUT_SRC_SCAN 0x02 /**< Scanning timeout. */ +#define BLE_GAP_TIMEOUT_SRC_CONN 0x03 /**< Connection timeout. */ +/**@} */ + + +/**@defgroup BLE_GAP_ADDR_TYPES GAP Address types + * @{ */ +#define BLE_GAP_ADDR_TYPE_PUBLIC 0x00 /**< Public address. */ +#define BLE_GAP_ADDR_TYPE_RANDOM_STATIC 0x01 /**< Random Static address. */ +#define BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE 0x02 /**< Private Resolvable address. */ +#define BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE 0x03 /**< Private Non-Resolvable address. */ +/**@} */ + +/**@defgroup BLE_GAP_ADDR_CYCLE_MODES GAP Address cycle modes + * @{ */ +#define BLE_GAP_ADDR_CYCLE_MODE_NONE 0x00 /**< Set addresses directly, no automatic address cycling. */ +#define BLE_GAP_ADDR_CYCLE_MODE_AUTO 0x01 /**< Automatically generate and update private addresses. */ +/** @} */ + +/**@brief The default interval in seconds at which a private address is refreshed when address cycle mode is @ref BLE_GAP_ADDR_CYCLE_MODE_AUTO. */ +#define BLE_GAP_DEFAULT_PRIVATE_ADDR_CYCLE_INTERVAL_S (60 * 15) + +/** @brief BLE address length. */ +#define BLE_GAP_ADDR_LEN 6 + + +/**@defgroup BLE_GAP_AD_TYPE_DEFINITIONS GAP Advertising and Scan Response Data format + * @note Found at https://www.bluetooth.org/Technical/AssignedNumbers/generic_access_profile.htm + * @{ */ +#define BLE_GAP_AD_TYPE_FLAGS 0x01 /**< Flags for discoverability. */ +#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE 0x02 /**< Partial list of 16 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE 0x03 /**< Complete list of 16 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE 0x04 /**< Partial list of 32 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE 0x05 /**< Complete list of 32 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE 0x06 /**< Partial list of 128 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE 0x07 /**< Complete list of 128 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME 0x08 /**< Short local device name. */ +#define BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME 0x09 /**< Complete local device name. */ +#define BLE_GAP_AD_TYPE_TX_POWER_LEVEL 0x0A /**< Transmit power level. */ +#define BLE_GAP_AD_TYPE_CLASS_OF_DEVICE 0x0D /**< Class of device. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C 0x0E /**< Simple Pairing Hash C. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R 0x0F /**< Simple Pairing Randomizer R. */ +#define BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE 0x10 /**< Security Manager TK Value. */ +#define BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS 0x11 /**< Security Manager Out Of Band Flags. */ +#define BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE 0x12 /**< Slave Connection Interval Range. */ +#define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT 0x14 /**< List of 16-bit Service Solicitation UUIDs. */ +#define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT 0x15 /**< List of 128-bit Service Solicitation UUIDs. */ +#define BLE_GAP_AD_TYPE_SERVICE_DATA 0x16 /**< Service Data - 16-bit UUID. */ +#define BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS 0x17 /**< Public Target Address. */ +#define BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS 0x18 /**< Random Target Address. */ +#define BLE_GAP_AD_TYPE_APPEARANCE 0x19 /**< Appearance. */ +#define BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL 0x1A /**< Advertising Interval. */ +#define BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS 0x1B /**< LE Bluetooth Device Address. */ +#define BLE_GAP_AD_TYPE_LE_ROLE 0x1C /**< LE Role. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256 0x1D /**< Simple Pairing Hash C-256. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256 0x1E /**< Simple Pairing Randomizer R-256. */ +#define BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID 0x20 /**< Service Data - 32-bit UUID. */ +#define BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID 0x21 /**< Service Data - 128-bit UUID. */ +#define BLE_GAP_AD_TYPE_URI 0x24 /**< URI */ +#define BLE_GAP_AD_TYPE_3D_INFORMATION_DATA 0x3D /**< 3D Information Data. */ +#define BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA 0xFF /**< Manufacturer Specific Data. */ +/**@} */ + + +/**@defgroup BLE_GAP_ADV_FLAGS GAP Advertisement Flags + * @{ */ +#define BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE (0x01) /**< LE Limited Discoverable Mode. */ +#define BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE (0x02) /**< LE General Discoverable Mode. */ +#define BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED (0x04) /**< BR/EDR not supported. */ +#define BLE_GAP_ADV_FLAG_LE_BR_EDR_CONTROLLER (0x08) /**< Simultaneous LE and BR/EDR, Controller. */ +#define BLE_GAP_ADV_FLAG_LE_BR_EDR_HOST (0x10) /**< Simultaneous LE and BR/EDR, Host. */ +#define BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE (BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) /**< LE Limited Discoverable Mode, BR/EDR not supported. */ +#define BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE (BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) /**< LE General Discoverable Mode, BR/EDR not supported. */ +/**@} */ + + +/**@defgroup BLE_GAP_ADV_INTERVALS GAP Advertising interval max and min + * @{ */ +#define BLE_GAP_ADV_INTERVAL_MIN 0x0020 /**< Minimum Advertising interval in 625 us units, i.e. 20 ms. */ +#define BLE_GAP_ADV_NONCON_INTERVAL_MIN 0x00A0 /**< Minimum Advertising interval in 625 us units for non connectable mode, i.e. 100 ms. */ +#define BLE_GAP_ADV_INTERVAL_MAX 0x4000 /**< Maximum Advertising interval in 625 us units, i.e. 10.24 s. */ + /**@} */ + + +/**@defgroup BLE_GAP_SCAN_INTERVALS GAP Scan interval max and min + * @{ */ +#define BLE_GAP_SCAN_INTERVAL_MIN 0x0004 /**< Minimum Scan interval in 625 us units, i.e. 2.5 ms. */ +#define BLE_GAP_SCAN_INTERVAL_MAX 0x4000 /**< Maximum Scan interval in 625 us units, i.e. 10.24 s. */ + /** @} */ + + +/**@defgroup BLE_GAP_SCAN_WINDOW GAP Scan window max and min + * @{ */ +#define BLE_GAP_SCAN_WINDOW_MIN 0x0004 /**< Minimum Scan window in 625 us units, i.e. 2.5 ms. */ +#define BLE_GAP_SCAN_WINDOW_MAX 0x4000 /**< Maximum Scan window in 625 us units, i.e. 10.24 s. */ + /** @} */ + + +/**@defgroup BLE_GAP_SCAN_TIMEOUT GAP Scan timeout max and min + * @{ */ +#define BLE_GAP_SCAN_TIMEOUT_MIN 0x0001 /**< Minimum Scan timeout in seconds. */ +#define BLE_GAP_SCAN_TIMEOUT_MAX 0xFFFF /**< Maximum Scan timeout in seconds. */ + /** @} */ + + +/**@brief Maximum size of advertising data in octets. */ +#define BLE_GAP_ADV_MAX_SIZE 31 + + +/**@defgroup BLE_GAP_ADV_TYPES GAP Advertising types + * @{ */ +#define BLE_GAP_ADV_TYPE_ADV_IND 0x00 /**< Connectable undirected. */ +#define BLE_GAP_ADV_TYPE_ADV_DIRECT_IND 0x01 /**< Connectable directed. */ +#define BLE_GAP_ADV_TYPE_ADV_SCAN_IND 0x02 /**< Scannable undirected. */ +#define BLE_GAP_ADV_TYPE_ADV_NONCONN_IND 0x03 /**< Non connectable undirected. */ +/**@} */ + + +/**@defgroup BLE_GAP_ADV_FILTER_POLICIES GAP Advertising filter policies + * @{ */ +#define BLE_GAP_ADV_FP_ANY 0x00 /**< Allow scan requests and connect requests from any device. */ +#define BLE_GAP_ADV_FP_FILTER_SCANREQ 0x01 /**< Filter scan requests with whitelist. */ +#define BLE_GAP_ADV_FP_FILTER_CONNREQ 0x02 /**< Filter connect requests with whitelist. */ +#define BLE_GAP_ADV_FP_FILTER_BOTH 0x03 /**< Filter both scan and connect requests with whitelist. */ +/**@} */ + + +/**@defgroup BLE_GAP_ADV_TIMEOUT_VALUES GAP Advertising timeout values + * @{ */ +#define BLE_GAP_ADV_TIMEOUT_LIMITED_MAX 180 /**< Maximum advertising time in limited discoverable mode (TGAP(lim_adv_timeout) = 180s). */ +#define BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED 0 /**< Unlimited advertising in general discoverable mode. */ +/**@} */ + + +/**@defgroup BLE_GAP_DISC_MODES GAP Discovery modes + * @{ */ +#define BLE_GAP_DISC_MODE_NOT_DISCOVERABLE 0x00 /**< Not discoverable discovery Mode. */ +#define BLE_GAP_DISC_MODE_LIMITED 0x01 /**< Limited Discovery Mode. */ +#define BLE_GAP_DISC_MODE_GENERAL 0x02 /**< General Discovery Mode. */ +/**@} */ + +/**@defgroup BLE_GAP_IO_CAPS GAP IO Capabilities + * @{ */ +#define BLE_GAP_IO_CAPS_DISPLAY_ONLY 0x00 /**< Display Only. */ +#define BLE_GAP_IO_CAPS_DISPLAY_YESNO 0x01 /**< Display and Yes/No entry. */ +#define BLE_GAP_IO_CAPS_KEYBOARD_ONLY 0x02 /**< Keyboard Only. */ +#define BLE_GAP_IO_CAPS_NONE 0x03 /**< No I/O capabilities. */ +#define BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY 0x04 /**< Keyboard and Display. */ +/**@} */ + +/**@defgroup BLE_GAP_AUTH_KEY_TYPES GAP Authentication Key Types + * @{ */ +#define BLE_GAP_AUTH_KEY_TYPE_NONE 0x00 /**< No key (may be used to reject). */ +#define BLE_GAP_AUTH_KEY_TYPE_PASSKEY 0x01 /**< 6-digit Passkey. */ +#define BLE_GAP_AUTH_KEY_TYPE_OOB 0x02 /**< Out Of Band data. */ +/**@} */ + +/**@defgroup BLE_GAP_KP_NOT_TYPES GAP Keypress Notification Types + * @{ */ +#define BLE_GAP_KP_NOT_TYPE_PASSKEY_START 0x00 /**< Passkey entry started. */ +#define BLE_GAP_KP_NOT_TYPE_PASSKEY_DIGIT_IN 0x01 /**< Passkey digit entered. */ +#define BLE_GAP_KP_NOT_TYPE_PASSKEY_DIGIT_OUT 0x02 /**< Passkey digit erased. */ +#define BLE_GAP_KP_NOT_TYPE_PASSKEY_CLEAR 0x03 /**< Passkey cleared. */ +#define BLE_GAP_KP_NOT_TYPE_PASSKEY_END 0x04 /**< Passkey entry completed. */ +/**@} */ + +/**@defgroup BLE_GAP_SEC_STATUS GAP Security status + * @{ */ +#define BLE_GAP_SEC_STATUS_SUCCESS 0x00 /**< Procedure completed with success. */ +#define BLE_GAP_SEC_STATUS_TIMEOUT 0x01 /**< Procedure timed out. */ +#define BLE_GAP_SEC_STATUS_PDU_INVALID 0x02 /**< Invalid PDU received. */ +#define BLE_GAP_SEC_STATUS_RFU_RANGE1_BEGIN 0x03 /**< Reserved for Future Use range #1 begin. */ +#define BLE_GAP_SEC_STATUS_RFU_RANGE1_END 0x80 /**< Reserved for Future Use range #1 end. */ +#define BLE_GAP_SEC_STATUS_PASSKEY_ENTRY_FAILED 0x81 /**< Passkey entry failed (user cancelled or other). */ +#define BLE_GAP_SEC_STATUS_OOB_NOT_AVAILABLE 0x82 /**< Out of Band Key not available. */ +#define BLE_GAP_SEC_STATUS_AUTH_REQ 0x83 /**< Authentication requirements not met. */ +#define BLE_GAP_SEC_STATUS_CONFIRM_VALUE 0x84 /**< Confirm value failed. */ +#define BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP 0x85 /**< Pairing not supported. */ +#define BLE_GAP_SEC_STATUS_ENC_KEY_SIZE 0x86 /**< Encryption key size. */ +#define BLE_GAP_SEC_STATUS_SMP_CMD_UNSUPPORTED 0x87 /**< Unsupported SMP command. */ +#define BLE_GAP_SEC_STATUS_UNSPECIFIED 0x88 /**< Unspecified reason. */ +#define BLE_GAP_SEC_STATUS_REPEATED_ATTEMPTS 0x89 /**< Too little time elapsed since last attempt. */ +#define BLE_GAP_SEC_STATUS_INVALID_PARAMS 0x8A /**< Invalid parameters. */ +#define BLE_GAP_SEC_STATUS_DHKEY_FAILURE 0x8B /**< DHKey check failure. */ +#define BLE_GAP_SEC_STATUS_NUM_COMP_FAILURE 0x8C /**< Numeric Comparison failure. */ +#define BLE_GAP_SEC_STATUS_BR_EDR_IN_PROG 0x8D /**< BR/EDR pairing in progress. */ +#define BLE_GAP_SEC_STATUS_X_TRANS_KEY_DISALLOWED 0x8E /**< BR/EDR Link Key cannot be used for LE keys. */ +#define BLE_GAP_SEC_STATUS_RFU_RANGE2_BEGIN 0x8F /**< Reserved for Future Use range #2 begin. */ +#define BLE_GAP_SEC_STATUS_RFU_RANGE2_END 0xFF /**< Reserved for Future Use range #2 end. */ +/**@} */ + +/**@defgroup BLE_GAP_SEC_STATUS_SOURCES GAP Security status sources + * @{ */ +#define BLE_GAP_SEC_STATUS_SOURCE_LOCAL 0x00 /**< Local failure. */ +#define BLE_GAP_SEC_STATUS_SOURCE_REMOTE 0x01 /**< Remote failure. */ +/**@} */ + +/**@defgroup BLE_GAP_CP_LIMITS GAP Connection Parameters Limits + * @{ */ +#define BLE_GAP_CP_MIN_CONN_INTVL_NONE 0xFFFF /**< No new minimum connection interval specified in connect parameters. */ +#define BLE_GAP_CP_MIN_CONN_INTVL_MIN 0x0006 /**< Lowest minimum connection interval permitted, in units of 1.25 ms, i.e. 7.5 ms. */ +#define BLE_GAP_CP_MIN_CONN_INTVL_MAX 0x0C80 /**< Highest minimum connection interval permitted, in units of 1.25 ms, i.e. 4 s. */ +#define BLE_GAP_CP_MAX_CONN_INTVL_NONE 0xFFFF /**< No new maximum connection interval specified in connect parameters. */ +#define BLE_GAP_CP_MAX_CONN_INTVL_MIN 0x0006 /**< Lowest maximum connection interval permitted, in units of 1.25 ms, i.e. 7.5 ms. */ +#define BLE_GAP_CP_MAX_CONN_INTVL_MAX 0x0C80 /**< Highest maximum connection interval permitted, in units of 1.25 ms, i.e. 4 s. */ +#define BLE_GAP_CP_SLAVE_LATENCY_MAX 0x01F3 /**< Highest slave latency permitted, in connection events. */ +#define BLE_GAP_CP_CONN_SUP_TIMEOUT_NONE 0xFFFF /**< No new supervision timeout specified in connect parameters. */ +#define BLE_GAP_CP_CONN_SUP_TIMEOUT_MIN 0x000A /**< Lowest supervision timeout permitted, in units of 10 ms, i.e. 100 ms. */ +#define BLE_GAP_CP_CONN_SUP_TIMEOUT_MAX 0x0C80 /**< Highest supervision timeout permitted, in units of 10 ms, i.e. 32 s. */ +/**@} */ + + +/**@brief GAP device name maximum length. */ +#define BLE_GAP_DEVNAME_MAX_LEN 31 + +/**@brief Disable RSSI events for connections */ +#define BLE_GAP_RSSI_THRESHOLD_INVALID 0xFF + +/**@defgroup BLE_GAP_CONN_SEC_MODE_SET_MACROS GAP attribute security requirement setters + * + * See @ref ble_gap_conn_sec_mode_t. + * @{ */ +/**@brief Set sec_mode pointed to by ptr to have no access rights.*/ +#define BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(ptr) do {(ptr)->sm = 0; (ptr)->lv = 0;} while(0) +/**@brief Set sec_mode pointed to by ptr to require no protection, open link.*/ +#define BLE_GAP_CONN_SEC_MODE_SET_OPEN(ptr) do {(ptr)->sm = 1; (ptr)->lv = 1;} while(0) +/**@brief Set sec_mode pointed to by ptr to require encryption, but no MITM protection.*/ +#define BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(ptr) do {(ptr)->sm = 1; (ptr)->lv = 2;} while(0) +/**@brief Set sec_mode pointed to by ptr to require encryption and MITM protection.*/ +#define BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(ptr) do {(ptr)->sm = 1; (ptr)->lv = 3;} while(0) +/**@brief Set sec_mode pointed to by ptr to require LESC encryption and MITM protection.*/ +#define BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(ptr) do {(ptr)->sm = 1; (ptr)->lv = 4;} while(0) +/**@brief Set sec_mode pointed to by ptr to require signing or encryption, no MITM protection needed.*/ +#define BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(ptr) do {(ptr)->sm = 2; (ptr)->lv = 1;} while(0) +/**@brief Set sec_mode pointed to by ptr to require signing or encryption with MITM protection.*/ +#define BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(ptr) do {(ptr)->sm = 2; (ptr)->lv = 2;} while(0) +/**@} */ + + +/**@brief GAP Security Random Number Length. */ +#define BLE_GAP_SEC_RAND_LEN 8 + +/**@brief GAP Security Key Length. */ +#define BLE_GAP_SEC_KEY_LEN 16 + +/**@brief GAP LE Secure Connections Elliptic Curve Diffie-Hellman P-256 Public Key Length. */ +#define BLE_GAP_LESC_P256_PK_LEN 64 + +/**@brief GAP LE Secure Connections Elliptic Curve Diffie-Hellman DHKey Length. */ +#define BLE_GAP_LESC_DHKEY_LEN 32 + +/**@brief GAP Passkey Length. */ +#define BLE_GAP_PASSKEY_LEN 6 + +/**@brief Maximum amount of addresses in a whitelist. */ +#define BLE_GAP_WHITELIST_ADDR_MAX_COUNT (8) + +/**@brief Maximum amount of IRKs in a whitelist. + * @note The number of IRKs is limited to 8, even if the hardware supports more. + */ +#define BLE_GAP_WHITELIST_IRK_MAX_COUNT (8) + +/**@defgroup GAP_SEC_MODES GAP Security Modes + * @{ */ +#define BLE_GAP_SEC_MODE 0x00 /**< No key (may be used to reject). */ +/**@} */ +/** @} */ + +/**@addtogroup BLE_GAP_STRUCTURES Structures + * @{ */ + +/** + * @brief BLE GAP initialization parameters. + */ +typedef struct +{ + uint8_t periph_conn_count; /**< Number of connections acting as a peripheral */ + uint8_t central_conn_count; /**< Number of connections acting as a central */ + uint8_t central_sec_count; /**< Number of SMP instances for all connections acting as a central. */ +} ble_gap_enable_params_t; + +/**@brief Bluetooth Low Energy address. */ +typedef struct +{ + uint8_t addr_type; /**< See @ref BLE_GAP_ADDR_TYPES. */ + uint8_t addr[BLE_GAP_ADDR_LEN]; /**< 48-bit address, LSB format. */ +} ble_gap_addr_t; + + +/**@brief GAP connection parameters. + * + * @note When ble_conn_params_t is received in an event, both min_conn_interval and + * max_conn_interval will be equal to the connection interval set by the central. + * + * @note If both conn_sup_timeout and max_conn_interval are specified, then the following constraint applies: + * conn_sup_timeout * 4 > (1 + slave_latency) * max_conn_interval + * that corresponds to the following Bluetooth Spec requirement: + * The Supervision_Timeout in milliseconds shall be larger than + * (1 + Conn_Latency) * Conn_Interval_Max * 2, where Conn_Interval_Max is given in milliseconds. + */ +typedef struct +{ + uint16_t min_conn_interval; /**< Minimum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/ + uint16_t max_conn_interval; /**< Maximum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/ + uint16_t slave_latency; /**< Slave Latency in number of connection events, see @ref BLE_GAP_CP_LIMITS.*/ + uint16_t conn_sup_timeout; /**< Connection Supervision Timeout in 10 ms units, see @ref BLE_GAP_CP_LIMITS.*/ +} ble_gap_conn_params_t; + + +/**@brief GAP connection security modes. + * + * Security Mode 0 Level 0: No access permissions at all (this level is not defined by the Bluetooth Core specification).\n + * Security Mode 1 Level 1: No security is needed (aka open link).\n + * Security Mode 1 Level 2: Encrypted link required, MITM protection not necessary.\n + * Security Mode 1 Level 3: MITM protected encrypted link required.\n + * Security Mode 1 Level 4: LESC MITM protected encrypted link required.\n + * Security Mode 2 Level 1: Signing or encryption required, MITM protection not necessary.\n + * Security Mode 2 Level 2: MITM protected signing required, unless link is MITM protected encrypted.\n + */ +typedef struct +{ + uint8_t sm : 4; /**< Security Mode (1 or 2), 0 for no permissions at all. */ + uint8_t lv : 4; /**< Level (1, 2, 3 or 4), 0 for no permissions at all. */ + +} ble_gap_conn_sec_mode_t; + + +/**@brief GAP connection security status.*/ +typedef struct +{ + ble_gap_conn_sec_mode_t sec_mode; /**< Currently active security mode for this connection.*/ + uint8_t encr_key_size; /**< Length of currently active encryption key, 7 to 16 octets (only applicable for bonding procedures). */ +} ble_gap_conn_sec_t; + + +/**@brief Identity Resolving Key. */ +typedef struct +{ + uint8_t irk[BLE_GAP_SEC_KEY_LEN]; /**< Array containing IRK. */ +} ble_gap_irk_t; + + +/**@brief Whitelist structure. */ +typedef struct +{ + ble_gap_addr_t **pp_addrs; /**< Pointer to an array of device address pointers, pointing to addresses to be used in whitelist. NULL if none are given. */ + uint8_t addr_count; /**< Count of device addresses in array, up to @ref BLE_GAP_WHITELIST_ADDR_MAX_COUNT. */ + ble_gap_irk_t **pp_irks; /**< Pointer to an array of Identity Resolving Key (IRK) pointers, each pointing to an IRK in the whitelist. NULL if none are given. */ + uint8_t irk_count; /**< Count of IRKs in array, up to @ref BLE_GAP_WHITELIST_IRK_MAX_COUNT. */ +} ble_gap_whitelist_t; + +/**@brief Channel mask for RF channels used in advertising. */ +typedef struct +{ + uint8_t ch_37_off : 1; /**< Setting this bit to 1 will turn off advertising on channel 37 */ + uint8_t ch_38_off : 1; /**< Setting this bit to 1 will turn off advertising on channel 38 */ + uint8_t ch_39_off : 1; /**< Setting this bit to 1 will turn off advertising on channel 39 */ +} ble_gap_adv_ch_mask_t; + +/**@brief GAP advertising parameters.*/ +typedef struct +{ + uint8_t type; /**< See @ref BLE_GAP_ADV_TYPES. */ + ble_gap_addr_t *p_peer_addr; /**< For @ref BLE_GAP_ADV_TYPE_ADV_DIRECT_IND mode only, known peer address. */ + uint8_t fp; /**< Filter Policy, see @ref BLE_GAP_ADV_FILTER_POLICIES. */ + ble_gap_whitelist_t *p_whitelist; /**< Pointer to whitelist, NULL if no whitelist or the current active whitelist is to be used. */ + uint16_t interval; /**< Advertising interval between 0x0020 and 0x4000 in 0.625 ms units (20ms to 10.24s), see @ref BLE_GAP_ADV_INTERVALS. + - If type equals @ref BLE_GAP_ADV_TYPE_ADV_DIRECT_IND, this parameter must be set to 0 for high duty cycle directed advertising. + - If type equals @ref BLE_GAP_ADV_TYPE_ADV_DIRECT_IND, set @ref BLE_GAP_ADV_INTERVAL_MIN <= interval <= @ref BLE_GAP_ADV_INTERVAL_MAX for low duty cycle advertising.*/ + uint16_t timeout; /**< Advertising timeout between 0x0001 and 0x3FFF in seconds, 0x0000 disables timeout. See also @ref BLE_GAP_ADV_TIMEOUT_VALUES. If type equals @ref BLE_GAP_ADV_TYPE_ADV_DIRECT_IND, this parameter must be set to 0 for High duty cycle directed advertising. */ + ble_gap_adv_ch_mask_t channel_mask; /**< Advertising channel mask. See @ref ble_gap_adv_ch_mask_t. */ +} ble_gap_adv_params_t; + + +/**@brief GAP scanning parameters. */ +typedef struct +{ + uint8_t active : 1; /**< If 1, perform active scanning (scan requests). */ + uint8_t selective : 1; /**< If 1, ignore unknown devices (non whitelisted). */ + ble_gap_whitelist_t * p_whitelist; /**< Pointer to whitelist, NULL if no whitelist or the current active whitelist is to be used. */ + uint16_t interval; /**< Scan interval between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ + uint16_t window; /**< Scan window between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ + uint16_t timeout; /**< Scan timeout between 0x0001 and 0xFFFF in seconds, 0x0000 disables timeout. */ +} ble_gap_scan_params_t; + + +/** @brief Keys that can be exchanged during a bonding procedure. */ +typedef struct +{ + uint8_t enc : 1; /**< Long Term Key and Master Identification. */ + uint8_t id : 1; /**< Identity Resolving Key and Identity Address Information. */ + uint8_t sign : 1; /**< Connection Signature Resolving Key. */ + uint8_t link : 1; /**< Derive the Link Key from the LTK. */ +} ble_gap_sec_kdist_t; + + +/**@brief GAP security parameters. */ +typedef struct +{ + uint8_t bond : 1; /**< Perform bonding. */ + uint8_t mitm : 1; /**< Enable Man In The Middle protection. */ + uint8_t lesc : 1; /**< Enable LE Secure Connection pairing. */ + uint8_t keypress : 1; /**< Enable generation of keypress notifications. */ + uint8_t io_caps : 3; /**< IO capabilities, see @ref BLE_GAP_IO_CAPS. */ + uint8_t oob : 1; /**< Out Of Band data available. */ + uint8_t min_key_size; /**< Minimum encryption key size in octets between 7 and 16. If 0 then not applicable in this instance. */ + uint8_t max_key_size; /**< Maximum encryption key size in octets between min_key_size and 16. */ + ble_gap_sec_kdist_t kdist_own; /**< Key distribution bitmap: keys that the local device will distribute. */ + ble_gap_sec_kdist_t kdist_peer; /**< Key distribution bitmap: keys that the remote device will distribute. */ +} ble_gap_sec_params_t; + + +/**@brief GAP Encryption Information. */ +typedef struct +{ + uint8_t ltk[BLE_GAP_SEC_KEY_LEN]; /**< Long Term Key. */ + uint8_t lesc : 1; /**< Key generated using LE Secure Connections. */ + uint8_t auth : 1; /**< Authenticated Key. */ + uint8_t ltk_len : 6; /**< LTK length in octets. */ +} ble_gap_enc_info_t; + + +/**@brief GAP Master Identification. */ +typedef struct +{ + uint16_t ediv; /**< Encrypted Diversifier. */ + uint8_t rand[BLE_GAP_SEC_RAND_LEN]; /**< Random Number. */ +} ble_gap_master_id_t; + + +/**@brief GAP Signing Information. */ +typedef struct +{ + uint8_t csrk[BLE_GAP_SEC_KEY_LEN]; /**< Connection Signature Resolving Key. */ +} ble_gap_sign_info_t; + +/**@brief GAP LE Secure Connections P-256 Public Key. */ +typedef struct +{ + uint8_t pk[BLE_GAP_LESC_P256_PK_LEN]; /**< LE Secure Connections Elliptic Curve Diffie-Hellman P-256 Public Key. Stored in the standard SMP protocol format: {X,Y} both in little-endian. */ +} ble_gap_lesc_p256_pk_t; + +/**@brief GAP LE Secure Connections DHKey. */ +typedef struct +{ + uint8_t key[BLE_GAP_LESC_DHKEY_LEN]; /**< LE Secure Connections Elliptic Curve Diffie-Hellman Key. Stored in little-endian. */ +} ble_gap_lesc_dhkey_t; + +/**@brief GAP LE Secure Connections OOB data. */ +typedef struct +{ + ble_gap_addr_t addr; /**< Bluetooth address of the device. */ + uint8_t r[BLE_GAP_SEC_KEY_LEN]; /**< Random Number. */ + uint8_t c[BLE_GAP_SEC_KEY_LEN]; /**< Confirm Value. */ +} ble_gap_lesc_oob_data_t; + +/**@brief Event structure for @ref BLE_GAP_EVT_CONNECTED. */ +typedef struct +{ + ble_gap_addr_t peer_addr; /**< Bluetooth address of the peer device. */ + ble_gap_addr_t own_addr; /**< Bluetooth address of the local device used during connection setup. */ + uint8_t role; /**< BLE role for this connection, see @ref BLE_GAP_ROLES */ + uint8_t irk_match :1; /**< If 1, peer device's address resolved using an IRK. */ + uint8_t irk_match_idx :7; /**< Index in IRK list where the address was matched. */ + ble_gap_conn_params_t conn_params; /**< GAP Connection Parameters. */ +} ble_gap_evt_connected_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_DISCONNECTED. */ +typedef struct +{ + uint8_t reason; /**< HCI error code, see @ref BLE_HCI_STATUS_CODES. */ +} ble_gap_evt_disconnected_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_CONN_PARAM_UPDATE. */ +typedef struct +{ + ble_gap_conn_params_t conn_params; /**< GAP Connection Parameters. */ +} ble_gap_evt_conn_param_update_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_SEC_PARAMS_REQUEST. */ +typedef struct +{ + ble_gap_sec_params_t peer_params; /**< Initiator Security Parameters. */ +} ble_gap_evt_sec_params_request_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_SEC_INFO_REQUEST. */ +typedef struct +{ + ble_gap_addr_t peer_addr; /**< Bluetooth address of the peer device. */ + ble_gap_master_id_t master_id; /**< Master Identification for LTK lookup. */ + uint8_t enc_info : 1; /**< If 1, Encryption Information required. */ + uint8_t id_info : 1; /**< If 1, Identity Information required. */ + uint8_t sign_info : 1; /**< If 1, Signing Information required. */ +} ble_gap_evt_sec_info_request_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_PASSKEY_DISPLAY. */ +typedef struct +{ + uint8_t passkey[BLE_GAP_PASSKEY_LEN]; /**< 6-digit passkey in ASCII ('0'-'9' digits only). */ + uint8_t match_request : 1; /**< If 1 requires the application to report the match using @ref sd_ble_gap_auth_key_reply + with either @ref BLE_GAP_AUTH_KEY_TYPE_NONE if there is no match or + @ref BLE_GAP_AUTH_KEY_TYPE_PASSKEY if there is a match. */ +} ble_gap_evt_passkey_display_t; + +/**@brief Event structure for @ref BLE_GAP_EVT_KEY_PRESSED. */ +typedef struct +{ + uint8_t kp_not; /**< Keypress notification type, see @ref BLE_GAP_KP_NOT_TYPES. */ +} ble_gap_evt_key_pressed_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_AUTH_KEY_REQUEST. */ +typedef struct +{ + uint8_t key_type; /**< See @ref BLE_GAP_AUTH_KEY_TYPES. */ +} ble_gap_evt_auth_key_request_t; + +/**@brief Event structure for @ref BLE_GAP_EVT_LESC_DHKEY_REQUEST. */ +typedef struct +{ + ble_gap_lesc_p256_pk_t *p_pk_peer; /**< LE Secure Connections remote P-256 Public Key. This will point to the application-supplied memory + inside the keyset during the call to @ref sd_ble_gap_sec_params_reply. */ + uint8_t oobd_req :1; /**< LESC OOB data required. A call to @ref sd_ble_gap_lesc_oob_data_set is required to complete the procedure. */ +} ble_gap_evt_lesc_dhkey_request_t; + + +/**@brief Security levels supported. + * @note See Bluetooth Specification Version 4.2 Volume 3, Part C, Chapter 10, Section 10.2.1. +*/ +typedef struct +{ + uint8_t lv1 : 1; /**< If 1: Level 1 is supported. */ + uint8_t lv2 : 1; /**< If 1: Level 2 is supported. */ + uint8_t lv3 : 1; /**< If 1: Level 3 is supported. */ + uint8_t lv4 : 1; /**< If 1: Level 4 is supported. */ +} ble_gap_sec_levels_t; + + +/**@brief Encryption Key. */ +typedef struct +{ + ble_gap_enc_info_t enc_info; /**< Encryption Information. */ + ble_gap_master_id_t master_id; /**< Master Identification. */ +} ble_gap_enc_key_t; + + +/**@brief Identity Key. */ +typedef struct +{ + ble_gap_irk_t id_info; /**< Identity Information. */ + ble_gap_addr_t id_addr_info; /**< Identity Address Information. */ +} ble_gap_id_key_t; + + +/**@brief Security Keys. */ +typedef struct +{ + ble_gap_enc_key_t *p_enc_key; /**< Encryption Key, or NULL. */ + ble_gap_id_key_t *p_id_key; /**< Identity Key, or NULL. */ + ble_gap_sign_info_t *p_sign_key; /**< Signing Key, or NULL. */ + ble_gap_lesc_p256_pk_t *p_pk; /**< LE Secure Connections P-256 Public Key. When in debug mode the application must use the value defined + in the Core Bluetooth Specification v4.2 Vol.3, Part H, Section 2.3.5.6.1 */ +} ble_gap_sec_keys_t; + + +/**@brief Security key set for both local and peer keys. */ +typedef struct +{ + ble_gap_sec_keys_t keys_own; /**< Keys distributed by the local device. For LE Secure Connections the encryption key will be generated locally and will always be stored if bonding. */ + ble_gap_sec_keys_t keys_peer; /**< Keys distributed by the remote device. For LE Secure Connections, p_enc_key must always be NULL. */ +} ble_gap_sec_keyset_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_AUTH_STATUS. */ +typedef struct +{ + uint8_t auth_status; /**< Authentication status, see @ref BLE_GAP_SEC_STATUS. */ + uint8_t error_src : 2; /**< On error, source that caused the failure, see @ref BLE_GAP_SEC_STATUS_SOURCES. */ + uint8_t bonded : 1; /**< Procedure resulted in a bond. */ + ble_gap_sec_levels_t sm1_levels; /**< Levels supported in Security Mode 1. */ + ble_gap_sec_levels_t sm2_levels; /**< Levels supported in Security Mode 2. */ + ble_gap_sec_kdist_t kdist_own; /**< Bitmap stating which keys were exchanged (distributed) by the local device. If bonding with LE Secure Connections, the enc bit will be always set. */ + ble_gap_sec_kdist_t kdist_peer; /**< Bitmap stating which keys were exchanged (distributed) by the remote device. If bonding with LE Secure Connections, the enc bit will never be set. */ +} ble_gap_evt_auth_status_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_CONN_SEC_UPDATE. */ +typedef struct +{ + ble_gap_conn_sec_t conn_sec; /**< Connection security level. */ +} ble_gap_evt_conn_sec_update_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_TIMEOUT. */ +typedef struct +{ + uint8_t src; /**< Source of timeout event, see @ref BLE_GAP_TIMEOUT_SOURCES. */ +} ble_gap_evt_timeout_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_RSSI_CHANGED. */ +typedef struct +{ + int8_t rssi; /**< Received Signal Strength Indication in dBm. */ +} ble_gap_evt_rssi_changed_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_ADV_REPORT. */ +typedef struct +{ + ble_gap_addr_t peer_addr; /**< Bluetooth address of the peer device. */ + int8_t rssi; /**< Received Signal Strength Indication in dBm. */ + uint8_t scan_rsp : 1; /**< If 1, the report corresponds to a scan response and the type field may be ignored. */ + uint8_t type : 2; /**< See @ref BLE_GAP_ADV_TYPES. Only valid if the scan_rsp field is 0. */ + uint8_t dlen : 5; /**< Advertising or scan response data length. */ + uint8_t data[BLE_GAP_ADV_MAX_SIZE]; /**< Advertising or scan response data. */ +} ble_gap_evt_adv_report_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_SEC_REQUEST. */ +typedef struct +{ + uint8_t bond : 1; /**< Perform bonding. */ + uint8_t mitm : 1; /**< Man In The Middle protection requested. */ + uint8_t lesc : 1; /**< LE Secure Connections requested. */ + uint8_t keypress : 1; /**< Generation of keypress notifications requested. */ +} ble_gap_evt_sec_request_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST. */ +typedef struct +{ + ble_gap_conn_params_t conn_params; /**< GAP Connection Parameters. */ +} ble_gap_evt_conn_param_update_request_t; + + +/**@brief Event structure for @ref BLE_GAP_EVT_SCAN_REQ_REPORT. */ +typedef struct +{ + int8_t rssi; /**< Received Signal Strength Indication in dBm. */ + ble_gap_addr_t peer_addr; /**< Bluetooth address of the peer device. */ +} ble_gap_evt_scan_req_report_t; + + + +/**@brief GAP event structure. */ +typedef struct +{ + uint16_t conn_handle; /**< Connection Handle on which event occurred. */ + union /**< union alternative identified by evt_id in enclosing struct. */ + { + ble_gap_evt_connected_t connected; /**< Connected Event Parameters. */ + ble_gap_evt_disconnected_t disconnected; /**< Disconnected Event Parameters. */ + ble_gap_evt_conn_param_update_t conn_param_update; /**< Connection Parameter Update Parameters. */ + ble_gap_evt_sec_params_request_t sec_params_request; /**< Security Parameters Request Event Parameters. */ + ble_gap_evt_sec_info_request_t sec_info_request; /**< Security Information Request Event Parameters. */ + ble_gap_evt_passkey_display_t passkey_display; /**< Passkey Display Event Parameters. */ + ble_gap_evt_key_pressed_t key_pressed; /**< Key Pressed Event Parameters. */ + ble_gap_evt_auth_key_request_t auth_key_request; /**< Authentication Key Request Event Parameters. */ + ble_gap_evt_lesc_dhkey_request_t lesc_dhkey_request; /**< LE Secure Connections DHKey calculation request. */ + ble_gap_evt_auth_status_t auth_status; /**< Authentication Status Event Parameters. */ + ble_gap_evt_conn_sec_update_t conn_sec_update; /**< Connection Security Update Event Parameters. */ + ble_gap_evt_timeout_t timeout; /**< Timeout Event Parameters. */ + ble_gap_evt_rssi_changed_t rssi_changed; /**< RSSI Event parameters. */ + ble_gap_evt_adv_report_t adv_report; /**< Advertising Report Event Parameters. */ + ble_gap_evt_sec_request_t sec_request; /**< Security Request Event Parameters. */ + ble_gap_evt_conn_param_update_request_t conn_param_update_request; /**< Connection Parameter Update Parameters. */ + ble_gap_evt_scan_req_report_t scan_req_report; /**< Scan Request Report parameters. */ + } params; /**< Event Parameters. */ + +} ble_gap_evt_t; + + +/**@brief Channel Map option. + * Used with @ref sd_ble_opt_get to get the current channel map + * or @ref sd_ble_opt_set to set a new channel map. When setting the + * channel map, it applies to all current and future connections. When getting the + * current channel map, it applies to a single connection and the connection handle + * must be supplied. + * + * @note Setting the channel map may take some time, depending on connection parameters. + * The time taken may be different for each connection and the get operation will + * return the previous channel map until the new one has taken effect. + * + * @note After setting the channel map, by spec it can not be set again until at least 1 s has passed. + * See Bluetooth Specification Version 4.1 Volume 2, Part E, Section 7.3.46. + * + * @retval ::NRF_SUCCESS Get or set successful. + * @retval ::NRF_ERROR_BUSY Channel map was set again before enough time had passed. + * @retval ::NRF_ERROR_INVALID_STATE Invalid state to perform operation. + * @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid connection handle supplied for get. + * @retval ::NRF_ERROR_NOT_SUPPORTED Returned by sd_ble_opt_set in peripheral-only SoftDevices. + * + */ +typedef struct +{ + uint16_t conn_handle; /**< Connection Handle (only applicable for get) */ + uint8_t ch_map[5]; /**< Channel Map (37-bit). */ +} ble_gap_opt_ch_map_t; + + +/**@brief Local connection latency option. + * + * Local connection latency is a feature which enables the slave to improve + * current consumption by ignoring the slave latency set by the peer. The + * local connection latency can only be set to a multiple of the slave latency, + * and cannot be longer than half of the supervision timeout. + * + * Used with @ref sd_ble_opt_set to set the local connection latency. The + * @ref sd_ble_opt_get is not supported for this option, but the actual + * local connection latency (unless set to NULL) is set as a return parameter + * when setting the option. + * + * @note The latency set will be truncated down to the closest slave latency event + * multiple, or the nearest multiple before half of the supervision timeout. + * + * @note The local connection latency is disabled by default, and needs to be enabled for new + * connections and whenever the connection is updated. + * + * @retval ::NRF_SUCCESS Set successfully. + * @retval ::NRF_ERROR_NOT_SUPPORTED Get is not supported. + * @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid connection handle parameter. + */ +typedef struct +{ + uint16_t conn_handle; /**< Connection Handle */ + uint16_t requested_latency; /**< Requested local connection latency. */ + uint16_t * p_actual_latency; /**< Pointer to storage for the actual local connection latency (can be set to NULL to skip return value). */ +} ble_gap_opt_local_conn_latency_t; + + +/**@brief Passkey Option. + * + * Structure containing the passkey to be used during pairing. This can be used with @ref + * sd_ble_opt_set to make the SoftDevice use a pre-programmed passkey for authentication + * instead of generating a random one. + * + * @note @ref sd_ble_opt_get is not supported for this option. + * + */ +typedef struct +{ + uint8_t * p_passkey; /**< Pointer to 6-digit ASCII string (digit 0..9 only, no NULL termination) passkey to be used during pairing. If this is NULL, the SoftDevice will generate a random passkey if required.*/ +} ble_gap_opt_passkey_t; + + +/**@brief Custom Privacy Option. + * + * This structure is used with both @ref sd_ble_opt_set (as input) and with + * @ref sd_ble_opt_get (as output). + * + * Structure containing: + * - A pointer to an IRK to set (if input), or a place to store a read IRK (if output). + * - A private address refresh cycle. + * + * @note The specified address cycle interval is used when the address cycle mode is + * @ref BLE_GAP_ADDR_CYCLE_MODE_AUTO. If 0 is given, the address will not be automatically + * refreshed at all. The default interval is @ref BLE_GAP_DEFAULT_PRIVATE_ADDR_CYCLE_INTERVAL_S. + * + * @note If the current address cycle mode is @ref BLE_GAP_ADDR_CYCLE_MODE_AUTO, the address will immediately be + * refreshed when a custom privacy option is set. A new address can be generated manually by calling + * @ref sd_ble_gap_address_set with the same type again. + * + * @note If the IRK is updated, the new IRK becomes the one to be distributed in all + * bonding procedures performed after @ref sd_ble_opt_set returns. + * + * @retval ::NRF_SUCCESS Set or read successfully. + * @retval ::NRF_ERROR_INVALID_ADDR The pointer to IRK storage is invalid. + */ +typedef struct +{ + ble_gap_irk_t * p_irk; /**< When input: Pointer to custom IRK, or NULL to use/reset to the device's default IRK. When output: Pointer to where the current IRK is to be stored, or NULL to not read out the IRK. */ + uint16_t interval_s; /**< When input: Custom private address cycle interval in seconds. When output: The current private address cycle interval. */ +} ble_gap_opt_privacy_t; + + +/**@brief Scan request report option. + * + * This can be used with @ref sd_ble_opt_set to make the SoftDevice send + * @ref BLE_GAP_EVT_SCAN_REQ_REPORT events. + * + * @note Due to the limited space reserved for scan request report events, + * not all received scan requests will be reported. + * + * @note If whitelisting is used, only whitelisted requests are reported. + * + * @retval ::NRF_SUCCESS Set successfully. + * @retval ::NRF_ERROR_INVALID_STATE When advertising is ongoing while the option is set. + */ +typedef struct +{ + uint8_t enable : 1; /**< Enable scan request reports. */ +} ble_gap_opt_scan_req_report_t; + +/**@brief Compatibility mode option. + * + * This can be used with @ref sd_ble_opt_set to enable and disable + * compatibility modes. Compatibility modes are disabled by default. + * + * @note Compatibility mode 1 enables interoperability with devices that do not support + * a value of 0 for the WinOffset parameter in the Link Layer CONNECT_REQ packet. + * + * @retval ::NRF_SUCCESS Set successfully. + * @retval ::NRF_ERROR_INVALID_STATE When connection creation is ongoing while mode 1 is set. + */ +typedef struct +{ + uint8_t mode_1_enable : 1; /**< Enable compatibility mode 1.*/ +} ble_gap_opt_compat_mode_t; + +/**@brief Option structure for GAP options. */ +typedef union +{ + ble_gap_opt_ch_map_t ch_map; /**< Parameters for the Channel Map option. */ + ble_gap_opt_local_conn_latency_t local_conn_latency; /**< Parameters for the Local connection latency option */ + ble_gap_opt_passkey_t passkey; /**< Parameters for the Passkey option.*/ + ble_gap_opt_privacy_t privacy; /**< Parameters for the Custom privacy option. */ + ble_gap_opt_scan_req_report_t scan_req_report; /**< Parameters for the scan request report option.*/ + ble_gap_opt_compat_mode_t compat_mode; /**< Parameters for the compatibility mode option.*/ +} ble_gap_opt_t; +/**@} */ + + + +#ifdef __cplusplus +} +#endif +#endif // BLE_GAP_H__ + +/** + @} +*/ diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/ble_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/ble_lib_mock.c new file mode 100644 index 0000000..2bb73fb --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/ble_lib_mock.c @@ -0,0 +1,317 @@ +#include "ble_lib.h" + +#include "callback_generator_lib.h" +#include "data_generator_lib.h" + +#include "debug_lib.h" +#include "app_fifo.h" + + +#define TX_FIFO_SIZE 1024 + +static uint8_t ble_state; /**< The current BLE-state. */ + + +static ble_on_receive_callback_t external_ble_on_receive_callback = NULL; /**< The external on receive callback function */ +static ble_on_transmit_callback_t external_ble_on_transmit_callback = NULL; /**< The external on transmit callback function */ +static ble_on_connect_callback_t external_ble_on_connect_callback = NULL; /**< The external on connect callback function */ +static ble_on_disconnect_callback_t external_ble_on_disconnect_callback = NULL; /**< The external on disconnect callback function */ +static ble_on_scan_timeout_callback_t external_ble_on_scan_timeout_callback = NULL; /**< The external on scan timeout callback function */ +static ble_on_scan_report_callback_t external_ble_on_scan_report_callback = NULL; /**< The external on scan report callback function */ + + +static app_fifo_t tx_fifo; /**< The transmit FIFO to check functionallity of transmit. */ +static uint8_t tx_fifo_buf[TX_FIFO_SIZE]; /**< The buffer of the transmit FIFO. */ + + + +static void ble_nus_on_receive_callback(uint8_t * p_data, uint16_t length); +static void ble_nus_on_transmit_complete_callback(void); +static void ble_on_connect_callback(void); +static void ble_on_disconnect_callback(void); +static void ble_on_scan_report_callback(ble_gap_evt_adv_report_t* scan_report); +static void ble_on_scan_timeout_callback(void); + + + +ret_code_t ble_init(void) { + + + callback_generator_ble_on_connect_init(); + callback_generator_ble_on_connect_set_handler(ble_on_connect_callback); + callback_generator_ble_on_disconnect_init(); + callback_generator_ble_on_disconnect_set_handler(ble_on_disconnect_callback); + callback_generator_ble_on_receive_init(); + callback_generator_ble_on_receive_set_handler(ble_nus_on_receive_callback); + callback_generator_ble_on_transmit_complete_init(); + callback_generator_ble_on_transmit_complete_set_handler(ble_nus_on_transmit_complete_callback); + callback_generator_ble_on_scan_report_init(); + callback_generator_ble_on_scan_report_set_handler(ble_on_scan_report_callback); + callback_generator_ble_on_scan_timeout_init(); + callback_generator_ble_on_scan_timeout_set_handler(ble_on_scan_timeout_callback); + + + ret_code_t ret = app_fifo_init(&tx_fifo, tx_fifo_buf, sizeof(tx_fifo_buf)); + if(ret != NRF_SUCCESS) return ret; + + + + // The trigger for the ble_on_connect_callback should be called + callback_generator_ble_on_connect_trigger(); + + + ble_state = BLE_STATE_INACTIVE; + + + return NRF_SUCCESS; +} + + +ret_code_t ble_set_advertising_custom_advdata(uint16_t company_identifier, uint8_t* custom_advdata, uint16_t custom_advdata_size) { + + + return NRF_SUCCESS; + +} + + +ret_code_t ble_start_advertising(void) { + ble_state |= BLE_STATE_ADVERTISING; + + return NRF_SUCCESS; +} + +void ble_stop_advertising(void) { + ble_state &= ~BLE_STATE_ADVERTISING; +} + + + +ret_code_t ble_start_scanning(uint16_t scan_interval_ms, uint16_t scan_window_ms, uint16_t scan_duration_seconds) { + + if(scan_interval_ms == 0 || scan_window_ms == 0 || scan_duration_seconds == 0) + return NRF_ERROR_INVALID_PARAM; + + // The trigger function for ble_on_scan_report_callback has to be called + callback_generator_ble_on_scan_report_trigger(); + + // The trigger function for ble_on_scan_timeout_callback has to be called + the scan timeout period could be set here + uint32_t trigger_timepoint = ((uint32_t) scan_duration_seconds) * 1000; + callback_generator_ble_on_scan_timeout_add_trigger_timepoints(&trigger_timepoint, 1); + callback_generator_ble_on_scan_timeout_trigger(); + + ble_state |= BLE_STATE_SCANNING; + + return NRF_SUCCESS; +} + +void ble_stop_scanning(void) { + + // TODO: Check if this function is called when scanning is manually stopped? + //ble_on_scan_timeout_callback(); + + ble_state &= ~BLE_STATE_SCANNING; +} + + +// For this a callback-generator has to be implemented +/**@brief Handler function that is called when some data were received via the Nordic Uart Service. + * + * @param[in] p_nus Pointer to the nus-identifier. + * @param[in] p_data Pointer to the received data. + * @param[in] length Length of the received data. + */ +static void ble_nus_on_receive_callback(uint8_t * p_data, uint16_t length) { + if(!(ble_state & BLE_STATE_CONNECTED)) + return; + + debug_log("BLE: BLE on receive callback. Len = %u\n", length); + + // TODO: (I think it is ok, but...) The trigger function for receive has to be called (because we don't want to wait for the next ble_on_connect-event to receive sth again!) This doesn't work! + // Could not call its own trigger-function in an executing trigger.. + // callback_generator_ble_on_receive_trigger(); + + if(external_ble_on_receive_callback != NULL) + external_ble_on_receive_callback(p_data, length); +} + + +/**@brief Handler function that is called when data were transmitted via the Nordic Uart Service. + */ +static void ble_nus_on_transmit_complete_callback(void) { + if(!(ble_state & BLE_STATE_CONNECTED)) + return; + + debug_log("BLE: BLE on transmit complete callback\n"); + + if(external_ble_on_transmit_callback != NULL) + external_ble_on_transmit_callback(); +} + + +// For this a callback-generator has to be implemented +/**@brief Handler function that is called when a BLE-connection was established. + */ +static void ble_on_connect_callback(void) { + if(ble_state & BLE_STATE_CONNECTED) + return; + + debug_log("BLE: BLE on connect callback\n"); + + // The trigger function for receive has to be called + callback_generator_ble_on_receive_trigger(); + + // The trigger function for the ble_on_disconnect_callback has be called (because after connecting we should/could disconnect) + callback_generator_ble_on_disconnect_trigger(); + + ble_state |= BLE_STATE_CONNECTED; + if(external_ble_on_connect_callback != NULL) + external_ble_on_connect_callback(); +} + + +// For this a callback-generator has to be implemented +/**@brief Handler function that is called when disconnecting from an exisiting BLE-connection. + */ +static void ble_on_disconnect_callback(void) { + if(!(ble_state & BLE_STATE_CONNECTED)) + return; + + debug_log("BLE: BLE on disconnect callback\n"); + + // The trigger function for the ble_on_connect_callback has be called + callback_generator_ble_on_connect_trigger(); + + ble_state &= ~BLE_STATE_CONNECTED; + if(external_ble_on_disconnect_callback != NULL) + external_ble_on_disconnect_callback(); +} + + +// For this a callback-generator has to be implemented +/**@brief Handler function that is called when there is an advertising report event during scanning. + * + * @param[in] scan_report Pointer to the advertsising report event. + */ +static void ble_on_scan_report_callback(ble_gap_evt_adv_report_t* scan_report) { + if(!(ble_state & BLE_STATE_SCANNING)) + return; + if(external_ble_on_scan_report_callback != NULL) + external_ble_on_scan_report_callback(scan_report); + + debug_log("BLE: BLE on scan report callback. RSSI: %d\n", scan_report->rssi); +} + +// For this a callback-generator has to be implemented +/**@brief Handler function that is called when the scan process timed-out. + */ +static void ble_on_scan_timeout_callback(void) { + if(!(ble_state & BLE_STATE_SCANNING)) + return; + + ble_state &= ~BLE_STATE_SCANNING; + if(external_ble_on_scan_timeout_callback != NULL) + external_ble_on_scan_timeout_callback(); + + debug_log("BLE: BLE on scan timeout callback\n"); +} + + +// Implement a data-generator function for that +// BLE_GAP_ADDR_LEN = 6 from ble_gap.h +void ble_get_MAC_address(uint8_t* MAC_address) { + data_generator_ble_get_MAC_address(MAC_address, BLE_GAP_ADDR_LEN); +} + + + +ble_state_t ble_get_state(void) { + if(ble_state & BLE_STATE_CONNECTED) { // BLE_STATE_CONNECTED has higher priority than BLE_STATE_SCANNING and BLE_STATE_ADVERTISING! + return BLE_STATE_CONNECTED; + } else if (ble_state & BLE_STATE_SCANNING) { // BLE_STATE_SCANNING has higher priority than BLE_STATE_ADVERTISING! + return BLE_STATE_SCANNING; + } else if(ble_state & BLE_STATE_ADVERTISING) { + return BLE_STATE_ADVERTISING; + } + return BLE_STATE_INACTIVE; +} + + + +ret_code_t ble_transmit(uint8_t* data, uint16_t len) { + // If not connected, return invalid state + if(!(ble_state & BLE_STATE_CONNECTED)) + return NRF_ERROR_INVALID_STATE; + + if(len > 20) + return NRF_ERROR_INVALID_PARAM; + + + uint32_t len_32 = len; + ret_code_t ret = app_fifo_write(&tx_fifo, data, &len_32); + if(ret != NRF_SUCCESS) return ret; + + // The trigger function for ble_on_transmit_complete_callback has to be called + the "transmit-time" could be set here + uint32_t trigger_timepoint = 10; + callback_generator_ble_on_transmit_complete_add_trigger_timepoints(&trigger_timepoint, 1); + callback_generator_ble_on_transmit_complete_trigger(); + + + + return NRF_SUCCESS; +} + +/**@brief (Private) Function to retrive the current number of bytes in the transmit-fifo of the simulated ble-interface (only for testing purposes). + * + * @retval Number of bytes in the transmit-fifo. + */ +uint32_t ble_transmit_fifo_get_size(void) { + uint32_t len = 0; + app_fifo_read(&tx_fifo, NULL, &len); + return len; +} + +/**@brief (Private) Function to retrive the read a certain number of bytes from the transmit-fifo of the simulated ble-interface (only for testing purposes). + * + * @retval NRF_SUCCESS If the bytes could be read successfully. + * NRF_ERROR_NOT_FOUND If len bytes were not found + */ +ret_code_t ble_transmit_fifo_read(uint8_t* data, uint32_t len) { + uint32_t available_len = ble_transmit_fifo_get_size(); + if(available_len < len) + return NRF_ERROR_NOT_FOUND; + + return app_fifo_read(&tx_fifo, data, &len); +} + + +void ble_disconnect(void) { + ble_on_disconnect_callback(); +} + + + +void ble_set_on_receive_callback(ble_on_receive_callback_t ble_on_receive_callback) { + external_ble_on_receive_callback = ble_on_receive_callback; +} + +void ble_set_on_transmit_callback(ble_on_transmit_callback_t ble_on_transmit_callback) { + external_ble_on_transmit_callback = ble_on_transmit_callback; +} + +void ble_set_on_connect_callback(ble_on_connect_callback_t ble_on_connect_callback) { + external_ble_on_connect_callback = ble_on_connect_callback; +} + +void ble_set_on_disconnect_callback(ble_on_disconnect_callback_t ble_on_disconnect_callback) { + external_ble_on_disconnect_callback = ble_on_disconnect_callback; +} + +void ble_set_on_scan_timeout_callback(ble_on_scan_timeout_callback_t ble_on_scan_timeout_callback) { + external_ble_on_scan_timeout_callback = ble_on_scan_timeout_callback; +} + +void ble_set_on_scan_report_callback(ble_on_scan_report_callback_t ble_on_scan_report_callback) { + external_ble_on_scan_report_callback = ble_on_scan_report_callback; +} + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_internal_lib.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_internal_lib.h new file mode 100644 index 0000000..ee077bf --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_internal_lib.h @@ -0,0 +1,402 @@ +/** @file + * + * + * @brief This module provides an easy to use callback-generating capability for creating asynchronous events after triggering. + * + * @details The module provides macros to define the needed functions and data-types. + * The application can use this module to create asynchronous events at specific timepoints after the application triggers the events. + * Therefore the application has to add a timepoint-array for each trigger. When the trigger()-function is called, this module + * generates interrupts at the timepoints of this timepoint-array (in milliseconds). + * Example: The application wants to simulate 5 interrupts after the accelerometer has been initialized: + * - The application (or the test-suite) has to add the timepoints for the trigger: here [100, 100, 100, 100, 100]. + * - The init-function of the accelerometer is the "trigger", so it calls the trigger()-function of this module to start the interrupt-creating process. + * Each time an interrupt/callback is generated an internal callback-handler function is called. This function is not implemented, and has to be implemented by the application. + * Normally, this internal callback-handler calls a "generator" and a "handler"-function. The generator-function generates data for the actual handler-function that should be called afterwords. + * Example: To embed the interrupt/callback-creating process into the application like the accelerometer module, the ISR of the accelerometer has e.g. a parameter "polarity" of the pin. + * - That means the callback-creating process needs to generate a "polarity" (by the generator-function) and has to call the ISR (handler-function) with this "polarity". + * - CALLBACK_IMPLEMENTATION(ACCEL) { + * uint8_t polarity; + * callback_generator_ACCEL_get_generator()(&polarity); + * callback_generator_ACCEL_get_handler()(polarity); + * } + * - The generator-function has to be implemented by the test-suite. The application (or test-suite) can also get the current state of the callback-creating process by calling the functions: + * callback_generator_ACCEL_get_trigger_index() or callback_generator_ACCEL_get_timepoint_index() + * + * The macros get a NAME parameter. This parameter represents the individual-name for one callback-creating process (in the following the NAME is X). + * - CALLBACK_HANDLER_DECLARATION(NAME, RET, ...) declares the handler-function type (NAME_handler_t) that should be called after a callback is created and the data are generated + * Example: CALLBACK_HANDLER_DECLARATION(X, void, uint8_t x) + * - CALLBACK_GENERATOR_DECLARATION(NAME, RET, ...) declares the generator-function type (NAME_generator_t) that should be called after a callback is created to generate the data for this callback + * Example: CALLBACK_HANDLER_DECLARATION(X, void, uint8_t* x) + * - CALLBACK_DECLARATION(NAME) declares all the functions that are needed to configure and start the callback-creating process + * Example: CALLBACK_DECLARATION(X) + * - CALLBACK_IMPLEMENTATION(NAME) implements all the functions that are needed to configure and start the callback-creating process. + * Furthermore it creates a function that is called when a callback is created. This function is not implementation, so the application needs to do this + * Example: CALLBACK_IMPLEMENTATION(X) { + * uint8_t polarity; + * callback_generator_ACCEL_get_generator()(&polarity); + * callback_generator_ACCEL_get_handler()(polarity); + * } + * + * The following functions are used to configure the callback-creating process. They can be devided in functions the test-suite calls/implements and functions the application calls/implements: + * + * - uint8_t callback_generator_X_init(void) + * Initializes the callback-creating process for X. + * @retval 1 on success + * @retval 0 on failure + * This should be called by the application (e.g. in accel_init()). + * - void callback_generator_X_reset(void) + * Clears the internal structures and states to have a clean callback-creating process again. + * This should be called by the test-suite or application. + * - void callback_generator_X_set_handler(X_handler_t) + * Sets the ISR/Callback-handler that should be called after the callback and the data for the callback is generated. + * @param[in] X_handler_t The handler that should be called after the callback and the data for the callback is generated (could also be NULL --> there will be no callback for the application) + * This should be set by the application. + * - void callback_generator_X_set_generator(X_generator_t) + * Sets the generator-function that should be called to generate the data for a callback. + * @param[in] X_generator_t The generator-function that generated the data for a callback (could also be NULL --> the default implementation should be taken). + * This should be set by the test-suite. + * - void callback_generator_X_trigger(void) + * Triggers the callback-creating process for one added timepoint-array. + * This should be called by the application (e.g. accel_init() or accel_reset_interrupt()). + * - void callback_generator_X_add_trigger_timepoints(uint32_t* timepoint_array, uint32_t len) + * Adds callback-timepoints in milliseconds for one trigger (when the trigger()-function is called, the callback will be generated at these timepoints). + * @param[in] timepoint_array Array of timepoints in milliseconds when the interrupt/callback should be generated after a trigger()-call. + * @param[in] len Number of timepoints in the timepoint_array. + * This should be called by the test-suite. + * - X_handler_t callback_generator_X_get_handler(void) + * Function to retrieve the current handler-function. + * @retval The handler-function. + * This could be called by the test-suite. + * - X_generator_t callback_generator_X_get_generator(void) + * Function to retrieve the current generator-function. + * @retval The generator-function. + * This could be called by the test-suite. + * - uint32_t callback_generator_X_get_trigger_index(void) + * Function to retrieve the trigger index that is currently processed. + * @retval The current trigger-index that is currently processed. + * This could be called by the test-suite. + * - uint32_t callback_generator_X_get_timepoint_index(void) + * Function to retrieve the timepoint index (within the current trigger) that is currently processed. + * @retval The current timpoint-index (within the current trigger) that is currently processed. + * This could be called by the test-suite. + * + * + * + * @note It is not allowed to call the trigger()-function before the init()-function. All the other functions can be called beforehand without problems. + * If the trigger()-function is called before some timepoint(s) has been added for this trigger, it will be delayed, until new timepoint(s) has been added. + * If the trigger()-function is called while another trigger is processed, it will be ignored. + */ + +#ifndef CALLBACK_GENERATOR_INTERNAL_LIB_H +#define CALLBACK_GENERATOR_INTERNAL_LIB_H + + +#include "stdint.h" +#include "timer_lib.h" +#include "stdio.h" +#include +#include "pthread.h" + + + +#define CALLBACK_HANDLER_FUNCTION_DECLARATION(NAME, RET, ...) \ +typedef RET (* NAME##_handler_t) (__VA_ARGS__) + +#define CALLBACK_GENERATOR_FUNCTION_DECLARATION(NAME, RET, ...) \ +typedef RET (* NAME##_generator_t) (__VA_ARGS__) + +#define CALLBACK_GENERATOR_DECLARATION(NAME) \ +uint8_t callback_generator_##NAME##_init(void); \ +void callback_generator_##NAME##_reset(void); \ +void callback_generator_##NAME##_set_handler(NAME##_handler_t handler); \ +NAME##_handler_t callback_generator_##NAME##_get_handler(void); \ +void callback_generator_##NAME##_set_generator(NAME##_generator_t generator); \ +NAME##_generator_t callback_generator_##NAME##_get_generator(void); \ +void callback_generator_##NAME##_trigger(void) ; \ +void callback_generator_##NAME##_add_trigger_timepoints(uint32_t* timepoint_array, uint32_t len); \ +uint8_t callback_generator_##NAME##_is_ready(void); \ +uint32_t callback_generator_##NAME##_get_trigger_index(void); \ +uint32_t callback_generator_##NAME##_get_timepoint_index(void); + + + + +#define CALLBACK_GENERATOR_IMPLEMENTATION(NAME) \ +static NAME##_handler_t NAME##_handler = NULL; \ +static NAME##_generator_t NAME##_generator = NULL; \ +static std::vector > NAME##_trigger_vectors; \ +static volatile uint8_t NAME##_pending_trigger = 0; \ +static volatile uint8_t NAME##_processing_trigger = 0; \ +static volatile uint32_t NAME##_trigger_index = 0; \ +static volatile uint32_t NAME##_timepoint_index = 0; \ +static pthread_mutex_t NAME##_critical_section_mutex; \ +static volatile uint8_t NAME##_start_timer = 0; \ +static volatile uint64_t NAME##_timer_end_timepoint_microseconds = 0; \ +static pthread_t NAME##_thread_handle; \ +static void callback_generator_##NAME##_internal_handler(void); \ +static void callback_generator_##NAME##_stop_timer(void) { \ + NAME##_trigger_index++; \ + NAME##_timepoint_index = 0; \ + NAME##_processing_trigger = 0; \ + NAME##_start_timer = 0; \ +} \ +static void callback_generator_##NAME##_start_timer(void) { \ + NAME##_processing_trigger = 1; \ + NAME##_timepoint_index = 0; \ + uint32_t trigger_len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index < trigger_len) { \ + uint32_t timepoint_len = (uint32_t) NAME##_trigger_vectors[NAME##_trigger_index].size(); \ + if(NAME##_timepoint_index < timepoint_len) { \ + uint64_t timeout_microseconds = ((uint64_t)(NAME##_trigger_vectors[NAME##_trigger_index][NAME##_timepoint_index])) * ((uint64_t)1000); \ + NAME##_timer_end_timepoint_microseconds = timer_get_microseconds_since_start() + timeout_microseconds; \ + NAME##_start_timer = 1; \ + } else { \ + callback_generator_##NAME##_stop_timer(); \ + } \ + } \ +} \ +static void* callback_generator_##NAME##_thread_handler(void* ptr) { \ + while(1) { \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + if(NAME##_start_timer && timer_get_microseconds_since_start() >= NAME##_timer_end_timepoint_microseconds) { \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ + callback_generator_##NAME##_internal_handler(); \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + NAME##_timepoint_index++; \ + uint32_t trigger_len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index < trigger_len) { \ + uint32_t timepoint_len = (uint32_t) NAME##_trigger_vectors[NAME##_trigger_index].size(); \ + if(NAME##_timepoint_index < timepoint_len) { \ + uint64_t timeout_microseconds = ((uint64_t)(NAME##_trigger_vectors[NAME##_trigger_index][NAME##_timepoint_index])) * ((uint64_t)1000); \ + NAME##_timer_end_timepoint_microseconds = timer_get_microseconds_since_start() + timeout_microseconds; \ + NAME##_start_timer = 1; \ + } else { \ + callback_generator_##NAME##_stop_timer(); \ + } \ + } else { \ + callback_generator_##NAME##_stop_timer(); \ + } \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ + } \ + return NULL; \ +} \ +void callback_generator_##NAME##_reset(void) { \ + NAME##_generator = NULL; \ + NAME##_handler = NULL; \ + NAME##_processing_trigger = 0; \ + NAME##_pending_trigger = 0; \ + NAME##_trigger_vectors.clear(); \ + NAME##_trigger_index = 0; \ + NAME##_timepoint_index = 0; \ + NAME##_start_timer = 0; \ +} \ +uint8_t callback_generator_##NAME##_init(void) { \ + static uint8_t init_done = 0; \ + if(!init_done) { \ + pthread_mutex_init (&NAME##_critical_section_mutex, NULL); \ + pthread_attr_t attr; \ + pthread_attr_init(&attr); \ + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); \ + pthread_create(&NAME##_thread_handle, &attr, callback_generator_##NAME##_thread_handler, NULL); \ + pthread_attr_destroy(&attr); \ + } \ + init_done = 1; \ + return 1; \ +} \ +void callback_generator_##NAME##_set_handler(NAME##_handler_t handler) { \ + NAME##_handler = handler; \ +} \ +NAME##_handler_t callback_generator_##NAME##_get_handler(void) { \ + return NAME##_handler; \ +} \ +void callback_generator_##NAME##_set_generator(NAME##_generator_t generator) { \ + NAME##_generator = generator; \ +} \ +NAME##_generator_t callback_generator_##NAME##_get_generator(void) { \ + return NAME##_generator; \ +} \ +void callback_generator_##NAME##_trigger(void) { \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + uint32_t len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index < len) { \ + if(!NAME##_processing_trigger) { \ + NAME##_processing_trigger = 1; \ + NAME##_pending_trigger = 0; \ + callback_generator_##NAME##_start_timer(); \ + } \ + } else { \ + NAME##_pending_trigger = 1; \ + NAME##_processing_trigger = 0; \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ +} \ +void callback_generator_##NAME##_add_trigger_timepoints(uint32_t* timepoint_array, uint32_t len) { \ + if(len == 0) \ + return; \ + std::vector timepoint_vector; \ + for(uint32_t i = 0; i < len; i++) { \ + timepoint_vector.push_back(timepoint_array[i]); \ + } \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + NAME##_trigger_vectors.push_back(timepoint_vector); \ + if(NAME##_pending_trigger) { \ + NAME##_processing_trigger = 1; \ + NAME##_pending_trigger = 0; \ + callback_generator_##NAME##_start_timer(); \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ +} \ +uint8_t callback_generator_##NAME##_is_ready(void) { \ + uint8_t ready = 0; \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + uint32_t trigger_len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index >= trigger_len) { \ + ready = 1; \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ + return ready; \ +} \ +uint32_t callback_generator_##NAME##_get_trigger_index(void) { \ + return NAME##_trigger_index; \ +} \ +uint32_t callback_generator_##NAME##_get_timepoint_index(void) { \ + return NAME##_timepoint_index; \ +} \ +static void callback_generator_##NAME##_internal_handler(void) + + +/* +#define CALLBACK_GENERATOR_IMPLEMENTATION(NAME) \ +static uint32_t NAME##_timer_id; \ +static NAME##_handler_t NAME##_handler = NULL; \ +static NAME##_generator_t NAME##_generator = NULL; \ +static std::vector > NAME##_trigger_vectors; \ +static volatile uint8_t NAME##_pending_trigger = 0; \ +static volatile uint8_t NAME##_processing_trigger = 0; \ +static volatile uint32_t NAME##_trigger_index = 0; \ +static volatile uint32_t NAME##_timepoint_index = 0; \ +static pthread_mutex_t NAME##_critical_section_mutex; \ +static void callback_generator_##NAME##_internal_handler(void); \ +static void callback_generator_##NAME##_stop_timer(void) { \ + NAME##_trigger_index++; \ + NAME##_timepoint_index = 0; \ + NAME##_processing_trigger = 0; \ + timer_stop_timer(NAME##_timer_id); \ +} \ +static void callback_generator_##NAME##_start_timer(void) { \ + NAME##_processing_trigger = 1; \ + NAME##_timepoint_index = 0; \ + uint32_t trigger_len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index < trigger_len) { \ + uint32_t timepoint_len = (uint32_t) NAME##_trigger_vectors[NAME##_trigger_index].size(); \ + if(NAME##_timepoint_index < timepoint_len) { \ + uint64_t timeout_microseconds = ((uint64_t)(NAME##_trigger_vectors[NAME##_trigger_index][NAME##_timepoint_index])) * ((uint64_t)1000); \ + timer_start_timer(NAME##_timer_id, timeout_microseconds, NULL); \ + } else { \ + callback_generator_##NAME##_stop_timer(); \ + } \ + } \ +} \ +void callback_generator_##NAME##_internal_timeout_handler(void * p_context) { \ + callback_generator_##NAME##_internal_handler(); \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + NAME##_timepoint_index++; \ + uint32_t trigger_len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index < trigger_len) { \ + uint32_t timepoint_len = (uint32_t) NAME##_trigger_vectors[NAME##_trigger_index].size(); \ + if(NAME##_timepoint_index < timepoint_len) { \ + uint64_t timeout_microseconds = ((uint64_t)(NAME##_trigger_vectors[NAME##_trigger_index][NAME##_timepoint_index])) * ((uint64_t)1000); \ + timer_start_timer(NAME##_timer_id, timeout_microseconds, NULL); \ + } else { \ + callback_generator_##NAME##_stop_timer(); \ + } \ + } else { \ + callback_generator_##NAME##_stop_timer(); \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ +} \ +void callback_generator_##NAME##_reset(void) { \ + NAME##_generator = NULL; \ + NAME##_handler = NULL; \ + NAME##_processing_trigger = 0; \ + NAME##_pending_trigger = 0; \ + NAME##_trigger_vectors.clear(); \ + NAME##_trigger_index = 0; \ + NAME##_timepoint_index = 0; \ +} \ +uint8_t callback_generator_##NAME##_init(void) { \ + static uint8_t init_done = 0; \ + if(!init_done) { \ + timer_init(); \ + uint8_t ret = timer_create_timer(&NAME##_timer_id, TIMER_MODE_SINGLE_SHOT, callback_generator_##NAME##_internal_timeout_handler, 1); \ + if(ret == 0) return 0; \ + pthread_mutex_init (&NAME##_critical_section_mutex, NULL); \ + } \ + init_done = 1; \ + return 1; \ +} \ +void callback_generator_##NAME##_set_handler(NAME##_handler_t handler) { \ + NAME##_handler = handler; \ +} \ +NAME##_handler_t callback_generator_##NAME##_get_handler(void) { \ + return NAME##_handler; \ +} \ +void callback_generator_##NAME##_set_generator(NAME##_generator_t generator) { \ + NAME##_generator = generator; \ +} \ +NAME##_generator_t callback_generator_##NAME##_get_generator(void) { \ + return NAME##_generator; \ +} \ +void callback_generator_##NAME##_trigger(void) { \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + uint32_t len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index < len) { \ + if(!NAME##_processing_trigger) { \ + NAME##_processing_trigger = 1; \ + NAME##_pending_trigger = 0; \ + callback_generator_##NAME##_start_timer(); \ + } \ + } else { \ + NAME##_pending_trigger = 1; \ + NAME##_processing_trigger = 0; \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ +} \ +void callback_generator_##NAME##_add_trigger_timepoints(uint32_t* timepoint_array, uint32_t len) { \ + if(len == 0) \ + return; \ + std::vector timepoint_vector; \ + for(uint32_t i = 0; i < len; i++) { \ + timepoint_vector.push_back(timepoint_array[i]); \ + } \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + NAME##_trigger_vectors.push_back(timepoint_vector); \ + if(NAME##_pending_trigger) { \ + NAME##_processing_trigger = 1; \ + NAME##_pending_trigger = 0; \ + callback_generator_##NAME##_start_timer(); \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ +} \ +uint8_t callback_generator_##NAME##_is_ready(void) { \ + uint8_t ready = 0; \ + pthread_mutex_lock(&NAME##_critical_section_mutex); \ + uint32_t trigger_len = (uint32_t) NAME##_trigger_vectors.size(); \ + if(NAME##_trigger_index >= trigger_len) { \ + ready = 1; \ + } \ + pthread_mutex_unlock(&NAME##_critical_section_mutex); \ + return ready; \ +} \ +uint32_t callback_generator_##NAME##_get_trigger_index(void) { \ + return NAME##_trigger_index; \ +} \ +uint32_t callback_generator_##NAME##_get_timepoint_index(void) { \ + return NAME##_timepoint_index; \ +} \ +static void callback_generator_##NAME##_internal_handler(void) +*/ + + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_lib.cc b/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_lib.cc new file mode 100644 index 0000000..001268a --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_lib.cc @@ -0,0 +1,102 @@ +#include "callback_generator_internal_lib.h" +#include "callback_generator_lib.h" +#include + + + +CALLBACK_GENERATOR_IMPLEMENTATION(ACCEL_INT1) { + nrf_drv_gpiote_pin_t pin = 25; + nrf_gpiote_polarity_t action = NRF_GPIOTE_POLARITY_LOTOHI; + + if(callback_generator_ACCEL_INT1_get_generator() != NULL) { + callback_generator_ACCEL_INT1_get_generator()(&pin, &action); + } + + if(callback_generator_ACCEL_INT1_get_handler() != NULL) { + callback_generator_ACCEL_INT1_get_handler()(pin, action); + } +} + + +/** Callback generator implementation for ble_on_connect-callback + * + * @details There is no generator, because we don't need data to be generated. + */ +CALLBACK_GENERATOR_IMPLEMENTATION(ble_on_connect) { + // We don't need to generate any data for the ble_on_connect-handler --> So just directly call the handler + if(callback_generator_ble_on_connect_get_handler() != NULL) { + callback_generator_ble_on_connect_get_handler()(); + } +} + + +/** Callback generator implementation for ble_on_disconnect-callback + * + * @details There is no generator, because we don't need data to be generated. + */ +CALLBACK_GENERATOR_IMPLEMENTATION(ble_on_disconnect) { + // We don't need to generate any data for the ble_on_disconnect-handler --> So just directly call the handler + if(callback_generator_ble_on_disconnect_get_handler() != NULL) { + callback_generator_ble_on_disconnect_get_handler()(); + } +} + + +/** Callback generator implementation for ble_on_receive-callback */ +CALLBACK_GENERATOR_IMPLEMENTATION(ble_on_receive) { + uint8_t data[20]; + uint16_t len = 0; + + if(callback_generator_ble_on_receive_get_generator() != NULL) { + callback_generator_ble_on_receive_get_generator()(data, &len, sizeof(data)); + } + + if(callback_generator_ble_on_receive_get_handler() != NULL) { + callback_generator_ble_on_receive_get_handler()(data, len); + } +} + +/** Callback generator implementation for ble_on_transmit_complete-callback. + * + * @details The trigger-timepoints are added in ble_lib_mock.c not in the unit-test. + * There is no generator, because we don't need data to be generated. + */ +CALLBACK_GENERATOR_IMPLEMENTATION(ble_on_transmit_complete) { + // We don't need to generate any data for the ble_on_transmit_complete-handler --> So just directly call the handler + if(callback_generator_ble_on_transmit_complete_get_handler() != NULL) { + callback_generator_ble_on_transmit_complete_get_handler()(); + } +} + + + +/** Callback generator implementation for ble_on_scan_report-callback */ +CALLBACK_GENERATOR_IMPLEMENTATION(ble_on_scan_report) { + ble_gap_evt_adv_report_t scan_report; + memset(&scan_report, 0, sizeof(scan_report)); + + if(callback_generator_ble_on_scan_report_get_generator() != NULL) { + callback_generator_ble_on_scan_report_get_generator()(&scan_report); + } + + if(callback_generator_ble_on_scan_report_get_handler() != NULL) { + callback_generator_ble_on_scan_report_get_handler()(&scan_report); + } +} + + +/** Callback generator implementation for ble_on_scan_timeout-callback. + * + * @details The trigger-timepoints are added in ble_lib_mock.c not in the unit-test. + * There is no generator, because we don't need data to be generated. + */ +CALLBACK_GENERATOR_IMPLEMENTATION(ble_on_scan_timeout) { + // We don't need to generate any data for the ble_on_scan_timeout-handler --> So just directly call the handler + if(callback_generator_ble_on_scan_timeout_get_handler() != NULL) { + callback_generator_ble_on_scan_timeout_get_handler()(); + } +} + + + + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_lib.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_lib.h new file mode 100644 index 0000000..0159e6d --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/callback_generator_lib.h @@ -0,0 +1,69 @@ +/** @file + * + * + * @brief + */ + +#ifndef CALLBACK_GENERATOR_LIB_H +#define CALLBACK_GENERATOR_LIB_H + +#include "callback_generator_internal_lib.h" +#include "stdint.h" + +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + +/** Accelerometer defines */ +// From nrf51_bitfields.h +#define GPIOTE_CONFIG_POLARITY_LoToHi (1UL) /*!< Task mode: Set pin from OUT[n] task. Event mode: Generate IN[n] event when rising edge on pin. */ +#define GPIOTE_CONFIG_POLARITY_HiToLo (2UL) /*!< Task mode: Clear pin from OUT[n] task. Event mode: Generate IN[n] event when falling edge on pin. */ +#define GPIOTE_CONFIG_POLARITY_Toggle (3UL) /*!< Task mode: Toggle pin from OUT[n]. Event mode: Generate IN[n] when any change on pin. */ + +// From nrf_gpiote.h +typedef enum +{ + NRF_GPIOTE_POLARITY_LOTOHI = GPIOTE_CONFIG_POLARITY_LoToHi, ///< Low to high. + NRF_GPIOTE_POLARITY_HITOLO = GPIOTE_CONFIG_POLARITY_HiToLo, ///< High to low. + NRF_GPIOTE_POLARITY_TOGGLE = GPIOTE_CONFIG_POLARITY_Toggle ///< Toggle. +} nrf_gpiote_polarity_t; + +// From nrf_drv_gpiote.h +typedef uint32_t nrf_drv_gpiote_pin_t; + +// Declare all the needed data-types and functions: +CALLBACK_HANDLER_FUNCTION_DECLARATION(ACCEL_INT1, void, nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action); +CALLBACK_GENERATOR_FUNCTION_DECLARATION(ACCEL_INT1, void, nrf_drv_gpiote_pin_t* pin, nrf_gpiote_polarity_t* action); +CALLBACK_GENERATOR_DECLARATION(ACCEL_INT1); + + +/** BLE defines */ +#include "ble_lib.h" + +// Declare all the needed data-types and functions: +CALLBACK_HANDLER_FUNCTION_DECLARATION(ble_on_connect, void, void); +CALLBACK_GENERATOR_FUNCTION_DECLARATION(ble_on_connect, void, void); +CALLBACK_GENERATOR_DECLARATION(ble_on_connect); + +CALLBACK_HANDLER_FUNCTION_DECLARATION(ble_on_disconnect, void, void); +CALLBACK_GENERATOR_FUNCTION_DECLARATION(ble_on_disconnect, void, void); +CALLBACK_GENERATOR_DECLARATION(ble_on_disconnect); + +CALLBACK_HANDLER_FUNCTION_DECLARATION(ble_on_receive, void, uint8_t* data, uint16_t length); +CALLBACK_GENERATOR_FUNCTION_DECLARATION(ble_on_receive, void, uint8_t* data, uint16_t* length, uint16_t max_len); +CALLBACK_GENERATOR_DECLARATION(ble_on_receive); + +CALLBACK_HANDLER_FUNCTION_DECLARATION(ble_on_transmit_complete, void, void); +CALLBACK_GENERATOR_FUNCTION_DECLARATION(ble_on_transmit_complete, void, void); +CALLBACK_GENERATOR_DECLARATION(ble_on_transmit_complete); + +CALLBACK_HANDLER_FUNCTION_DECLARATION(ble_on_scan_report, void, ble_gap_evt_adv_report_t* scan_report); +CALLBACK_GENERATOR_FUNCTION_DECLARATION(ble_on_scan_report, void, ble_gap_evt_adv_report_t* scan_report); +CALLBACK_GENERATOR_DECLARATION(ble_on_scan_report); + +CALLBACK_HANDLER_FUNCTION_DECLARATION(ble_on_scan_timeout, void, void); +CALLBACK_GENERATOR_FUNCTION_DECLARATION(ble_on_scan_timeout, void, void); +CALLBACK_GENERATOR_DECLARATION(ble_on_scan_timeout); + + + + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_internal_lib.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_internal_lib.h new file mode 100644 index 0000000..c9f2d6c --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_internal_lib.h @@ -0,0 +1,72 @@ +/** @file + * + * + * @brief This module provides an easy to use data-generating capability for creating data (and return values) for "read"-functions (e.g. accel_read_acceleration). + * + * @details The advantage of this abstraction is the independence of the test-suite, because in the data-generating functions the default data could be generated, + * and only if it is needed the test-suite can set its own data-generating function. + * The module provides macros to define the needed functions and data-types. + * Example: + * - DATA_GENERATOR_FUNCTION_DECLARATION(TEST, uint8_t, uint8_t* x, uint8_t* y); + * Declaring the generator-function type + * - DATA_GENERATOR_DECLARATION(TEST, uint8_t, uint8_t* x, uint8_t* y); + * Declaring all the needed data-generating functions + * - DATA_GENERATOR_IMPLEMENTATION(TEST, uint8_t, uint8_t* x, uint8_t* y) { + * if(data_generator_TEST_get_generator() != NULL) + * return data_generator_TEST_get_generator()(x, y); + * + * *x = 0; + * *y = 0; + * return 0; + * } + * Implementation of the default data-generator function + * + * The following functions are provided to control the data-generating module (NAME = X): + * - void data_generator_X_set_generator(X_data_generator_t) + * Function to set a external generator-function (could also be NULL) with the signature defined with the "DATA_GENERATOR_FUNCTION_DECLARATION" macro. + * @param[in] X_data_generator_t The generator function that should be called to generate the data. + * - X_data_generator_t data_generator_X_get_generator(void) + * Function to retrieve the external generator-function (could also be NULL). + * @retval The current external data-generator function. + * - void data_generator_X_reset(void) + * Function to reset the data-generator module. It only set the external generator-function to NULL. + * - RET data_generator_X(...) + * The function that is called by the actual "read"-function. This function has to be implemented by the application to provide default values, + * if no generator-function is set, and to call the generator-function if it is set. + * @params[in/out] The parameter-sequence that are defined by the macros. + * @retval RET The return value of the function setted by the macro. + */ + +#ifndef DATA_GENERATOR_INTERNAL_LIB_H +#define DATA_GENERATOR_INTERNAL_LIB_H + + +#include "stdint.h" +#include "stdio.h" + + +#define DATA_GENERATOR_FUNCTION_DECLARATION(NAME, RET, ...) \ +typedef RET (* NAME##_data_generator_t) (__VA_ARGS__) + +#define DATA_GENERATOR_DECLARATION(NAME, RET, ...) \ +void data_generator_##NAME##_reset(void); \ +NAME##_data_generator_t data_generator_##NAME##_get_generator(void); \ +void data_generator_##NAME##_set_generator(NAME##_data_generator_t data_generator); \ +RET data_generator_##NAME(__VA_ARGS__); + + + +#define DATA_GENERATOR_IMPLEMENTATION(NAME, RET, ...) \ +static NAME##_data_generator_t NAME##_data_generator = NULL; \ +void data_generator_##NAME##_reset(void){ \ + NAME##_data_generator = NULL; \ +} \ +NAME##_data_generator_t data_generator_##NAME##_get_generator(void) { \ + return NAME##_data_generator; \ +} \ +void data_generator_##NAME##_set_generator(NAME##_data_generator_t data_generator) { \ + NAME##_data_generator = data_generator; \ +} \ +RET data_generator_##NAME(__VA_ARGS__) + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_lib.cc b/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_lib.cc new file mode 100644 index 0000000..3addda9 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_lib.cc @@ -0,0 +1,27 @@ +#include "data_generator_internal_lib.h" +#include "data_generator_lib.h" +#include + + +DATA_GENERATOR_IMPLEMENTATION(accel_read_acceleration, ret_code_t, int16_t* accel_x, int16_t* accel_y, int16_t* accel_z, uint8_t* num_samples, uint32_t max_num_samples) { + + if(data_generator_accel_read_acceleration_get_generator() != NULL) { + return data_generator_accel_read_acceleration_get_generator() (accel_x, accel_y, accel_z, num_samples, max_num_samples); + } + + *accel_x = 0; + *accel_y = 0; + *accel_z = 0; + *num_samples = (max_num_samples == 0) ? 0 : 1; + return NRF_SUCCESS; +} + + +DATA_GENERATOR_IMPLEMENTATION(ble_get_MAC_address, void, uint8_t* MAC_address, uint8_t len) { + + memset(MAC_address, 0, len); + if(data_generator_ble_get_MAC_address_get_generator() != NULL) { + data_generator_ble_get_MAC_address_get_generator()(MAC_address, len); + } + +} \ No newline at end of file diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_lib.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_lib.h new file mode 100644 index 0000000..78b0f5f --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/data_generator_lib.h @@ -0,0 +1,23 @@ +/** @file + * + * + * @brief + */ + +#ifndef DATA_GENERATOR_LIB_H +#define DATA_GENERATOR_LIB_H + +#include "data_generator_internal_lib.h" +#include "stdint.h" +#include "sdk_errors.h" // Needed for the definition of ret_code_t and the error-codes + +/** Accelerometer defines */ +DATA_GENERATOR_FUNCTION_DECLARATION(accel_read_acceleration, ret_code_t, int16_t* accel_x, int16_t* accel_y, int16_t* accel_z, uint8_t* num_samples, uint32_t max_num_samples); +DATA_GENERATOR_DECLARATION(accel_read_acceleration, ret_code_t, int16_t* accel_x, int16_t* accel_y, int16_t* accel_z, uint8_t* num_samples, uint32_t max_num_samples); + + +/** BLE defines */ +DATA_GENERATOR_FUNCTION_DECLARATION(ble_get_MAC_address, void, uint8_t* MAC_address, uint8_t len); +DATA_GENERATOR_DECLARATION(ble_get_MAC_address, void, uint8_t* MAC_address, uint8_t len); + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/debug_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/debug_lib_mock.c new file mode 100644 index 0000000..78fd604 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/debug_lib_mock.c @@ -0,0 +1,31 @@ +#include "debug_lib.h" + + +#ifdef DEBUG_LOG_ENABLE + +#include // Needed for the printf-function +#include // Needed for the printf-function +#include // Needed for the vsnprintf-function + + +void debug_init(void) +{ +} + +void debug_log(const char* format, ...) { + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} + +void debug_log_dump(uint8_t * p_buffer, uint32_t len) +{ + debug_log("\r\n"); + for (uint32_t index = 0; index < len; index++) { + debug_log("0x%02X ", p_buffer[index]); + } + debug_log("\r\n"); +} + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/eeprom_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/eeprom_lib_mock.c new file mode 100644 index 0000000..2fc42fd --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/eeprom_lib_mock.c @@ -0,0 +1,176 @@ +#include "eeprom_lib.h" + + +#include "debug_lib.h" + + +#include "string.h" // For memset + +#include "storage_file_lib.h" + + +static uint8_t eeprom_data[EEPROM_SIZE]; /**< Simulator of the external EEPROM data bytes */ + + +static volatile eeprom_operation_t eeprom_operation = EEPROM_NO_OPERATION; /**< The current EEPROM operation */ + + + +/**@brief Function for initializing the in the simulated EEPROM module. + * + * @details If there is no EEPROM file there this function creates one and initializes the file with 0xFF. + * + * + * @retval NRF_SUCCESS If the module was successfully initialized. + */ +ret_code_t eeprom_init(void) { + + + memset(eeprom_data, 0xFF, EEPROM_SIZE); + + //debug_log("EEPROM initialized\n"); + return NRF_SUCCESS; +} + + +/**@brief Function for storing bytes to simulated EEPROM (it is actually not non-blocking). + * + * @details Tries to store the bytes in the simulated EEPROM. + * It updates the file and the internal RAM-array accordingly. + * + * + * @retval NRF_SUCCESS If the store operation was successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing EEPROM operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + */ +ret_code_t eeprom_store_bkgnd(uint32_t address, const uint8_t* tx_data, uint32_t length_tx_data) { + + + + // Check if the specified address is valid. The EEPROM has 256kByte of memory. + if((address + length_tx_data > (EEPROM_SIZE))) + return NRF_ERROR_INVALID_PARAM; + + if(tx_data == NULL) + return NRF_ERROR_INVALID_PARAM; + + + // Check if the EEPROM has already an ongoing operation. + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + return NRF_ERROR_BUSY; + } + + eeprom_operation = EEPROM_STORE_OPERATION; + + // Set data also in the RAM array (to read from it) + memcpy(&eeprom_data[address], tx_data, length_tx_data); + + + eeprom_operation = EEPROM_NO_OPERATION; + + + return NRF_SUCCESS; + +} + +/**@brief Function for storing bytes to simulated EEPROM (it is actually the same as eeprom_store_bkgnd()). + * + * @retval NRF_SUCCESS If the store operation was successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing EEPROM operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + */ +ret_code_t eeprom_store(uint32_t address, const uint8_t* tx_data, uint32_t length_tx_data) { + // Start a background store operation + ret_code_t ret = eeprom_store_bkgnd(address, tx_data, length_tx_data); + if(ret != NRF_SUCCESS) { + return ret; + } + + // Wait until the operation terminates + while(eeprom_get_operation() == EEPROM_STORE_OPERATION); + + return NRF_SUCCESS; +} + + + + + + +/**@brief Function for reading words from simulated EEPROM (it is actually not non-blocking). + * + * + * @details Reads the words from the internal RAM-array. + * + * @retval NRF_SUCCESS If the read operation was successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing EEPROM operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + */ +ret_code_t eeprom_read_bkgnd(uint32_t address, uint8_t* rx_data, uint32_t length_rx_data) { + // Check if the specified address is valid. The EEPROM has 256kByte of memory. + if((address + length_rx_data > (EEPROM_SIZE))) + return NRF_ERROR_INVALID_PARAM; + + if(rx_data == NULL) + return NRF_ERROR_INVALID_PARAM; + + // Check if the EEPROM has already an ongoing operation. + if(eeprom_get_operation() != EEPROM_NO_OPERATION) { + return NRF_ERROR_BUSY; + } + + eeprom_operation = EEPROM_READ_OPERATION; + + // Set data also in array + memcpy(rx_data, &eeprom_data[address], length_rx_data); + + + eeprom_operation = EEPROM_NO_OPERATION; + + return NRF_SUCCESS; + +} + +/**@brief Function for reading words from simulated EEPROM (it is actually the same as eeprom_read_bkgnd()). + * + * @retval NRF_SUCCESS If the read operation was successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing EEPROM operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + */ +ret_code_t eeprom_read(uint32_t address, uint8_t* rx_data, uint32_t length_rx_data) { + // Start a background read operation + ret_code_t ret = eeprom_read_bkgnd(address, rx_data, length_rx_data); + if(ret != NRF_SUCCESS) { + return ret; + } + + // Wait until the operation terminates + while(eeprom_get_operation() == EEPROM_READ_OPERATION); + + return NRF_SUCCESS; + +} + + + + +eeprom_operation_t eeprom_get_operation(void) { + + return eeprom_operation; + +} + + + +uint32_t eeprom_get_size(void) { + return EEPROM_SIZE; +} + + +void eeprom_write_to_file(const char* filename) { + uint8_t* bytes = (uint8_t*) eeprom_data; + uint32_t len = EEPROM_SIZE; + storage_file_write_to_file(filename, bytes, len); +} + + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/flash_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/flash_lib_mock.c new file mode 100644 index 0000000..d411032 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/flash_lib_mock.c @@ -0,0 +1,233 @@ +#include "flash_lib.h" + +#include "debug_lib.h" + + +#include "string.h" // For memset + +#include "storage_file_lib.h" + + + + + + +#define FLASH_SIZE (FLASH_PAGE_SIZE_WORDS*FLASH_NUM_PAGES*sizeof(uint32_t)) /**< Flash size in bytes */ +#define FLASH_NUM_WORDS (FLASH_PAGE_SIZE_WORDS*FLASH_NUM_PAGES) /**< Flash size in words */ + + +static uint32_t flash_words[FLASH_NUM_WORDS]; /**< Simulator of the internal flash words */ + + + + +static volatile flash_operation_t flash_operation = FLASH_NO_OPERATION; /**< The current flash operation ((in simulation it is actually not really used/set) */ + + + +/**@brief Function for initializing the in the simulated flash module. + * + * @details If there is no flash file there this function creates one and initializes the file with 0xFF. + * + * + * @retval NRF_SUCCESS If the module was successfully initialized. + */ +ret_code_t flash_init(void) { + + + + memset(flash_words, 0xFF, FLASH_SIZE); + + + //debug_log("Flash initialized\n"); + return NRF_SUCCESS; +} + + +/**@brief Function for erasing a page in the simulated flash (it is actually not non-blocking). + * + * @details Erases the desired pages by setting the bytes in the pages to 0xFF. + * It updates the file and the internal RAM-array. + * + * + * @retval NRF_SUCCESS If the erased the pages successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing Flash operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + */ +ret_code_t flash_erase_bkgnd(uint32_t page_num, uint16_t num_pages) { + + // Check if we have already an ongoing operation + if((flash_get_operation() & FLASH_STORE_OPERATION) || (flash_get_operation() & FLASH_ERASE_OPERATION)) { + return NRF_ERROR_BUSY; + } + + if(page_num + num_pages > flash_get_page_number()) + return NRF_ERROR_INVALID_PARAM; + + flash_operation = (flash_operation_t) (flash_operation | FLASH_ERASE_OPERATION); + // Reset the Error flag (if it exists) + flash_operation = (flash_operation_t) (flash_operation & ~FLASH_ERASE_ERROR); + + + + uint32_t start_word_address = page_num*flash_get_page_size_words(); + uint32_t number_of_words = num_pages*flash_get_page_size_words(); + + + memset(&flash_words[start_word_address], 0xFF, number_of_words*sizeof(uint32_t)); + + + flash_operation = (flash_operation_t) (flash_operation & ~FLASH_ERASE_OPERATION); + + return NRF_SUCCESS; +} + + +/**@brief Function for erasing a page in the simulated flash (it is actually the same as flash_erase_bkgnd()). + * + * @retval NRF_SUCCESS If the erased the pages successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing Flash operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + * @retval NRF_ERROR_TIMEOUT If the operation timed out (should not happen). + */ +ret_code_t flash_erase(uint32_t page_num, uint16_t num_pages) { + ret_code_t ret = flash_erase_bkgnd(page_num, num_pages); + + // Return if erase operation was not started successfully + if(ret != NRF_SUCCESS) { + return ret; + } + + // Wait for the erase operation to terminate. + while(flash_get_operation() & FLASH_ERASE_OPERATION); + + // Return an error if the erase operation was not successful. + if(flash_get_operation() & FLASH_ERASE_ERROR) { + return NRF_ERROR_TIMEOUT; + } + + return NRF_SUCCESS; + +} + + +/**@brief Function for storing words to simulated flash (it is actually not non-blocking). + * + * @details Tries to store the words in the simulated flash. It mimics the dame behaviour flash has: + * Write from 1 to 0 is ok. From 0 to 1 is not valid/will not work. + * It updates the file and the internal RAM-array accordingly. + * + * + * @retval NRF_SUCCESS If the store operation was successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing Flash operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + */ +ret_code_t flash_store_bkgnd(uint32_t word_num, const uint32_t* p_words, uint16_t length_words) { + + // Check if we have already an ongoing operation + if((flash_get_operation() & FLASH_STORE_OPERATION) || (flash_get_operation() & FLASH_ERASE_OPERATION)) { + return NRF_ERROR_BUSY; + } + + if(p_words == NULL) + return NRF_ERROR_INVALID_PARAM; + + if(((uintptr_t)p_words) % sizeof(uint32_t) != 0) + return NRF_ERROR_INVALID_PARAM; + + if(word_num + length_words > flash_get_page_number()*flash_get_page_size_words()) + return NRF_ERROR_INVALID_PARAM; + + + flash_operation = (flash_operation_t) (flash_operation | FLASH_STORE_OPERATION); + // Reset the Error flag (if it exists) + flash_operation = (flash_operation_t) (flash_operation & ~FLASH_STORE_ERROR); + + + uint32_t start_word_address = word_num; + uint32_t number_of_words = length_words; + + // Simulate the "flash behaviour" of setting bits to zero, but not to one again --> logical & + for(uint16_t i = 0; i < number_of_words; i++) { + flash_words[start_word_address + i] = flash_words[start_word_address + i] & p_words[i]; + } + + // Reset the store operation + flash_operation = (flash_operation_t) (flash_operation & ~FLASH_STORE_OPERATION); + + return NRF_SUCCESS; +} + + +/**@brief Function for storing words to simulated flash (it is actually the same as flash_store_bkgnd()). + * + * @retval NRF_SUCCESS If the store operation was successfully. + * @retval NRF_ERROR_BUSY If there is already an ongoing Flash operation. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + * @retval NRF_ERROR_TIMEOUT If the operation timed out (should not happen). + */ +ret_code_t flash_store(uint32_t word_num, const uint32_t* p_words, uint16_t length_words) { + // Call the non-blocking flash store function. + ret_code_t ret = flash_store_bkgnd(word_num, p_words, length_words); + + // Return if store operation was not started successfully + if(ret != NRF_SUCCESS) { + return ret; + } + + // Wait for the store operation to terminate. + while(flash_get_operation() & FLASH_STORE_OPERATION); + + // Return an error if the store operation was not successful. + if(flash_get_operation() & FLASH_STORE_ERROR) { + return NRF_ERROR_TIMEOUT; + } + + return NRF_SUCCESS; + +} + + +flash_operation_t flash_get_operation(void) { + return flash_operation; +} + +/**@brief Function for reading words from simulated flash. + * + * @details Reads the words from the internal RAM-array. + * + * @retval NRF_SUCCESS If the read operation was successfully. + * @retval NRF_ERROR_INVALID_PARAM If the specified parameters are bad. + */ +ret_code_t flash_read(uint32_t word_num, uint32_t* p_words, uint16_t length_words) { + + + if(word_num + length_words > (flash_get_page_size_words()*flash_get_page_number())) + return NRF_ERROR_INVALID_PARAM; + + if(p_words == NULL) + return NRF_ERROR_INVALID_PARAM; + + if(((uintptr_t)p_words) % sizeof(uint32_t) != 0) + return NRF_ERROR_INVALID_PARAM; + + memcpy(p_words, &flash_words[word_num], length_words*sizeof(uint32_t)); + + return NRF_SUCCESS; +} + + +uint32_t flash_get_page_size_words(void) { + + return FLASH_PAGE_SIZE_WORDS; +} + +uint32_t flash_get_page_number(void) { + return FLASH_NUM_PAGES; +} + +void flash_write_to_file(const char* filename) { + uint8_t* bytes = (uint8_t*) flash_words; + uint32_t len = FLASH_NUM_WORDS*sizeof(uint32_t); + storage_file_write_to_file(filename, bytes, len); +} diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/microphone_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/microphone_lib_mock.c new file mode 100644 index 0000000..1247580 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/microphone_lib_mock.c @@ -0,0 +1,22 @@ +#include "microphone_lib.h" + + +#include "callback_generator_lib.h" +#include "data_generator_lib.h" + +void microphone_init(void) { +} + +ret_code_t microphone_read(uint8_t* value) { + *value = 99; + return NRF_SUCCESS;; +} + + +bool microphone_selftest(void) { + return 1; + +} + + + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/sdk_common.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/sdk_common.h new file mode 100644 index 0000000..9111275 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/sdk_common.h @@ -0,0 +1,30 @@ +/**@file + * + * @details Copy of the sdk_common.h file, needed because app_util.h will otherwise be taken from the SDK-path not the mock-path. + */ + +#ifndef SDK_COMMON_H__ +#define SDK_COMMON_H__ + +#include +#include +#include +#include "sdk_config.h" +#include "nordic_common.h" +#include "compiler_abstraction.h" +#include "sdk_os.h" +#include "sdk_errors.h" +#include "app_util.h" +#include "sdk_macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // SDK_COMMON_H__ + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/selftest_lib_mock.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/selftest_lib_mock.c new file mode 100644 index 0000000..44265f2 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/selftest_lib_mock.c @@ -0,0 +1,10 @@ +#include "selftest_lib.h" + + + +selftest_status_t selftest_test(void) { + selftest_status_t selftest_status = SELFTEST_PASSED; + + + return selftest_status; +} diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/storage_file_lib.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/storage_file_lib.c new file mode 100644 index 0000000..964b5a9 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/storage_file_lib.c @@ -0,0 +1,112 @@ +#include "storage_file_lib.h" + + +#include "stdio.h" +#include "string.h" + + +/** @brief Function to convert a byte into hex string. + * + * @param[out] hex Pointer to memory where to store the hex string (at least 2 Bytes) + * @param[in] data The byte to convert. + */ +static void convert_to_hex(char* hex, uint8_t data) { + + for(uint8_t i = 0; i < 2; i++) { + uint8_t tmp = (data >> (i*4)) & 0x0F; + if(tmp < 10) { + hex[1-i] = tmp + 48; + } else { + hex[1-i] = tmp - 10 + 65; + } + } +} + + +/** @brief Function to convert a word into decimal string. + * + * @param[out] dec Pointer to memory where to store the decimal string (at least 10 Bytes) + * @param[in] data The word to convert. + */ +static void convert_to_dec(char* dec, uint32_t data) { + + char tmp[11]; + + sprintf(tmp, "%u", data); + uint32_t len = strlen(tmp); + uint32_t offset = 10 - len; + for(uint8_t i = 0; i < 10; i++) { + if(i < offset) { + dec[i] = '0'; + } else { + dec[i] = tmp[i - offset]; + } + } +} + + + +void storage_file_write_to_file(const char* filename, uint8_t* bytes, uint32_t len) { + + FILE* pfile = fopen(filename, "wb"); + + + + + //char header [] = "Address (hex)\tAddress(dec)\t0 1 2 3 4 5 6 7 8 9 A B C D E F\n"; + char header [] = "Address (hex)\tAddress(dec)\t 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F\n"; + fwrite(header, strlen(header), 1, pfile); + + + + for(uint32_t i = 0; i < len/16; i++) { + + { + char tmp[] = {'0', 'x', '0', '0', '0', '0', '0', '0', '0', '0'}; + uint32_t address = i*16; + convert_to_hex(&tmp[2], (address >> 24) & 0xFF); + convert_to_hex(&tmp[4], (address >> 16) & 0xFF); + convert_to_hex(&tmp[6], (address >> 8) & 0xFF); + convert_to_hex(&tmp[8], (address >> 0) & 0xFF); + fwrite(tmp, 10, 1, pfile); + } + fwrite("\t\t", 2, 1, pfile); + { + char tmp[] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; + uint32_t address = i*16; + convert_to_dec(&tmp[0], address); + fwrite(tmp, 10, 1, pfile); + } + fwrite("\t\t", 2, 1, pfile); + + + for(uint32_t j = 0; j < 16; j++) { + char tmp[] = {'0', 'x', '0', '0', ' '}; + convert_to_hex(&tmp[2], bytes[i*16 + j]); + fwrite(tmp, 5, 1, pfile); + } + fwrite("\t", 1, 1, pfile); + + + for(uint32_t j = 0; j < 16; j++) { + char tmp[5]; + uint8_t len = 0; + if(bytes[i*16 + j] == 0x0A) { // \n + sprintf(tmp, "\\n"); + len = strlen(tmp); + } else if(bytes[i*16 + j] == 0x0D) { // \r + sprintf(tmp, "\\r"); + len = strlen(tmp); + } else { + tmp[0]= bytes[i*16 + j]; + len = 1; + } + fwrite(tmp, len, 1, pfile); + } + fwrite("\n", 1, 1, pfile); + + } + + fclose(pfile); +} + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/storage_file_lib.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/storage_file_lib.h new file mode 100644 index 0000000..078b859 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/storage_file_lib.h @@ -0,0 +1,19 @@ +#ifndef __STORAGE_FILE_LIB_H +#define __STORAGE_FILE_LIB_H + +#include "stdint.h" + + +/** @brief Function to write some bytes as formatted storage layout/visualization to a file (text-file). + * + * @details The bytes are represented in their hexadecimal-representation. + * 16 Bytes are represented in one line. At the beginning of a line the address (in hex and dec) is displayed. + * At the end of a line the 16 Bytes are dumped (written in plain). + * + * @param[in] filename The filename where to store the storage visualization. + * @param[in] bytes Pointer to the bytes to visualize. + * @param[in] len The number of bytes to visualize. + */ +void storage_file_write_to_file(const char* filename, uint8_t* bytes, uint32_t len); + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/timer_lib.c b/firmware/nRF_badge/data_collector/unit_test/mock/incl/timer_lib.c new file mode 100644 index 0000000..73f10b3 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/timer_lib.c @@ -0,0 +1,312 @@ +#include "timer_lib.h" + + +#include "pthread.h" + +#include "time.h" + + + + + + + +static uint64_t time_start_microseconds = 0; /**< The microseconds that are set through timer_set_start() to be a reference point for all the other time related functions. */ + +static pthread_t thread_handle; /**< Handle of the timer thread. */ + +static pthread_mutex_t critical_section_mutex; /**< Mutex for critical sections. */ + +static volatile uint8_t timer_running = 0; /**< Flag if the timer-thread is running */ + +static volatile timer_node_t* queue_head; /**< Pointer to head of the timer node queue*/ + +volatile timer_node_t timer_nodes[MAX_NUMBER_OF_TIMERS]; /**< Timer node queue, where the timer are inserted with respect to their expiration times */ + +uint32_t number_of_timers = 0; /**< Number of created timers */ + + +/**@brief Function for entering a critical section by locking the mutex. + */ +void timer_enter_critical_section(void) { + pthread_mutex_lock(&critical_section_mutex); +} + +/**@brief Function for exiting a critical section by locking the mutex. + */ +void timer_exit_critical_section(void) { + pthread_mutex_unlock(&critical_section_mutex); +} + +/**@brief Function to retrieve the current time in microseconds since a timepoint in the past. + * + * @retval Microseconds since a timepoint in the past. + */ +static uint64_t timer_get_microseconds(void) { + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + + uint64_t t = ((uint64_t) (time.tv_sec *1000*1000)) + ((uint64_t)time.tv_nsec/1000); + + + t = t*TIMER_PRESCALER; + + return t; +} + + +void timer_set_start(void) { + time_start_microseconds = timer_get_microseconds(); +} + +uint64_t timer_get_microseconds_since_start(void) { + return timer_get_microseconds() - time_start_microseconds; +} + +uint64_t timer_get_milliseconds_since_start(void) { + return timer_get_microseconds_since_start() / 1000; +} + +void timer_sleep_microseconds(uint64_t microseconds) { + microseconds = microseconds/TIMER_PRESCALER; + + uint64_t nsec = (microseconds*1000) % 1000000000; + uint64_t sec = microseconds/1000000; + struct timespec time; + time.tv_sec = sec; + time.tv_nsec = nsec; + clock_nanosleep(CLOCK_MONOTONIC, 0, &time, NULL); +} + +void timer_sleep_milliseconds(uint64_t milliseconds) { + uint64_t microseconds = milliseconds * 1000; + timer_sleep_microseconds(microseconds); +} + + + + + + +/**@brief Function to insert a timer-node in the correct position in the timer-queue. + * + * @details The function searches for the correct point to insert the new timer node by comparing the microseconds_at_end timepoint of the nodes. + * + * @note This function needs to be saved by enter/exit_critical_section() + * + * @param[in] timer_id The timer identifier. + * @param[in] microseconds_interval The interval of the timer in microseconds. + */ +static void timer_insert_node(uint32_t timer_id, uint32_t microseconds_interval) { + + timer_nodes[timer_id].microseconds_at_start = timer_get_microseconds_since_start(); + timer_nodes[timer_id].microseconds_at_end = timer_nodes[timer_id].microseconds_at_start + microseconds_interval; + + timer_nodes[timer_id].is_running = 1; + timer_nodes[timer_id].next = NULL; + + + volatile timer_node_t* new_node = &timer_nodes[timer_id]; + + if(queue_head == NULL) { + new_node->next = NULL; + queue_head = new_node; + } else { + + + if((queue_head->microseconds_at_end == new_node->microseconds_at_end && queue_head->timer_priority < new_node->timer_priority) || queue_head->microseconds_at_end > new_node->microseconds_at_end) { + // queue_head ersetzen + new_node->next = queue_head; + queue_head = new_node; + + } else { + volatile timer_node_t* cur_node = queue_head; + volatile timer_node_t* prev_node = queue_head; + + while(cur_node != NULL) { + // Search the node into the right position + if((cur_node->microseconds_at_end == new_node->microseconds_at_end && cur_node->timer_priority < new_node->timer_priority) || cur_node->microseconds_at_end > new_node->microseconds_at_end) { + // cur_node ersetzen + break; + } + prev_node = cur_node; + cur_node = (volatile timer_node_t*) cur_node->next; + } + + new_node->next = cur_node; + prev_node->next = new_node; + } + } +} + + +/**@brief Function to remove a timer-node from timer-queue. + * + * @note This function needs to be saved by enter/exit_critical_section() + * + * @param[in] timer_id The timer identifier. + */ +static void timer_remove_node(uint32_t timer_id) { + timer_nodes[timer_id].is_running = 0; + // Search for the node that has timer_node[timer_id] as next-node. + for(uint32_t i = 0; i < MAX_NUMBER_OF_TIMERS; i++) { + if(timer_nodes[i].next == &timer_nodes[timer_id]) { + timer_nodes[i].next = timer_nodes[timer_id].next; + break; + } + } +} + +/**@brief Function to check for a timeout. + * + * @details The function is periodically called by the timer-thread. + * It checks if the queue-head timer-node expires and calls the timeout handler. + * Furthermore, it sets the queue-head to the next timer-node + * and reinserts the expired timer if it is a repeated timer. + * + * @param[in] ptr Just a dummy parameter, needed by the pthread_create()-function. + * + * @retval NULL Needed by the pthread_create()-function. + */ +static void* timer_check_queue(void* ptr) { + while(timer_running) { + timer_enter_critical_section(); + uint64_t cur_time = timer_get_microseconds_since_start(); + + + timer_timeout_handler_t timeout_handler = NULL; + void * timeout_context = NULL; + + + if(queue_head != NULL) { + if(cur_time >= queue_head->microseconds_at_end) { + // Backup the current head-queue (needed for the reinsert, if timer-mode is REPEATED) + volatile timer_node_t* prev_queue_head = queue_head; + + // Set the new queue head + queue_head = (volatile timer_node_t*) queue_head->next; + + // Check if the element is running (not stopped) + if(prev_queue_head->is_running) { + prev_queue_head->is_running = 0; + timeout_handler = prev_queue_head->p_timeout_handler; + timeout_context = prev_queue_head->p_context; + + if(prev_queue_head->mode == TIMER_MODE_REPEATED) { + + // Insert the former queue_head-node again (with a small correction) + uint64_t delta_t = timer_get_microseconds_since_start() - cur_time; + uint64_t microseconds_interval = (delta_t > prev_queue_head->microseconds_interval) ? 0 : (prev_queue_head->microseconds_interval - delta_t); + timer_insert_node(prev_queue_head->timer_id, microseconds_interval); + } + } + } + } + timer_exit_critical_section(); + + // Call the handler outside the critical section (because probably the application timeout-handler could call timer_start_timer()) + if(timeout_handler != NULL) { + timeout_handler(timeout_context); + } + + } + return NULL; +} + + + +void timer_init(void) { + + if(timer_running) + return; + pthread_mutex_init (&critical_section_mutex, NULL); + + timer_enter_critical_section(); + timer_running = 1; + queue_head = NULL; + number_of_timers = 0; + + + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + pthread_create(&thread_handle, &attr, timer_check_queue, NULL); + + pthread_attr_destroy(&attr); + + timer_exit_critical_section(); +} + +void timer_stop(void) { + timer_enter_critical_section(); + timer_running = 0; + pthread_mutex_destroy (&critical_section_mutex); + timer_exit_critical_section(); +} + + + + +uint8_t timer_create_timer(uint32_t* timer_id, timer_mode_t timer_mode, timer_timeout_handler_t timeout_handler, uint8_t timer_priority) { + if(number_of_timers >= MAX_NUMBER_OF_TIMERS) { + return 0; + } + + *timer_id = number_of_timers; + + timer_nodes[*timer_id].timer_id = *timer_id; + timer_nodes[*timer_id].mode = timer_mode; + timer_nodes[*timer_id].timer_priority = timer_priority; + timer_nodes[*timer_id].is_running = 0; + timer_nodes[*timer_id].next = NULL; + timer_nodes[*timer_id].p_timeout_handler = timeout_handler; + timer_nodes[*timer_id].p_context = NULL; + + timer_nodes[*timer_id].microseconds_at_start = 0; + timer_nodes[*timer_id].microseconds_at_end = 0; + timer_nodes[*timer_id].microseconds_interval = 0; + + number_of_timers ++; + + return 1; +} + + + + + +uint8_t timer_start_timer(uint32_t timer_id, uint64_t timeout_microseconds, void* p_context) { + + if(timer_nodes[timer_id].is_running) + return 0; + + timer_nodes[timer_id].p_context = p_context; + timer_nodes[timer_id].microseconds_interval = timeout_microseconds; + + timer_enter_critical_section(); + timer_insert_node(timer_id, timer_nodes[timer_id].microseconds_interval); + timer_exit_critical_section(); + + return 1; + +} + + + +void timer_stop_timer(uint32_t timer_id) { + timer_enter_critical_section(); + timer_remove_node(timer_id); + timer_exit_critical_section(); +} + + + + + + + + + + diff --git a/firmware/nRF_badge/data_collector/unit_test/mock/incl/timer_lib.h b/firmware/nRF_badge/data_collector/unit_test/mock/incl/timer_lib.h new file mode 100644 index 0000000..d765583 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/mock/incl/timer_lib.h @@ -0,0 +1,143 @@ +/** @file + * + * + * @brief Timer implementation to call timeout-handlers at specified intervals through a timer-thread. + * + * @details This module provides the functionality to create and start timer that expires at specific intervals. + * If a timer expires a timeout-handler is called. + * + * + * @note This module is not based on RTOS, just on normal pthreads. So the main-context can still be executed, + * although the timeout-handler is currently executed. + * Each timeout-handler is completely executed before another/next timeout-handler could be called. + */ + +#ifndef TIMER_LIB_H +#define TIMER_LIB_H + +#include "stdint.h" + + + +#define MAX_NUMBER_OF_TIMERS 100 /**< Maximum number of timers that could be handled */ + +#define TIMER_PRESCALER 1 /**< Prescaler for the timer. 1 --> time is like the real time. 10 --> time is 10x faster than the real time. */ + + + +typedef void (*timer_timeout_handler_t)(void * p_context); /**< The timeout handler function type. */ + +typedef enum +{ + TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */ + TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */ +} timer_mode_t; + +typedef struct { + uint32_t timer_id; /**< The timer identifier. */ + + uint64_t microseconds_at_start; /**< The startpoint of the timer. */ + uint64_t microseconds_at_end; /**< The endpoint of the timer (when the time-out handler should be called). */ + uint64_t microseconds_interval; /**< The interval of the timer in microseconds. */ + + uint8_t timer_priority; /**< The priority of the timer. */ + + uint8_t is_running; /**< Flag if the timer is running. */ + timer_mode_t mode; /**< The timer mode. */ + volatile void * next; /**< Pointer to the next timer-node in queue. */ + timer_timeout_handler_t p_timeout_handler; /**< The timer identifier. */ + void * p_context; /**< The timer identifier. */ + + +} timer_node_t; /**< Timer-node-struct for managing a timer. */ + + +/**@brief Function for initializing the timer-module. + * + * @details The function resets the number of timers to 0, and starts the internal timer-thread. + * + */ +void timer_init(void); + +/**@brief Function for entering a critical section by locking the mutex. + */ +void timer_enter_critical_section(void); + +/**@brief Function for exiting a critical section by locking the mutex. + */ +void timer_exit_critical_section(void); + + +/**@brief Function for stopping the timer-module. + * + * @details The function stops the internal timer-thread. + * + */ +void timer_stop(void); + + +/**@brief Function to set the time reference to the current time. + */ +void timer_set_start(void); + +/**@brief Function to retrieve the current time in microseconds since the reference timepoint. + * + * @retval Microseconds since the reference timepoint. + */ +uint64_t timer_get_microseconds_since_start(void); + +/**@brief Function to retrieve the current time in milliseconds since the reference timepoint. + * + * @retval Milliseconds since the reference timepoint. + */ +uint64_t timer_get_milliseconds_since_start(void); + +/**@brief Function to sleep in microseconds. + * + * @param[in] Microseconds to sleep. + */ +void timer_sleep_microseconds(uint64_t microseconds); + +/**@brief Function to sleep in milliseconds. + * + * @param[in] Milliseconds to sleep. + */ +void timer_sleep_milliseconds(uint64_t milliseconds); + +/**@brief Function to create a timer. + * + * @details The function creates a timer-node with a certain priority. + * The priority is just in case there are two timers that expire exactly at the same time (same microsecond), + * then the timer with the higher priority will be executed first. + * + * @param[out] timer_id The identifier of the created timer. + * @param[in] timer_mode The mode (single_shot, repeated) of the timer. + * @param[in] timeout_handler The handler that should be called when the timer expires. + * @param[in] timer_priority The priority of the timer. + * + * @retval 1 If the timer was created successfully. + * @retval 0 If the timer was not created successfully, because there are already MAX_NUMBER_OF_TIMERS created. + */ +uint8_t timer_create_timer(uint32_t* timer_id, timer_mode_t timer_mode, timer_timeout_handler_t timeout_handler, uint8_t timer_priority); + +/**@brief Function for starting a timer. + * + * @param[in] timer_id The timer identifier. + * @param[in] timeout_microseconds Microseconds to time-out event. + * @param[in] p_context General purpose pointer. Will be passed to the time-out handler when + * the timer expires. + * + * @retval 1 If the timer was started successfully. + * @retval 0 If the timer is already running. + */ +uint8_t timer_start_timer(uint32_t timer_id, uint64_t timeout_microseconds, void* p_context); + + +/**@brief Function for stopping a timer. + * + * @param[in] timer_id Timer identifier. + */ +void timer_stop_timer(uint32_t timer_id); + + +#endif diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/accel_lib_mock_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/accel_lib_mock_unittest.cc new file mode 100644 index 0000000..ee11b8a --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/accel_lib_mock_unittest.cc @@ -0,0 +1,146 @@ +/** + * This unittest tests the functionality of the accel_lib_mock.c, parts of the callback_generator_lib.cc and data_generator_lib.cc that are responsible for the accelerometer. + * The name of the functions for the interrupt-generating is ACCEL_INT1. So all the functions related to the interrupt-generating start with callback_generator_ACCEL_INT1...(). + * The name of the functions for the data-generating for the accel_read_accelerometer-function is accel_read_accelerometer. So all the functions related to the data-generating start with data_generator_accel_read_acceleration...(). + */ + +// Don't forget gtest.h, which declares the testing framework. + +#include "accel_lib.h" +#include "timer_lib.h" +#include "callback_generator_lib.h" +#include "data_generator_lib.h" +#include "gtest/gtest.h" + + +static volatile uint32_t accel_interrupt_handler_event_sum = 0; // Just a variable to test how often the accel_interrupt_handler-function is called. +// This is the interrupt-handler that is called by the accelerometer module, when an interrupt occured +void accel_interrupt_handler(accel_interrupt_event_t const * p_event) { + accel_interrupt_handler_event_sum += *p_event; +} + +// This is a function to generate the data for the interrupt-generation of the accelerometer +void correct_accel_interrupt_generator_handler(nrf_drv_gpiote_pin_t* pin, nrf_gpiote_polarity_t* action) { + *pin = 25; + *action = NRF_GPIOTE_POLARITY_LOTOHI; +} + +void false_accel_interrupt_generator_handler(nrf_drv_gpiote_pin_t* pin, nrf_gpiote_polarity_t* action) { + // These parameters are invalid for the accelerometer, because the accelerometer-module expects pin == 25 and action == NRF_GPIOTE_POLARITY_LOTOHI. + // So the accelerometer module won't call the accel_interrupt_handler-function. + *pin = 24; + *action = NRF_GPIOTE_POLARITY_HITOLO; +} + + +// Data-generator function for the accel_read_acceleration()-function of the accelerometer module. +ret_code_t accel_read_acceleration_generator_handler(int16_t* x, int16_t* y, int16_t* z, uint8_t* num_samples, uint32_t max_num_samples) { + + for(int16_t i = 0; i < 10; i++) { + x[i] = i; + y[i] = -i; + z[i] = 2*i; + } + + *num_samples = 10; + return NRF_SUCCESS; +} + + +namespace { + +class AccelLibMockTest : public ::testing::Test { + virtual void SetUp() { + callback_generator_ACCEL_INT1_reset(); + data_generator_accel_read_acceleration_reset(); + } +}; + + +// Tests for the interrupt-generating +TEST_F(AccelLibMockTest, InterruptNoGeneratorHandlerTest) { + // Add the timepoints for the callback/interrupt-generation + uint32_t timepoint_array[5] = {100, 200, 300, 400, 500}; + callback_generator_ACCEL_INT1_add_trigger_timepoints(timepoint_array, 5); + // Set the event-data generator handler to NULL --> The default implementation in callback_generator_lib.cc will be taken. + callback_generator_ACCEL_INT1_set_generator(NULL); + accel_interrupt_handler_event_sum = 0; + + accel_init(); + accel_set_interrupt_handler(accel_interrupt_handler); + // This function triggers the interrupt-generating by calling callback_generator_ACCEL_INT1_trigger() + accel_set_interrupt(ACCEL_MOTION_INTERRUPT); + + while(!callback_generator_ACCEL_INT1_is_ready()); + + EXPECT_EQ(accel_interrupt_handler_event_sum, 5); +} + + + +TEST_F(AccelLibMockTest, InterruptCorrectGeneratorHandlerTest) { + // Add the timepoints for the callback/interrupt-generation + uint32_t timepoint_array[5] = {100, 200, 300, 400, 500}; + callback_generator_ACCEL_INT1_add_trigger_timepoints(timepoint_array, 5); + // Set the event-data generator handler to the + callback_generator_ACCEL_INT1_set_generator(correct_accel_interrupt_generator_handler); + accel_interrupt_handler_event_sum = 0; + + accel_init(); + accel_set_interrupt_handler(accel_interrupt_handler); + // This function triggers the interrupt-generating by calling callback_generator_ACCEL_INT1_trigger() + accel_set_interrupt(ACCEL_MOTION_INTERRUPT); + + while(!callback_generator_ACCEL_INT1_is_ready()); + EXPECT_EQ(accel_interrupt_handler_event_sum, 5); +} + +TEST_F(AccelLibMockTest, InterruptFalseGeneratorHandlerTest) { + // Add the timepoints for the callback/interrupt-generation + uint32_t timepoint_array[5] = {100, 200, 300, 400, 500}; + callback_generator_ACCEL_INT1_add_trigger_timepoints(timepoint_array, 5); + // Set the event-data generator handler to the + callback_generator_ACCEL_INT1_set_generator(false_accel_interrupt_generator_handler); + accel_interrupt_handler_event_sum = 0; + + accel_init(); + accel_set_interrupt_handler(accel_interrupt_handler); + // This function triggers the interrupt-generating by calling callback_generator_ACCEL_INT1_trigger() + accel_set_interrupt(ACCEL_MOTION_INTERRUPT); + + while(!callback_generator_ACCEL_INT1_is_ready()); + EXPECT_EQ(accel_interrupt_handler_event_sum, 0); +} + + +// Test for the data-generating (accel_read_acceleration) +TEST_F(AccelLibMockTest, ReadFunctionNoGeneratorTest) { + // Read the acceleration from the accelerometer. In this case no data-generator function is set --> the default implementation in data_generator_lib.cc is taken. + int16_t x[32], y[32], z[32]; + uint8_t num_samples; + ret_code_t ret = accel_read_acceleration(x, y, z, &num_samples, 32); + + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(num_samples, 1); + EXPECT_EQ(x[0], 0); + EXPECT_EQ(y[0], 0); + EXPECT_EQ(z[0], 0); +} + + +TEST_F(AccelLibMockTest, ReadFunctionGeneratorTest) { + // Read the acceleration from the accelerometer. In this case the data-generator function is set, so the data_generator_lib.cc takes this implementation. + data_generator_accel_read_acceleration_set_generator(accel_read_acceleration_generator_handler); + int16_t x[32], y[32], z[32]; + uint8_t num_samples; + ret_code_t ret = accel_read_acceleration(x, y, z, &num_samples, 32); + + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(num_samples, 10); + for(int16_t i = 0; i < num_samples; i++) { + EXPECT_EQ(x[i], i); + EXPECT_EQ(y[i], -i); + EXPECT_EQ(z[i], i*2); + } +} +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/app_scheduler_mock_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/app_scheduler_mock_unittest.cc new file mode 100644 index 0000000..defb926 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/app_scheduler_mock_unittest.cc @@ -0,0 +1,94 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "app_scheduler.h" +#include "app_scheduler.h" +#include "gtest/gtest.h" + +#define SCHED_MAX_EVENT_DATA_SIZE sizeof(uint32_t) +#define SCHED_QUEUE_SIZE 100 + + +namespace { + +class AppSchedulerTest : public ::testing::Test { + virtual void SetUp() { + APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); + } +}; + + +uint32_t callback_event_size_sum = 0; +uint32_t callback_event_data_sum = 0; +void callback_function(void * p_event_data, uint16_t event_size) { + callback_event_size_sum += event_size; + for(uint8_t i = 0; i < event_size; i++) + callback_event_data_sum += ((uint8_t*)p_event_data)[i]; +} + +TEST_F(AppSchedulerTest, FunctionalTest) { + callback_event_size_sum = 0; + callback_event_data_sum = 0; + uint8_t event_data[10]; + for(uint8_t i = 0; i < 10; i++) + event_data[i] = i; + + app_sched_event_put(event_data, 1, callback_function); + + EXPECT_EQ(callback_event_size_sum, 0); + EXPECT_EQ(callback_event_data_sum, 0); + + app_sched_execute(); + EXPECT_EQ(callback_event_size_sum, 1); + EXPECT_EQ(callback_event_data_sum, 0); + + app_sched_event_put(event_data, 2, callback_function); + + app_sched_execute(); + EXPECT_EQ(callback_event_size_sum, 3); + EXPECT_EQ(callback_event_data_sum, 1); + + + callback_event_size_sum = 0; + callback_event_data_sum = 0; + + app_sched_event_put(event_data, 1, callback_function); + app_sched_event_put(event_data, 2, callback_function); + app_sched_event_put(event_data, 3, callback_function); + + uint32_t space = app_sched_queue_space_get(); + EXPECT_EQ(space, SCHED_QUEUE_SIZE - 3); + + EXPECT_EQ(callback_event_size_sum, 0); + EXPECT_EQ(callback_event_data_sum, 0); + app_sched_execute(); + EXPECT_EQ(callback_event_size_sum, 1+2+3); + EXPECT_EQ(callback_event_data_sum, 0 + (0+1) + (0+1+2)); + + space = app_sched_queue_space_get(); + EXPECT_EQ(space, SCHED_QUEUE_SIZE); + +} + +TEST_F(AppSchedulerTest, ExceptionTest) { + uint8_t event_data[10]; + ret_code_t ret = app_sched_event_put(event_data, 10, callback_function); + EXPECT_EQ(ret, NRF_ERROR_INVALID_LENGTH); + + ret = app_sched_event_put(event_data, 0, callback_function); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = app_sched_event_put(NULL, 1, callback_function); + EXPECT_EQ(ret, NRF_SUCCESS); + + for(uint8_t i = 0; i < SCHED_QUEUE_SIZE - 2; i++) { + ret = app_sched_event_put(event_data, 1, callback_function); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + ret = app_sched_event_put(event_data, 1, callback_function); + EXPECT_EQ(ret, NRF_ERROR_NO_MEM); +} + + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/app_timer_mock_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/app_timer_mock_unittest.cc new file mode 100644 index 0000000..d3b934e --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/app_timer_mock_unittest.cc @@ -0,0 +1,240 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "app_timer.h" +#include "timer_lib.h" +#include "gtest/gtest.h" + + +#define MAX_NUMBER_OF_APP_TIMERS_TEST MAX_NUMBER_OF_TIMERS + +extern volatile timer_node_t timer_nodes[]; + +extern uint32_t timer_ids[]; + +extern uint32_t number_of_app_timers; + +namespace { + +class AppTimerTest : public ::testing::Test { + virtual void SetUp() { + APP_TIMER_INIT(0, 20, NULL); + timer_set_start(); + + } + + virtual void TearDown() { + timer_stop(); + } + +}; + +void* p_callback_context_1 = NULL; +void callback_function_1(void* p_context) { + p_callback_context_1 = p_context; +} + +TEST_F(AppTimerTest, SingleShotTimerTest) { + APP_TIMER_DEF(timer_id); + ret_code_t ret = app_timer_create(&timer_id, APP_TIMER_MODE_SINGLE_SHOT, callback_function_1); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(number_of_app_timers, 1); + EXPECT_EQ(timer_ids[0], 0); + + uint8_t context_data[10]; + p_callback_context_1 = NULL; + + uint32_t timeout_ms = 100; + uint32_t timeout_ticks = APP_TIMER_TICKS(timeout_ms, 0); + ret = app_timer_start(timer_id, timeout_ticks, context_data); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Check the entries in the timer_lib + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[0].microseconds_interval, timeout_ms*1000); + EXPECT_TRUE(timer_nodes[0].p_context == context_data); + + + // The initial callback state + EXPECT_TRUE(p_callback_context_1 == NULL); + + + // Sleep for the a little bit smaller than the callback time, to check, whether it is still running + timer_sleep_milliseconds(timeout_ms - 20); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == NULL); + EXPECT_EQ(timer_nodes[0].is_running, 1); + + // Sleep again, to wake up after the callback event + timer_sleep_milliseconds(20); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == context_data); + EXPECT_EQ(timer_nodes[0].is_running, 0); + +} + + +TEST_F(AppTimerTest, RepeatedTimerTest) { + APP_TIMER_DEF(timer_id); + ret_code_t ret = app_timer_create(&timer_id, APP_TIMER_MODE_REPEATED, callback_function_1); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(number_of_app_timers, 1); + EXPECT_EQ(timer_ids[0], 0); + + uint8_t context_data[10]; + p_callback_context_1 = NULL; + + uint32_t timeout_ms = 100; + uint32_t timeout_ticks = APP_TIMER_TICKS(timeout_ms, 0); + ret = app_timer_start(timer_id, timeout_ticks, context_data); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Check the entries in the timer_lib + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[0].microseconds_interval, timeout_ms*1000); + EXPECT_TRUE(timer_nodes[0].p_context == context_data); + + + // The initial callback state + EXPECT_TRUE(p_callback_context_1 == NULL); + + + // Sleep for the a little bit smaller than the callback time, to check, whether it is still running + timer_sleep_milliseconds(timeout_ms - 20); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == NULL); + EXPECT_EQ(timer_nodes[0].is_running, 1); + + // Sleep again, to wake up after the callback event + timer_sleep_milliseconds(20); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == context_data); + EXPECT_EQ(timer_nodes[0].is_running, 1); + + +} + + +volatile uint32_t callback_function_2_counter = 0; +void callback_function_2(void* p_context) { + callback_function_2_counter++; +} + + + +TEST_F(AppTimerTest, StopTimerTest) { + callback_function_2_counter = 0; + + APP_TIMER_DEF(timer_id_0); + ret_code_t ret = app_timer_create(&timer_id_0, APP_TIMER_MODE_REPEATED, callback_function_2); + EXPECT_EQ(ret, NRF_SUCCESS); + + APP_TIMER_DEF(timer_id_1); + ret = app_timer_create(&timer_id_1, APP_TIMER_MODE_REPEATED, callback_function_2); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(number_of_app_timers, 2); + EXPECT_EQ(timer_ids[0], 0); + EXPECT_EQ(timer_ids[1], 1); + + + uint32_t timeout_ms = 100; + uint32_t timeout_ticks = APP_TIMER_TICKS(timeout_ms, 0); + ret = app_timer_start(timer_id_0, timeout_ticks, NULL); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = app_timer_start(timer_id_1, timeout_ticks, NULL); + EXPECT_EQ(ret, NRF_SUCCESS); + + + // Sleep for the a little bit smaller than the callback time, to check, whether it is still running + timer_sleep_milliseconds(timeout_ms - 20); + + // Check if the callback was called succesfully + EXPECT_EQ(callback_function_2_counter, 0); + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[1].is_running, 1); + + // Sleep again, to wake up after the callback event + timer_sleep_milliseconds(20); + + // Check if the callback was called succesfully + EXPECT_EQ(callback_function_2_counter, 2); + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[1].is_running, 1); + + + timer_sleep_milliseconds(timeout_ms); + + EXPECT_EQ(callback_function_2_counter, 4); + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[1].is_running, 1); + + // Stopping all the timers + ret = app_timer_stop(timer_id_0); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = app_timer_stop_all(); + EXPECT_EQ(ret, NRF_SUCCESS); + + timer_sleep_milliseconds(timeout_ms); + + EXPECT_EQ(callback_function_2_counter, 4); + EXPECT_EQ(timer_nodes[0].is_running, 0); + EXPECT_EQ(timer_nodes[1].is_running, 0); +} + + + + +TEST_F(AppTimerTest, AppTimerCntDiffComputeTest) { + uint32_t max_ticks = 0x00FFFFFF; // Max rtc ticks + uint32_t ticks_to = 0; + uint32_t ticks_from = 0; + uint32_t ticks_diff = 0; + + ret_code_t ret = app_timer_cnt_diff_compute(ticks_to, ticks_from, &ticks_diff); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(ticks_diff, 0); + + ticks_to = 10; + ticks_from = max_ticks - 10; + ret = app_timer_cnt_diff_compute(ticks_to, ticks_from, &ticks_diff); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(ticks_diff, 21); +} + + +TEST_F(AppTimerTest, AppTimerCntGetTest) { + + uint32_t ticks_from = app_timer_cnt_get(); + + uint32_t timeout_ms = 100; + uint32_t timeout_ticks = APP_TIMER_TICKS(timeout_ms, 0); + + + timer_sleep_milliseconds(timeout_ms); + + uint32_t ticks_to = app_timer_cnt_get(); + + + + + uint32_t ticks_diff = 0; + + + ret_code_t ret = app_timer_cnt_diff_compute(ticks_to, ticks_from, &ticks_diff); + + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_GE(ticks_diff, timeout_ticks - 700); + EXPECT_LE(ticks_diff, timeout_ticks + 700); + + + + +} +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/ble_lib_mock_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/ble_lib_mock_unittest.cc new file mode 100644 index 0000000..779c130 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/ble_lib_mock_unittest.cc @@ -0,0 +1,244 @@ + + +// Don't forget gtest.h, which declares the testing framework. + + +#include "gtest/gtest.h" +#include "ble_lib.h" + +#include "callback_generator_lib.h" +#include "data_generator_lib.h" + +/** Include some (private) functions only for testing purposes */ +extern uint32_t ble_transmit_fifo_get_size(void); +extern ret_code_t ble_transmit_fifo_read(uint8_t* data, uint32_t len); + +/** Some variables and (callback) functions to test functionallity) */ +static volatile uint8_t connected = 0; +static volatile uint8_t transmitted = 0; +static volatile uint8_t receive_count = 0; +static volatile uint8_t scan_report_count = 0; +static volatile int8_t scan_report_rssi[100]; +static volatile uint8_t scan_timeout = 0; + +static void get_MAC_address_generator(uint8_t* MAC_address, uint8_t len) { + for(uint8_t i = 0; i < len; i++) + MAC_address[i] = i; + + EXPECT_EQ(len, 6); +} + +static void on_connect(void) { + connected = 1; +} +static void on_disconnect(void) { + connected = 0; +} + +static void on_transmit_complete(void) { + transmitted = 1; +} + +static void on_receive(uint8_t* data, uint16_t len) { + + EXPECT_EQ(callback_generator_ble_on_receive_get_trigger_index(), 0); + EXPECT_EQ(callback_generator_ble_on_receive_get_timepoint_index(), receive_count); + + EXPECT_EQ(len, 20); + + for(uint32_t i = 0; i < len; i++) + EXPECT_EQ(data[i], (i*i + receive_count) % 256); + + receive_count++; +} +static void on_receive_generator(uint8_t* data, uint16_t* length, uint16_t max_len) { + *length = max_len; + for(uint32_t i = 0; i < *length; i++) + data[i] = (i*i + receive_count) % 256; +} + + +static void on_scan_report(ble_gap_evt_adv_report_t* scan_report) { + EXPECT_EQ(callback_generator_ble_on_scan_report_get_trigger_index(), 0); + EXPECT_EQ(callback_generator_ble_on_scan_report_get_timepoint_index(), scan_report_count); + + scan_report_rssi[scan_report_count] = scan_report->rssi; + scan_report_count ++; +} +static void on_scan_report_generator(ble_gap_evt_adv_report_t* scan_report) { + scan_report->rssi = -50 - ((int8_t) scan_report_count); +} +static void on_scan_timeout(void) { + scan_timeout = 1; +} + + + + + + +namespace { + +class BLELibMockTest : public ::testing::Test { + virtual void SetUp() { + callback_generator_ble_on_connect_reset(); + callback_generator_ble_on_disconnect_reset(); + callback_generator_ble_on_receive_reset(); + callback_generator_ble_on_transmit_complete_reset(); + callback_generator_ble_on_scan_report_reset(); + callback_generator_ble_on_scan_timeout_reset(); + data_generator_ble_get_MAC_address_reset(); + } +}; + +TEST_F(BLELibMockTest, GetMACAddressTest) { + data_generator_ble_get_MAC_address_set_generator(get_MAC_address_generator); + + uint8_t MAC_address[6]; + ble_get_MAC_address(MAC_address); + for(uint8_t i = 0; i < 6; i++) + EXPECT_EQ(MAC_address[i], i); +} + +TEST_F(BLELibMockTest, ConnectDisconnectTest) { + uint32_t timepoint_connect = 10; + uint32_t timepoint_disconnect = 120; + callback_generator_ble_on_connect_add_trigger_timepoints(&timepoint_connect, 1); + callback_generator_ble_on_disconnect_add_trigger_timepoints(&timepoint_disconnect, 1); + + connected = 0; + + ret_code_t ret = ble_init(); + ble_set_on_connect_callback(on_connect); + ble_set_on_disconnect_callback(on_disconnect); + + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(connected, 0); + + while( !callback_generator_ble_on_connect_is_ready() ); + + EXPECT_EQ(connected, 1); + + while( !callback_generator_ble_on_disconnect_is_ready() ); + + EXPECT_EQ(connected, 0); + +} + + +TEST_F(BLELibMockTest, TransmitTest) { + uint32_t timepoint_connect = 10; + callback_generator_ble_on_connect_add_trigger_timepoints(&timepoint_connect, 1); + + + connected = 0; + transmitted = 0; + uint8_t data[100]; + + ret_code_t ret = ble_init(); + ble_set_on_connect_callback(on_connect); + ble_set_on_transmit_callback(on_transmit_complete); + + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(connected, 0); + + // If we try to sent data before it is connected --> Invalid state + ret = ble_transmit(data, 10); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + + while( !callback_generator_ble_on_connect_is_ready() ); + + EXPECT_EQ(connected, 1); + + // If we try to sent too large data --> Invalid param + ret = ble_transmit(data, 30); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + ret = ble_transmit(data, 20); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(transmitted, 0); + + + while( !callback_generator_ble_on_transmit_complete_is_ready() ); + + EXPECT_EQ(transmitted, 1); + EXPECT_EQ(ble_transmit_fifo_get_size(), 20); + +} + +TEST_F(BLELibMockTest, ReceiveTest) { + uint32_t timepoint_connect = 10; + uint32_t timepoints_receive[2] = {100, 100}; + callback_generator_ble_on_connect_add_trigger_timepoints(&timepoint_connect, 1); + callback_generator_ble_on_receive_add_trigger_timepoints(timepoints_receive, 2); + callback_generator_ble_on_receive_set_generator(on_receive_generator); + + + connected = 0; + receive_count = 0; + + ret_code_t ret = ble_init(); + ble_set_on_connect_callback(on_connect); + ble_set_on_receive_callback(on_receive); + + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(connected, 0); + EXPECT_EQ(receive_count, 0); + + + while( !callback_generator_ble_on_connect_is_ready() ); + + EXPECT_EQ(connected, 1); + EXPECT_EQ(receive_count, 0); + + + while( !callback_generator_ble_on_receive_is_ready() ); + + EXPECT_EQ(receive_count, 2); + + +} + +TEST_F(BLELibMockTest, ScanTest) { + uint8_t number_of_scan_reports = 20; + uint32_t timepoints_scan_report[number_of_scan_reports]; + for(uint8_t i = 0; i < number_of_scan_reports; i++) + timepoints_scan_report[i] = 25; + callback_generator_ble_on_scan_report_add_trigger_timepoints(timepoints_scan_report, number_of_scan_reports); + callback_generator_ble_on_scan_report_set_generator(on_scan_report_generator); + + scan_timeout = 0; + memset((int8_t*) scan_report_rssi, 0, sizeof(scan_report_rssi)); + scan_report_count = 0; + + + ret_code_t ret = ble_init(); + ble_set_on_scan_report_callback(on_scan_report); + ble_set_on_scan_timeout_callback(on_scan_timeout); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = ble_start_scanning(0, 200, 2); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + ret = ble_start_scanning(100, 200, 2); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(scan_timeout, 0); + EXPECT_EQ(scan_report_count, 0); + + + while( !callback_generator_ble_on_scan_report_is_ready() || !callback_generator_ble_on_scan_timeout_is_ready() ); + + EXPECT_EQ(scan_timeout, 1); + EXPECT_EQ(scan_report_count, number_of_scan_reports); + for(uint8_t i = 0; i < number_of_scan_reports; i++) { + EXPECT_EQ(scan_report_rssi[i], -50 - (int8_t) i); + } + + +} + + + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/callback_generator_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/callback_generator_lib_unittest.cc new file mode 100644 index 0000000..7efa926 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/callback_generator_lib_unittest.cc @@ -0,0 +1,159 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "callback_generator_internal_lib.h" +#include "gtest/gtest.h" + + + +#include "timer_lib.h" + +// Declaring the handler-function signature +CALLBACK_HANDLER_FUNCTION_DECLARATION(TEST, void, uint32_t val); + +// Declaring the generator-function signature +CALLBACK_GENERATOR_FUNCTION_DECLARATION(TEST, void, uint32_t trigger_index, uint32_t timepoint_index, uint32_t* val); + +// Declare all the callback-related functions +CALLBACK_GENERATOR_DECLARATION(TEST); + +// Implement all the callback-realted functions and implement what should actually be executed when the callback is generated. +CALLBACK_GENERATOR_IMPLEMENTATION(TEST) { + uint32_t trigger_index = callback_generator_TEST_get_trigger_index(); + uint32_t timepoint_index = callback_generator_TEST_get_timepoint_index(); + //printf("Called: %u, %u\n", trigger_index, timepoint_index); + + uint32_t val = 0; + if(callback_generator_TEST_get_generator() != NULL) { + callback_generator_TEST_get_generator()(trigger_index, timepoint_index, &val); + } + + if(callback_generator_TEST_get_handler() != NULL) { + callback_generator_TEST_get_handler()(val); + } +} + + + + +void callback_generator(uint32_t trigger_index, uint32_t timepoint_index, uint32_t* val) { + *val = trigger_index*10+timepoint_index; +} + +volatile uint32_t callback_val_sum = 0; +void callback_handler(uint32_t val) { + callback_val_sum += val; +// printf("Callback handler called. val: %u\n", val); +} + + +namespace { + +class CallbackGeneratorTest : public ::testing::Test { + virtual void SetUp() { + timer_init(); + } + + virtual void TearDown() { + timer_stop(); + } + +}; + + +TEST_F(CallbackGeneratorTest, FunctionalTest) { + callback_val_sum = 0; + + callback_generator_TEST_reset(); + uint8_t ret = callback_generator_TEST_init(); + EXPECT_EQ(ret, 1); + + callback_generator_TEST_set_handler(callback_handler); + callback_generator_TEST_set_generator(callback_generator); + + uint32_t trigger_array[5] = {500, 500, 500, 500, 500}; + uint32_t trigger1_array[3] = {100, 100, 100}; + callback_generator_TEST_add_trigger_timepoints(trigger_array, 5); + callback_generator_TEST_add_trigger_timepoints(trigger1_array, 3); + + callback_generator_TEST_trigger(); + + // Wait until the first trigger is processed completely + timer_sleep_milliseconds(500*5 + 50); + EXPECT_EQ(callback_val_sum, 0+1+2+3+4); + + // Trigger again for the second trigger-processing + callback_generator_TEST_trigger(); + + // Waiting until the callback-generating process is done + while(!callback_generator_TEST_is_ready()); + + EXPECT_EQ(callback_val_sum, 0+1+2+3+4+10+11+12); + +} + + +TEST_F(CallbackGeneratorTest, TriggerBeforeAddTest) { + callback_val_sum = 0; + + callback_generator_TEST_reset(); + uint8_t ret = callback_generator_TEST_init(); + EXPECT_EQ(ret, 1); + + callback_generator_TEST_set_handler(callback_handler); + callback_generator_TEST_set_generator(callback_generator); + + callback_generator_TEST_trigger(); + + uint32_t trigger_array[5] = {500, 500, 500, 500, 500}; + callback_generator_TEST_add_trigger_timepoints(trigger_array, 5); + + + // Waiting until the callback-generating process is done + while(!callback_generator_TEST_is_ready()); + + EXPECT_EQ(callback_val_sum, 0+1+2+3+4); + + + callback_generator_TEST_trigger(); + uint32_t trigger1_array[3] = {100, 100, 100}; + callback_generator_TEST_add_trigger_timepoints(trigger1_array, 3); + + // Waiting until the callback-generating process is done + while(!callback_generator_TEST_is_ready()); + + EXPECT_EQ(callback_val_sum, 0+1+2+3+4+10+11+12); +} + +TEST_F(CallbackGeneratorTest, TriggerIgnoreTest) { + callback_val_sum = 0; + uint64_t milliseconds_start = timer_get_milliseconds_since_start(); + + callback_generator_TEST_reset(); + uint8_t ret = callback_generator_TEST_init(); + EXPECT_EQ(ret, 1); + + callback_generator_TEST_set_handler(callback_handler); + callback_generator_TEST_set_generator(callback_generator); + + callback_generator_TEST_trigger(); + + uint32_t trigger_array[5] = {500, 500, 500, 500, 500}; + uint32_t trigger1_array[3] = {100, 100, 100}; + callback_generator_TEST_add_trigger_timepoints(trigger_array, 5); + callback_generator_TEST_add_trigger_timepoints(trigger1_array, 3); + + // Trigger again, although the first trigger hasn't been processed + callback_generator_TEST_trigger(); + + + + + // Waiting until the callback-generating process is done, it will actually never be done, because the second trigger won't be processed (that's why there is a timeout check) + while(!callback_generator_TEST_is_ready() && (timer_get_milliseconds_since_start() - milliseconds_start <= 500*5 + 100*3 + 100)); + EXPECT_FALSE(callback_generator_TEST_is_ready()); + EXPECT_EQ(callback_val_sum, 0+1+2+3+4); +} + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/chunk_fifo_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/chunk_fifo_lib_unittest.cc new file mode 100644 index 0000000..783c74b --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/chunk_fifo_lib_unittest.cc @@ -0,0 +1,152 @@ + + +// Don't forget gtest.h, which declares the testing framework. + + +#include "gtest/gtest.h" +#include "chunk_fifo_lib.h" + +/**< Struct that contains the actual chunk-data. */ +typedef struct { + uint8_t buf[100]; +} data_t; + +/**< Struct that contains the additional-infos for a chunk. */ +typedef struct { + uint8_t buf[4]; +} additional_info_t; + +namespace { + + +TEST(ChunkFifoTest, InitTest) { + chunk_fifo_t chunk_fifo; + ret_code_t ret; + CHUNK_FIFO_INIT(ret, chunk_fifo, 2, 100, 4); + EXPECT_EQ(ret, NRF_SUCCESS); +} + +TEST(ChunkFifoTest, InitErrorTest) { + chunk_fifo_t chunk_fifo; + ret_code_t ret; + CHUNK_FIFO_INIT(ret, chunk_fifo, 0, 100, 4); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + // Initialize the chunk-fifo with NULL as buffer + ret = chunk_fifo_init(&chunk_fifo, 1, 100, 4, NULL); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); +} + +TEST(ChunkFifoTest, OneElementTest) { + chunk_fifo_t chunk_fifo; + ret_code_t ret; + CHUNK_FIFO_INIT(ret, chunk_fifo, 1, 100, 4); + + + + uint8_t num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // Close the chunk-fifo without opening it --> nothing should happen + chunk_fifo_write_close(&chunk_fifo); + + // Open the chunk-fifo for writing one element + data_t* data = NULL; + additional_info_t* additional_info = NULL; + chunk_fifo_write_open(&chunk_fifo, (void**)&data, (void**)&additional_info); + + // Put some data into the chunk-fifo + memset(data->buf, 0xAB, 100); + memset(additional_info->buf, 0xCD, 4); + + // Num should still be 0, because not closed the write yet + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // There should be no chunk to read: + data_t* data_read; + additional_info_t* additional_info_read; + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + chunk_fifo_read_close(&chunk_fifo); + + // Close the chunk-fifo + chunk_fifo_write_close(&chunk_fifo); + + // Now the number of chunks should be 1 + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 1); + + + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Test the equality of the data + EXPECT_ARRAY_EQ(data->buf, data_read->buf, 100); + EXPECT_ARRAY_EQ(additional_info->buf, additional_info_read->buf, 4); + + chunk_fifo_read_close(&chunk_fifo); + + // Now the number of chunks should be 0 + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // And there should be no chunk to read: + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + + +} + + +TEST(ChunkFifoTest, MultipleElementsTest) { + chunk_fifo_t chunk_fifo; + ret_code_t ret; + CHUNK_FIFO_INIT(ret, chunk_fifo, 10, 100, 4); + + + + uint8_t num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // Open the chunk-fifo for writing one element + data_t* data = NULL; + additional_info_t* additional_info = NULL; + data_t* data_read; + additional_info_t* additional_info_read; + + for(uint8_t i = 0; i < 20; i++) { + chunk_fifo_write_open(&chunk_fifo, (void**)&data, (void**)&additional_info); + chunk_fifo_write_close(&chunk_fifo); + } + // Number of elements should be maximum 10 (although 20 written chunks). But the latest chunks will be ignored. + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 10); + + + + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_SUCCESS); + chunk_fifo_read_close(&chunk_fifo); + + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 9); + + + chunk_fifo_write_open(&chunk_fifo, (void**)&data, (void**)&additional_info); + chunk_fifo_write_close(&chunk_fifo); + + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 10); + + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_SUCCESS); + chunk_fifo_read_close(&chunk_fifo); + + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 9); + + +} + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/circular_fifo_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/circular_fifo_lib_unittest.cc new file mode 100644 index 0000000..e9b3bf2 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/circular_fifo_lib_unittest.cc @@ -0,0 +1,254 @@ + + +// Don't forget gtest.h, which declares the testing framework. + + +#include "gtest/gtest.h" +#include "circular_fifo_lib.h" + + +namespace { + + +TEST(CircularFifoTest, InitTest) { + circular_fifo_t circular_fifo; + ret_code_t ret; + CIRCULAR_FIFO_INIT(ret, circular_fifo, 100); + EXPECT_EQ(ret, NRF_SUCCESS); +} + + +TEST(CircularFifoTest, InitErrorTest) { + circular_fifo_t circular_fifo; + ret_code_t ret; + ret = circular_fifo_init(&circular_fifo, NULL, 100); + EXPECT_EQ(ret, NRF_ERROR_NULL); +} + +TEST(CircularFifoTest, ReadWriteTest) { + circular_fifo_t circular_fifo; + ret_code_t ret; + CIRCULAR_FIFO_INIT(ret, circular_fifo, 200); + EXPECT_EQ(ret, NRF_SUCCESS); + + + uint8_t write_data[100]; + uint8_t read_data[100]; + memset(read_data, 0, sizeof(read_data)); + for(uint32_t i = 0; i < sizeof(write_data); i++) { + write_data[i] = i % 256; + } + + uint32_t size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, 0); + + circular_fifo_write(&circular_fifo, write_data, sizeof(write_data)); + size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, sizeof(write_data)); + + uint32_t read_size = sizeof(read_data); + circular_fifo_read(&circular_fifo, read_data, &read_size); + EXPECT_EQ(read_size, sizeof(read_data)); + EXPECT_ARRAY_EQ(write_data, read_data, read_size); + + size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, 0); + + read_size = sizeof(read_data); + circular_fifo_read(&circular_fifo, read_data, &read_size); + EXPECT_EQ(read_size, 0); +} + + + +TEST(CircularFifoTest, MultipleReadWriteTest) { + circular_fifo_t circular_fifo; + ret_code_t ret; + CIRCULAR_FIFO_INIT(ret, circular_fifo, 200); + EXPECT_EQ(ret, NRF_SUCCESS); + + for(uint32_t k = 0; k < 10; k++) { + uint8_t write_data[150]; + uint8_t read_data[150]; + memset(read_data, 0, sizeof(read_data)); + for(uint32_t i = 0; i < sizeof(write_data); i++) { + write_data[i] = i % 256; + } + + uint32_t size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, 0); + + circular_fifo_write(&circular_fifo, write_data, sizeof(write_data)); + size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, sizeof(write_data)); + + uint32_t read_size = sizeof(read_data); + circular_fifo_read(&circular_fifo, read_data, &read_size); + EXPECT_EQ(read_size, sizeof(read_data)); + EXPECT_ARRAY_EQ(write_data, read_data, read_size); + + size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, 0); + } +} + +TEST(CircularFifoTest, FlushTest) { + circular_fifo_t circular_fifo; + ret_code_t ret; + CIRCULAR_FIFO_INIT(ret, circular_fifo, 200); + EXPECT_EQ(ret, NRF_SUCCESS); + + + uint8_t write_data[150]; + uint8_t read_data[150]; + memset(read_data, 0, sizeof(read_data)); + for(uint32_t i = 0; i < sizeof(write_data); i++) { + write_data[i] = i % 256; + } + + uint32_t size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, 0); + + circular_fifo_write(&circular_fifo, write_data, sizeof(write_data)); + size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, sizeof(write_data)); + + circular_fifo_flush(&circular_fifo); + + size = circular_fifo_get_size(&circular_fifo); + EXPECT_EQ(size, 0); + + uint32_t read_size = sizeof(read_data); + circular_fifo_read(&circular_fifo, read_data, &read_size); + EXPECT_EQ(read_size, 0); + + +} + + +/* + +TEST(ChunkFifoTest, InitErrorTest) { + chunk_fifo_t chunk_fifo; + ret_code_t ret; + CHUNK_FIFO_INIT(ret, chunk_fifo, 0, 100, 4); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + // Initialize the chunk-fifo with NULL as buffer + ret = chunk_fifo_init(&chunk_fifo, 1, 100, 4, NULL); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); +} + +TEST(ChunkFifoTest, OneElementTest) { + chunk_fifo_t chunk_fifo; + ret_code_t ret; + CHUNK_FIFO_INIT(ret, chunk_fifo, 1, 100, 4); + + + + uint8_t num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // Close the chunk-fifo without opening it --> nothing should happen + chunk_fifo_write_close(&chunk_fifo); + + // Open the chunk-fifo for writing one element + data_t* data = NULL; + additional_info_t* additional_info = NULL; + chunk_fifo_write_open(&chunk_fifo, (void**)&data, (void**)&additional_info); + + // Put some data into the chunk-fifo + memset(data->buf, 0xAB, 100); + memset(additional_info->buf, 0xCD, 4); + + // Num should still be 0, because not closed the write yet + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // There should be no chunk to read: + data_t* data_read; + additional_info_t* additional_info_read; + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + chunk_fifo_read_close(&chunk_fifo); + + // Close the chunk-fifo + chunk_fifo_write_close(&chunk_fifo); + + // Now the number of chunks should be 1 + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 1); + + + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Test the equality of the data + EXPECT_ARRAY_EQ(data->buf, data_read->buf, 100); + EXPECT_ARRAY_EQ(additional_info->buf, additional_info_read->buf, 4); + + chunk_fifo_read_close(&chunk_fifo); + + // Now the number of chunks should be 0 + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // And there should be no chunk to read: + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + + +} + + +TEST(ChunkFifoTest, MultipleElementsTest) { + chunk_fifo_t chunk_fifo; + ret_code_t ret; + CHUNK_FIFO_INIT(ret, chunk_fifo, 10, 100, 4); + + + + uint8_t num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 0); + + // Open the chunk-fifo for writing one element + data_t* data = NULL; + additional_info_t* additional_info = NULL; + data_t* data_read; + additional_info_t* additional_info_read; + + for(uint8_t i = 0; i < 20; i++) { + chunk_fifo_write_open(&chunk_fifo, (void**)&data, (void**)&additional_info); + chunk_fifo_write_close(&chunk_fifo); + } + // Number of elements should be maximum 10 (although 20 written chunks). But the latest chunks will be ignored. + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 10); + + + + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_SUCCESS); + chunk_fifo_read_close(&chunk_fifo); + + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 9); + + + chunk_fifo_write_open(&chunk_fifo, (void**)&data, (void**)&additional_info); + chunk_fifo_write_close(&chunk_fifo); + + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 10); + + ret = chunk_fifo_read_open(&chunk_fifo, (void**) &data_read, (void**) &additional_info_read); + EXPECT_EQ(ret, NRF_SUCCESS); + chunk_fifo_read_close(&chunk_fifo); + + num = chunk_fifo_get_number_of_chunks(&chunk_fifo); + EXPECT_EQ(num, 9); + + +} +*/ +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/data_generator_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/data_generator_lib_unittest.cc new file mode 100644 index 0000000..8f7dd19 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/data_generator_lib_unittest.cc @@ -0,0 +1,66 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "data_generator_internal_lib.h" +#include "gtest/gtest.h" + +// Declaring the generator-function type +DATA_GENERATOR_FUNCTION_DECLARATION(TEST, uint8_t, uint8_t* x, uint8_t* y); + +// Declaring all the needed data-generating functions +DATA_GENERATOR_DECLARATION(TEST, uint8_t, uint8_t* x, uint8_t* y); + +// Implementing all the needed data-generating functions +DATA_GENERATOR_IMPLEMENTATION(TEST, uint8_t, uint8_t* x, uint8_t* y) { + + if(data_generator_TEST_get_generator() != NULL) + return data_generator_TEST_get_generator()(x, y); + + *x = 0; + *y = 0; + return 0; +} + + +uint8_t generator_handler(uint8_t* x, uint8_t* y) { + *x = 1; + *y = 2; + + return 1; +} + + +namespace { + +class DataGeneratorTest : public ::testing::Test { + virtual void SetUp() { + data_generator_TEST_reset(); + } +}; + + +TEST_F(DataGeneratorTest, DefaultTest) { + uint8_t x, y; + uint8_t ret = data_generator_TEST(&x, &y); + + EXPECT_EQ(ret, 0); + EXPECT_EQ(x, 0); + EXPECT_EQ(y, 0); +} + +TEST_F(DataGeneratorTest, GeneratorTest) { + uint8_t x, y; + data_generator_TEST_set_generator(generator_handler); + uint8_t ret = data_generator_TEST(&x, &y); + + EXPECT_EQ(ret, 1); + EXPECT_EQ(x, 1); + EXPECT_EQ(y, 2); +} + +}; + + + + diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/eeprom_lib_mock_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/eeprom_lib_mock_unittest.cc new file mode 100644 index 0000000..0541e29 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/eeprom_lib_mock_unittest.cc @@ -0,0 +1,97 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "eeprom_lib.h" +#include "gtest/gtest.h" + +#define EEPROM_SIZE_TEST (256*1024) + + +extern void eeprom_write_to_file(const char* filename); + + +namespace { + + +// Test for init the module +TEST(EEPROMInitTest, ReturnValue) { + ret_code_t ret = eeprom_init(); + ASSERT_EQ(ret, NRF_SUCCESS); +} + +TEST(EEPROMInitTest, SizeCheck) { + uint32_t size = eeprom_get_size(); + ASSERT_EQ(size, EEPROM_SIZE_TEST); +} + +TEST(EEPROMStoreTest, StartOfEEPROMTest) { + char store_data[] = "Test data!"; + int len = sizeof(store_data); + ret_code_t ret = eeprom_store(0, (uint8_t*) store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = eeprom_read(0, (uint8_t*) read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_STREQ(read_data, store_data); +} + + + + +TEST(EEPROMStoreTest, EndOfEEPROMTest) { + char store_data[] = "Test data!"; + int len = sizeof(store_data); + ret_code_t ret = eeprom_store(EEPROM_SIZE_TEST-len, (uint8_t*) store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = eeprom_read(EEPROM_SIZE_TEST-len, (uint8_t*) read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_STREQ(read_data, store_data); +} + +TEST(EEPROMStoreTest, StoreAllOfEEPROMTest) { + char store_data[EEPROM_SIZE_TEST]; + int len = sizeof(store_data); + memset((uint8_t*) store_data, 0xAB, len); + + ret_code_t ret = eeprom_store(0, (uint8_t*) store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = eeprom_read(0, (uint8_t*) read_data, len); + + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(read_data, store_data, len) == 0); +} + +TEST(EEPROMAddressTest, FalseAddressTest) { + char store_data[20]; + int len = sizeof(store_data); + memset((uint8_t*) store_data, 0xDE, len); + + ret_code_t ret = eeprom_store(EEPROM_SIZE_TEST-len+1, (uint8_t*) store_data, len); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + char read_data[len]; + ret = eeprom_read(EEPROM_SIZE_TEST-len+1, (uint8_t*) read_data, len); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); +} + +TEST(EEPROMNullPointerTest, ReturnValueTest) { + + + ret_code_t ret = eeprom_store(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + ret = eeprom_read(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + eeprom_write_to_file("EEPROM.txt"); + +} + + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/filesystem_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/filesystem_lib_unittest.cc new file mode 100644 index 0000000..8bcb35d --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/filesystem_lib_unittest.cc @@ -0,0 +1,1091 @@ + + +// Don't forget gtest.h, which declares the testing framework. +#include "gtest/gtest.h" + +#include "filesystem_lib.h" +#include "storage_lib.h" +#include "storage1_lib.h" +#include "storage2_lib.h" + + + +#define STORAGE1_SIZE_TEST (30*256*sizeof(uint32_t)) +#define STORAGE1_UNIT_SIZE_TEST (256*sizeof(uint32_t)) + +#define STORAGE2_SIZE_TEST (1024*256) +#define STORAGE2_UNIT_SIZE_TEST (1) + + +#define MAX_NUMBER_OF_PARTITIONS_TEST 20 +#define PARTITION_METADATA_SIZE_TEST 14 +#define PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE_TEST 2 +#define PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE_TEST 2 +#define PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE_TEST 2 + + + +extern ret_code_t filesystem_compute_available_size(uint32_t partition_start_address, uint32_t required_size, uint32_t* available_size); + +extern uint32_t filesystem_get_swap_page_address_of_partition(uint16_t partition_id); + + + +extern uint32_t filesystem_compute_next_element_address(uint16_t partition_id, uint32_t cur_element_address, uint16_t cur_element_len, uint16_t next_element_len); + +extern uint16_t filesystem_get_element_header_len(uint16_t partition_id); + + + +extern partition_iterator_t partition_iterators[]; +extern partition_t partitions[]; +extern uint32_t next_free_address; + + + +extern void eeprom_write_to_file(const char* filename); +extern void flash_write_to_file(const char* filename); + + + +namespace { + +class FilesystemTest : public ::testing::Test { + virtual void SetUp() { + filesystem_init(); + } +}; + + +TEST_F(FilesystemTest, InitTest) { + ret_code_t ret = filesystem_init(); + ASSERT_EQ(ret, NRF_SUCCESS); + + ASSERT_EQ(PARTITION_METADATA_SIZE_TEST, PARTITION_METADATA_SIZE); + + ASSERT_EQ(PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE_TEST, PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE); + ASSERT_EQ(PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE_TEST, PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE); + ASSERT_EQ(PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE_TEST, PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE); + + + ASSERT_EQ(MAX_NUMBER_OF_PARTITIONS_TEST, MAX_NUMBER_OF_PARTITIONS); + + ASSERT_EQ(STORAGE1_SIZE_TEST, storage1_get_size()); + ASSERT_EQ(STORAGE1_UNIT_SIZE_TEST, storage1_get_unit_size()); + ASSERT_EQ(STORAGE2_SIZE_TEST, storage2_get_size()); + ASSERT_EQ(STORAGE2_UNIT_SIZE_TEST, storage2_get_unit_size()); +} + +TEST_F(FilesystemTest, NextFreeAdressTest) { + EXPECT_EQ(SWAP_PAGE_SIZE, (MAX_NUMBER_OF_PARTITIONS_TEST*(PARTITION_METADATA_SIZE_TEST + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE_TEST + PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE_TEST + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE_TEST))); + + // The next free address should be after the Swap page + EXPECT_EQ(next_free_address, 1024); +} + +TEST_F(FilesystemTest, ComputeAvailableSizeTest) { + uint32_t partition_start_address = 0; + uint32_t required_size = 100; + uint32_t available_size = 0; + ret_code_t ret; + + // Storage 1 start + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(available_size, STORAGE1_UNIT_SIZE_TEST); + + // Storage 1 end + partition_start_address = STORAGE1_SIZE_TEST - STORAGE1_UNIT_SIZE_TEST; + required_size = 100; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(available_size, STORAGE1_UNIT_SIZE_TEST); + + // Storage 2 start + partition_start_address = STORAGE1_SIZE_TEST; + required_size = 100; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(available_size, 100); + + // Storage 2 end + partition_start_address = STORAGE1_SIZE_TEST + STORAGE2_SIZE_TEST - 100; + required_size = 100; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(available_size, 100); + + // Storage 1 and 2 + partition_start_address = STORAGE1_SIZE_TEST - STORAGE1_UNIT_SIZE_TEST; + required_size = STORAGE1_UNIT_SIZE_TEST + 100; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(available_size, STORAGE1_UNIT_SIZE_TEST + 100); + + // Exceptions + // Too large data --> clip to available data + partition_start_address = STORAGE1_SIZE_TEST + STORAGE2_SIZE_TEST - 3; + required_size = STORAGE1_UNIT_SIZE_TEST + 100; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(available_size, 3); + + + // Storage 1 and 2 false start address (not unit-aligned) + partition_start_address = STORAGE1_SIZE_TEST - 3; + required_size = 100; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + // Too large address + partition_start_address = STORAGE1_SIZE_TEST + STORAGE2_SIZE_TEST; + required_size = 100; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + // Zero length required size + partition_start_address = 0; + required_size = 0; + ret = filesystem_compute_available_size(partition_start_address, required_size, &available_size); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); +} + + +TEST_F(FilesystemTest, GetSwapPageAddressTest) { + uint32_t header_len = (PARTITION_METADATA_SIZE_TEST + PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE_TEST + PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE_TEST + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE_TEST); + + // Static partition + uint32_t swap_page_address = filesystem_get_swap_page_address_of_partition(0); + EXPECT_EQ(swap_page_address, 0); + + // Static partition with crc + swap_page_address = filesystem_get_swap_page_address_of_partition(0x4001); + EXPECT_EQ(swap_page_address, 1*header_len); + + // Dynamic partition + swap_page_address = filesystem_get_swap_page_address_of_partition(0x8000); + EXPECT_EQ(swap_page_address, 0); + + // Dynamic partition with crc + swap_page_address = filesystem_get_swap_page_address_of_partition(0xC005); + EXPECT_EQ(swap_page_address, 5*header_len); +} + + +TEST_F(FilesystemTest, GetHeaderLenTest) { + + + uint16_t partition_id = 0x8000; + uint16_t header_len = filesystem_get_element_header_len(partition_id); + EXPECT_EQ(header_len, PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE_TEST + PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE_TEST); + + partition_id = 0xC000; + header_len = filesystem_get_element_header_len(partition_id); + EXPECT_EQ(header_len, PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE_TEST + PARTITION_ELEMENT_HEADER_PREVIOUS_LEN_XOR_CUR_LEN_SIZE_TEST + PARTITION_ELEMENT_HEADER_ELEMENT_CRC_SIZE_TEST); + + partition_id = 0x0001; + header_len = filesystem_get_element_header_len(partition_id); + EXPECT_EQ(header_len, PARTITION_ELEMENT_HEADER_RECORD_ID_SIZE_TEST); + +} + + +TEST_F(FilesystemTest, RegisterPartitionTest) { + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = 1000; + + // Registering a dynamic partition with crc + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partition_id, 0x8000 | 0x4000); + EXPECT_EQ(required_size, 1024); + EXPECT_EQ(next_free_address, 1024 + 1024); + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 0); + EXPECT_EQ(partitions[0].latest_element_address, 1024); + EXPECT_EQ(partitions[0].latest_element_record_id, 1); + EXPECT_EQ(partitions[0].latest_element_len, 0); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 1024); + EXPECT_EQ(partitions[0].metadata.first_element_len, 0); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024); + + + + // Registering a static partition with crc + ret = filesystem_register_partition(&partition_id, &required_size, 0, 1, 100); + ASSERT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partition_id, 0x4001); + EXPECT_EQ(required_size, 1024); + EXPECT_EQ(next_free_address, 1024 + 1024*2); + + EXPECT_EQ(partitions[1].first_element_address, 1024*2); + EXPECT_EQ(partitions[1].has_first_element, 0); + EXPECT_EQ(partitions[1].latest_element_address, 1024*2); + EXPECT_EQ(partitions[1].latest_element_record_id, 1); + EXPECT_EQ(partitions[1].latest_element_len, 0); + + EXPECT_EQ(partitions[1].metadata.partition_id, 0x4001); + EXPECT_EQ(partitions[1].metadata.partition_size, 1024); + EXPECT_EQ(partitions[1].metadata.first_element_len, 100); + EXPECT_EQ(partitions[1].metadata.last_element_address, 1024*2); + + + + // Registering a static partition without crc + ret = filesystem_register_partition(&partition_id, &required_size, 0, 0, 200); + ASSERT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partition_id, 0x2); + EXPECT_EQ(required_size, 1024); + EXPECT_EQ(next_free_address, 1024 + 1024*3); + + EXPECT_EQ(partitions[2].first_element_address, 1024*3); + EXPECT_EQ(partitions[2].has_first_element, 0); + EXPECT_EQ(partitions[2].latest_element_address, 1024*3); + EXPECT_EQ(partitions[2].latest_element_record_id, 1); + EXPECT_EQ(partitions[2].latest_element_len, 0); + + EXPECT_EQ(partitions[2].metadata.partition_id, 0x2); + EXPECT_EQ(partitions[2].metadata.partition_size, 1024); + EXPECT_EQ(partitions[2].metadata.first_element_len, 200); + EXPECT_EQ(partitions[2].metadata.last_element_address, 1024*3); + + + + // Registering a dynamic partition without crc + ret = filesystem_register_partition(&partition_id, &required_size, 1, 0, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partition_id, 0x8003); + EXPECT_EQ(required_size, 1024); + EXPECT_EQ(next_free_address, 1024 + 1024*4); + + EXPECT_EQ(partitions[3].first_element_address, 1024*4); + EXPECT_EQ(partitions[3].has_first_element, 0); + EXPECT_EQ(partitions[3].latest_element_address, 1024*4); + EXPECT_EQ(partitions[3].latest_element_record_id, 1); + EXPECT_EQ(partitions[3].latest_element_len, 0); + + EXPECT_EQ(partitions[3].metadata.partition_id, 0x8003); + EXPECT_EQ(partitions[3].metadata.partition_size, 1024); + EXPECT_EQ(partitions[3].metadata.first_element_len, 0); + EXPECT_EQ(partitions[3].metadata.last_element_address, 1024*4); +} + + +TEST_F(FilesystemTest, StoreElementTest) { + + + uint8_t data[1000]; + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = 1000; + + // Registering a dynamic partition with crc + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + + + ret = filesystem_store_element(partition_id, data, 105); + EXPECT_EQ(ret, NRF_SUCCESS); + + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 1); + EXPECT_EQ(partitions[0].latest_element_address, 1024); + EXPECT_EQ(partitions[0].latest_element_record_id, 1); + EXPECT_EQ(partitions[0].latest_element_len, 105); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 1024); + EXPECT_EQ(partitions[0].metadata.first_element_len, 105); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024); + + + // Read the raw data from storage + uint8_t read_data[sizeof(data)]; + ret = storage_read(1024, read_data, sizeof(read_data)); + EXPECT_EQ(ret, NRF_SUCCESS); + + uint8_t tmp[PARTITION_METADATA_SIZE_TEST + filesystem_get_element_header_len(partition_id)]; + tmp[2] = 0x80 | 0x40; // partition_id msb + tmp[3] = 0x00; // partition_id lsb + tmp[4] = 0x00; // partition_size msb + tmp[5] = 0x00; // partition_size + tmp[6] = 0x04; // partition_size + tmp[7] = 0x00; // partition_size lsb + tmp[8] = 0x00; // first_element_len msb + tmp[9] = 0x69; // first_element_len lsb + tmp[10] = 0x00; // last_element_address msb + tmp[11] = 0x00; // last_element_address + tmp[12] = 0x04; // last_element_address + tmp[13] = 0x00; // last_element_address lsb + tmp[14] = 0x00; // record_id msb + tmp[15] = 0x01; // record_id lsb + tmp[16] = 0x00; // previous_len_XOR_cur_len msb + tmp[17] = 0x00; // previous_len_XOR_cur_len lsb + + + // Check if the metadata and element header is correct (except header-crc and element-crc) + EXPECT_TRUE(memcmp(&tmp[2], &read_data[2], 16) == 0); + + // Check if the data are in storage after the metadata and element header + EXPECT_TRUE(memcmp(data, &read_data[PARTITION_METADATA_SIZE_TEST + filesystem_get_element_header_len(partition_id)], 105) == 0); + + + + // Registering a static partition with crc + ret = filesystem_register_partition(&partition_id, &required_size, 0, 1, 100); + ASSERT_EQ(ret, NRF_SUCCESS); + + + ret = filesystem_store_element(partition_id, data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = storage_read(1024 + 1024, read_data, sizeof(read_data)); + EXPECT_EQ(ret, NRF_SUCCESS); + + uint8_t tmp1[PARTITION_METADATA_SIZE_TEST + filesystem_get_element_header_len(partition_id)]; + tmp1[2] = 0x40; // partition_id msb + tmp1[3] = 0x01; // partition_id lsb + tmp1[4] = 0x00; // partition_size msb + tmp1[5] = 0x00; // partition_size + tmp1[6] = 0x04; // partition_size + tmp1[7] = 0x00; // partition_size lsb + tmp1[8] = 0x00; // first_element_len msb + tmp1[9] = 0x64; // first_element_len lsb + tmp1[10] = 0x00; // last_element_address msb + tmp1[11] = 0x00; // last_element_address + tmp1[12] = 0x08; // last_element_address + tmp1[13] = 0x00; // last_element_address lsb + tmp1[14] = 0x00; // record_id msb + tmp1[15] = 0x01; // record_id lsb + + + + + + // Check if the metadata and element header is correct (except header-crc and element-crc) + EXPECT_TRUE(memcmp(&tmp1[2], &read_data[2], 14) == 0); + + // Check if the data are in storage after the metadata and element header + EXPECT_TRUE(memcmp(data, &read_data[PARTITION_METADATA_SIZE_TEST + filesystem_get_element_header_len(partition_id)], 100) == 0); + + + + + + + // Exception because element_len != 0 and != 100 + ret = filesystem_store_element(partition_id, data, 105); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); +} + + +TEST_F(FilesystemTest, RegisterAfterStoreTest) { + + + uint8_t data[1000]; + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = 2000; + + // Registering a dynamic partition with crc + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 0); + EXPECT_EQ(partitions[0].latest_element_address, 1024); + EXPECT_EQ(partitions[0].latest_element_record_id, 1); + EXPECT_EQ(partitions[0].latest_element_len, 0); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 2048); + EXPECT_EQ(partitions[0].metadata.first_element_len, 0); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024); + + + + // TODO: store some more data (2 pages) and overwrite the first element header. + + ret = filesystem_store_element(partition_id, data, 500); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_store_element(partition_id, data, 500); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_store_element(partition_id, data, 500); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_store_element(partition_id, data, 500); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_store_element(partition_id, data, 500); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_store_element(partition_id, data, 500); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Reset the filesystem + filesystem_reset(); + + // Try to find the former created filesystem + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 1); + EXPECT_EQ(partitions[0].latest_element_address, 1024 + PARTITION_METADATA_SIZE_TEST + 1*(filesystem_get_element_header_len(partition_id) + 500)); + EXPECT_EQ(partitions[0].latest_element_record_id, 6); + EXPECT_EQ(partitions[0].latest_element_len, 500); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 2048); + EXPECT_EQ(partitions[0].metadata.first_element_len, 500); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024 + PARTITION_METADATA_SIZE_TEST + 3*(filesystem_get_element_header_len(partition_id) + 500)); + + + + // Corrupt the first_element_header, so the entry in swap page should be used. + uint8_t tmp[10]; + memset(tmp, 0xAB, 10); + storage_store(1024, tmp, 2); + + + + // Reset the filesystem + filesystem_reset(); + + // Try to find the former created filesystem + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 1); + EXPECT_EQ(partitions[0].latest_element_address, 1024); // Because the other elements on same page have been deleted + EXPECT_EQ(partitions[0].latest_element_record_id, 5); // Because the other elements on same page have been deleted + EXPECT_EQ(partitions[0].latest_element_len, 500); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 2048); + EXPECT_EQ(partitions[0].metadata.first_element_len, 500); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024 + PARTITION_METADATA_SIZE_TEST + 3*(filesystem_get_element_header_len(partition_id) + 500)); + +} + + +TEST_F(FilesystemTest, StoreLargeElementTest) { + uint8_t data[5000]; + for(uint16_t i =0; i < 5000; i++) + data[i] = i % 256; + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = 2000; + + // Registering a dynamic partition with crc + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 0); + EXPECT_EQ(partitions[0].latest_element_address, 1024); + EXPECT_EQ(partitions[0].latest_element_record_id, 1); + EXPECT_EQ(partitions[0].latest_element_len, 0); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 2048); + EXPECT_EQ(partitions[0].metadata.first_element_len, 0); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024); + + // Store a large element + ret = filesystem_store_element(partition_id, data, 2000); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 1); + EXPECT_EQ(partitions[0].latest_element_address, 1024); + EXPECT_EQ(partitions[0].latest_element_record_id, 1); + EXPECT_EQ(partitions[0].latest_element_len, 2000); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 2048); + EXPECT_EQ(partitions[0].metadata.first_element_len, 2000); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024); + + // Store another large element + ret = filesystem_store_element(partition_id, data, 1999); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partitions[0].first_element_address, 1024); + EXPECT_EQ(partitions[0].has_first_element, 1); + EXPECT_EQ(partitions[0].latest_element_address, 1024); + EXPECT_EQ(partitions[0].latest_element_record_id, 2); + EXPECT_EQ(partitions[0].latest_element_len, 1999); + + EXPECT_EQ(partitions[0].metadata.partition_id, 0x8000 | 0x4000); + EXPECT_EQ(partitions[0].metadata.partition_size, 2048); + EXPECT_EQ(partitions[0].metadata.first_element_len, 1999); + EXPECT_EQ(partitions[0].metadata.last_element_address, 1024); + + // Store a too large element + ret = filesystem_store_element(partition_id, data, 2029); + EXPECT_EQ(ret, NRF_ERROR_NO_MEM); + + + +} + +TEST_F(FilesystemTest, IteratorInitTest) { + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = STORAGE1_SIZE_TEST; + + // Register partition + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + + // Create 3 elements in partition + uint8_t data[3000]; + for(uint32_t j = 0; j < 3; j++) { + + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, (j+1)*1000); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + + + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partition_iterators[0].iterator_valid, 0xA5); + +} + + +TEST_F(FilesystemTest, DynamicIteratorReadTest) { + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = STORAGE1_SIZE_TEST; + + // Register partition + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + + // Create elements in partition + uint8_t data[1000]; + for(uint32_t j = 0; j < 1000; j++) { + + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, j); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + + // Reset the filesystem + filesystem_reset(); + + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_iterator_init(partition_id); + ASSERT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(partition_iterators[0].iterator_valid, 0xA5); + + + uint8_t read_data[1000]; + uint32_t j = 999; + while(ret == NRF_SUCCESS) { + uint16_t element_len, record_id; + + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(element_len, j); + EXPECT_EQ(record_id, j + 1); + + uint8_t data[1000]; + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + + EXPECT_TRUE(memcmp(read_data, data, element_len) == 0); + j--; + + ret = filesystem_iterator_previous(partition_id); + } + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + + + + + ret = NRF_SUCCESS; + j++; + while(ret == NRF_SUCCESS) { + uint16_t element_len, record_id; + + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(element_len, j); + EXPECT_EQ(record_id, j + 1); + + uint8_t data[1000]; + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + + EXPECT_TRUE(memcmp(read_data, data, element_len) == 0); + j++; + + ret = filesystem_iterator_next(partition_id); + } + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + +} + + +TEST_F(FilesystemTest, StaticIteratorReadTest) { + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = STORAGE1_SIZE_TEST; + + // Register partition + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 0, 0, 500); + ASSERT_EQ(ret, NRF_SUCCESS); + + + // Create elements in partition + uint8_t data[1000]; + for(uint32_t j = 0; j < 1000; j++) { + + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + + // Reset the filesystem + filesystem_reset(); + + ret = filesystem_register_partition(&partition_id, &required_size, 0, 0, 500); + ASSERT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_iterator_init(partition_id); + ASSERT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(partition_iterators[0].iterator_valid, 0xA5); + + + uint8_t read_data[1000]; + uint32_t j = 999; + while(ret == NRF_SUCCESS) { + uint16_t element_len, record_id; + + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(element_len, 500); + EXPECT_EQ(record_id, j + 1); + + uint8_t data[1000]; + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + + EXPECT_TRUE(memcmp(read_data, data, element_len) == 0); + j--; + + ret = filesystem_iterator_previous(partition_id); + } + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + + + + + ret = NRF_SUCCESS; + j++; + while(ret == NRF_SUCCESS) { + uint16_t element_len, record_id; + + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(element_len, 500); + EXPECT_EQ(record_id, j + 1); + + uint8_t data[1000]; + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + + EXPECT_TRUE(memcmp(read_data, data, element_len) == 0); + j++; + + ret = filesystem_iterator_next(partition_id); + } + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + +} + +TEST_F(FilesystemTest, CorruptedDataTest) { + + + + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = STORAGE1_SIZE_TEST; + + uint8_t data[1000]; + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + + // Register dummy partition (so our actual partition is in EEPROM --> manipulate bytes) + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + + // Register a dynamic partition with CRC + required_size = 3000; + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + + // Create 2 elements in partition + ret = filesystem_store_element(partition_id, data, 1000); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_store_element(partition_id, data, 1000); + EXPECT_EQ(ret, NRF_SUCCESS); + + + + ret = filesystem_iterator_init(partition_id); + ASSERT_EQ(ret, NRF_SUCCESS); + + uint8_t read_data[1000]; + uint16_t element_len, record_id; + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(element_len, 1000); + EXPECT_EQ(record_id, 2); + + + + + uint32_t element_address = partitions[1].latest_element_address; + uint8_t tmp = 0xAB; + uint8_t tmp_read; + ret = storage_read(element_address + filesystem_get_element_header_len(partition_id) + 1, &tmp_read, 1); + + // Manipulate one byte in element data + ret = storage_store(element_address + filesystem_get_element_header_len(partition_id) + 1, &tmp, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_DATA); + + // Restore the entry + ret = storage_store(element_address + filesystem_get_element_header_len(partition_id) + 1, &tmp_read, 1); + + // Manipulate one byte in element header + ret = storage_store(element_address + filesystem_get_element_header_len(partition_id) - 1, &tmp, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + // The iterator should stay in invalid state + ret = filesystem_iterator_previous(partition_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + + + // Another test with a static partition without CRC + + + // Register a static partition without CRC + required_size = 3000; + ret = filesystem_register_partition(&partition_id, &required_size, 0, 0, 100); + ASSERT_EQ(ret, NRF_SUCCESS); + + // Create 2 elements in partition + ret = filesystem_store_element(partition_id, data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_store_element(partition_id, data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Initialize the iterator + ret = filesystem_iterator_init(partition_id); + ASSERT_EQ(ret, NRF_SUCCESS); + + // Read current (latest element) + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(element_len, 100); + EXPECT_EQ(record_id, 2); + + + // Now manipulate some bytes + element_address = partitions[2].latest_element_address; + tmp = 0xAB; + + ret = storage_read(element_address + filesystem_get_element_header_len(partition_id) + 1, &tmp_read, 1); + + // Manipulate one byte in element data + ret = storage_store(element_address + filesystem_get_element_header_len(partition_id) + 1, &tmp, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_SUCCESS); // Because we don't use CRC here, we could not detect corrupted data + + // Restore the entry + ret = storage_store(element_address + filesystem_get_element_header_len(partition_id) + 1, &tmp_read, 1); + + // Manipulate one byte in element header + ret = storage_store(element_address + filesystem_get_element_header_len(partition_id) - 1, &tmp, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_read_element(partition_id, read_data, &element_len, &record_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + // The iterator should stay in invalid state + ret = filesystem_iterator_previous(partition_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + +} + + +TEST_F(FilesystemTest, ClearTest) { + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = STORAGE1_SIZE_TEST; + + // Register partition + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + + // Create 3 elements in partition + uint8_t data[3000]; + for(uint32_t j = 0; j < 3; j++) { + + for(uint16_t i =0; i < 3000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, (j+1)*1000); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + + + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partition_iterators[0].iterator_valid, 0xA5); + + // If we only reset the filesystem, the partition should be still there and we can create an iterator for it + ret = filesystem_reset(); + EXPECT_EQ(ret, NRF_SUCCESS); + // Register partition + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(partition_iterators[0].iterator_valid, 0xA5); + + + + // If we clear the filesystem completely, we should not find a valid iterator + ret = filesystem_clear(); + EXPECT_EQ(ret, NRF_SUCCESS); + // Register partition + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + +} + +TEST_F(FilesystemTest, ClearPartitionTest) { + + + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = STORAGE1_SIZE_TEST; + + // Register partition to fill flash: + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + // Now in EEPROM + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + + // Create 3 elements in partition + uint8_t data[3000]; + for(uint32_t j = 0; j < 3; j++) { + + for(uint16_t i =0; i < 3000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, (j+1)*1000); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + + + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(partition_iterators[1].iterator_valid, 0xA5); + + // If we only reset the filesystem, the partition should be still there and we can create an iterator for it + ret = filesystem_reset(); + EXPECT_EQ(ret, NRF_SUCCESS); + // Register partition + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + // Now in EEPROM + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(partition_iterators[1].iterator_valid, 0xA5); + + // Check if number of elements is 3: + ret = filesystem_iterator_previous(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_previous(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = filesystem_iterator_previous(partition_id); + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + + + + // Now clear the partition: + ret = filesystem_clear_partition(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + + + // If we clear the partition, we should not find a valid iterator (because there are no elements anymore) + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_ERROR_INVALID_STATE); + + + // Write just one element, the same size like the first element before: + for(uint32_t j = 0; j < 1; j++) { + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, (j+1)*1000); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + + + // Now reset the filesystem, and reregister the partition again. + ret = filesystem_reset(); + EXPECT_EQ(ret, NRF_SUCCESS); + // Fill again flash partition + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + // Now in EEPROM + ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Now we should find an iterator + ret = filesystem_iterator_init(partition_id); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(partition_iterators[1].iterator_valid, 0xA5); + + // Actually now there should only be one element!!! + + // Check if number of elements is 1: + ret = filesystem_iterator_previous(partition_id); + EXPECT_EQ(ret, NRF_ERROR_NOT_FOUND); + +} + +TEST_F(FilesystemTest, AvailableSizeTest) { + uint32_t available_size = 0; + available_size = filesystem_get_available_size(); + EXPECT_EQ(available_size, STORAGE1_SIZE_TEST + STORAGE2_SIZE_TEST - STORAGE1_UNIT_SIZE_TEST); + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = 1222; + // Register partition + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + available_size = filesystem_get_available_size(); + EXPECT_EQ(available_size, STORAGE1_SIZE_TEST + STORAGE2_SIZE_TEST - STORAGE1_UNIT_SIZE_TEST - required_size); + +} + +TEST_F(FilesystemTest, IteratorConflictTest) { + + uint16_t partition_id = 0xFFFF; + uint32_t required_size = 2048; + + // Register partition + ret_code_t ret = filesystem_register_partition(&partition_id, &required_size, 1, 1, 0); + ASSERT_EQ(ret, NRF_SUCCESS); + + + // Create 3 elements in partition + uint8_t data[1000]; + for(uint32_t j = 0; j < 3; j++) { + + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, j); + EXPECT_EQ(ret, NRF_SUCCESS); + } + + + ret = filesystem_iterator_init(partition_id); + ASSERT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(partition_iterators[0].iterator_valid, 0xA5); + + // Create some more elements in partition + for(uint32_t j = 3; j < 58; j++) { + + for(uint16_t i =0; i < 1000; i++) + data[i] = i % 256; + + ret_code_t ret = filesystem_store_element(partition_id, data, j); + EXPECT_EQ(ret, NRF_SUCCESS); + } + // At element 59 we have a conflict now.. + ret = filesystem_store_element(partition_id, data, 59); + EXPECT_EQ(ret, NRF_ERROR_INTERNAL); +} + +}; + diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/flash_lib_mock_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/flash_lib_mock_unittest.cc new file mode 100644 index 0000000..9894a7d --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/flash_lib_mock_unittest.cc @@ -0,0 +1,235 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "flash_lib.h" +#include "gtest/gtest.h" + +#define FLASH_NUM_PAGES_TEST 30 +#define FLASH_PAGE_SIZE_WORDS_TEST 256 + + +extern void flash_write_to_file(const char* filename); + +namespace { + + +// Test for init the module +TEST(FlashInitTest, ReturnValue) { + ret_code_t ret = flash_init(); + ASSERT_EQ(ret, NRF_SUCCESS); +} + + +TEST(FlashInitTest, SizeCheck) { + uint32_t num_pages = flash_get_page_number(); + ASSERT_EQ(num_pages, FLASH_NUM_PAGES_TEST); + + uint32_t page_size_words = flash_get_page_size_words(); + ASSERT_EQ(page_size_words, FLASH_PAGE_SIZE_WORDS_TEST); +} + + +TEST(FlashStoreTest, StartOfFlashTest) { + char store_data[] = "Test data!!"; + int len = sizeof(store_data); + ASSERT_EQ(len%sizeof(uint32_t), 0); + int word_num = len/sizeof(uint32_t); + ret_code_t ret = flash_store(0, (uint32_t*) store_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = flash_read(0, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_STREQ(read_data, store_data); + + + + +} + + +TEST(FlashStoreTest, EndOfFlashTest) { + char store_data[] = "ABCDEFGHIJK"; + int len = sizeof(store_data); + ASSERT_EQ(len%sizeof(uint32_t), 0); + int word_num = len/sizeof(uint32_t); + int address = flash_get_page_number()*flash_get_page_size_words()-word_num; + ret_code_t ret = flash_store(address, (uint32_t*) store_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_STREQ(read_data, store_data); +} + + + +TEST(FlashEraseTest, StartOfFlash) { + ret_code_t ret = flash_erase(0, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + + char store_data[12]; + int len = sizeof(store_data); + ASSERT_EQ(len%sizeof(uint32_t), 0); + memset(store_data, 0x01, len); + int word_num = len/sizeof(uint32_t); + int address = 0; + ret = flash_store(address, (uint32_t*) store_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len)==0); + + ret = flash_erase(0, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(store_data, 0xFF, len); + EXPECT_TRUE(memcmp(store_data, read_data, len)==0); +} + + +TEST(FlashEraseTest, EndOfFlash) { + ret_code_t ret = flash_erase(flash_get_page_number()-1, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + + char store_data[12]; + int len = sizeof(store_data); + ASSERT_EQ(len%sizeof(uint32_t), 0); + memset(store_data, 0x11, len); + int word_num = len/sizeof(uint32_t); + int address = flash_get_page_number()*flash_get_page_size_words()-word_num; + ret = flash_store(address, (uint32_t*) store_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len)==0); + + ret = flash_erase(flash_get_page_number()-1, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(store_data, 0xFF, len); + EXPECT_TRUE(memcmp(store_data, read_data, len)==0); +} + + +TEST(FlashStoreTest, WriteTwiceToStartOfFlashTest) { + ret_code_t ret = flash_erase(0, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + + uint32_t store_word = 0xDEADBEEF; + int word_num = 1; + int address = 0; + ret = flash_store(address, (uint32_t*) &store_word, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + uint32_t read_word; + ret = flash_read(address, (uint32_t*) &read_word, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(read_word, store_word); + + + store_word = 0xFFFFFFFF; + ret = flash_store(address, (uint32_t*) &store_word, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = flash_read(address, (uint32_t*) &read_word, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_NE(read_word, store_word); +} + + +TEST(FlashStoreEraseTest, MultiPageTest) { + ret_code_t ret = flash_erase(1, 3); + EXPECT_EQ(ret, NRF_SUCCESS); + + + char store_data[flash_get_page_size_words()*2*sizeof(uint32_t)]; + int len = sizeof(store_data); + ASSERT_EQ(len%sizeof(uint32_t), 0); + memset(store_data, 0x22, len); + int word_num = len/sizeof(uint32_t); + int address = flash_get_page_size_words() + 10; + ret = flash_store(address, (uint32_t*) store_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len)==0); + + + ret = flash_erase(2, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + + memset(&store_data[(flash_get_page_size_words()-10)*sizeof(uint32_t)], 0xFF, flash_get_page_size_words()*sizeof(uint32_t)); + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len)==0); + +} + +TEST(FlashStoreTest, StoreAllOfFlashTest) { + ret_code_t ret = flash_erase(0, flash_get_page_number()); + EXPECT_EQ(ret, NRF_SUCCESS); + + + char store_data[flash_get_page_size_words()*flash_get_page_number()*sizeof(uint32_t)]; + int len = sizeof(store_data); + ASSERT_EQ(len%sizeof(uint32_t), 0); + memset(store_data, 0x44, len); + int word_num = len/sizeof(uint32_t); + int address = 0; + ret = flash_store(address, (uint32_t*) store_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + + char read_data[len]; + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len)==0); + +} + +TEST(FlashAddressTest, FalseAddressTest) { + + ret_code_t ret = flash_erase(0, flash_get_page_number() + 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + char store_data[12]; + int len = sizeof(store_data); + ASSERT_EQ(len%sizeof(uint32_t), 0); + memset(store_data, 0x88, len); + int word_num = len/sizeof(uint32_t); + int address = flash_get_page_size_words()*flash_get_page_number()-word_num+1; + ret = flash_store(address, (uint32_t*) store_data, word_num); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + char read_data[len]; + ret = flash_read(address, (uint32_t*) read_data, word_num); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + +} + +TEST(FlashNullPointerTest, ReturnValueTest) { + + + ret_code_t ret = flash_store(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + ret = flash_read(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + flash_write_to_file("Flash.txt"); + +} + + + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/scan_integration_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/scan_integration_unittest.cc new file mode 100644 index 0000000..99e7adf --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/scan_integration_unittest.cc @@ -0,0 +1,204 @@ + + +// Don't forget gtest.h, which declares the testing framework. +#include +#include + +#include "gtest/gtest.h" +#include "app_timer.h" +#include "systick_lib.h" +#include "timeout_lib.h" +#include "ble_lib.h" +#include "advertiser_lib.h" +#include "request_handler_lib_02v1.h" +#include "storer_lib.h" +#include "sampling_lib.h" +#include "app_scheduler.h" +#include "debug_lib.h" +#include "chunk_messages.h" +#include "processing_lib.h" + +#include "callback_generator_lib.h" +#include "data_generator_lib.h" + + +#define SCAN_GROUP_ID 10 +#define NUMBER_OF_SCANS 5 + + +static uint32_t number_generated_beacons = 0; + + +static void on_scan_report_generator(ble_gap_evt_adv_report_t* scan_report) { + uint16_t id = (uint16_t) (rand() % 20000); + /* + while(id < SCAN_BEACON_ID_THRESHOLD && number_generated_beacons < SCAN_PRIORITIZED_BEACONS) { + id = (uint16_t) (rand() % 32768); + }*/ + + if(id >= SCAN_PRIORITIZED_BEACONS) { + number_generated_beacons++; + } + + scan_report->rssi = (-1)*((int8_t) (rand() % 120)); + + // Advertising data (got from sender_lib.c, print out received adv packet data) + uint8_t data[29] = {0x02, 0x01, 0x06, 0x03, 0x03, + 0x01, 0x00, 0x0E, 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0x64, 0x00, + 0x09, 0xD1, 0x1F, 0xFD, 0xCE, + 0x32, 0xB3, 0x06, 0x09, + 0x48, 0x44, 0x42, 0x44, 0x47}; + + + + data[13] = (uint8_t)((id) & 0xFF); + data[14] = (uint8_t)((id >> 8) & 0xFF); + data[15] = SCAN_GROUP_ID; + + + scan_report->dlen = 29; + memcpy((uint8_t*) scan_report->data, data, scan_report->dlen); +} + + + + +static void check_scan_chunk(ScanChunk* scan_chunk) { + debug_log("Scan Chunk size %u\n", scan_chunk->scan_result_data_count); + EXPECT_EQ(scan_chunk->scan_result_data_count, SCAN_CHUNK_DATA_SIZE); + + + for(uint32_t i = 0; i < scan_chunk->scan_result_data_count; i++) { + debug_log("Scan [%u]: %u, %d\n", i, scan_chunk->scan_result_data[i].scan_device.ID, scan_chunk->scan_result_data[i].scan_device.rssi); + } + + + // Check if the first SCAN_PRIORITIZED_BEACONS are beacons and the RSSI values are getting smaller + for(uint8_t i = 0; i < SCAN_PRIORITIZED_BEACONS; i++) { + EXPECT_GE(scan_chunk->scan_result_data[i].scan_device.ID, SCAN_PRIORITIZED_BEACONS); + if(i > 0) { + EXPECT_LE(scan_chunk->scan_result_data[i].scan_device.rssi, scan_chunk->scan_result_data[i-1].scan_device.rssi); + } + } + + for(uint8_t i = SCAN_PRIORITIZED_BEACONS; i < scan_chunk->scan_result_data_count; i++) { + if(i > SCAN_PRIORITIZED_BEACONS) { + EXPECT_LE(scan_chunk->scan_result_data[i].scan_device.rssi, scan_chunk->scan_result_data[i-1].scan_device.rssi); + } + } + + + + +} + + + +namespace { + +class ScanIntegrationTest : public ::testing::Test { + virtual void SetUp() { + + time_t t; + srand((unsigned) time(&t)); + + callback_generator_ble_on_scan_report_reset(); + callback_generator_ble_on_scan_timeout_reset(); + } +}; + + + +TEST_F(ScanIntegrationTest, Test) { + ret_code_t ret; + + APP_SCHED_INIT(4, 100); + APP_TIMER_INIT(0, 60, NULL); + + debug_init(); + + ret = systick_init(0); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = timeout_init(); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = ble_init(); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = sampling_init(); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = storer_init(); + //EXPECT_EQ(ret, NRF_SUCCESS); + + advertiser_init(); + + ret = advertiser_start_advertising(); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = request_handler_init(); + EXPECT_EQ(ret, NRF_SUCCESS); + + //ret = sampling_start_microphone(0, 50, 0); + //EXPECT_EQ(ret, NRF_SUCCESS); + number_generated_beacons = 0; + + uint16_t number_of_scan_reports = 300; + uint32_t timepoints_scan_report[number_of_scan_reports]; + for(uint16_t i = 0; i < number_of_scan_reports; i++) + timepoints_scan_report[i] = 5; + + for(uint8_t i = 0; i < NUMBER_OF_SCANS; i++) + callback_generator_ble_on_scan_report_add_trigger_timepoints(timepoints_scan_report, number_of_scan_reports); + + callback_generator_ble_on_scan_report_set_generator(on_scan_report_generator); + + + ret = sampling_start_scan(0, 15, 300, 100, 14, SCAN_GROUP_ID, 0, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + uint64_t end_ms = systick_get_continuous_millis() + (90*1000); + while((!callback_generator_ble_on_scan_report_is_ready() || !callback_generator_ble_on_scan_timeout_is_ready()) && (systick_get_continuous_millis() < end_ms) ) { + app_sched_execute(); + } + // Give some more time for the storing... + end_ms = systick_get_continuous_millis() + (1*1000); + while(systick_get_continuous_millis() < end_ms) { + app_sched_execute(); + } + + sampling_stop_scan(0); + + + // Now read out the scan chunks... + uint32_t timestamp_seconds = 0; + uint16_t timestamp_ms = 0; + systick_get_timestamp(×tamp_seconds, ×tamp_ms); + + ScanChunk scan_chunk; + Timestamp timestamp; + timestamp.seconds = 0; + timestamp.ms = 0; + + ret = storer_find_scan_chunk_from_timestamp(timestamp, &scan_chunk); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Check if we generated at least SCAN_PRIORITIZED_BEACONS beacons. If not (unlikely, but could happen) --> restart/retry + ASSERT_GE(number_generated_beacons, SCAN_PRIORITIZED_BEACONS); + + + uint8_t number_of_stored_scan_chunks = 0; + do { + ret = storer_get_next_scan_chunk(&scan_chunk); + if(ret != NRF_SUCCESS) break; + number_of_stored_scan_chunks++; + check_scan_chunk(&scan_chunk); + } while(ret == NRF_SUCCESS); + EXPECT_EQ(number_of_stored_scan_chunks, NUMBER_OF_SCANS); + +} + + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/storage1_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/storage1_lib_unittest.cc new file mode 100644 index 0000000..d5dd84c --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/storage1_lib_unittest.cc @@ -0,0 +1,537 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "storage1_lib.h" +#include "gtest/gtest.h" + +#define FLASH_NUM_PAGES_TEST 30 +#define FLASH_PAGE_SIZE_WORDS_TEST 256 + +#define STORAGE1_SIZE_TEST (FLASH_NUM_PAGES_TEST*FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)) + + +// Some external functions and variables +extern uint32_t storage1_get_page_number(uint32_t byte_address, uint32_t byte_length); +extern uint32_t storage1_get_page_address(uint32_t byte_address); +extern uint32_t storage1_get_last_stored_element_addresses_size(void); +extern ret_code_t storage1_compute_pages_to_erase(uint32_t address, uint32_t length_data, uint32_t* erase_start_page_address, uint32_t* erase_num_pages); +extern int32_t storage1_last_stored_element_addresses[]; +extern void storage1_compute_word_aligned_addresses(uint32_t address, uint32_t length_data, uint32_t* leading_num_bytes, uint32_t* intermediate_num_bytes, uint32_t* final_num_bytes); +extern ret_code_t storage1_store_uint8_as_uint32(uint32_t address, uint8_t* data, uint32_t length_data); +extern ret_code_t storage1_read_uint32_as_uint8(uint32_t address, uint8_t* data, uint32_t length_data); + + + + + +namespace { + + +class Storage1Test : public ::testing::Test { + virtual void SetUp() { + storage1_init(); + } +}; + +TEST_F(Storage1Test, GetSizeTest) { + uint32_t size = storage1_get_size(); + ASSERT_EQ(size, STORAGE1_SIZE_TEST); +} + +TEST_F(Storage1Test, GetUnitSizeTest) { + uint32_t unit_size = storage1_get_unit_size(); + ASSERT_EQ(unit_size, FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)); +} + + +TEST_F(Storage1Test, GetPageAddressTest) { + uint32_t page_address = storage1_get_page_address(0); + EXPECT_EQ(page_address, 0); + + page_address = storage1_get_page_address(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)-1); + EXPECT_EQ(page_address, 0); + + page_address = storage1_get_page_address(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2); + EXPECT_EQ(page_address, 2); + + page_address = storage1_get_page_address(STORAGE1_SIZE_TEST-FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)); + EXPECT_EQ(page_address, FLASH_NUM_PAGES_TEST-1); + + page_address = storage1_get_page_address(STORAGE1_SIZE_TEST-1); + EXPECT_EQ(page_address, FLASH_NUM_PAGES_TEST-1); + + page_address = storage1_get_page_address(STORAGE1_SIZE_TEST); + EXPECT_EQ(page_address, 0); + +} + + +TEST_F(Storage1Test, GetPageNumberTest) { + uint32_t num_pages = storage1_get_page_number(0, 0); + EXPECT_EQ(num_pages, 0); + + num_pages = storage1_get_page_number(0, 1); + EXPECT_EQ(num_pages, 1); + + num_pages = storage1_get_page_number(1, FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)); + EXPECT_EQ(num_pages, 2); + + num_pages = storage1_get_page_number(2, FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2-2); + EXPECT_EQ(num_pages, 2); + + num_pages = storage1_get_page_number(3, FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2-2); + EXPECT_EQ(num_pages, 3); + + num_pages = storage1_get_page_number(0, STORAGE1_SIZE_TEST-FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)); + EXPECT_EQ(num_pages, FLASH_NUM_PAGES_TEST-1); + + num_pages = storage1_get_page_number(0, STORAGE1_SIZE_TEST); + EXPECT_EQ(num_pages, FLASH_NUM_PAGES_TEST); + + num_pages = storage1_get_page_number(1, STORAGE1_SIZE_TEST); + EXPECT_EQ(num_pages, 0); +} + + +TEST_F(Storage1Test, ComputePagesToEraseTest) { + uint32_t erase_start_page_address, erase_num_pages; + ASSERT_EQ(storage1_get_last_stored_element_addresses_size(), 4); + + ret_code_t ret; + + + + // Error value tests: + // Zero length + ret = storage1_compute_pages_to_erase(0, 0, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 0); + EXPECT_EQ(erase_num_pages, 0); + + // Data length to large + ret = storage1_compute_pages_to_erase(1, storage1_get_size(), &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + EXPECT_EQ(erase_start_page_address, 0); + EXPECT_EQ(erase_num_pages, 0); + + + + + // Fill the internal state with some data + ret = storage1_compute_pages_to_erase(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*5, FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*3, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 5); + EXPECT_EQ(erase_num_pages, 3); + ret = storage1_compute_pages_to_erase(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4, 30, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 4); + EXPECT_EQ(erase_num_pages, 1); + ret = storage1_compute_pages_to_erase(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2, 20, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 2); + EXPECT_EQ(erase_num_pages, 1); + ret = storage1_compute_pages_to_erase(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1, 10, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 1); + EXPECT_EQ(erase_num_pages, 1); + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2 + 19); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + + + // Now write to page 0 --> it should replace storage1_last_stored_element_addresses at index 3 + ret = storage1_compute_pages_to_erase(0, 10, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 0); + EXPECT_EQ(erase_num_pages, 1); + + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2 + 19); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[3], 9); + + // Now write again to page 0 with higher address (and full page write) --> it should set storage1_last_stored_element_addresses[3] to -1, and should erase no pages + ret = storage1_compute_pages_to_erase(20, FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 -20, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 1); + EXPECT_EQ(erase_num_pages, 0); + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2 + 19); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + + + // Now write to page 1 --> it should replace storage1_last_stored_element_addresses at index 0 (because == address-1) and should set storage1_last_stored_element_addresses[3] to -1 + ret = storage1_compute_pages_to_erase(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1, 10, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 1); + EXPECT_EQ(erase_num_pages, 1); + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2 + 19); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + + + // Now write again to page 0 --> it should replace the storage1_last_stored_element_addresses[3] (because it is -1) + ret = storage1_compute_pages_to_erase(10, 10, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 0); + EXPECT_EQ(erase_num_pages, 1); + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2 + 19); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[3], 19); + + + // Now write to page 3 --> it should replace storage1_last_stored_element_addresses at index 1 (because of min difference) + ret = storage1_compute_pages_to_erase(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*3, 10, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 3); + EXPECT_EQ(erase_num_pages, 1); + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*3 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[3], 19); + + // Now write again to page 0 with large data --> it should set [2] and [3] to -1, erase page 1 (not 0 because larger than former address in page 0) and should insert to [2] + ret = storage1_compute_pages_to_erase(20, FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, 1); + EXPECT_EQ(erase_num_pages, 1); + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*3 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 19); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + + + + + + // Now write to the end of the flash some data --> it should not insert anything into storage1_last_stored_element_addresses[], because we write to STORAGE1_SIZE - 1! + ret = storage1_compute_pages_to_erase(STORAGE1_SIZE_TEST-11, 11, &erase_start_page_address, &erase_num_pages); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(erase_start_page_address, FLASH_NUM_PAGES_TEST - 1); + EXPECT_EQ(erase_num_pages, 1); + EXPECT_EQ(storage1_last_stored_element_addresses[0], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*4 + 29); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*3 + 9); + EXPECT_EQ(storage1_last_stored_element_addresses[2], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*1 + 19); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); // This one should stay -1 + +} + + + +TEST_F(Storage1Test, ComputeWordAlignedAdressesTest) { + + // Some general Tests + uint32_t address = 1, length_data = 7; + uint32_t leading_num_bytes, intermediate_num_bytes, final_num_bytes; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 3); + EXPECT_EQ(intermediate_num_bytes, 4); + EXPECT_EQ(final_num_bytes, 0); + + address = 4; + length_data = 7; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 4); + EXPECT_EQ(final_num_bytes, 3); + + address = 0; + length_data = 8; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 8); + EXPECT_EQ(final_num_bytes, 0); + + address = 17; + length_data = 8; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 3); + EXPECT_EQ(intermediate_num_bytes, 4); + EXPECT_EQ(final_num_bytes, 1); + + address = 19; + length_data = 15; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 1); + EXPECT_EQ(intermediate_num_bytes, 12); + EXPECT_EQ(final_num_bytes, 2); + + + // Edge cases with small data size + address = 7; + length_data = 1; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 1); + EXPECT_EQ(intermediate_num_bytes, 0); + EXPECT_EQ(final_num_bytes, 0); + + + address = 5; + length_data = 1; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 1); + EXPECT_EQ(final_num_bytes, 0); + + address = 5; + length_data = 2; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 2); + EXPECT_EQ(final_num_bytes, 0); + + + address = 4; + length_data = 3; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 0); + EXPECT_EQ(final_num_bytes, 3); + + address = 4; + length_data = 4; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 4); + EXPECT_EQ(final_num_bytes, 0); + + address = 8; + length_data = 1; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 0); + EXPECT_EQ(final_num_bytes, 1); + + + address = 9; + length_data = 0; + storage1_compute_word_aligned_addresses(address, length_data, &leading_num_bytes, &intermediate_num_bytes, &final_num_bytes); + EXPECT_EQ(leading_num_bytes, 0); + EXPECT_EQ(intermediate_num_bytes, 0); + EXPECT_EQ(final_num_bytes, 0); + + + +} + +TEST_F(Storage1Test, StoreAndReadUint8Uint32Test) { + uint8_t store_data[98]; + uint32_t len = sizeof(store_data); + uint8_t read_data[len]; + memset(read_data, 0xFF, len); + ret_code_t ret; + for(uint32_t i = 0; i < len; i++) { + store_data[i] = i; + } + + ret = storage1_store_uint8_as_uint32(0, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + + ret = storage1_read_uint32_as_uint8(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + // Write again to next address + ret = storage1_store_uint8_as_uint32(len, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + memset(read_data, 0xFF, len); + ret = storage1_read_uint32_as_uint8(len, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + // Check if the former written bytes are still correct + memset(read_data, 0xFF, len); + ret = storage1_read_uint32_as_uint8(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + // Check when addresses are not aligned + ret = storage1_store_uint8_as_uint32(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)+1, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + uint8_t read_data1[len + 2]; + memset(read_data1, 0xFF, len + 2); + + ret = storage1_read_uint32_as_uint8(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t), read_data1, len+2); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(read_data1[0], 0xFF); + EXPECT_TRUE(memcmp(store_data, &read_data1[1], len) == 0); + EXPECT_EQ(read_data1[len+1], 0xFF); + + + + // Write small data + ret = storage1_store_uint8_as_uint32(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2+1, store_data, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage1_read_uint32_as_uint8(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2+1, read_data, 1); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, 1) == 0); + + ret = storage1_store_uint8_as_uint32(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2+6, store_data, 3); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage1_read_uint32_as_uint8(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2+6, read_data, 3); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, 3) == 0); + + // Write 0 data + ret = storage1_store_uint8_as_uint32(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2+22, store_data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage1_read_uint32_as_uint8(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*2+22, read_data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Write to large address + ret = storage1_store_uint8_as_uint32(storage1_get_size()-10, store_data, 11); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + memset(read_data, 0xFF, len); + ret = storage1_read_uint32_as_uint8(storage1_get_size()-10, read_data, 11); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + + + +} + + +TEST_F(Storage1Test, StoreAndReadTest) { + + ASSERT_EQ(storage1_get_last_stored_element_addresses_size(), 4); + + uint8_t store_data[98]; + uint32_t len = sizeof(store_data); + uint8_t read_data[len]; + + ret_code_t ret; + for(uint32_t i = 0; i < len; i++) { + store_data[i] = i; + } + memset(read_data, 0, sizeof(read_data)); + // Erase page 0 and store data + ret = storage1_store(0, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(storage1_last_stored_element_addresses[0], len-1); + EXPECT_EQ(storage1_last_stored_element_addresses[1], -1); + EXPECT_EQ(storage1_last_stored_element_addresses[2], -1); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + ret = storage1_read(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + memset(read_data, 0, sizeof(read_data)); + // Write again to page 0 directly behind the former data (page should not be erased) + ret = storage1_store(len, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(storage1_last_stored_element_addresses[0], len*2-1); + EXPECT_EQ(storage1_last_stored_element_addresses[1], -1); + EXPECT_EQ(storage1_last_stored_element_addresses[2], -1); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + ret = storage1_read(len, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + // Check if the former data are still there + ret = storage1_read(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + + memset(read_data, 0, sizeof(read_data)); + // Write again to page 0 to the same address like before (page should be erased, and the former data should be restored) + ret = storage1_store(len, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(storage1_last_stored_element_addresses[0], len*2-1); + EXPECT_EQ(storage1_last_stored_element_addresses[1], -1); + EXPECT_EQ(storage1_last_stored_element_addresses[2], -1); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + ret = storage1_read(len, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + // Check if the former data are still there + ret = storage1_read(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + + // Initialize large data set + uint8_t store_data1[FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t)*5]; + uint32_t len1 = sizeof(store_data1); + uint8_t read_data1[len1]; + for(uint32_t i = 0; i < len1; i++) { + store_data1[i] = (uint8_t) (i % 256); + } + + memset(read_data1, 0, sizeof(read_data1)); + // Write to multiple pages (should erase the pages, and restore data on the first page that was erased) + ret = storage1_store(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t) + 1, store_data1, len1); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(storage1_last_stored_element_addresses[0], len*2-1); + EXPECT_EQ(storage1_last_stored_element_addresses[1], FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t) + 1 + len1 - 1); + EXPECT_EQ(storage1_last_stored_element_addresses[2], -1); + EXPECT_EQ(storage1_last_stored_element_addresses[3], -1); + ret = storage1_read(FLASH_PAGE_SIZE_WORDS_TEST*sizeof(uint32_t) + 1, read_data1, len1); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data1, read_data1, len1) == 0); + + + // Finally test some exception parameters + ret = storage1_store(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + ret = storage1_store(0, store_data1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = storage1_store(1, store_data1, storage1_get_size()); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + ret = storage1_read(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + ret = storage1_read(0, read_data1, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = storage1_read(1, read_data1, storage1_get_size()); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + +} + +TEST_F(Storage1Test, ClearTest) { + + ret_code_t ret; + + uint8_t store_data[2000]; + uint32_t len = sizeof(store_data); + uint8_t read_data[len]; + + for(uint32_t i = 0; i < len; i++) { + store_data[i] = i % 256; + } + ret = storage1_store(0, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = storage1_read(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_ARRAY_EQ(store_data, read_data, len); + + ret = storage1_clear(len/4, len/2); + EXPECT_EQ(ret, NRF_SUCCESS); + + memset(read_data, 0, sizeof(read_data)); + ret = storage1_read(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + for(uint32_t i = 0; i < len; i++) { + if(i < len/4) + store_data[i] = i % 256; + else if(i < len/2 + len/4) + store_data[i] = 0xFF; + else // Because it is flash, and will erase the following page completely + store_data[i] = 0xFF; + } + EXPECT_ARRAY_EQ(store_data, read_data, len); +} + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/storage2_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/storage2_lib_unittest.cc new file mode 100644 index 0000000..08e9506 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/storage2_lib_unittest.cc @@ -0,0 +1,145 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "storage2_lib.h" +#include "gtest/gtest.h" + + + +#define STORAGE2_SIZE_TEST (1024*256) + + + + + + +namespace { + + +class Storage2Test : public ::testing::Test { + virtual void SetUp() { + storage2_init(); + } +}; + +TEST_F(Storage2Test, GetSizeTest) { + uint32_t size = storage2_get_size(); + ASSERT_EQ(size, STORAGE2_SIZE_TEST); +} + +TEST_F(Storage2Test, GetUnitSizeTest) { + uint32_t unit_size = storage2_get_unit_size(); + ASSERT_EQ(unit_size, 1); +} + +TEST_F(Storage2Test, StoreAndReadTest) { + uint8_t store_data[98]; + uint32_t len = sizeof(store_data); + uint8_t read_data[len]; + memset(read_data, 0xFF, len); + + ret_code_t ret; + for(uint32_t i = 0; i < len; i++) { + store_data[i] = i; + } + + // One simple write + ret = storage2_store(11, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + memset(read_data, 0xFF, len); + ret = storage2_read(11, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + // Write to same address + for(uint32_t i = 0; i < len; i++) { + store_data[i] = 255 - (i % 256); + } + ret = storage2_store(11, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + memset(read_data, 0xFF, len); + ret = storage2_read(11, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + + + // Write 0 length + ret = storage2_store(0, store_data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage2_read(0, read_data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + // Null pointer test + ret = storage2_store(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + ret = storage2_read(0, NULL, 1); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + + // Write big data + uint8_t store_data1[4096]; + len = sizeof(store_data1); + uint8_t read_data1[len]; + memset(read_data1, 0xFF, len); + for(uint32_t i = 0; i < len; i++) { + store_data1[i] = i % 256; + } + + ret = storage2_store(0, store_data1, len); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = storage2_read(0, read_data1, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data1, read_data1, len) == 0); + + + // Large address test + ret = storage2_store(1, store_data1, storage2_get_size()); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + ret = storage2_read(1, read_data1, storage2_get_size()); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + + +} + + +TEST_F(Storage2Test, ClearTest) { + + ret_code_t ret; + + uint8_t store_data[2000]; + uint32_t len = sizeof(store_data); + uint8_t read_data[len]; + + for(uint32_t i = 0; i < len; i++) { + store_data[i] = i % 256; + } + ret = storage2_store(0, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = storage2_read(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_ARRAY_EQ(store_data, read_data, len); + + ret = storage2_clear(len/4, len/2); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = storage2_read(0, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + for(uint32_t i = 0; i < len; i++) { + if(i < len/4) + store_data[i] = i % 256; + else if(i < len/2 + len/4) + store_data[i] = 0xFF; + else + store_data[i] = i % 256; + } + EXPECT_ARRAY_EQ(store_data, read_data, len); +} + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/storage_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/storage_lib_unittest.cc new file mode 100644 index 0000000..e5ee9ea --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/storage_lib_unittest.cc @@ -0,0 +1,337 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "storage_lib.h" +#include "gtest/gtest.h" + + +#define STORAGE1_SIZE_TEST (30*256*sizeof(uint32_t)) +#define STORAGE1_UNIT_SIZE_TEST (256*sizeof(uint32_t)) + +#define STORAGE2_SIZE_TEST (1024*256) +#define STORAGE2_UNIT_SIZE_TEST (1) + +#define STORAGE_SIZE_TEST (STORAGE1_SIZE_TEST + STORAGE2_SIZE_TEST) + + + +extern void storage_split_to_storage_modules(uint32_t address, uint8_t* data, uint32_t length_data, uint32_t splitted_address[], uint8_t* splitted_data[], uint32_t splitted_length_data[], uint32_t storage_sizes[], uint8_t number_of_storage_modules); + +extern void eeprom_write_to_file(const char* filename); +extern void flash_write_to_file(const char* filename); + + + + +namespace { + + +class StorageTest : public ::testing::Test { + virtual void SetUp() { + storage_init(); + } +}; + +TEST_F(StorageTest, GetSizeTest) { + uint32_t size = storage_get_size(); + ASSERT_EQ(size, STORAGE_SIZE_TEST); + + +} + +TEST_F(StorageTest, SplitToStorageModulesTest) { + uint32_t splitted_address[3]; + uint8_t* splitted_data[3]; + uint32_t splitted_length_data[3]; + uint32_t storage_sizes[] = {10, 10, 10}; + + + // Only write in first storage-module + uint32_t address = 0; + uint8_t data[100]; + uint32_t length_data = 10; + + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, 3); + EXPECT_EQ(splitted_address[0], 0); + EXPECT_EQ(splitted_data[0], &data[0]); + EXPECT_EQ(splitted_length_data[0], 10); + + EXPECT_EQ(splitted_address[1], 0); + EXPECT_TRUE(splitted_data[1] == NULL); + EXPECT_EQ(splitted_length_data[1], 0); + + EXPECT_EQ(splitted_address[2], 0); + EXPECT_TRUE(splitted_data[2] == NULL); + EXPECT_EQ(splitted_length_data[2], 0); + + + // Write in first and second storage-module + address = 5; + length_data = 10; + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, 3); + EXPECT_EQ(splitted_address[0], 5); + EXPECT_EQ(splitted_data[0], &data[0]); + EXPECT_EQ(splitted_length_data[0], 5); + + EXPECT_EQ(splitted_address[1], 0); + EXPECT_EQ(splitted_data[1], &data[5]); + EXPECT_EQ(splitted_length_data[1], 5); + + EXPECT_EQ(splitted_address[2], 0); + EXPECT_TRUE(splitted_data[2] == NULL); + EXPECT_EQ(splitted_length_data[2], 0); + + + // Write in first, second and third storage-module + address = 6; + length_data = 20; + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, 3); + EXPECT_EQ(splitted_address[0], 6); + EXPECT_EQ(splitted_data[0], &data[0]); + EXPECT_EQ(splitted_length_data[0], 4); + + EXPECT_EQ(splitted_address[1], 0); + EXPECT_EQ(splitted_data[1], &data[4]); + EXPECT_EQ(splitted_length_data[1], 10); + + EXPECT_EQ(splitted_address[2], 0); + EXPECT_EQ(splitted_data[2], &data[14]); + EXPECT_EQ(splitted_length_data[2], 6); + + + // Write in second and third storage-module + address = 10; + length_data = 16; + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, 3); + EXPECT_EQ(splitted_address[0], 0); + EXPECT_TRUE(splitted_data[0] == NULL); + EXPECT_EQ(splitted_length_data[0], 0); + + EXPECT_EQ(splitted_address[1], 0); + EXPECT_EQ(splitted_data[1], &data[0]); + EXPECT_EQ(splitted_length_data[1], 10); + + EXPECT_EQ(splitted_address[2], 0); + EXPECT_EQ(splitted_data[2], &data[10]); + EXPECT_EQ(splitted_length_data[2], 6); + + + + // Write 0 data + address = 10; + length_data = 0; + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, 3); + EXPECT_EQ(splitted_address[0], 0); + EXPECT_TRUE(splitted_data[0] == NULL); + EXPECT_EQ(splitted_length_data[0], 0); + + EXPECT_EQ(splitted_address[1], 0); + EXPECT_TRUE(splitted_data[1] == NULL); + EXPECT_EQ(splitted_length_data[1], 0); + + EXPECT_EQ(splitted_address[2], 0); + EXPECT_TRUE(splitted_data[2] == NULL); + EXPECT_EQ(splitted_length_data[2], 0); + + + // Write too large data + address = 22; + length_data = 10; + storage_split_to_storage_modules(address, data, length_data, splitted_address, splitted_data, splitted_length_data, storage_sizes, 3); + EXPECT_EQ(splitted_address[0], 0); + EXPECT_TRUE(splitted_data[0] == NULL); + EXPECT_EQ(splitted_length_data[0], 0); + + EXPECT_EQ(splitted_address[1], 0); + EXPECT_TRUE(splitted_data[1] == NULL); + EXPECT_EQ(splitted_length_data[1], 0); + + EXPECT_EQ(splitted_address[2], 2); + EXPECT_EQ(splitted_data[2], &data[0]); + EXPECT_EQ(splitted_length_data[2], 8); + +} + +TEST_F(StorageTest, GetUnitAddressLimits) { + ret_code_t ret; + uint32_t address = 0; + uint32_t length_data = 10; + uint32_t start_unit_address, end_unit_address; + + // Storage 1 (unit size: page size of flash) + ret = storage_get_unit_address_limits(address, length_data, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(start_unit_address, 0); + EXPECT_EQ(end_unit_address, STORAGE1_UNIT_SIZE_TEST - 1); + + address = 1; + length_data = STORAGE1_UNIT_SIZE_TEST; + ret = storage_get_unit_address_limits(address, length_data, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(start_unit_address, 0); + EXPECT_EQ(end_unit_address, STORAGE1_UNIT_SIZE_TEST*2 - 1); + + // Storage 2 (unit size: byte) + address = STORAGE1_SIZE_TEST; + length_data = 100; + ret = storage_get_unit_address_limits(address, length_data, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(start_unit_address, STORAGE1_SIZE_TEST); + EXPECT_EQ(end_unit_address, STORAGE1_SIZE_TEST + 100 - 1); + + address = STORAGE1_SIZE_TEST + 101; + length_data = 100; + ret = storage_get_unit_address_limits(address, length_data, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(start_unit_address, STORAGE1_SIZE_TEST + 101); + EXPECT_EQ(end_unit_address, STORAGE1_SIZE_TEST + 101 + 100 - 1); + + // Storage 1 and 2 + address = STORAGE1_SIZE_TEST - 23; + length_data = 100; + ret = storage_get_unit_address_limits(address, length_data, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(start_unit_address, STORAGE1_SIZE_TEST - STORAGE1_UNIT_SIZE_TEST); + EXPECT_EQ(end_unit_address, STORAGE1_SIZE_TEST -23 + 100 - 1); + + ret = storage_get_unit_address_limits(0, STORAGE_SIZE_TEST, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_EQ(start_unit_address, 0); + EXPECT_EQ(end_unit_address, STORAGE1_SIZE_TEST + STORAGE2_SIZE_TEST - 1); + + // Exceptions + ret = storage_get_unit_address_limits(0, 0, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + ret = storage_get_unit_address_limits(0, STORAGE_SIZE_TEST + 1, &start_unit_address, &end_unit_address); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); +} + +TEST_F(StorageTest, StoreAndReadTest) { + ret_code_t ret; + uint8_t store_data[1000]; + uint32_t len = sizeof(store_data); + uint8_t read_data[len]; + for(uint32_t i = 0; i < len; i++) + store_data[i] = i % 256; + memset(read_data, 0xFF, len); + uint32_t address; + + + // Store in storage 1 + address = 0; + ret = storage_store(address, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = storage_read(address, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + + // Store in storage 2 + address = STORAGE1_SIZE_TEST; + ret = storage_store(address, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage_read(address, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + + // Store in storage 1 and 2 + address = STORAGE1_SIZE_TEST - len/2; + ret = storage_store(address, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage_read(address, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, len) == 0); + + // Writing small amount of data not word aligned + address = STORAGE1_SIZE_TEST - 3; + ret = storage_store(address, store_data, 2); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage_read(address, read_data, 2); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, 2) == 0); + + // Writing small amount of data not word aligned + address = STORAGE1_SIZE_TEST - 1; + ret = storage_store(address, store_data, 3); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage_read(address, read_data, 3); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_TRUE(memcmp(store_data, read_data, 3) == 0); + + + // Expceptions + // NULL Pointer + address = 0; + ret = storage_store(address, NULL, 3); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + ret = storage_read(address, NULL, 3); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + // Too large data + address = 1; + ret = storage_store(address, store_data, STORAGE_SIZE_TEST); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + ret = storage_read(address, read_data, STORAGE_SIZE_TEST); + EXPECT_EQ(ret, NRF_ERROR_INVALID_PARAM); + + // Zero length data + address = 1; + ret = storage_store(address, store_data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + ret = storage_read(address, read_data, 0); + EXPECT_EQ(ret, NRF_SUCCESS); + + eeprom_write_to_file("Storage_EEPROM.txt"); + flash_write_to_file("Storage_Flash.txt"); +} + +TEST_F(StorageTest, ClearTest) { + ret_code_t ret; + uint8_t store_data[1000]; + uint32_t len = sizeof(store_data); + uint8_t read_data[len]; + for(uint32_t i = 0; i < len; i++) + store_data[i] = i % 256; + memset(read_data, 0xFF, len); + uint32_t address; + + // Store in storage 1 and 2 + address = STORAGE1_SIZE_TEST - len/2; + ret = storage_store(address, store_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + memset(read_data, 0xFF, len); + ret = storage_read(address, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + EXPECT_ARRAY_EQ(store_data, read_data, len); + + + + // Now clear a little bit of storage 1 and 2 + ret = storage_clear(address + len/4, len/2); + EXPECT_EQ(ret, NRF_SUCCESS); + + memset(read_data, 0xFF, len); + ret = storage_read(address, read_data, len); + EXPECT_EQ(ret, NRF_SUCCESS); + + for(uint32_t i = 0; i < len; i++) { + if(i < len/4) + store_data[i] = i % 256; + else if(i < len/2 + len/4) + store_data[i] = 0xFF; + else + store_data[i] = i % 256; + } + EXPECT_ARRAY_EQ(store_data, read_data, len); + + +} + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/timeout_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/timeout_lib_unittest.cc new file mode 100644 index 0000000..2edeede --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/timeout_lib_unittest.cc @@ -0,0 +1,171 @@ + + +// Don't forget gtest.h, which declares the testing framework. + + +#include "gtest/gtest.h" +#include "timeout_lib.h" +#include "app_timer.h" +#include "systick_lib.h" + + +extern volatile uint8_t timeout_timer_running; + +namespace { + +TEST(TimeoutTest, InitTest) { + APP_TIMER_INIT(0, 20, NULL); + systick_init(0); + ret_code_t ret = timeout_init(); + EXPECT_EQ(ret, NRF_SUCCESS); +} + +volatile uint32_t timeout_handler_1_count = 0; +void timeout_handler_1(void) { + timeout_handler_1_count++; +} + +volatile uint32_t timeout_handler_2_count = 0; +void timeout_handler_2(void) { + timeout_handler_2_count++; +} + +TEST(TimeoutTest, RegisterOne) { + ret_code_t ret; + + uint32_t timeout_id_1; + ret = timeout_register(&timeout_id_1, timeout_handler_1); + EXPECT_EQ(ret, NRF_SUCCESS); + + + EXPECT_EQ(timeout_timer_running, 0); + + timeout_handler_1_count = 0; + timeout_start(timeout_id_1, 200); + + EXPECT_EQ(timeout_timer_running, 1); + + EXPECT_EQ(timeout_handler_1_count, 0); + systick_delay_millis(180); + EXPECT_EQ(timeout_handler_1_count, 0); + systick_delay_millis(40); + EXPECT_EQ(timeout_handler_1_count, 1); + + EXPECT_EQ(timeout_timer_running, 0); +} + + +TEST(TimeoutTest, RegisterTwo) { + ret_code_t ret; + EXPECT_EQ(ret, NRF_SUCCESS); + + uint32_t timeout_id_1, timeout_id_2; + ret = timeout_register(&timeout_id_1, timeout_handler_1); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = timeout_register(&timeout_id_2, timeout_handler_2); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(timeout_timer_running, 0); + + timeout_handler_1_count = 0; + timeout_handler_2_count = 0; + timeout_start(timeout_id_1, 200); + EXPECT_EQ(timeout_timer_running, 1); + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + systick_delay_millis(100); + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + + timeout_start(timeout_id_2, 50); + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + + systick_delay_millis(40); + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + + systick_delay_millis(20); + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 1); + + systick_delay_millis(50); + EXPECT_EQ(timeout_handler_1_count, 1); + EXPECT_EQ(timeout_handler_2_count, 1); + + EXPECT_EQ(timeout_timer_running, 0); +} + +TEST(TimeoutTest, ResetTwo) { + ret_code_t ret; + EXPECT_EQ(ret, NRF_SUCCESS); + + uint32_t timeout_id_1, timeout_id_2; + ret = timeout_register(&timeout_id_1, timeout_handler_1); + EXPECT_EQ(ret, NRF_SUCCESS); + + ret = timeout_register(&timeout_id_2, timeout_handler_2); + EXPECT_EQ(ret, NRF_SUCCESS); + + EXPECT_EQ(timeout_timer_running, 0); + + timeout_handler_1_count = 0; + timeout_handler_2_count = 0; + + + + timeout_start(timeout_id_1, 200); + + + EXPECT_EQ(timeout_timer_running, 1); + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + systick_delay_millis(100); + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + + timeout_start(timeout_id_2, 50); + + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + + systick_delay_millis(40); // 140 + + timeout_reset(timeout_id_2); + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + + systick_delay_millis(20); // 160 + + timeout_reset(timeout_id_1); + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 0); + + systick_delay_millis(50); // 210 + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 1); + EXPECT_EQ(timeout_timer_running, 1); + + systick_delay_millis(140); // 350 + + EXPECT_EQ(timeout_handler_1_count, 0); + EXPECT_EQ(timeout_handler_2_count, 1); + + systick_delay_millis(20); // 370 + + EXPECT_EQ(timeout_handler_1_count, 1); + EXPECT_EQ(timeout_handler_2_count, 1); + + EXPECT_EQ(timeout_timer_running, 0); +} + + +}; diff --git a/firmware/nRF_badge/data_collector/unit_test/tests/timer_lib_unittest.cc b/firmware/nRF_badge/data_collector/unit_test/tests/timer_lib_unittest.cc new file mode 100644 index 0000000..5bf09b3 --- /dev/null +++ b/firmware/nRF_badge/data_collector/unit_test/tests/timer_lib_unittest.cc @@ -0,0 +1,389 @@ + + +// Don't forget gtest.h, which declares the testing framework. + +#include "timer_lib.h" +#include "gtest/gtest.h" + +#define MAX_NUMBER_OF_TIMERS_TEST 100 + +#define TIMER_PRESCALER_TEST 1 + + +extern volatile timer_node_t timer_nodes[]; + +extern uint32_t number_of_timers; + + + +namespace { +class TimerTest : public ::testing::Test { + virtual void SetUp() { + timer_init(); + timer_set_start(); + } + + virtual void TearDown() { + timer_stop(); + } + +}; + +TEST_F(TimerTest, SleepTest) { + + ASSERT_EQ(MAX_NUMBER_OF_TIMERS, MAX_NUMBER_OF_TIMERS_TEST); + ASSERT_EQ(TIMER_PRESCALER, TIMER_PRESCALER_TEST); + + // Just re-init the timer-module for test reasons (LCOV) + timer_init(); + + uint32_t sleep_time_ms = 100; + + uint64_t start_time_ms = timer_get_milliseconds_since_start(); + timer_sleep_milliseconds(sleep_time_ms); + uint64_t end_time_ms = timer_get_milliseconds_since_start(); + + uint32_t delta_time_ms = (uint32_t) (end_time_ms - start_time_ms); + + EXPECT_LE(delta_time_ms, sleep_time_ms + 50); + EXPECT_GE(delta_time_ms, sleep_time_ms); + +} + +void* p_callback_context_1 = NULL; +void callback_function_1(void* p_context) { + p_callback_context_1 = p_context; +} + +TEST_F(TimerTest, SingleShotTimerTest) { + + uint32_t timer_id; + uint8_t ret = timer_create_timer(&timer_id, TIMER_MODE_SINGLE_SHOT, callback_function_1, 5); + EXPECT_EQ(ret, 1); + EXPECT_EQ(number_of_timers, 1); + + EXPECT_EQ(timer_nodes[0].timer_id, 0); + EXPECT_EQ(timer_nodes[0].timer_priority, 5); + EXPECT_EQ(timer_nodes[0].is_running, 0); + EXPECT_EQ(timer_nodes[0].mode, TIMER_MODE_SINGLE_SHOT); + EXPECT_TRUE(timer_nodes[0].next == NULL); + EXPECT_TRUE(timer_nodes[0].p_timeout_handler == (timer_timeout_handler_t) callback_function_1); + EXPECT_TRUE(timer_nodes[0].p_context == NULL); + + + uint64_t timeout_microseconds = 100*1000; + + uint64_t start_time_microseconds = timer_get_microseconds_since_start(); + + uint8_t context_data[10]; + p_callback_context_1 = NULL; + // Start the timer + ret = timer_start_timer(timer_id, timeout_microseconds, context_data); + + EXPECT_EQ(ret, 1); + + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[0].microseconds_interval, timeout_microseconds); + EXPECT_TRUE(timer_nodes[0].p_context == context_data); + + EXPECT_LE(timer_nodes[0].microseconds_at_start, start_time_microseconds + 10*1000); + EXPECT_GE(timer_nodes[0].microseconds_at_start, start_time_microseconds); + EXPECT_EQ(timer_nodes[0].microseconds_at_end, timer_nodes[0].microseconds_at_start + timer_nodes[0].microseconds_interval); + + EXPECT_TRUE(timer_nodes[0].next == NULL); + + // The initial callback state + EXPECT_TRUE(p_callback_context_1 == NULL); + + // Sleep for the a little bit smaller than the callback time, to check, whether it is still running + timer_sleep_microseconds(timeout_microseconds - 20*1000); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == NULL); + EXPECT_EQ(timer_nodes[0].is_running, 1); + + // Sleep again, to wake up after the callback event + timer_sleep_microseconds(40*1000); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == context_data); + EXPECT_EQ(timer_nodes[0].is_running, 0); + +} + + + +TEST_F(TimerTest, RepeatedTimerTest) { + + uint32_t timer_id; + uint8_t ret = timer_create_timer(&timer_id, TIMER_MODE_REPEATED, callback_function_1, 5); + EXPECT_EQ(ret, 1); + EXPECT_EQ(number_of_timers, 1); + + EXPECT_EQ(timer_nodes[0].timer_id, 0); + EXPECT_EQ(timer_nodes[0].timer_priority, 5); + EXPECT_EQ(timer_nodes[0].is_running, 0); + EXPECT_EQ(timer_nodes[0].mode, TIMER_MODE_REPEATED); + EXPECT_TRUE(timer_nodes[0].next == NULL); + EXPECT_TRUE(timer_nodes[0].p_timeout_handler == (timer_timeout_handler_t) callback_function_1); + EXPECT_TRUE(timer_nodes[0].p_context == NULL); + + + uint64_t timeout_microseconds = 100*1000; + + uint64_t start_time_microseconds = timer_get_microseconds_since_start(); + + uint8_t context_data[10]; + p_callback_context_1 = NULL; + + // Start the timer + ret = timer_start_timer(timer_id, timeout_microseconds, context_data); + + EXPECT_EQ(ret, 1); + + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[0].microseconds_interval, timeout_microseconds); + EXPECT_TRUE(timer_nodes[0].p_context == context_data); + + EXPECT_LE(timer_nodes[0].microseconds_at_start, start_time_microseconds + 10*1000); + EXPECT_GE(timer_nodes[0].microseconds_at_start, start_time_microseconds); + EXPECT_EQ(timer_nodes[0].microseconds_at_end, timer_nodes[0].microseconds_at_start + timer_nodes[0].microseconds_interval); + + EXPECT_TRUE(timer_nodes[0].next == NULL); + + // The initial callback state + EXPECT_TRUE(p_callback_context_1 == NULL); + + // Sleep for the a little bit smaller than the callback time, to check, whether it is still running + timer_sleep_microseconds(timeout_microseconds - 20*1000); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == NULL); + EXPECT_EQ(timer_nodes[0].is_running, 1); + + // Sleep again, to wake up after the callback event + timer_sleep_microseconds(40*1000); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == context_data); + EXPECT_EQ(timer_nodes[0].is_running, 1); + + // Because it is repeated, we should now be able to get another timer event after another wait time + p_callback_context_1 = NULL; + timer_sleep_microseconds(timeout_microseconds); + + // Check if the callback was called succesfully + EXPECT_TRUE(p_callback_context_1 == context_data); + EXPECT_EQ(timer_nodes[0].is_running, 1); +} + + +volatile uint32_t callback_function_2_counter = 0; +void callback_function_2(void* p_context) { + callback_function_2_counter++; +} + +volatile uint32_t callback_function_3_counter = 0; +void callback_function_3(void* p_context) { + callback_function_3_counter++; +} + +TEST_F(TimerTest, TwoTimerTest) { + + uint32_t timer_id_2, timer_id_3; + uint8_t ret = timer_create_timer(&timer_id_2, TIMER_MODE_SINGLE_SHOT, callback_function_2, 5); + EXPECT_EQ(ret, 1); + EXPECT_EQ(number_of_timers, 1); + + ret = timer_create_timer(&timer_id_3, TIMER_MODE_REPEATED, callback_function_3, 4); + EXPECT_EQ(ret, 1); + EXPECT_EQ(number_of_timers, 2); + + + + + uint64_t timeout_2_microseconds = 100*1000; + uint64_t timeout_3_microseconds = 100*1000; + + callback_function_2_counter = 0; + callback_function_3_counter = 0; + + + // Start the timer_2 and _3 + ret = timer_start_timer(timer_id_2, timeout_2_microseconds, NULL); + EXPECT_EQ(ret, 1); + ret = timer_start_timer(timer_id_3, timeout_3_microseconds, NULL); + EXPECT_EQ(ret, 1); + + EXPECT_TRUE(timer_nodes[0].next == &timer_nodes[1]); + EXPECT_TRUE(timer_nodes[1].next == NULL); + + // Sleep until shortly before timer expires the first time + timer_sleep_microseconds(80*1000); + + EXPECT_EQ(timer_nodes[0].is_running, 1); + EXPECT_EQ(timer_nodes[1].is_running, 1); + + // Sleep until shortly after timer expires the first time + timer_sleep_microseconds(40*1000); + + // The first timer shouldn't run anymore, the second (REPEATED) should run + EXPECT_EQ(timer_nodes[0].is_running, 0); + EXPECT_EQ(timer_nodes[1].is_running, 1); + + EXPECT_EQ(callback_function_2_counter, 1); + EXPECT_EQ(callback_function_3_counter, 1); + + + timer_sleep_microseconds(5*timeout_3_microseconds); + + // Check if the number of callbacks is correct + EXPECT_EQ(callback_function_2_counter, 1); + EXPECT_EQ(callback_function_3_counter, 1 + 5); + +} + + + +volatile uint32_t callback_function_4_counter = 0; +void callback_function_4(void* p_context) { + callback_function_4_counter++; +} + +uint32_t next = 1; +uint32_t rand(void) { + return ( ( next = next * 1103515245L + 12345L ) % 2147483647L ); +} + +TEST_F(TimerTest, MultipleSingleShotTimerTest) { + + uint32_t timer_id; + uint8_t ret; + callback_function_4_counter = 0; + + for(uint8_t i = 0; i < MAX_NUMBER_OF_TIMERS_TEST; i++) { + ret = timer_create_timer(&timer_id, TIMER_MODE_SINGLE_SHOT, callback_function_4, i); + EXPECT_EQ(ret, 1); + ret = timer_start_timer(timer_id, (rand() % 100 + 1)*1000, NULL); + EXPECT_EQ(ret, 1); + } + + // Sleep again, to wake up after the callback events + timer_sleep_microseconds(120*1000); + + EXPECT_EQ(callback_function_4_counter, 100); + EXPECT_EQ(number_of_timers, 100); + // Create again a timer --> number_of_timers is too big + ret = timer_create_timer(&timer_id, TIMER_MODE_SINGLE_SHOT, callback_function_4, 0); + EXPECT_EQ(ret, 0); + + // Start again timer 0 + ret = timer_start_timer(0, 10*1000, NULL); + EXPECT_EQ(ret, 1); + ret = timer_start_timer(0, 10*1000, NULL); + EXPECT_EQ(ret, 0); + + // Sleep again, to wake up after the callback events + timer_sleep_microseconds(20*1000); + + EXPECT_EQ(callback_function_4_counter, 101); + + +} + +TEST_F(TimerTest, StopTimerTest) { + + uint32_t timer_id; + uint8_t ret; + callback_function_4_counter = 0; + for(uint8_t i = 0; i < MAX_NUMBER_OF_TIMERS_TEST; i++) { + ret = timer_create_timer(&timer_id, TIMER_MODE_SINGLE_SHOT, callback_function_4, i); + EXPECT_EQ(ret, 1); + ret = timer_start_timer(timer_id, (i+10)*1000, NULL); + EXPECT_EQ(ret, 1); + } + + // Stop a timer somewhere in between and start it again with a higher time + timer_stop_timer(10); + ret = timer_start_timer(10, (100)*1000, NULL); + EXPECT_EQ(ret, 1); + + + + // Sleep again, to wake up after the callback events + timer_sleep_microseconds((100 + 20) *1000); + + EXPECT_EQ(callback_function_4_counter, 100); + EXPECT_EQ(number_of_timers, 100); + + +} + +TEST_F(TimerTest, MultipleRepeatedTimerTest) { + + uint32_t timer_id; + uint8_t ret; + callback_function_4_counter = 0; + for(uint8_t i = 0; i < MAX_NUMBER_OF_TIMERS_TEST; i++) { + ret = timer_create_timer(&timer_id, TIMER_MODE_REPEATED, callback_function_4, i); + EXPECT_EQ(ret, 1); + ret = timer_start_timer(timer_id, 100*1000, NULL); + EXPECT_EQ(ret, 1); + } + + // Sleep again, to wake up after the callback events + timer_sleep_microseconds((5*100 + 20) *1000); + + EXPECT_EQ(callback_function_4_counter, 500); + EXPECT_EQ(number_of_timers, 100); + + for(uint8_t i = 0; i < MAX_NUMBER_OF_TIMERS_TEST; i++) { + timer_stop_timer(i); + } + + // Sleep again, to wake up after the callback events + timer_sleep_microseconds(100 *1000); + EXPECT_EQ(callback_function_4_counter, 500); + + + // Start again timer 0 + ret = timer_start_timer(0, 100*1000, NULL); + EXPECT_EQ(ret, 1); + + // Sleep again, to wake up after the callback events + timer_sleep_microseconds(120*1000); + + EXPECT_EQ(callback_function_4_counter, 501); + +} + + + + +uint32_t callback_function_5_counter = 0; +uint32_t timer_id; +uint32_t timeout_microseconds; +void callback_function_5(void* p_context) { + callback_function_5_counter ++; + // Restart the timer withih the Callback + uint8_t ret = timer_start_timer(timer_id, timeout_microseconds, NULL); + EXPECT_EQ(ret, 1); +} + +TEST_F(TimerTest, CallbackRestartTest) { + + uint8_t ret; + callback_function_5_counter = 0; + timeout_microseconds = 100*1000; + + ret = timer_create_timer(&timer_id, TIMER_MODE_SINGLE_SHOT, callback_function_5, 0); + EXPECT_EQ(ret, 1); + + ret = timer_start_timer(timer_id, timeout_microseconds, NULL); + EXPECT_EQ(ret, 1); + + // Sleep to wake up after the callback events + timer_sleep_microseconds((5*100 + 20)*1000); + EXPECT_EQ(callback_function_5_counter, 5); +} +}; diff --git a/flash.sh b/flash.sh new file mode 100644 index 0000000..7b8e25b --- /dev/null +++ b/flash.sh @@ -0,0 +1 @@ +sudo docker-compose run nrf make badge_03v6_noDebug flashUnlock flashErase flashS130 flashAPP PROTOCOL_VERSION=2 \ No newline at end of file diff --git a/programming_loop_docker.sh b/programming_loop_docker.sh index 5a8d8c8..41d9c13 100755 --- a/programming_loop_docker.sh +++ b/programming_loop_docker.sh @@ -74,6 +74,7 @@ do sleep 5 load + sleep 1 get_mac echo "########## Finished (${COUNTER}) #######" let COUNTER=COUNTER+1 diff --git a/requirements.txt b/requirements.txt index c3ae1a5..155e041 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ wheel bluepy==1.0.4 pyserial - +matplotlib