diff --git a/.gitignore b/.gitignore index bcfb6110f26..758a76cd798 100644 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,6 @@ *.pdb *.qm -# Bin folder with exceptions -/lib/ - # External device SDKs (if installed) /applications/mne_scan/plugins/eegosports/eemagine/ /applications/mne_scan/plugins/gusbamp/gtec_gUSBamp.h @@ -55,24 +52,34 @@ ui_*.h *.user Makefile +# Generated docu Docker files +/_site + +# Binary data folders +/build* +/out* + +# Resources folders +/src/external/fftw +/resources/data* +__pycache__/ +venv/ +/resources/**/fieldline/*.whl +/resources/**/fieldline/sample_restart.py +/resources/**/fieldline/venv/ +.cache/* + +codecov + # IDE settings .cproject .project .settings .vscode .session +.clang-format mne-cpp.pro.user* compile_commands.json +CPPLINT.cfg -# FFTW -/src/external/fftw - -# Generated docu Docker files -/_site -/build* -/out* -/resources/data* -.cache/* - -codecov diff --git a/config2.py b/config2.py new file mode 100644 index 00000000000..75ef296a7c8 --- /dev/null +++ b/config2.py @@ -0,0 +1,32 @@ +import sys +def dump(obj): + for attr in dir(obj): + print("obj.%s = %r" % (attr, getattr(obj, attr))) + +#sys.argv = '/autofs/cluster/fusion/juan/anaconda3/bin/anaconda' +print(' === configuration script =======================================================') +dump(sys) +print(' ==================================================================================') + +print(' === system path ===============================================================') +for p in sys.path: + print(p) +print(' ==================================================================================') + +print(' === system argv ===============================================================') +print(sys.argv) +print(' ==================================================================================') +sys.argv = '/autofs/cluster/fusion/juan/anaconda3/bin/python' +print(' === system argv ===============================================================') +print(sys.argv) +print(' ==================================================================================') + +import logging +import argparse +import queue +import sys +import time +import multiprocessing + +multiprocessing.set_executable('autofs/cluster/fusion/juan/anaconda3/bin/python') + diff --git a/resources/mne_scan/plugins/fieldline/.flake8 b/resources/mne_scan/plugins/fieldline/.flake8 new file mode 100644 index 00000000000..9a79ff74f60 --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 125 +extend-ignore = E251 + diff --git a/resources/mne_scan/plugins/fieldline/README.md b/resources/mne_scan/plugins/fieldline/README.md new file mode 100644 index 00000000000..97f7d89bc1f --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/README.md @@ -0,0 +1,21 @@ +FieldLine API README + +Prerequisits: +- Python 3.8 needs to be installed - due to an issue in the shared memory library that was fixed +- Download api-example.zip and fieldline_api_XXX.whl + +1) Download and extract api-example.zip +2) cd api-example +3) python3.8 -m venv venv +4) . venv/bin/activate +5) pip install -r requirements-api.txt +6) pip install +7) python main.py + +Release Notes: +0.0.13 - add SENSOR_READY state +0.0.12 - fix wrong file include +0.0.11 - Add sensor status call +0.0.10 - Few API cleanup items +0.0.2 - add start_adc and stop_adc calls +0.0.1 - initial release diff --git a/resources/mne_scan/plugins/fieldline/fieldline_api_mock/__init__.py b/resources/mne_scan/plugins/fieldline/fieldline_api_mock/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/resources/mne_scan/plugins/fieldline/fieldline_api_mock/fieldline_service.py b/resources/mne_scan/plugins/fieldline/fieldline_api_mock/fieldline_service.py new file mode 100644 index 00000000000..caa79c85fe9 --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/fieldline_api_mock/fieldline_service.py @@ -0,0 +1,353 @@ +import threading +import time +import random + +class FieldLineService: + class SensorApi: + def __init__(self): + self.sleep_mean = 1 + self.sleep_var = .2 + self.prob_success = 0.9 + + def __init__(self, ip_list, prefix=""): + """ + callback - required callback class + should be of type FieldLineCallback + """ + print("Initializing FieldLine Service") + self.is_open = False + + self.num_sensors_per_chassis = 16 + self.num_chassis = 2 + + self.ip_list = ip_list + self.num_chassis = len(self.ip_list) + # print("IP list: " + str(self.ip_list)) + + self.sensors = {i: list(range(1, self.num_sensors_per_chassis + 1)) for i in range(0, self.num_chassis) } + self.sensor_api = self.SensorApi() + + self.data_random_mean = 0 + self.data_random_var = 1000 + self.sampling_frequency = 1000 + self.callback_function = self.callback_function_default + self.continue_data_acquisition = False + self.data_acquisition_thread = threading.Thread(target=self.data_acquisition) + + def callback_function_default(self, _): + pass + + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def open(self): + self.is_open = True + print("FieldLineService Open") + print("Connecting to devices in ip_list: " + str(self.ip_list)) + + def close(self): + self.is_open = False + print("FieldLineService Close.") + print("Disconnecting.") + + def load_sensors(self): + if self.is_open: + print("Loading sensors.") + return self.sensors + else: + print("Cannot load sensors.") + print("Fieldline service closed.") + + def get_chassis_list(self): + if self.is_open: + print("Get chassis list") + return list(range(0, len(self.ip_list))) + else: + print("Fieldline service closed.") + return None + + def generate_data(self): + timestamp = int(time.time()) + + data_frames = {} + + num_sensors = sum(len(sensor_list) for sensor_list in self.sensors.values()) + + data_values = [91599] + [round(random.normalvariate(self.data_random_mean, self.data_random_var)) + for _ in range(1, num_sensors + 1)] + + chassis_labels = [0] + [chassis for chassis, sensors in self.sensors.items() for _ in sensors] + sensor_labels = [0] + [sensor for sensor_list in self.sensors.values() for sensor in sensor_list] + + data_type_labels = [0] + ([50] * num_sensors) + global_labels = [f'{chassis_label:02}:{sensor_label:02}:{data_type:02}' + for chassis_label, sensor_label, data_type in zip(chassis_labels, sensor_labels, data_type_labels)] + + for global_l, data_i, sensor_l, chassis_l, data_type_l in \ + zip(global_labels, data_values, sensor_labels, chassis_labels, data_type_labels): + data_frames[global_l] = \ + {'data': data_i, 'sensor': f'{chassis_l:02}:{sensor_l:02}', 'sensor_id': sensor_l, 'data_type': data_type_l} + + return {'timestamp': timestamp, 'data_frames': data_frames} + + def data_acquisition(self): + sampling_period = 1/self.sampling_frequency + + start_time = time.time() + data = self.generate_data() + end_time = time.time() + elapsed_time = end_time - start_time + time.sleep(sampling_period - elapsed_time) + end_time = time.time() + + elapsed_time = end_time - start_time + elapsed_time_diff = (sampling_period - elapsed_time) + + while(self.continue_data_acquisition): + start_time = time.time() + data = self.generate_data() + self.callback_function(data) + end_time = time.time() + elapsed_time = end_time - start_time + # time_to_sleep = max(0, .001 - elapsed_time) + # time.sleep(abs(sampling_period + elapsed_time_diff - 0.7 * elapsed_time)) + time.sleep(sampling_period) + # print(f"elapsed_time: {elapsed_time:04}") + # start_time = time.time() + # end_time = time.time() + # time_meas = end_time - start_time + # + # start_time = time.time() + # data = self.generate_data() + # self.callback_function(data) + # time.sleep(sampling_period) + # end_time = time.time() + # + # time_diff = end_time - start_time - sampling_period - time_meas + # time_to_sleep_adjusted = sampling_period - 1.5 * time_diff + # time_to_sleep_final = 0 if time_to_sleep_adjusted < 0 else time_to_sleep_adjusted + # print(time_to_sleep_final) + # + # while(self.continue_data_acquisition): + # data = self.generate_data() + # self.callback_function(data) + # time.sleep(time_to_sleep_final) + + def read_data(self, data_callback=None): + print("Setting the data callback fcn.") + if self.is_open: + if(data_callback is not None): + self.callback_function = data_callback + print(f"Data callback set to: {data_callback.__name__}.") + else: + self.callback_function = self.callback_function_default + print(f'Data callback disabled.') + else: + print("Fieldline service closed.") + return None + + def start_adc(self, _): + """ + Start ADC from chassis + + chassis_id - unique ID of chassis + """ + if self.is_open: + self.continue_data_acquisition = True + self.data_acquisition_thread.start() + print("Starting data acquisition.") + else: + print("Fieldline service closed.") + return None + + def stop_adc(self, _): + """ + Stop ADC from chassis + + chassis_id - unique ID of chassis + """ + if self.is_open: + self.continue_data_acquisition = False + if self.data_acquisition_thread.is_alive(): + self.data_acquisition_thread.join(timeout=5) + print("Stopping data acquisition.") + else: + print("Data acquisition running.") + else: + print("Fieldline service closed.") + return None + + + def turn_off_sensors(self, sensor_dict): + """ + Turn off sensors + Note: can be performed anytime + + sensor_dict - dictionary of chassis id to list of sensors ex. {0: [0,1], 1:[0,1]} + """ + print("Turning off sensors.") + + def sensors_api(self, sensor_dict, on_next=None, on_error=None, on_completed=None): + def sensor_thread(chassis_id: int, sensor_id: int): + sleep_time = random.normalvariate(self.sensor_api.sleep_mean, self.sensor_api.sleep_var) + if sleep_time <= 0: + sleep_time = 0.1 + time.sleep(sleep_time) + result = random.random() + if result <= self.sensor_api.prob_success: + if on_next: + on_next(chassis_id, sensor_id) + else: + codes = [117, 31337, 2626] + if on_error: + on_error(chassis_id, sensor_id, random.choice(codes)) + + threads = [] + for chassis_id, sensor_ids in sensor_dict.items(): + for sensor_id in sensor_ids: + t = threading.Thread(target=sensor_thread, args=(chassis_id, sensor_id)) + t.start() + threads.append(t) + + for t in threads: + t.join() + + if on_completed: + on_completed() + + def restart_sensors(self, sensor_dict, on_next=None, on_error=None, on_completed=None): + """ + Restart the sensors + Note: can be performed anytime + + sensor_dict - dictionary of chassis id to list of sensors ex. {0: [0,1], 1:[0,1]} + on_next - callback when sensor succeeds a step + on_error - callback when sensor fails a step + on_completed - callback when all sensors have either succeeded or failed + """ + print("Restarting sensors.") + self.sensors_api(sensor_dict, on_next, on_error, on_completed) + + + def coarse_zero_sensors(self, sensor_dict, on_next=None, on_error=None, on_completed=None): + """ + Coarse zero sensor + Note: can only be performed after restart is complete + + sensor_dict - dictionary of chassis id to list of sensors ex. {0: [0,1], 1:[0,1]} + on_next - callback when sensor succeeds a step + on_error - callback when sensor fails a step + on_completed - callback when all sensors have either succeeded or failed + """ + print("coarse zero sensor") + self.sensors_api(sensor_dict, on_next, on_error, on_completed) + + def fine_zero_sensors(self, sensor_dict, on_next=None, on_error=None, on_completed=None): + """ + Fine zero sensor + Note: can only be performed after coarse zero is complete + + sensor_dict - dictionary of chassis id to list of sensors ex. {0: [0,1], 1:[0,1]} + on_next - callback when sensor succeeds a step + on_error - callback when sensor fails a step + on_completed - callback when all sensors have either succeeded or failed + """ + print("fine zero sensors") + self.sensors_api(sensor_dict, on_next, on_error, on_completed) + + def set_bz_wave(self, chassis_id, sensor_id, wave_type, freq=None, amplitude=None): + """ + Apply a known magnetic field to the BZ coil (e.g. sine wave) + + chassis_id - unique ID of chassis + sensor_id - unique ID of sensor + wave_type - FieldLineWaveType (WAVE_OFF, WAVE_RAMP, WAVE_SINE) + freq - frequency of wave + amplitude - amplitude of wave (nT) + """ + print('Setting bz wave') + print('UuuUUUuuuuu...') + # + # if wave_type == FieldLineWaveType.WAVE_OFF: + # self.data_source.set_wave_off(chassis_id, sensor_id) + # elif wave_type == FieldLineWaveType.WAVE_RAMP: + # self.data_source.set_wave_ramp(chassis_id, sensor_id, freq, amplitude) + # elif wave_type == FieldLineWaveType.WAVE_SINE: + # self.data_source.set_wave_sine(chassis_id, sensor_id, freq, amplitude) + + def set_closed_loop(self, enable): + """ + Turn closed loop on or off + + enable - True or False for on or off + """ + print("set closed loop") + # self.data_source.set_closed_loop(enable) + + def get_fields(self, chassis_id, sensor_id): + """ + Get the fields for a sensor + + chassis_id - unique ID of chassis + sensor_id - unique ID of sensor + """ + print("get fields") + # for s in self.data_source.get_sensors(): + # if s.chassis_id == chassis_id and s.sensor_id == sensor_id: + # return s.fields + # return None + + def get_serial_numbers(self, chassis_id, sensor_id): + """ + Get the card and sensor serial number for a sensor + + chassis_id - unique ID of chassis + sensor_id - unique ID of sensor + """ + print("get serial numbers") + # for s in self.data_source.get_sensors(): + # if s.chassis_id == chassis_id and s.sensor_id == sensor_id: + # return (s.card_serial,s.sensor_serial) + + def get_chassis_serial_number(self, chassis_id): + """ + Get the chassis serial number + + chassis_id - unique ID of chassis + """ + print("get chassis serial number") + # return self.data_source.get_chassis_serial(chassis_id) + + def get_version(self, chassis_id): + """ + Get the build version + + chassis_id - unique ID of chassis + """ + print("get version") + # return self.data_source.get_chassis_version(chassis_id) + + def get_sensor_state(self, chassis_id, sensor_id): + """ + Get the current state of the sensor + + chassis_id - unique ID of chassis + sensor_id - ID of sensor + """ + print("get sensor state") + # return self.data_source.get_sensor_state(chassis_id, sensor_id) + + def get_calibration_value(self, ch_name): + """ + Get the calibration value for a channel + + ch_name - channel name in chassis:sensor:datatype format + """ + print("get calibration value") + # channel_dict = self.hardware_state.get_channel_dict() + # return channel_dict[ch_name] if ch_name in channel_dict else 1.0 + diff --git a/resources/mne_scan/plugins/fieldline/main.py b/resources/mne_scan/plugins/fieldline/main.py new file mode 100644 index 00000000000..cef2a2ea39a --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/main.py @@ -0,0 +1,15 @@ +"""This is my super module""" +import sys +from fieldline_api_mock.fieldline_service import FieldLineService +import time + +for path in sys.path: + print(path) + +print("sys.executable: " + str(sys.executable)) + +print("hello pepe vamos vamos!!!") + +ip_list = ["8.8.8.8", "9.9.9.9"] + + diff --git a/resources/mne_scan/plugins/fieldline/random_data.py b/resources/mne_scan/plugins/fieldline/random_data.py new file mode 100644 index 00000000000..59753c3bf6c --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/random_data.py @@ -0,0 +1,32 @@ +import random + + +def generate_data(): + timestamp = 179331926 + num_sensors_per_chassis = 16 + num_chassis = 2 + + data_frames = {} + + data_values = [91599] + \ + [round(random.normalvariate(0, 1000)) + for _ in range(1, num_sensors_per_chassis * num_chassis + 1)] + + chassis_labels = [0] + [num for num in range(0, num_chassis) for _ in range(num_sensors_per_chassis)] + sensor_labels = [0] + (list(range(1, num_sensors_per_chassis + 1))) * num_chassis + data_type_labels = [0] + ([50] * num_sensors_per_chassis * num_chassis) + global_labels = [f'{chassis_label:02}:{sensor_label:02}:{data_type:02}' + for chassis_label, sensor_label, data_type in zip(chassis_labels, sensor_labels, data_type_labels)] + + for global_l, data_i, sensor_l, chassis_l, data_type_l in \ + zip(global_labels, data_values, sensor_labels, chassis_labels, data_type_labels): + data_frames[global_l] = \ + {'data': data_i, 'sensor': f'{chassis_l:02}:{sensor_l:02}', 'sensor_id': sensor_l, 'data_type': data_type_l} + + + # data_frames = [] + return {'timestamp': timestamp, 'data_frames': data_frames} + +if __name__ == "__main__": + data = generate_data() + print(data) diff --git a/resources/mne_scan/plugins/fieldline/readme.md b/resources/mne_scan/plugins/fieldline/readme.md new file mode 100644 index 00000000000..97f7d89bc1f --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/readme.md @@ -0,0 +1,21 @@ +FieldLine API README + +Prerequisits: +- Python 3.8 needs to be installed - due to an issue in the shared memory library that was fixed +- Download api-example.zip and fieldline_api_XXX.whl + +1) Download and extract api-example.zip +2) cd api-example +3) python3.8 -m venv venv +4) . venv/bin/activate +5) pip install -r requirements-api.txt +6) pip install +7) python main.py + +Release Notes: +0.0.13 - add SENSOR_READY state +0.0.12 - fix wrong file include +0.0.11 - Add sensor status call +0.0.10 - Few API cleanup items +0.0.2 - add start_adc and stop_adc calls +0.0.1 - initial release diff --git a/resources/mne_scan/plugins/fieldline/test.py b/resources/mne_scan/plugins/fieldline/test.py new file mode 100644 index 00000000000..810ab282292 --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/test.py @@ -0,0 +1,30 @@ +data = {'timestamp': 179331926, 'data_frames': + {'00:00:0': {'data': 91599, 'sensor': '00:00', 'sensor_id': 0, 'data_type': 0}, + '00:01:50': {'data': 3440, 'sensor': '00:01', 'sensor_id': 1, 'data_type': 50}, + '00:02:50': {'data': 1434, 'sensor': '00:02', 'sensor_id': 2, 'data_type': 50}, + '00:03:50': {'data': 1073, 'sensor': '00:03', 'sensor_id': 3, 'data_type': 50}, + '00:04:50': {'data': -2361, 'sensor': '00:04', 'sensor_id': 4, 'data_type': 50}, + '00:05:50': {'data': 1936, 'sensor': '00:05', 'sensor_id': 5, 'data_type': 50}, + '00:06:50': {'data': 1385, 'sensor': '00:06', 'sensor_id': 6, 'data_type': 50}, + '00:08:50': {'data': 564, 'sensor': '00:08', 'sensor_id': 8, 'data_type': 50}, + '00:09:50': {'data': 3224, 'sensor': '00:09', 'sensor_id': 9, 'data_type': 50}, + '00:10:50': {'data': 2654, 'sensor': '00:10', 'sensor_id': 10, 'data_type': 50}, + '00:11:50': {'data': 1168, 'sensor': '00:11', 'sensor_id': 11, 'data_type': 50}, + '00:12:50': {'data': 6068, 'sensor': '00:12', 'sensor_id': 12, 'data_type': 50}, + '00:13:50': {'data': 1012, 'sensor': '00:13', 'sensor_id': 13, 'data_type': 50}, + '00:14:50': {'data': 1244, 'sensor': '00:14', 'sensor_id': 14, 'data_type': 50}, + '00:15:50': {'data': 749, 'sensor': '00:15', 'sensor_id': 15, 'data_type': 50}, + '00:16:50': {'data': 720, 'sensor': '00:16', 'sensor_id': 16, 'data_type': 50}, + '01:01:50': {'data': 1148, 'sensor': '01:02', 'sensor_id': 2, 'data_type': 50}, + '01:02:50': {'data': 1148, 'sensor': '01:02', 'sensor_id': 2, 'data_type': 50}, + '01:03:50': {'data': 1823, 'sensor': '01:03', 'sensor_id': 3, 'data_type': 50}, + '01:06:50': {'data': 1179, 'sensor': '01:06', 'sensor_id': 6, 'data_type': 50}, + '01:07:50': {'data': 2701, 'sensor': '01:07', 'sensor_id': 7, 'data_type': 50}, + '01:08:50': {'data': 4647, 'sensor': '01:08', 'sensor_id': 8, 'data_type': 50}, + '01:09:50': {'data': 3595, 'sensor': '01:09', 'sensor_id': 9, 'data_type': 50}, + '01:10:50': {'data': 936, 'sensor': '01:10', 'sensor_id': 10, 'data_type': 50}, + '01:12:50': {'data': -4956, 'sensor': '01:12', 'sensor_id': 12, 'data_type': 50}, + '01:13:50': {'data': 2274, 'sensor': '01:13', 'sensor_id': 13, 'data_type': 50}, + '01:14:50': {'data': 1698, 'sensor': '01:14', 'sensor_id': 14, 'data_type': 50}, + '01:15:50': {'data': 3942, 'sensor': '01:15', 'sensor_id': 15, 'data_type': 50}, + '01:16:50': {'data': 4695, 'sensor': '01:16', 'sensor_id': 16, 'data_type': 50}}} diff --git a/resources/mne_scan/plugins/fieldline/test2.py b/resources/mne_scan/plugins/fieldline/test2.py new file mode 100644 index 00000000000..ad5380693f3 --- /dev/null +++ b/resources/mne_scan/plugins/fieldline/test2.py @@ -0,0 +1,32 @@ +data = {'timestamp': 179331926, 'data_frames': + {'00:00:00': {'data': 91599, 'sensor': '00:00', 'sensor_id': 0, 'data_type': 0}, + '00:01:50': {'data': -187, 'sensor': '00:01', 'sensor_id': 1, 'data_type': 50}, + '00:02:50': {'data': -606, 'sensor': '00:02', 'sensor_id': 2, 'data_type': 50}, + '00:03:50': {'data': -261, 'sensor': '00:03', 'sensor_id': 3, 'data_type': 50}, + '00:04:50': {'data': 2100, 'sensor': '00:04', 'sensor_id': 4, 'data_type': 50}, + '00:05:50': {'data': -880, 'sensor': '00:05', 'sensor_id': 5, 'data_type': 50}, + '00:06:50': {'data': -2066, 'sensor': '00:06', 'sensor_id': 6, 'data_type': 50}, + '00:07:50': {'data': -657, 'sensor': '00:07', 'sensor_id': 7, 'data_type': 50}, + '00:08:50': {'data': 443, 'sensor': '00:08', 'sensor_id': 8, 'data_type': 50}, + '00:09:50': {'data': -228, 'sensor': '00:09', 'sensor_id': 9, 'data_type': 50}, + '00:10:50': {'data': 922, 'sensor': '00:10', 'sensor_id': 10, 'data_type': 50}, + '00:11:50': {'data': 2129, 'sensor': '00:11', 'sensor_id': 11, 'data_type': 50}, + '00:12:50': {'data': -1235, 'sensor': '00:12', 'sensor_id': 12, 'data_type': 50}, + '00:13:50': {'data': -15, 'sensor': '00:13', 'sensor_id': 13, 'data_type': 50}, + '00:14:50': {'data': 1214, 'sensor': '00:14', 'sensor_id': 14, 'data_type': 50}, + '00:15:50': {'data': -535, 'sensor': '00:15', 'sensor_id': 15, 'data_type': 50}, + '00:16:50': {'data': -1326, 'sensor': '00:16', 'sensor_id': 16, 'data_type': 50}, + '01:01:50': {'data': -663, 'sensor': '01:01', 'sensor_id': 1, 'data_type': 50}, + '01:02:50': {'data': 740, 'sensor': '01:02', 'sensor_id': 2, 'data_type': 50}, + '01:03:50': {'data': -1406, 'sensor': '01:03', 'sensor_id': 3, 'data_type': 50}, + '01:04:50': {'data': 210, 'sensor': '01:04', 'sensor_id': 4, 'data_type': 50}, + '01:05:50': {'data': -811, 'sensor': '01:05', 'sensor_id': 5, 'data_type': 50}, + '01:06:50': {'data': -534, 'sensor': '01:06', 'sensor_id': 6, 'data_type': 50}, + '01:07:50': {'data': 1300, 'sensor': '01:07', 'sensor_id': 7, 'data_type': 50}, + '01:08:50': {'data': 1321, 'sensor': '01:08', 'sensor_id': 8, 'data_type': 50}, + '01:09:50': {'data': 75, 'sensor': '01:09', 'sensor_id': 9, 'data_type': 50}, + '01:10:50': {'data': -689, 'sensor': '01:10', 'sensor_id': 10, 'data_type': 50}, + '01:11:50': {'data': -233, 'sensor': '01:11', 'sensor_id': 11, 'data_type': 50}, + '01:12:50': {'data': 649, 'sensor': '01:12', 'sensor_id': 12, 'data_type': 50}, + '01:13:50': {'data': -319, 'sensor': '01:13', 'sensor_id': 13, 'data_type': 50}, + '01:14:50': {'data': -91, 'sensor': '01:14', 'sensor_id': 14, 'data_type': 50}}} diff --git a/src/applications/mne_scan/mne_scan/CMakeLists.txt b/src/applications/mne_scan/mne_scan/CMakeLists.txt index 959bc7cd5b6..03af1eaa54b 100644 --- a/src/applications/mne_scan/mne_scan/CMakeLists.txt +++ b/src/applications/mne_scan/mne_scan/CMakeLists.txt @@ -11,26 +11,26 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS ${QT_REQUIRED_COMPONENTS}) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${QT_REQUIRED_COMPONENTS}) set(SOURCES - main.cpp - mainsplashscreencloser.cpp - startupwidget.cpp - mainsplashscreen.cpp - pluginscene.cpp - pluginitem.cpp - plugingui.cpp - arrow.cpp + main.cpp + mainsplashscreencloser.cpp + startupwidget.cpp + mainsplashscreen.cpp + pluginscene.cpp + pluginitem.cpp + plugingui.cpp + arrow.cpp mainwindow.cpp ) set(HEADERS - info.h - mainsplashscreencloser.h - startupwidget.h - mainsplashscreen.h - pluginscene.h - pluginitem.h - plugingui.h - arrow.h + info.h + mainsplashscreencloser.h + startupwidget.h + mainsplashscreen.h + pluginscene.h + pluginitem.h + plugingui.h + arrow.h mainwindow.h ) diff --git a/src/applications/mne_scan/mne_scan/main.cpp b/src/applications/mne_scan/mne_scan/main.cpp index 7f09d4fd354..930637301bf 100644 --- a/src/applications/mne_scan/mne_scan/main.cpp +++ b/src/applications/mne_scan/mne_scan/main.cpp @@ -92,7 +92,8 @@ Q_IMPORT_PLUGIN(NeuronalConnectivity) Q_IMPORT_PLUGIN(FtBuffer) Q_IMPORT_PLUGIN(WriteToFile) Q_IMPORT_PLUGIN(Hpi) -//Q_IMPORT_PLUGIN(DummyToolbox) +Q_IMPORT_PLUGIN(Fieldline) +// Q_IMPORT_PLUGIN(DummyToolbox) #ifdef WITHGUSBAMP Q_IMPORT_PLUGIN(GUSBAmp) #endif @@ -134,6 +135,7 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(averaging); Q_INIT_RESOURCE(writetofile); Q_INIT_RESOURCE(hpi); + Q_INIT_RESOURCE(fieldline); #ifdef WITHBRAINAMP Q_INIT_RESOURCE(brainamp); #endif diff --git a/src/applications/mne_scan/mne_scan/mainwindow.cpp b/src/applications/mne_scan/mne_scan/mainwindow.cpp index ec6f3a67a09..73afe8621ff 100644 --- a/src/applications/mne_scan/mne_scan/mainwindow.cpp +++ b/src/applications/mne_scan/mne_scan/mainwindow.cpp @@ -801,17 +801,18 @@ void MainWindow::initMultiViewWidget(QListgetName() == "Filter" || - lPlugins.at(i)->getName() == "Fiff Simulator" || - lPlugins.at(i)->getName() == "FtBuffer" || - lPlugins.at(i)->getName() == "Natus" || - lPlugins.at(i)->getName() == "BabyMEG"|| - lPlugins.at(i)->getName() == "BrainFlow"|| - lPlugins.at(i)->getName() == "EEGoSports"|| - lPlugins.at(i)->getName() == "GUSBAmp"|| - lPlugins.at(i)->getName() == "LSL Adapter"|| - lPlugins.at(i)->getName() == "TMSI"|| - lPlugins.at(i)->getName() == "BrainAMP") { + if (lPlugins.at(i)->getName() == "Filter" || + lPlugins.at(i)->getName() == "Fiff Simulator" || + lPlugins.at(i)->getName() == "FtBuffer" || + lPlugins.at(i)->getName() == "Natus" || + lPlugins.at(i)->getName() == "BabyMEG" || + lPlugins.at(i)->getName() == "BrainFlow" || + lPlugins.at(i)->getName() == "EEGoSports" || + lPlugins.at(i)->getName() == "GUSBAmp" || + lPlugins.at(i)->getName() == "LSL Adapter" || + lPlugins.at(i)->getName() == "TMSI" || + lPlugins.at(i)->getName() == "BrainAMP" || + lPlugins.at(i)->getName() == "Fieldline OPM") { pMultiViewWinow = m_pMultiView->addWidgetBottom(pWidget, sCurPluginName); } else { pMultiViewWinow = m_pMultiView->addWidgetTop(pWidget, sCurPluginName); diff --git a/src/applications/mne_scan/plugins/CMakeLists.txt b/src/applications/mne_scan/plugins/CMakeLists.txt index 775c74dbf69..d2e5556de28 100644 --- a/src/applications/mne_scan/plugins/CMakeLists.txt +++ b/src/applications/mne_scan/plugins/CMakeLists.txt @@ -17,6 +17,9 @@ add_subdirectory(fiffsimulator) add_subdirectory(ftbuffer) add_subdirectory(babymeg) add_subdirectory(natus) +if(UNIX) + add_subdirectory(fieldline) +endif() # Algorithm Plugin add_subdirectory(rtcmne) diff --git a/src/applications/mne_scan/plugins/fieldline/CMakeLists.txt b/src/applications/mne_scan/plugins/fieldline/CMakeLists.txt new file mode 100644 index 00000000000..84330b3ea74 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/CMakeLists.txt @@ -0,0 +1,245 @@ +cmake_minimum_required(VERSION 3.14) +project(scan_fieldline LANGUAGES CXX) + +#Handle qt uic, moc, rrc automatically +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(QT NAMES Qt6 Qt5 REQUIRED + COMPONENTS Core Widgets Network Concurrent +) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED + COMPONENTS Core Widgets Network Concurrent +) + +set(HEADERS + fieldline_global.h + fieldline_definitions.h + fieldline.h + fieldline_acqsystem.h + fieldline_view.h + fieldline_view_chassis.h + fieldline_view_sensor.h + ipfinder.h + ipfinder_network_unix.h +) + +set(SOURCES + fieldline_global.cpp + fieldline.cpp + fieldline_acqsystem.cpp + fieldline_view.cpp + fieldline_view_chassis.cpp + fieldline_view_sensor.cpp + ipfinder.cpp + ipfinder_network_unix.cpp +) + +set(UI_FILES + formfiles/fieldline_view.ui + formfiles/fieldline_view_chassis.ui + formfiles/fieldline_view_sensor.ui +) + +set(FILE_TO_UPDATE fieldline_global.cpp) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/fieldline.json) +# message("File fieldline.json exists.") +else() +# message("File fieldline.json does not exist.") +# message("Creating a new fieldline.json file.") +execute_process(COMMAND "echo \"\"" + WORKING_DIRECTORY ${CMAKE_C + OUTPUT_FILE fieldline.json +) +endif() + +set(SOURCE_PATHS ${SOURCES}) + +list(TRANSFORM SOURCE_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") +set_source_files_properties(${FILE_TO_UPDATE} PROPERTIES + OBJECT_DEPENDS "${SOURCE_PATHS}" +) + +add_library(${PROJECT_NAME} ${SOURCES} ${HEADERS} ${UI_FILES}) + +set(FFTW_LIBS "") + +if(USE_FFTW) +if(WIN32) + set(FFTW_LIBS + ${FFTW_DIR_LIBS}/libfftw3-3.dll + ${FFTW_DIR_LIBS}/libfftw3f-3.dll + ${FFTW_DIR_LIBS}/libfftwf3l-3.dll + ) + target_include_directories(${PROJECT_NAME} PRIVATE ${FFTW_DIR_INCLUDE}) +elseif(UNIX AND NOT APPLE) + set(FFTW_LIBS ${FFTW_DIR_LIBS}/lib/libfftw3.so) + target_include_directories(${PROJECT_NAME} PRIVATE ${FFTW_DIR_INCLUDE}/api) +endif() +endif() + +target_include_directories(${PROJECT_NAME} PUBLIC ../) + +target_link_libraries(${PROJECT_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::Network + Qt${QT_VERSION_MAJOR}::Concurrent + eigen + mne_disp + mne_utils + mne_fiff + scDisp + scShared + scMeas + ${FFTW_LIBS} +) + +target_compile_definitions(${PROJECT_NAME} PRIVATE + SCAN_FIELDLINE_PLUGIN MNE_GIT_HASH_SHORT="${MNE_GIT_HASH_SHORT}" + MNE_GIT_HASH_LONG="${MNE_GIT_HASH_LONG}" +) + +if(NOT BUILD_SHARED_LIBS) +target_compile_definitions(${PROJECT_NAME} PRIVATE STATICBUILD QT_STATICPLUGIN) +endif() + +# ################################################################### +# ######### Python Dependencies ##################################### +# ################################################################### + +execute_process( + COMMAND which python + OUTPUT_VARIABLE Python_EXECUTABLE + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND dirname ${Python_EXECUTABLE} + OUTPUT_VARIABLE Python_BIN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND pwd -P + WORKING_DIRECTORY ${Python_BIN_DIR}/.. + OUTPUT_VARIABLE Python_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND pwd -P + WORKING_DIRECTORY ${Python_PREFIX}/lib + OUTPUT_VARIABLE Python_LIB_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND ${Python_EXECUTABLE} -c "import platform; major, minor, patch = platform.python_version_tuple(); print(major)" + OUTPUT_VARIABLE Python_VERSION_MAJOR + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${Python_EXECUTABLE} -c "import platform; major, minor, patch = platform.python_version_tuple(); print(minor)" + OUTPUT_VARIABLE Python_VERSION_MINOR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if(EXISTS ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config) + message(STATUS "python config found") +else() + message(STATUS "python config not found") + execute_process( + COMMAND readlink -f ${Python_EXECUTABLE} + OUTPUT_VARIABLE Python_EXECUTABLE_TRUE_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND dirname ${Python_EXECUTABLE_TRUE_PATH} + OUTPUT_VARIABLE Python_BIN_TRUE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(NOT EXISTS ${Python_BIN_TRUE_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config) + message(FATAL python-config not found. Python installation missing python-config.) + else() + execute_process( + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${Python_BIN_TRUE_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config + ) + message(STATUS "Creating sym link to python config") + endif() +endif() + +execute_process( + COMMAND ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config --includes + OUTPUT_VARIABLE Python_INCLUDE_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config --cflags + OUTPUT_VARIABLE Python_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config --libs + OUTPUT_VARIABLE Python_LIBS + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config --ldflags + OUTPUT_VARIABLE Python_LDFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# set(Python_VERSION_MAJOR "3") +# set(Python_VERSION_MINOR "9") +# set(Python_LIB_DIR "/opt/anaconda3/lib") +# set(Python_INCLUDE_DIRS "-I/opt/anacnda3/include/python3.9") +# set(Python_CFLAGS "-I/opt/anaconda3/include/python3.9 -I/opt/anaconda2/include/python3.9 -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -fwrapv -O2 -Wall -fPIC -O2 -isystem /opt/anaconda3/include -arch x86_64 -I/opt/anaconda3/include -fPIC -O2 -isystem /opt/anaconda3/include -arch x86_64") +# set(Python_LDFLAGS "-L/opt/anaconda3/lib/python3.9/config-3.9-darwin -ldl -framework CoreFoundation") + +separate_arguments(Python_INCLUDE_DIRS_NORM UNIX_COMMAND "${Python_INCLUDE_DIRS}") +separate_arguments(Python_CFLAGS_NORM UNIX_COMMAND "${Python_CFLAGS}") +separate_arguments(Python_LDFLAGS_NORM UNIX_COMMAND "${Python_LDFLAGS}") + +message(" ") +message(" python prefix dir: " ${Python_PREFIX}) +message(" python bin dir: " ${Python_BIN_DIR}) +message(" python lib dir: " ${Python_LIB_DIR}) +message(" python executable: " ${Python_EXECUTABLE}) +message(" python version major: " ${Python_VERSION_MAJOR}) +message(" python version minor: " ${Python_VERSION_MINOR}) +message(" python include dirs: " ${Python_INCLUDE_DIRS}) +message("*python include dirs norm: " ${Python_INCLUDE_DIRS_NORM}) +message(" python cflags: " ${Python_CFLAGS}) +message(" *python cflags norm: " ${Python_CFLAGS_NORM}) +message(" python libs: " ${Python_LIBS}) +message(" python ldflags: " ${Python_LDFLAGS}) +message(" *python ldflags norm: " ${Python_LDFLAGS_NORM}) +message(" (*)Message missrepresents content of lists.") +message(" ") + +target_compile_options(${PROJECT_NAME} PRIVATE + ${Python_CFLAGS_NORM} ${Python_INCLUDE_DIRS_NORM} +) +target_link_libraries(${PROJECT_NAME} PRIVATE + python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR} +) +target_link_directories(${PROJECT_NAME} PRIVATE ${Python_LIB_DIR}) +target_link_options(${PROJECT_NAME} PRIVATE ${Python_LDFLAGS_NORM}) + +target_compile_definitions(${PROJECT_NAME} PRIVATE + PYTHON_VERSION_MAJOR=${Python_VERSION_MAJOR} + PYTHON_VERSION_MINOR=${Python_VERSION_MINOR} +) + +# ################################################################### +# ################################################################### +# ################################################################### + diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline.cpp b/src/applications/mne_scan/plugins/fieldline/fieldline.cpp new file mode 100644 index 00000000000..be166101ef0 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline.cpp @@ -0,0 +1,316 @@ +//============================================================================================================= +/** + * @file fieldline.cpp + * @author Juan GarciaPrieto ; + * Gabriel B Motta ; + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Contains the definition of the Fieldline class. + * + */ + +//============================================================================================================= +// INCLUDES +//============================================================================================================= +#include +#include +#include + +#include "fieldline/fieldline.h" +#include "fieldline/fieldline_acqsystem.h" +#include "fieldline/fieldline_view.h" +#include "fieldline/ipfinder.h" + +#include +#include + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= +#include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// USED NAMESPACES +//============================================================================================================= + +namespace FIELDLINEPLUGIN { + +//============================================================================================================= + +// QSharedPointer createFiffInfo(int numChassis, +// int numChannels) { +// QSharedPointer pFiffInfo; +// pFiffInfo->sfreq = 1000.0f; +// pFiffInfo->nchan = numChassis * numChannels; +// pFiffInfo->chs.clear(); +// +// for (int chan_i = 0; chan_i < pFiffInfo->nchan; ++chan_i) { +// FIFFLIB::FiffChInfo channel; +// channel.ch_name = QString("%1:%2").arg(numChannels, 2).arg(chan_i, 2); +// channel.kind = FIFFV_MEG_CH; +// channel.unit = FIFF_UNIT_T; +// channel.unit_mul = FIFF_UNITM_NONE; +// channel.chpos.coil_type = FIFFV_COIL_NONE; +// pFiffInfo->chs.append(channel); +// pFiffInfo->ch_names.append(channel.ch_name); +// } +// } + +// QSharedPointer +// createFiffInfo(std::vector> fl_chassis, float sfreq = 1000.f) { +// QSharedPointer pFiffInfo; +// pFiffInfo->sfreq = sfreq; +// pFiffInfo->chs.clear(); +// +// int total_channels = 0; +// int chassis_num = 0; +// for (auto &chassis : fl_chassis) { +// for (auto &sensor : chassis) { +// FIFFLIB::FiffChInfo channel; +// channel.ch_name = QString("%1:%2").arg(chassis_num, 2).arg(sensor, 2); +// channel.kind = FIFFV_MEG_CH; +// channel.unit = FIFF_UNIT_T; +// channel.unit_mul = FIFF_UNITM_NONE; +// channel.chpos.coil_type = FIFFV_COIL_NONE; +// pFiffInfo->chs.append(channel); +// pFiffInfo->ch_names.append(channel.ch_name); +// ++total_channels; +// } +// } +// pFiffInfo->nchan = total_channels; +// +// return pFiffInfo; +// } +// +// QSharedPointer +// createFiffInfo(int numChassis, int numChannels, float sfreq = 1000.f) { +// std::vector> fl; +// for (int i = 0; i < numChassis; ++i) { +// std::vector ch(numChannels); +// std::iota(ch.begin(), ch.end(), 1); +// fl.push_back(std::move(ch)); +// } +// return createFiffInfo(fl, sfreq); +// } + +//============================================================================================================= +// DEFINE MEMBER METHODS +//============================================================================================================= + +Fieldline::Fieldline() +: m_pAcqSystem(nullptr) +, m_pFiffInfo(QSharedPointer(new FIFFLIB::FiffInfo())) +{ +} + +//============================================================================================================= + +Fieldline::~Fieldline() { + if (this->isRunning()) { + this->stop(); + } +} + +//============================================================================================================= + +QSharedPointer Fieldline::clone() const +{ + QSharedPointer pFieldlineClone(new Fieldline()); + return pFieldlineClone; +} + +//============================================================================================================= + +void Fieldline::init() +{ + printLog("Fieldline init"); + m_pCircularBuffer = QSharedPointer::create(40); + m_pRTMSA = SCSHAREDLIB::PluginOutputData + ::create(this, "Fieldline Plugin", + "FieldlinePlguin output"); + m_pRTMSA->measurementData()->setName(this->getName()); + m_outputConnectors.append(m_pRTMSA); + + m_pAcqSystem = new FieldlineAcqSystem(this); +} + +//============================================================================================================= + +void Fieldline::unload() { + printLog("unload"); + delete m_pAcqSystem; +} + +//============================================================================================================= + +bool Fieldline::start() +{ + printLog("start Fieldline"); + + initFiffInfo(); + + std::thread t([this]{ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + m_pAcqSystem->startADC(); + }); + t.detach(); + + QThread::start(); + return true; +} + +void Fieldline::initFiffInfo() +{ + m_pFiffInfo->sfreq = 1000.0f; + m_pFiffInfo->nchan = 3; + m_pFiffInfo->chs.clear(); + for (int chan_i = 0; chan_i < m_pFiffInfo->nchan; chan_i++) { + FIFFLIB::FiffChInfo channel; + channel.kind = FIFFV_MEG_CH; + channel.unit = FIFF_UNIT_T; + channel.unit_mul = FIFF_UNITM_NONE; + channel.chpos.coil_type = FIFFV_COIL_NONE; + std::string channel_name(std::string("Ch. ") + std::to_string(chan_i)); + channel.ch_name = QString::fromStdString(channel_name); + m_pFiffInfo->chs.append(channel); + m_pFiffInfo->ch_names.append(QString::fromStdString(channel_name)); + } + + m_pRTMSA->measurementData()->initFromFiffInfo(m_pFiffInfo); + m_pRTMSA->measurementData()->setMultiArraySize(1); + m_pRTMSA->measurementData()->setVisibility(true); +} + +//============================================================================================================= + +bool Fieldline::stop() { + printLog("stop"); + + m_pAcqSystem->stopADC(); + + requestInterruption(); + wait(500); + + m_pRTMSA->measurementData()->clear(); + m_pCircularBuffer->clear(); + + return true; +} + +//============================================================================================================= + +SCSHAREDLIB::AbstractPlugin::PluginType Fieldline::getType() const { + printLog("getType Fieldline"); + return SCSHAREDLIB::AbstractPlugin::PluginType::_ISensor; +} + +//============================================================================================================= + +QString Fieldline::getName() const { + return QString("Fieldline OPM"); +} + +//============================================================================================================= + +QWidget *Fieldline::setupWidget() { + return new FieldlineView(this); +} + +//============================================================================================================= + +void Fieldline::findIpAsync(std::vector& macList, + std::function&)> callback) { + std::thread ipFinder([macList, callback] { + IPFINDER::IpFinder ipFinder; + for (auto& mac : macList) { + ipFinder.addMacAddress(mac); + } + ipFinder.findIps(); + std::vector ipList; + ipList.reserve(ipFinder.macIpList.size()); + for (size_t i = 0; i < ipFinder.macIpList.size(); i++) { + ipList.emplace_back(ipFinder.macIpList[i].ip); + } + callback(ipList); + }); + ipFinder.detach(); +} + +//============================================================================================================= + +void Fieldline::run() +{ + // Eigen::MatrixXd matData; + // matData.resize(m_pFiffInfo->nchan, 200); + // + // while (!isInterruptionRequested()) { + // if (m_pCircularBuffer->pop(matData)) { + // if (!isInterruptionRequested()) { + // // matData = Eigen::MatrixXd::Random(m_pFiffInfo->nchan, 200); + // matData *= 4e-12; + // // msleep(200); + // m_pRTMSA->measurementData()->setValue(matData); + // } + // } + // } +} + +//============================================================================================================= + +QString Fieldline::getBuildInfo() { + printLog("getBuildInfo Fieldline"); + return QString(buildDateTime()) + QString(" - ") + QString(buildHash()); +} + +//============================================================================================================= + +void Fieldline::newData(double* data, size_t numChannels, size_t numSamples) +{ + Eigen::MatrixXd matData; + matData.resize(numChannels, numSamples); + for (size_t i = 0; i < numChannels; i++) { + for (size_t j = 0; j < numSamples; j++) { + matData(i, j) = data[i * numSamples + j]; + } + } + m_pRTMSA->measurementData()->setValue(matData); + // + // while (!m_pCircularBuffer->push(matData)) { + // printLog("Fieldline Plugin: Pushing data to circular buffer failed... Trying again."); + // } +} + +} // namespace FIELDLINEPLUGIN diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline.h b/src/applications/mne_scan/plugins/fieldline/fieldline.h new file mode 100644 index 00000000000..35310c06e7a --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline.h @@ -0,0 +1,135 @@ +//============================================================================================================= +/** + * @file fieldline.h + * @author Juan GarciaPrieto ; + * Gabriel B Motta ; + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Contains the declaration of the Fieldline plugin class. + * + */ + +#ifndef FIELDLINEPLUGIN_FIELDLINE_H +#define FIELDLINEPLUGIN_FIELDLINE_H + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include "fieldline/fieldline_global.h" + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +//============================================================================================================= +// FORWARD DECLARATION +//============================================================================================================= + +namespace SCMEASLIB { +class RealTimeMultiSampleArray; +} + +namespace FIFFLIB { +class FiffInfo; +} + +//============================================================================================================= +// DEFINE NAMESPACE FIELDLINEPLUGIN +//============================================================================================================= + +namespace FIELDLINEPLUGIN { + +class FieldlineAcqSystem; +class FieldlineView; +class IpFinder; + +//============================================================================================================ +/** + * The Fieldline class provides a MEG connector for receiving data from Fieldline box through its Python API. + * + * @brief The Fieldline class provides a MEG connector for receiving data from Fieldline API. + */ +class FIELDLINESHARED_EXPORT Fieldline : public SCSHAREDLIB::AbstractSensor +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "scsharedlib/1.0" FILE "fieldline.json") + Q_INTERFACES(SCSHAREDLIB::AbstractSensor) + + public: + //========================================================================================================= + // The plugin interface + Fieldline(); + + ~Fieldline(); + + virtual QSharedPointer clone() const; + + virtual void init(); + + virtual void unload(); + + virtual bool start(); + + virtual bool stop(); + + virtual AbstractPlugin::PluginType getType() const; + + virtual QString getName() const; + + virtual QWidget* setupWidget(); + + virtual QString getBuildInfo(); + + void findIpAsync(std::vector& macList, + std::function&)> callback); + FieldlineAcqSystem* m_pAcqSystem; + + void newData(double* data, size_t numChannels, size_t numSamples); + + protected: + virtual void run(); + private: + void initFiffInfo(); + + QSharedPointer > m_pRTMSA; /**< The RealTimeSampleArray to provide the EEG data.*/ + QSharedPointer m_pFiffInfo; /**< Fiff measurement info.*/ + QSharedPointer m_pCircularBuffer; /**< Holds incoming raw data. */ +}; + +} // namespace FIELDLINEPLUGIN + +#endif // FIELDLINEPLUGIN_FIELDLINE_H + diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline.json b/src/applications/mne_scan/plugins/fieldline/fieldline.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_acq_system/CMakeLists.txt b/src/applications/mne_scan/plugins/fieldline/fieldline_acq_system/CMakeLists.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_acqsystem.cpp b/src/applications/mne_scan/plugins/fieldline/fieldline_acqsystem.cpp new file mode 100644 index 00000000000..46dc4b721eb --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_acqsystem.cpp @@ -0,0 +1,989 @@ +//============================================================================================================= +/** + * @file fieldline_system_controller.cpp + * @author Juan GarciaPrieto ; + * Gabriel B Motta ; + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Contains the definition of the Fieldline class. + * + */ +#define PY_SSIZE_T_CLEAN +#include "Python.h" + +#define QUOTE(str) #str +#define EXPAND_AND_QUOTE(str) QUOTE(str) + +const char libPythonBugFix[] = "libpython" + EXPAND_AND_QUOTE(PYTHON_VERSION_MAJOR) + "." + EXPAND_AND_QUOTE(PYTHON_VERSION_MINOR) + ".so"; + +#include +#include +#include +#include +#include +#include + +#include + +#include "fieldline/fieldline_acqsystem.h" + +namespace FIELDLINEPLUGIN { + +const std::string resourcesPath(QCoreApplication::applicationDirPath().toStdString() + + "/../resources/mne_scan/plugins/fieldline/"); +const std::string entryFile(resourcesPath + "main.py"); + +struct GILHandler { + GILHandler():isAcquired(false) { + acquireGIL(); + } + + ~GILHandler() { + releaseGIL(); + } + + void acquireGIL() { + if (!isAcquired) { + m_gstate = PyGILState_Ensure(); + isAcquired = true; + } + } + void releaseGIL() { + if (isAcquired) { + PyGILState_Release(m_gstate); + isAcquired = false; + } + } + + bool isAcquired; + PyGILState_STATE m_gstate; +}; + +struct AcqOpener { + AcqOpener(PyObject* s): m_serviceInstance(s) { + openAcq(); + } + ~AcqOpener() { + closeAcq(); + } + + void openAcq() { + PyObject* openMethod = PyObject_GetAttrString(m_serviceInstance, "open"); + if (openMethod == NULL) + { + printLog("openMethod wrong!"); + } else{ + printLog("openMethod ok!"); + } + + PyObject* openMethodCall = PyObject_CallNoArgs(openMethod); + if (openMethodCall == NULL) + { + printLog("openMethodCall wrong!"); + } else{ + printLog("openMethodCall ok!"); + } + Py_XDECREF(openMethod); + Py_XDECREF(openMethodCall); + } + + void closeAcq() { + PyObject* closeMethod = PyObject_GetAttrString(m_serviceInstance, "close"); + if (closeMethod == NULL) + { + printLog("closeMethod wrong!"); + } else{ + printLog("closeMethod ok!"); + } + + PyObject* closeMethodCall = PyObject_CallNoArgs(closeMethod); + if (closeMethodCall == NULL) + { + printLog("closeMethodCall wrong!"); + } else{ + printLog("closeMethodCall ok!"); + } + + Py_XDECREF(closeMethod); + Py_XDECREF(closeMethodCall); + } + + PyObject* m_serviceInstance; + +}; + +bool restartFinished = false; +bool coarseZeroFinished = false; +bool fineZeroFinished = false; + +void callbackOnFinishedWhileRestart(int chassisId, int sensorId) { + std::cout << "Sensor: (" << chassisId << ", " << sensorId << ") Finished restart.\n"; +} + +void callbackOnFinishedWhileCoarseZero(int chassisId, int sensorId) { + std::cout << "Sensor: (" << chassisId << ", " << sensorId << ") Finished coarse zero.\n"; +} + +void callbackOnFinishedWhileFineZero(int chassisId, int sensorId) { + std::cout << "Sensor: (" << chassisId << ", " << sensorId << ") Finished fine zero.\n"; +} + +void callbackOnErrorWhileRestart(int chassisId, int sensorId, int errorId) { + std::cout << "Error while restarting sensor: (" << chassisId << ", " << sensorId << ") Code: " << errorId << ".\n"; +} + +void callbackOnErrorWhileCoarseZero(int chassisId, int sensorId, int errorId) { + std::cout << "Error while coarse zeroing sensor: (" << chassisId << ", " << sensorId << ") Code: " << errorId << ".\n"; +} + +void callbackOnErrorWhileFineZero(int chassisId, int sensorId, int errorId) { + std::cout << "Error while fine zeroing sensor: (" << chassisId << ", " << sensorId << ") Code: " << errorId << ".\n"; +} + +void callbackOnCompletionRestart() { + std::cout << "About to change restartFinished\n"; + restartFinished = true; + std::cout << "Restarting sensors, finished.\n"; +} + +void callbackOnCompletionCoarseZero() { + coarseZeroFinished = true; + std::cout << "Coarse zero sensors, finished.\n"; +} + +void callbackOnCompletionFineZero() { + fineZeroFinished = true; + std::cout << "Fine zero sensors, finished.\n"; +} + +static PyObject* callbackOnFinishedWhileRestart_py(PyObject* self, PyObject* args) { + int chassis, sensor; + if (!PyArg_ParseTuple(args, "ii", &chassis, &sensor)) { + return NULL; + } + callbackOnFinishedWhileRestart(chassis, sensor); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnFinishedWhileCoarseZero_py(PyObject* self, PyObject* args) { + int chassis, sensor; + if (!PyArg_ParseTuple(args, "ii", &chassis, &sensor)) { + return NULL; + } + callbackOnFinishedWhileCoarseZero(chassis, sensor); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnFinishedWhileFineZero_py(PyObject* self, PyObject* args) { + int chassis, sensor; + if (!PyArg_ParseTuple(args, "ii", &chassis, &sensor)) { + return NULL; + } + callbackOnFinishedWhileFineZero(chassis, sensor); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnErrorWhileRestart_py(PyObject* self, PyObject* args) { + int chassis, sensor, error; + if (!PyArg_ParseTuple(args, "iii", &chassis, &sensor, &error)) { + return NULL; + } + callbackOnErrorWhileRestart(chassis, sensor, error); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnErrorWhileCoarseZero_py(PyObject* self, PyObject* args) { + int chassis, sensor, error; + if (!PyArg_ParseTuple(args, "iii", &chassis, &sensor, &error)) { + return NULL; + } + callbackOnErrorWhileCoarseZero(chassis, sensor, error); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnErrorWhileFinzeZero_py(PyObject* self, PyObject* args) { + int chassis, sensor, error; + if (!PyArg_ParseTuple(args, "iii", &chassis, &sensor, &error)) { + return NULL; + } + callbackOnErrorWhileFineZero(chassis, sensor, error); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnCompletionRestart_py(PyObject* self, PyObject* args) { + Py_ssize_t arg_count = PyTuple_Size(args); + if (arg_count != 0) { + PyErr_SetString(PyExc_ValueError, "This function should be called with no arguments."); + return nullptr; + } + callbackOnCompletionRestart(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnCompletionCoarseZero_py(PyObject* self, PyObject* args) { + Py_ssize_t arg_count = PyTuple_Size(args); + if (arg_count != 0) { + PyErr_SetString(PyExc_ValueError, "This function should be called with no arguments."); + return nullptr; + } + callbackOnCompletionCoarseZero(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* callbackOnCompletionFineZero_py(PyObject* self, PyObject* args) { + Py_ssize_t arg_count = PyTuple_Size(args); + if (arg_count != 0) { + PyErr_SetString(PyExc_ValueError, "This function should be called with no arguments."); + return nullptr; + } + callbackOnCompletionFineZero(); + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef fieldlineCallbacksMethods[] = { + {"callbackOnFinishedWhileRestart", callbackOnFinishedWhileRestart_py, + METH_VARARGS, "Function to be called when a sensor has finished its restart."}, + {"callbackOnFinishedWhileCoarseZero", callbackOnFinishedWhileCoarseZero_py, + METH_VARARGS, "Function to be called when a sensor has finished its coarse zeroing."}, + {"callbackOnFinishedWhileFineZero", callbackOnFinishedWhileFineZero_py, + METH_VARARGS, "Function to be called when a sensor has finished its fine zeroing."}, + {"callbackOnErrorWhileRestart", callbackOnErrorWhileRestart_py, + METH_VARARGS, "Function to be called when a sensor has an error while restarting."}, + {"callbackOnErrorWhileCoarseZero", callbackOnErrorWhileCoarseZero_py, + METH_VARARGS, "Function to be called when a sensor has an error while coarse zeroing."}, + {"callbackOnErrorWhileFineZero", callbackOnErrorWhileFinzeZero_py, + METH_VARARGS, "Function to be called when a sensor has an error while fine zeroing"}, + {"callbackOnCompletionRestart", callbackOnCompletionRestart_py, + METH_VARARGS, "Function to be called when restarting has finished for all sensors."}, + {"callbackOnCompletionCoarseZero", callbackOnCompletionCoarseZero_py, + METH_VARARGS, "Function to be called when coarse zeroing has finished for all sensors."}, + {"callbackOnCompletionFineZero", callbackOnCompletionFineZero_py, + METH_VARARGS, "Function to be called when fine zeroing has finished for all sensors."}, + {NULL, NULL, 0, NULL} +}; + +static PyModuleDef FieldlineCallbacksModule = { + PyModuleDef_HEAD_INIT, "fieldline_callbacks", NULL, -1, fieldlineCallbacksMethods, + NULL, NULL, NULL, NULL +}; + +static PyObject* PyInit_fieldline_callbacks(void) { + return PyModule_Create(&FieldlineCallbacksModule); +} + +static FieldlineAcqSystem* acq_system(nullptr); + +static PyObject* dict_parser(PyObject* self, PyObject* args) { + PyObject* dataDict; + if (!PyArg_ParseTuple(args, "O", &dataDict)) { + printLog("problem with dataDict."); + PyErr_SetString(PyExc_TypeError, "data is not valid."); + return NULL; + } + + PyObject* data_frames = PyDict_GetItemString(dataDict, "data_frames"); + if (!data_frames || !PyDict_Check(data_frames)) { + printLog("Problem with data frames."); + PyErr_SetString(PyExc_TypeError, "data_frames is not a dictionary."); + return NULL; + } + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + while (PyDict_Next(data_frames, &pos, &key, &value)) { + PyObject* data = PyDict_GetItemString(value, "data"); + //printLog(std::string("pos: ") + std::to_string(pos)); + //printLog(std::string("value: ") + std::to_string((double) PyLong_AsLong(data))); + double sample = PyLong_AsDouble(data); + + Py_BEGIN_ALLOW_THREADS + acq_system->addSampleToSamplesColumn(pos-1, sample); + Py_END_ALLOW_THREADS + + if (pos == 3) + break; + } + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef callbacksParseMethods[] = { + {"dict_parser", dict_parser, METH_VARARGS, "Parse dictionary"}, + {NULL, NULL, 0, NULL} +}; + +static PyModuleDef CallbacksParseModule = { + PyModuleDef_HEAD_INIT, "callbacks_parsing", NULL, -1, callbacksParseMethods, + NULL, NULL, NULL, NULL +}; + +static PyObject* PyInit_callbacks_parsing(void) { + return PyModule_Create(&CallbacksParseModule); +} + +FieldlineAcqSystem::FieldlineAcqSystem(Fieldline* parent) +: m_pControllerParent(parent), + m_numSamplesPerBlock(200), + m_numSensors(3) +{ + printLog("Initializing Python"); + printLog(libPythonBugFix); + + void*const libBugFix = dlopen(libPythonBugFix, RTLD_LAZY | RTLD_GLOBAL); + + acq_system = this; + + Py_Initialize(); + + m_pThreadState = (void*)PyEval_SaveThread(); + + GILHandler g; + + preConfigurePython(); + + runPythonFile("./config2.py","bla"); + + m_pCallsModule = loadCModule("fieldline_callbacks", *(void*(*)(void))&PyInit_fieldline_callbacks); + + //PyObject* FieldlineModule = (PyObject*)loadModule("fieldline_api_mock.fieldline_service"); + PyObject* FieldlineModule = (PyObject*)loadModule("fieldline_api.fieldline_service"); + if (FieldlineModule == NULL) + { + printLog("fieldline module wrong!"); + } else{ + printLog("fieldline module ok!"); + } + + PyObject* fService = PyObject_GetAttrString(FieldlineModule, "FieldLineService"); + if (fService == NULL) + { + printLog("fservice wrong!"); + } else{ + printLog("fservice ok!"); + } + PyObject* ipList = Py_BuildValue("([ss])", "172.21.16.181", "172.21.16.139"); + PyObject* fServiceInstance = PyObject_CallObject(fService, ipList); + m_fServiceInstance = (void*) fServiceInstance; + if (fServiceInstance == NULL) + { + printLog("fServiceInstance wrong!"); + } else{ + printLog("fServiceInstance ok!"); + } + + Py_XDECREF(FieldlineModule); + Py_XDECREF(fService); + Py_XDECREF(ipList); + + initSampleArrays(); +} + +FieldlineAcqSystem::~FieldlineAcqSystem() +{ + GILHandler g; + + Py_XDECREF(m_fServiceInstance); + Py_XDECREF(m_pCallsModule); + + if (m_samplesBlock) { + delete[] m_samplesBlock; + } + Py_Finalize(); +} + +void* FieldlineAcqSystem::loadSensors() { + PyObject* loadSensors = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "load_sensors"); + PyObject* sensors = PyObject_CallNoArgs(loadSensors); + Py_XDECREF(loadSensors); + return (void*)sensors; +} + +void FieldlineAcqSystem::setCloseLoop() { + + GILHandler g; + + AcqOpener o((PyObject*)m_fServiceInstance); + + PyObject* setCloseLoopCall = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "set_closed_loop"); + PyObject* trueTuple = PyTuple_New(1); + PyTuple_SetItem(trueTuple, 0, Py_True); + PyObject* setCloseLoopCallResult = PyObject_CallObject(setCloseLoopCall, trueTuple); + if (setCloseLoopCallResult == NULL) + { + printLog("setCloseLoopCallResult wrong!"); + } else{ + printLog("setCloseLoopCallResult ok!"); + } + + Py_XDECREF(setCloseLoopCall); + Py_XDECREF(trueTuple); + Py_XDECREF(setCloseLoopCallResult); +} + +// for activating trigger sensor: service.restart_sensors({0:[0]}) +// for deactivating trigger sensors: service.turn_off_sensors({0:[0]}) +// typical calibration value: 2.27e-15 T +// + +void FieldlineAcqSystem::restartAllSensors() { + + restartFinished = false; + + GILHandler g; + + AcqOpener o((PyObject*)m_fServiceInstance); + + PyObject* sensors = (PyObject*) loadSensors(); + + PyObject* restartAllSensorsCall = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "restart_sensors"); + if (restartAllSensorsCall == NULL) + { + printLog("restart sensors broken"); + } else { + printLog("restartAllSensors ok!"); + } + + PyObject* callback_on_finished = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnFinishedWhileRestart"); + if (callback_on_finished == NULL) + { + printLog("callback on finished broken"); + } else { + printLog("callback restart ok!"); + } + + PyObject* callback_on_error = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnErrorWhileRestart"); + if (callback_on_error == NULL) + { + printLog("callback on error broken"); + } else { + printLog("callback error ok!"); + } + + PyObject* callback_on_completion = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnCompletionRestart"); + if (callback_on_completion == NULL) + { + printLog("callback on completion broken"); + } else { + printLog("callback completion ok!"); + } + + PyObject* argsRestart = PyTuple_New(4); + PyTuple_SetItem(argsRestart, 0, sensors); + PyTuple_SetItem(argsRestart, 1, callback_on_finished); + PyTuple_SetItem(argsRestart, 2, callback_on_error); + PyTuple_SetItem(argsRestart, 3, callback_on_completion); + + PyObject* resultRestart = PyObject_CallObject(restartAllSensorsCall, argsRestart); + if (resultRestart == NULL) + { + printLog("restart call broken"); + } else { + printLog("restart call ok!"); + } + + Py_BEGIN_ALLOW_THREADS + while (restartFinished == false) { + std::this_thread::sleep_for(std::chrono::milliseconds(600)); + } + Py_END_ALLOW_THREADS + + Py_XDECREF(sensors); + Py_XDECREF(restartAllSensorsCall); + Py_XDECREF(callback_on_finished); + Py_XDECREF(callback_on_error); + Py_XDECREF(callback_on_completion); + Py_XDECREF(argsRestart); + Py_XDECREF(resultRestart); +} + +void FieldlineAcqSystem::coarseZeroAllSensors() { + + coarseZeroFinished = false; + + GILHandler g; + + AcqOpener o((PyObject*)m_fServiceInstance); + + PyObject* sensors = (PyObject*) loadSensors(); + + PyObject* coarseZeroAllSensorsCall = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "coarse_zero_sensors"); + if (coarseZeroAllSensorsCall == NULL) + { + printLog("coarse zero sensors broken"); + } else { + printLog("coarseZeroAllSensors ok!"); + } + + PyObject* callback_on_finished_coarse_zero = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnFinishedWhileCoarseZero"); + if (callback_on_finished_coarse_zero == NULL) + { + printLog("callback on finished coarse zero broken"); + } else { + printLog("callback on finished coarse zero ok!"); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + PyObject* callback_on_error_coarse_zero = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnErrorWhileCoarseZero"); + if (callback_on_error_coarse_zero == NULL) + { + printLog("callback on error coarse zero broken"); + } else { + printLog("callback coarse zero error ok!"); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + PyObject* callback_on_completion_coarse_zero = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnCompletionCoarseZero"); + if (callback_on_completion_coarse_zero == NULL) + { + printLog("callback on completion coarse zero broken"); + } else { + printLog("callback coarse zero completion ok!"); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + PyObject* argsCoarseZero = PyTuple_New(4); + PyTuple_SetItem(argsCoarseZero, 0, sensors); + PyTuple_SetItem(argsCoarseZero, 1, callback_on_finished_coarse_zero); + PyTuple_SetItem(argsCoarseZero, 2, callback_on_error_coarse_zero); + PyTuple_SetItem(argsCoarseZero, 3, callback_on_completion_coarse_zero); + + PyObject* resultCoarseZero = PyObject_CallObject(coarseZeroAllSensorsCall, argsCoarseZero); + if (resultCoarseZero == NULL) + { + printLog("call coarsezero call broken"); + } else { + printLog("coarse zero call call ok!"); + } + + Py_BEGIN_ALLOW_THREADS + while (coarseZeroFinished == false) { + std::this_thread::sleep_for(std::chrono::milliseconds(600)); + } + Py_END_ALLOW_THREADS + + Py_XDECREF(sensors); + Py_XDECREF(coarseZeroAllSensorsCall); + Py_XDECREF(callback_on_finished_coarse_zero); + Py_XDECREF(callback_on_error_coarse_zero); + Py_XDECREF(callback_on_completion_coarse_zero); + Py_XDECREF(argsCoarseZero); + Py_XDECREF(resultCoarseZero); +} + +void FieldlineAcqSystem::fineZeroAllSensors() { + + fineZeroFinished = false; + + GILHandler g; + + AcqOpener o((PyObject*)m_fServiceInstance); + + PyObject* sensors = (PyObject*) loadSensors(); + + PyObject* fineZeroAllSensorsCall = PyObject_GetAttrString((PyObject*) m_fServiceInstance, "fine_zero_sensors"); + + if (fineZeroAllSensorsCall == NULL) + { + printLog("fine zero sensors broken"); + } else { + printLog("fineZeroAllSensors ok!"); + } + + PyObject* callback_on_finished_fine_zero = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnFinishedWhileFineZero"); + if (callback_on_finished_fine_zero == NULL) + { + printLog("callback on finished fine zero broken"); + } else { + printLog("callback on finished fine zero ok!"); + } + + PyObject* callback_on_error_fine_zero = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnErrorWhileFineZero"); + if (callback_on_error_fine_zero == NULL) + { + printLog("callback on error fine zero broken"); + } else { + printLog("callback fine zero error ok!"); + } + + PyObject* callback_on_completion_fine_zero = PyObject_GetAttrString((PyObject*)m_pCallsModule, "callbackOnCompletionFineZero"); + if (callback_on_completion_fine_zero == NULL) + { + printLog("callback on completion fine zero broken"); + } else { + printLog("callback fine zero completion ok!"); + } + + PyObject* argsFineZero = PyTuple_New(4); + PyTuple_SetItem(argsFineZero, 0, sensors); + PyTuple_SetItem(argsFineZero, 1, callback_on_finished_fine_zero); + PyTuple_SetItem(argsFineZero, 2, callback_on_error_fine_zero); + PyTuple_SetItem(argsFineZero, 3, callback_on_completion_fine_zero); + + PyObject* resultFineZero = PyObject_CallObject(fineZeroAllSensorsCall, argsFineZero); + if (resultFineZero == NULL) + { + printLog("call finezero call broken"); + } else { + printLog("fine zero call call ok!"); + } + + Py_BEGIN_ALLOW_THREADS + while (fineZeroFinished == false) { + std::this_thread::sleep_for(std::chrono::milliseconds(600)); + } + Py_END_ALLOW_THREADS + + Py_XDECREF(sensors); + Py_XDECREF(fineZeroAllSensorsCall); + Py_XDECREF(callback_on_finished_fine_zero); + Py_XDECREF(callback_on_error_fine_zero); + Py_XDECREF(callback_on_completion_fine_zero); + Py_XDECREF(argsFineZero); + Py_XDECREF(resultFineZero); +} + +void FieldlineAcqSystem::setDataCallback() { + + GILHandler g; + + AcqOpener opener((PyObject*)m_fServiceInstance); + + PyObject* readDataCall = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "read_data"); + if (readDataCall == NULL) { + printLog("problem readDataCall"); + } else { + printLog("readDataCall ok"); + } + + PyObject* parseCallbacksModule = (PyObject*)loadCModule("callbacks_parsing", *(void*(*)(void))&PyInit_callbacks_parsing); + if (parseCallbacksModule == NULL) + { + printLog("callbacks module wrong!"); + } else{ + printLog("callbacks module ok!"); + } + + PyObject* parserCallback = PyObject_GetAttrString(parseCallbacksModule, "dict_parser"); + if (parserCallback == NULL) { + printLog("problem parserCallback"); + } else { + printLog("parserCallback ok"); + } + PyObject* argsSetDataParser = PyTuple_New(1); + PyTuple_SetItem(argsSetDataParser, 0, parserCallback); + + PyObject* readDataReturn = PyObject_CallObject(readDataCall, argsSetDataParser); + if (readDataReturn == NULL) + { + printLog("readDataReturn bad"); + } else { + printLog("readDataReturn ok"); + } + Py_XDECREF(readDataCall); + Py_XDECREF(parseCallbacksModule); + Py_XDECREF(parserCallback); + Py_XDECREF(argsSetDataParser); + Py_XDECREF(readDataReturn); +} + +void FieldlineAcqSystem::startADC() { + + GILHandler g; + + PyObject* openMethod = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "open"); + if (openMethod == NULL) + { + printLog("openMethod wrong!"); + } else{ + printLog("openMethod ok!"); + } + + PyObject* openMethodCall = PyObject_CallNoArgs(openMethod); + if (openMethodCall == NULL) + { + printLog("openMethodCall wrong!"); + } else{ + printLog("openMethodCall ok!"); + } + Py_XDECREF(openMethod); + Py_XDECREF(openMethodCall); + + PyObject* readDataCall = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "read_data"); + if (readDataCall == NULL) { + printLog("problem readDataCall"); + } else { + printLog("readDataCall ok"); + } + + PyObject* parseCallbacksModule = (PyObject*)loadCModule("callbacks_parsing", *(void*(*)(void))&PyInit_callbacks_parsing); + if (parseCallbacksModule == NULL) + { + printLog("callbacks module wrong!"); + } else{ + printLog("callbacks module ok!"); + } + + PyObject* parserCallback = PyObject_GetAttrString(parseCallbacksModule, "dict_parser"); + if (parserCallback == NULL) { + printLog("problem parserCallback"); + } else { + printLog("parserCallback ok"); + } + PyObject* argsSetDataParser = PyTuple_New(1); + PyTuple_SetItem(argsSetDataParser, 0, parserCallback); + + PyObject* readDataReturn = PyObject_CallObject(readDataCall, argsSetDataParser); + if (readDataReturn == NULL) + { + printLog("readDataReturn bad"); + } else { + printLog("readDataReturn ok"); + } + + Py_XDECREF(readDataCall); + Py_XDECREF(parseCallbacksModule); + //Py_XDECREF(parserCallback); + Py_XDECREF(argsSetDataParser); + Py_XDECREF(readDataReturn); + + + PyObject* start_data = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "start_adc"); + PyObject* argsStartData = PyTuple_New(1); + PyObject* zeroArg = PyLong_FromLong(0); + PyTuple_SetItem(argsStartData, 0, zeroArg); + PyObject* startResult = PyObject_CallObject(start_data, argsStartData); + if (startResult == NULL) + { + printLog("startResult bad"); + } else { + printLog("startResult ok"); + } + + Py_XDECREF(argsStartData); + Py_XDECREF(zeroArg); + Py_XDECREF(startResult); +} + +void FieldlineAcqSystem::stopADC() { + GILHandler g; + + PyObject* stop_data = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "stop_adc"); + PyObject* argsstopData = PyTuple_New(1); + PyObject* stopArg = PyLong_FromLong(0); + + PyTuple_SetItem(argsstopData, 0, stopArg); + PyObject* stopResult = PyObject_CallObject(stop_data, argsstopData); + if (stopResult == NULL) + { + printLog("stopResult bad"); + } else { + printLog("stopResult ok"); + } + + PyObject* closeMethod = PyObject_GetAttrString((PyObject*)m_fServiceInstance, "close"); + if (closeMethod == NULL) + { + printLog("closeMethod wrong!"); + } else{ + printLog("closeMethod ok!"); + } + + PyObject* closeMethodCall = PyObject_CallNoArgs(closeMethod); + if (closeMethodCall == NULL) + { + printLog("closeMethodCall wrong!"); + } else{ + printLog("closeMethodCall ok!"); + } + + Py_XDECREF(closeMethod); + Py_XDECREF(closeMethodCall); + + Py_XDECREF(argsstopData); + Py_XDECREF(stopArg); + Py_XDECREF(stopResult); +} + +void* FieldlineAcqSystem::loadModule(const char* moduleName) +{ + GILHandler g; + + PyObject* pModule = PyImport_ImportModule(moduleName); + if (pModule == NULL) { + printLog(std::string("Error loading module ").append(moduleName).append(".").c_str()); + PyErr_Print(); + } + + Py_XDECREF(pModule); + + return (void*)pModule; +} + +void* FieldlineAcqSystem::loadCModule(const char* moduleName, void*(*moduleInitFunc)(void)) +{ + GILHandler g; + + if (Py_IsInitialized()) + { + PyImport_AddModule(moduleName); + PyObject* PyModule = (PyObject*)moduleInitFunc(); + PyObject* sys_modules = PyImport_GetModuleDict(); + PyDict_SetItemString(sys_modules, moduleName, PyModule); + Py_XDECREF(PyModule); + } else { + PyImport_AppendInittab(moduleName, (PyObject*(*)(void)) moduleInitFunc); + } + + return loadModule(moduleName); +} + +void FieldlineAcqSystem::callFunctionAsync(const char* moduleName, const char* funcName) +{ + std::thread t([this, moduleName, funcName]() { + this->callFunction(moduleName, funcName); + }); + t.detach(); +} + +void FieldlineAcqSystem::callFunction(const std::string& moduleName, const std::string& funcName) +{ + GILHandler g; + + PyObject* pModule = PyImport_GetModule(PyUnicode_FromString(moduleName.c_str())); + if (pModule == NULL) { + printLog(std::string("Module ").append(moduleName).append(" has not been imported yet.").c_str()); + printLog(std::string("Attempting to import the module ").append(moduleName).append(".").c_str()); + pModule = PyImport_ImportModule(moduleName.c_str()); + if (pModule == NULL) { + PyErr_Print(); + printLog(std::string("Import failed. Can't find ").append(moduleName).append(".").c_str()); + printLog("Check the Path."); + } + } + + PyObject* pFunc = PyObject_GetAttrString(pModule, funcName.c_str()); + if (pFunc == NULL) { + printLog(std::string("Error finding function: ").append(funcName).c_str()); + PyErr_Print(); + } + + PyObject* pResult = PyObject_CallObject(pFunc, NULL); + if (pResult == NULL) { + printLog(std::string("Error calling function: ").append(funcName).c_str()); + PyErr_Print(); + } + + Py_XDECREF(pModule); + Py_XDECREF(pFunc); + Py_XDECREF(pResult); +} + +void FieldlineAcqSystem::preConfigurePython() const +{ + PyObject* sys = PyImport_ImportModule("sys"); + PyObject* versionInfo = PyObject_GetAttrString(sys, "version_info"); + PyObject* versionInfoMajor = PyObject_GetAttrString(versionInfo, "major"); + PyObject* versionInfoMinor = PyObject_GetAttrString(versionInfo, "minor"); + const std::string pythonVer(std::to_string(PyLong_AsLong(versionInfoMajor)) + \ + "." + std::to_string(PyLong_AsLong(versionInfoMinor))); + Py_XDECREF(versionInfoMajor); + Py_XDECREF(versionInfoMinor); + Py_XDECREF(versionInfo); + + PyObject* path = PyObject_GetAttrString(sys, "path"); + PyObject* resourcesObj = PyUnicode_FromString(resourcesPath.c_str()); + PyList_Insert(path, 0, resourcesObj); + Py_XDECREF(resourcesObj); + + const std::string pathVenvMods(resourcesPath + "venv/lib/python" + pythonVer + "/site-packages/"); + printLog(pathVenvMods); + PyList_Insert(path, 1, PyUnicode_FromString(pathVenvMods.c_str())); + Py_XDECREF(path); + + Py_XDECREF(sys); +} + +void FieldlineAcqSystem::runPythonFile(const char* file, const char* comment) const +{ + FILE *py_file = fopen(file, "r"); + if (py_file) + { + PyObject* global_dict = PyDict_New(); + PyObject* local_dict = PyDict_New(); + PyObject* result = PyRun_File(py_file, comment, Py_file_input, global_dict, local_dict); + Py_XDECREF(global_dict); + Py_XDECREF(local_dict); + Py_XDECREF(result); + fclose(py_file); + } +} + +void FieldlineAcqSystem::initSampleArrays() +{ + m_samplesBlock = new double[m_numSensors * m_numSamplesPerBlock]; +} + +void FieldlineAcqSystem::addSampleToSamplesColumn(size_t sensorIdx, double value) +{ + static size_t sampleIdx = 0; + std::cout << "Value: " << 2.27e-15 * value << "\n"; + std::cout.flush(); + m_samplesBlock[sensorIdx * m_numSamplesPerBlock + sampleIdx] = 2.27e-15 * value; + if (sensorIdx == m_numSensors -1) { + sampleIdx++; + if (sampleIdx == m_numSamplesPerBlock) { + sampleIdx = 0; + m_pControllerParent->newData(m_samplesBlock, m_numSensors, m_numSamplesPerBlock); + } + } +} + +} // namespace FIELDLINEPLUGIN + diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_acqsystem.h b/src/applications/mne_scan/plugins/fieldline/fieldline_acqsystem.h new file mode 100644 index 00000000000..04c9c92f051 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_acqsystem.h @@ -0,0 +1,85 @@ +//============================================================================================================= +/** + * @file fieldline_system_controller.h + * @author Juan GarciaPrieto ; + * Gabriel B Motta ; + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Contains the declaration of the Fieldline Acquisition System Controller class. + * + */ +#ifndef FIELDLINEPLUGIN_FIELDLINEACQSYSTEM_H +#define FIELDLINEPLUGIN_FIELDLINEACQSYSTEM_H + +#include + +#include "fieldline/fieldline.h" + +namespace FIELDLINEPLUGIN { + +class FieldlineAcqSystem { + public: + explicit FieldlineAcqSystem(Fieldline* parent); + + ~FieldlineAcqSystem(); + + void callFunctionAsync(const char* moduleName, const char* funcName); + + void callFunction(const std::string& moduleName, const std::string& funcName); + + void startADC(); + void stopADC(); + Fieldline* m_pControllerParent; + void initSampleArrays(); + void addSampleToSamplesColumn(size_t sensorIdx, double value); + void restartAllSensors(); + void coarseZeroAllSensors(); + void fineZeroAllSensors(); + void setDataCallback(); + void setCloseLoop(); + + private: + void preConfigurePython() const; + void runPythonFile(const char* file, const char* comment) const; + void* loadModule(const char* moduleName); + void* loadCModule(const char* moduleName, void* (*moduleInitFunc)(void)); + void* loadSensors(); + + size_t m_numSamplesPerBlock; + size_t m_numSensors; + void* m_fServiceInstance; + void* m_pThreadState; + void* m_pCallsModule; + void* m_pOpenMethod; + double* m_samplesBlock; + double* m_samplesBlock2; +}; + +} // namespace FIELDLINEPLUGIN + +#endif // FIELDLINEPLUGIN_FIELDLINEACQSYSTEMCONTROLLER_H + diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_definitions.h b/src/applications/mne_scan/plugins/fieldline/fieldline_definitions.h new file mode 100644 index 00000000000..022917d74a6 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_definitions.h @@ -0,0 +1,73 @@ +//============================================================================================================= +/** + * @file fieldline_view.h + * @author Juan Garcia-Prieto + * Gabriel Motta + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineView class declaration. + * + */ + +#ifndef FIELDLINE_FIELDLINEDEFINITIONS_H +#define FIELDLINE_FIELDLINEDEFINITIONS_H + +namespace FIELDLINEPLUGIN { + +enum class FieldlineDataType { + DATA_BZ = 0, + DATA_BY = 1, + DATA_BX = 2 +}; + +enum class FieldLineWaveType { + WAVE_OFF = 0, + WAVE_RAMP = 1, + WAVE_SINE = 2, +}; + +enum class FieldlineSensorStatusType { + SENSOR_OFF = 0, + SENSOR_RESTARTING = 1, + SENSOR_RESTARTED = 2, + SENSOR_COARSE_ZEROING = 3, + SENSOR_COARSE_ZEROED = 4, + SENSOR_FINE_ZEROING = 5, + SENSOR_FINE_ZEROED = 6, + SENSOR_ERROR = 7, + SENSOR_READY = 8, +}; + +enum class FieldLineConnectStatusType { + CONNECT_OK = 0, + CONNECT_NOT_READY = 1, + CONNECT_CHASSIS_MISSING = 2, +}; + +} // namespace FIELDLINEPLUGIN + +#endif // FIELDLINE_FIELDLINEDEFINITIONS_H diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_global.cpp b/src/applications/mne_scan/plugins/fieldline/fieldline_global.cpp new file mode 100644 index 00000000000..aaf03c2cb6e --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_global.cpp @@ -0,0 +1,60 @@ +//============================================================================================================= +/** + * @file fieldline_global.cpp + * @author Juan G Prieto ; + * Gabriel B Motta ; + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Fieldline plugin global definitions. + * + */ + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include "fieldline_global.h" + +//============================================================================================================= +// DEFINE METHODS +//============================================================================================================= + +const char* FIELDLINEPLUGIN::buildDateTime() { return UTILSLIB::dateTimeNow();} + +//============================================================================================================= + +const char* FIELDLINEPLUGIN::buildHash() { return UTILSLIB::gitHash();} + +//============================================================================================================= + +const char* FIELDLINEPLUGIN::buildHashLong() { return UTILSLIB::gitHashLong();} + +void FIELDLINEPLUGIN::printLog(const std::string& str) { + std::cout << str << "\n"; + std::cout.flush(); +} + diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_global.h b/src/applications/mne_scan/plugins/fieldline/fieldline_global.h new file mode 100644 index 00000000000..0807588cc56 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_global.h @@ -0,0 +1,90 @@ +//============================================================================================================= +/** + * @file fieldlilne_global.h + * @author Juan GarciaPrieto ; + * Gabriel B Motta ; + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Contains the Fieldline plugin library export/import macros. + * + */ + +#ifndef FIELDLINE_GLOBAL_H +#define FIELDLINE_GLOBAL_H + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include +#include + +#include + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include + +//============================================================================================================= +// PREPROCESSOR DEFINES +//============================================================================================================= + +#if defined(SCAN_FIELDLINE_PLUGIN) +# define FIELDLINESHARED_EXPORT Q_DECL_EXPORT +#else +# define FIELDLINESHARED_EXPORT Q_DECL_IMPORT +#endif + +namespace FIELDLINEPLUGIN{ + +//============================================================================================================= +/** + * Returns build date and time. + */ +FIELDLINESHARED_EXPORT const char* buildDateTime(); + +//============================================================================================================= +/** + * Returns abbreviated build git hash. + */ +FIELDLINESHARED_EXPORT const char* buildHash(); + +//============================================================================================================= +/** + * Returns full build git hash. + */ +FIELDLINESHARED_EXPORT const char* buildHashLong(); + +void printLog(const std::string& str); + +} // namespace FIELDLINEPLUGIN + + + +#endif // FIELDLINE_GLOBAL_H diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_view.cpp b/src/applications/mne_scan/plugins/fieldline/fieldline_view.cpp new file mode 100644 index 00000000000..81a6235da9d --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_view.cpp @@ -0,0 +1,395 @@ +//============================================================================================================= +/** + * @file fieldline_view.cpp + * @author Juan Garcia-Prieto + * Gabriel Motta + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineView class definition. + * + */ + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include +#include +#include +#include +#include +#include + +#include +#include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include "fieldline/fieldline.h" +#include "fieldline/fieldline_view.h" +#include "fieldline/fieldline_view_chassis.h" +#include "fieldline/fieldline_acqsystem.h" +#include "formfiles/ui_fieldline_view.h" + +//============================================================================================================= +// DEFINE STATIC METHODS +//============================================================================================================= + +//============================================================================================================= +// DEFINE MEMBER METHODS +//============================================================================================================= + + +namespace FIELDLINEPLUGIN { + +FieldlineView::FieldlineView(Fieldline *parent) +: m_pFieldlinePlugin(parent), + m_pUi(new Ui::uiFieldlineView) +{ + m_pUi->setupUi(this); + initTopMenu(); + initAcqSystem(2); + connect(this, &FieldlineView::updateMacIpTable, + this, &FieldlineView::updateMacIpTableItem); +} + +FieldlineView::~FieldlineView() +{ + delete m_pUi; +} + +void FieldlineView::initTopMenu() +{ + m_pUi->topMenuFrame->setEnabled(true); + m_pUi->numChassisSpinBox->setMinimum(0); + m_pUi->numChassisSpinBox->setMaximum(6); + m_pUi->numChassisSpinBox->setValue(0); + m_pMacIpTable = new QTableWidget(this); + m_pMacIpTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + m_pMacIpTable->verticalHeader()->setSectionHidden(0, true); + + m_pMacIpTable->setColumnCount(2); + m_pMacIpTable->setHorizontalHeaderLabels(QStringList({"Mac", "IP"})); + m_pMacIpTable->horizontalHeader()->setSortIndicatorShown(false); + + QVBoxLayout* macIpTableLayout = qobject_cast(m_pUi->ipMacFrame->layout()); + macIpTableLayout->insertWidget(0, m_pMacIpTable); + + QObject::connect(m_pUi->numChassisSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &FieldlineView::setNumRowsIpMacFrame); + QObject::connect(m_pUi->findIPsBtn, &QPushButton::clicked, + this, &FieldlineView::findIps); + QObject::connect(m_pUi->connectBtn, &QPushButton::clicked, + this, &FieldlineView::connectToAcqSys); + QObject::connect(m_pUi->disconnectBtn, &QPushButton::clicked, + this, &FieldlineView::disconnectFromAcqSys); +} + +void FieldlineView::findIps() { + std::vector macList; + macList.reserve(m_pMacIpTable->rowCount()); + for (int i = 0; i < m_pMacIpTable->rowCount(); i++) { + macList.emplace_back(m_pMacIpTable->item(i, 0)->text().toStdString()); + } + auto callback = [this](std::vector& ipList) { + for (size_t i = 0; i < ipList.size(); i++) { + emit this->updateMacIpTable(i, 1, QString::fromStdString(ipList[i])); + } + }; + m_pFieldlinePlugin->findIpAsync(macList, callback); +} + +void FieldlineView::updateMacIpTableItem(int row, int col, const QString& str) { + m_pMacIpTable->item(row, col)->setText(str); +} + +void FieldlineView::setNumRowsIpMacFrame(int numRows) +{ + m_pMacIpTable->setRowCount(numRows); + QStringList vertLabels; + for ( int i = 0; i < numRows; i++ ) { + vertLabels.push_back(QString::number(i)); + } + m_pMacIpTable->setVerticalHeaderLabels(vertLabels); + + if (numRows > 0) { + m_pMacIpTable->setSortingEnabled(false); + m_pMacIpTable->setItem(numRows-1, 0, new QTableWidgetItem("a0:b1:c2:d3:e4:f5")); + m_pMacIpTable->setItem(numRows-1, 1, new QTableWidgetItem("0.0.0.0")); + m_pMacIpTable->item(numRows-1, 0)->setToolTip((QString("Doubleclick to find the IP."))); + m_pMacIpTable->setSortingEnabled(true); + } + // QVBoxLayout* vertIpMacLayout = qobject_cast(m_pUi->ipMacFrame->layout()); +// if ( i < m_ipMacList.size()) +// { +// vertIpMacLayout->removeItem(m_ipMacList.back()); +// delete m_ipMacList.back(); +// m_ipMacList.pop_back(); +// } +// if ( i > m_ipMacList.size()) +// { +// QHBoxLayout* ipMacLayout = new QHBoxLayout(m_pUi->ipMacFrame); +// QLineEdit* ip = new QLineEdit("0.0.0.0"); +// ip->setEnabled(false); +// QLineEdit* macAddr = new QLineEdit("AF:70:04:21:2D:28"); +// ipMacLayout->addWidget(macAddr); +// ipMacLayout->addWidget(ip); +// vertIpMacLayout->insertLayout(m_ipMacList.size() + 1, ipMacLayout); +// m_ipMacList.push_back(ipMacLayout); +// } +} + +void FieldlineView::connectToAcqSys() { + printLog("connect!\n"); +} + +void FieldlineView::disconnectFromAcqSys() { + printLog("disconnect!\n"); + //generate list of mac addresses + //call class finder. + // retrieve list of ips and set variable with it. +} + +void FieldlineView::initAcqSystemTopButtons() +{ + QHBoxLayout* acqSystemTopBtnMenuLayout = qobject_cast(m_pUi->acqSystemTopButtonsFrame->layout()); + + QPushButton* button = new QPushButton(QString("Start"), m_pUi->acqSystemTopButtonsFrame); + QObject::connect(button, &QPushButton::clicked, this, &FieldlineView::startAllSensors); + acqSystemTopBtnMenuLayout->insertWidget(0, button); + + button = new QPushButton(QString("Stop"), m_pUi->acqSystemTopButtonsFrame); + QObject::connect(button, &QPushButton::clicked, this, &FieldlineView::stopAllSensors); + acqSystemTopBtnMenuLayout->insertWidget(1, button); + + button = new QPushButton(QString("Auto-Tune"), m_pUi->acqSystemTopButtonsFrame); + QObject::connect(button, &QPushButton::clicked, this, &FieldlineView::autoTuneAllSensors); + acqSystemTopBtnMenuLayout->insertWidget(3, button); // after the spacer + + button = new QPushButton(QString("Restart Sensors"), m_pUi->acqSystemTopButtonsFrame); + QObject::connect(button, &QPushButton::clicked, this, &FieldlineView::restartAllSensors); + acqSystemTopBtnMenuLayout->insertWidget(4, button); + + button = new QPushButton(QString("Coarse Zero"), m_pUi->acqSystemTopButtonsFrame); + QObject::connect(button, &QPushButton::clicked, this, &FieldlineView::coarseZeroAllSensors); + acqSystemTopBtnMenuLayout->insertWidget(5, button); + + button = new QPushButton(QString("Fine Zero"), m_pUi->acqSystemTopButtonsFrame); + QObject::connect(button, &QPushButton::clicked, this, &FieldlineView::fineZeroAllSensors); + acqSystemTopBtnMenuLayout->insertWidget(6, button); +} + +void FieldlineView::initAcqSystem(int numChassis) +{ + // QHBoxLayout* acqSystemTopBtnMenuLayout = qobject_cast(m_pUi->acqSystemTopButtonsFrame->layout()); + initAcqSystemTopButtons(); + QVBoxLayout* acqSystemRackLayout = qobject_cast(m_pUi->chassisRackFrame->layout()); + for (int i = 0; i < numChassis; i++) { + FieldlineViewChassis* pChassis = new FieldlineViewChassis(this, i); + acqSystemRackLayout->insertWidget(i, pChassis); + m_pAcqSystem.push_back(pChassis); + } +} + + +// void FieldlineView::initAcqSystem() +// { +// QVBoxLayout* rackFrameLayout = qobject_cast(m_pUi->fieldlineRackFrame->layout()); +// for (int i = 0; i < numChassis; i++) +// { +// FieldlineChassis* chassis = new FieldlineViewChassis(chans[i]); +// rackFrameLayout->addWidget(chassis); +// } +// } + +void FieldlineView::startAllSensors() { + printLog("startAllSensors"); + // m_pFieldlinePlugin->m_pAcqSystem->callFunctionAsync("callback", "start"); +// m_pFieldlinePlugin->m_pAcqSystem->startADC(); +} + +void FieldlineView::stopAllSensors() { + printLog("stopAllSensors"); + // m_pFieldlinePlugin->m_pAcqSystem->callFunctionAsync("callback", "stop"); + m_pFieldlinePlugin->m_pAcqSystem->stopADC(); +} + +void FieldlineView::autoTuneAllSensors() { + printLog("autoTuneAllSensors"); + //m_pFieldlinePlugin->m_pAcqSystem->setCallback(); +} + +void FieldlineView::restartAllSensors() { + printLog("restartAllSensors"); + std::thread t([this]{ + m_pFieldlinePlugin->m_pAcqSystem->restartAllSensors(); + }); + t.detach(); +} + +void FieldlineView::coarseZeroAllSensors() { + printLog("coarseZeroAllSensors"); + std::thread t([this]{ + m_pFieldlinePlugin->m_pAcqSystem->coarseZeroAllSensors(); + }); + t.detach(); +} + +void FieldlineView::fineZeroAllSensors() { + printLog("fineZeroAllSensors"); + std::thread t([this]{ + m_pFieldlinePlugin->m_pAcqSystem->fineZeroAllSensors(); + }); + t.detach(); +} + + +//void FieldlineView::setChannelState(size_t chassis_i, size_t chan_i) +//{ +//} + +//statish FieldlineView::getChannelState(size_t chassis_i, size_t chan_i) +//{ +//} + +//statish FieldlineView::setAllChannelState(size_t chassis_i, statish) +//{ +//} + +} // namespace FIELDLINEPLUGIN + +// +// //============================================================================================================= +// +// +// void FieldlineView::clear() +// { +// for(auto* c : chassis){ +// ui->frame->layout()->removeWidget(c); +// c->deleteLater(); +// } +// }; +// +// //============================================================================================================= +// +// void FieldlineView::setColor(size_t chassis_id, size_t chan_num, const QColor& color) +// { +// if(chassis_id >= chassis.size()){ +// return; +// } +// chassis.at(chassis_id)->setColor(chan_num, color); +// } +// +// //============================================================================================================= +// +// void FieldlineView::setColor(size_t chassis_id, size_t chan_num, const QColor& color, bool blinking) +// { +// if(chassis_id >= chassis.size()){ +// return; +// } +// chassis.at(chassis_id)->setColor(chan_num, color, blinking); +// } +// +// //============================================================================================================= +// +// void FieldlineView::setChassisColor(size_t chassis_id, const QColor& color) +// { +// if(chassis_id >= chassis.size()){ +// return; +// } +// chassis.at(chassis_id)->setColor(color); +// } +// +// //============================================================================================================= +// +// void FieldlineView::setChassisColor(size_t chassis_id, const QColor& color, bool blinking) +// { +// if(chassis_id >= chassis.size()){ +// return; +// } +// chassis.at(chassis_id)->setColor(color, blinking); +// } +// +// //============================================================================================================= +// +// void FieldlineView::setAllColor(const QColor& color) +// { +// for(auto* c : chassis){ +// c->setColor(color); +// } +// } +// +// //============================================================================================================= +// +// void FieldlineView::setAllColor(const QColor& color, bool blinking) +// { +// for(auto* c : chassis){ +// c->setColor(color, blinking); +// } +// } +// +// //============================================================================================================= +// +// void FieldlineView::setBlinkState(size_t chassis_id, size_t chan_num, bool blinking) +// { +// if(chassis_id >= chassis.size()){ +// return; +// } +// chassis.at(chassis_id)->setBlinkState(chan_num, blinking); +// } +// +// //============================================================================================================= +// +// void FieldlineView::setChassisBlinkState(size_t chassis_id, bool blinking) +// { +// if(chassis_id >= chassis.size()){ +// return; +// } +// chassis.at(chassis_id)->setBlinkState(blinking); +// } +// +// //============================================================================================================= +// +// void FieldlineView::setAllBlinkState(bool blinking) +// { +// for(auto* c : chassis){ +// c->setBlinkState(blinking); +// } +// } +// +// //============================================================================================================= +// +// void FieldlineView::setDefaultNumchans(int num_chans) +// { +// default_num_chans = num_chans; +// } diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_view.h b/src/applications/mne_scan/plugins/fieldline/fieldline_view.h new file mode 100644 index 00000000000..38b07476f77 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_view.h @@ -0,0 +1,116 @@ +//============================================================================================================= +/** + * @file fieldline_view.h + * @author Juan Garcia-Prieto + * Gabriel Motta + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineView class declaration. + * + */ + +#ifndef FIELDLINE_FIELDLINEVIEW_H +#define FIELDLINE_FIELDLINEVIEW_H + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include +#include + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// FORWARD DECLARATIONS +//============================================================================================================= + +namespace Ui { +class uiFieldlineView; +} + +//============================================================================================================= +// DEFINE NAMESPACE DISPLIB +//============================================================================================================= +class QHBoxLayout; +class QTableWidget; +class QTableWidgetItem; + +namespace FIELDLINEPLUGIN { + +class Fieldline; +class FieldlineViewChassis; + +//============================================================================================================= + +class FieldlineView : public QWidget +{ + Q_OBJECT + +public: + explicit FieldlineView(Fieldline* parent); + ~FieldlineView(); + signals: + void updateMacIpTable(int row, int col, const QString& str); + + private: + void initAcqSystem(int numChassis); + void initTopMenu(); + + void updateMacIpTableItem(int row, int col, const QString& str); + + void initAcqSystemCallbacks(); + void initAcqSystemTopButtons(); + void setNumRowsIpMacFrame(int i); + + void findIps(); + void connectToAcqSys(); + void disconnectFromAcqSys(); + void startAllSensors(); + void stopAllSensors(); + void autoTuneAllSensors(); + void restartAllSensors(); + void coarseZeroAllSensors(); + void fineZeroAllSensors(); + + Fieldline* m_pFieldlinePlugin; + Ui::uiFieldlineView* m_pUi; + QTableWidget* m_pMacIpTable; + std::vector m_pAcqSystem; +}; + +} // namespace FIELDLINEPLUGIN + +#endif // FIELDLINE_UI_VIEW_H diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_view_chassis.cpp b/src/applications/mne_scan/plugins/fieldline/fieldline_view_chassis.cpp new file mode 100644 index 00000000000..b53d785467b --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_view_chassis.cpp @@ -0,0 +1,138 @@ +//============================================================================================================= +/** + * @file fieldline_view_chassis.cpp + * @author Juan Garcia-Prieto + * Gabriel Motta + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineView class definition. + * + */ + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include + +// #include "fieldlin/fieldline.h" +#include "fieldline/fieldline_view.h" +#include "fieldline/fieldline_view_chassis.h" +#include "fieldline/fieldline_view_sensor.h" +#include "formfiles/ui_fieldline_view_chassis.h" + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= +// +// #include +// #include +// #include +// #include +// #include +// #include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// USED NAMESPACES +//============================================================================================================= + +//============================================================================================================= +// DEFINE STATIC METHODS +//============================================================================================================= + +//============================================================================================================= +// DEFINE MEMBER METHODS +//============================================================================================================= + +namespace FIELDLINEPLUGIN { + +//============================================================================================================= + +FieldlineViewChassis::FieldlineViewChassis(FieldlineView *parent, int num ) +: QWidget(parent), + m_pFieldlineView(parent), + m_pUi(new Ui::uiFieldlineViewChassis), + chassisNum(num), + numSensors(16), + sensorLedUpdateFreq(150) + { + m_pUi->setupUi(this); + + std::string chassisName("Fieldline Chassis "); + chassisName += std::to_string(chassisNum); + m_pUi->chassisName->setText(QString::fromStdString(chassisName)); + createSensors(); + chassisActive.store(true); + chassisRunning.store(true); + updateSensorLedThread = std::thread(&FieldlineViewChassis::updateSensorLeds, this); +} + +void FieldlineViewChassis::updateSensorLeds() +{ + while (chassisRunning.load()) { + while (chassisActive.load()) { + for (auto pSensor : m_pSensors) { + pSensor->updateLedState(); + } + std::this_thread::sleep_for(sensorLedUpdateFreq); + } + } +} + +void FieldlineViewChassis::createSensors() +{ + QHBoxLayout* sensorLayout = qobject_cast(m_pUi->sensorFrame->layout()); + for (int i = 0; i < numSensors; i++) { + FieldlineViewSensor* pSensor = new FieldlineViewSensor(this, i); + sensorLayout->insertWidget(i, pSensor); + m_pSensors.push_back(pSensor); + } +} + +FieldlineViewChassis::~FieldlineViewChassis() +{ + // delete m_pUi; + chassisActive.store(false); + chassisRunning.store(false); + if (updateSensorLedThread.joinable()) { + updateSensorLedThread.join(); + } +} + +void FieldlineViewChassis::setActive() +{ + chassisActive.store(true); +} + + + +} // namespace FIELDLINEPLUGIN + diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_view_chassis.h b/src/applications/mne_scan/plugins/fieldline/fieldline_view_chassis.h new file mode 100644 index 00000000000..0fe524c2336 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_view_chassis.h @@ -0,0 +1,102 @@ +//============================================================================================================= +/** + * @file fieldline_view_chassis.h + * @author Juan Garcia-Prieto + * Gabriel Motta + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineView class declaration. + * + */ + +#ifndef FIELDLINEPLUGIN_FIELDLINEVIEWCHASSIS_H +#define FIELDLINEPLUGIN_FIELDLINEVIEWCHASSIS_H + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include +#include +#include +#include + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// FORWARD DECLARATIONS +//============================================================================================================= + +namespace Ui { +class uiFieldlineViewChassis; +} + +//============================================================================================================= +// DEFINE NAMESPACE DISPLIB +//============================================================================================================= + +namespace FIELDLINEPLUGIN { + +class FieldlineView; +class FieldlineViewSensor; + +//============================================================================================================= + +class FieldlineViewChassis : public QWidget +{ + Q_OBJECT + + public: + explicit FieldlineViewChassis(FieldlineView *parent, int num); + ~FieldlineViewChassis(); + void setActive(); + + private: + void createSensors(); + void updateSensorLeds(); + FieldlineView *m_pFieldlineView; + Ui::uiFieldlineViewChassis* m_pUi; + std::vector m_pSensors; + int chassisNum; + int numSensors; + std::chrono::milliseconds sensorLedUpdateFreq; + std::thread updateSensorLedThread; + std::atomic chassisActive; + std::atomic chassisRunning; +}; + +} // namespace FIELDLINEPLUGIN + +#endif // FIELDLINEPLUGIN_FIELDLINEVIEWCHASSIS_H diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_view_sensor.cpp b/src/applications/mne_scan/plugins/fieldline/fieldline_view_sensor.cpp new file mode 100644 index 00000000000..fc7f9d5b01f --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_view_sensor.cpp @@ -0,0 +1,155 @@ +//============================================================================================================= +/** + * @file fieldline_view_sensor.cpp + * @author Juan Garcia-Prieto + * Gabriel Motta + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineView Sensor class declaration. + * + */ + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + + +#include + +// #include "fieldline/fieldline.h" +// #include "fieldline/fieldline_view.h" +#include "fieldline/fieldline_view_sensor.h" +#include "fieldline/fieldline_view_chassis.h" +#include "formfiles/ui_fieldline_view_sensor.h" + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= +// +#include +#include +#include +// #include +// #include +// #include +// #include +// #include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// USED NAMESPACES +//============================================================================================================= + +//============================================================================================================= +// DEFINE STATIC METHODS +//============================================================================================================= + +//============================================================================================================= +// DEFINE MEMBER METHODS +//============================================================================================================= + + +namespace FIELDLINEPLUGIN { + +//============================================================================================================= + +FieldlineViewSensor::FieldlineViewSensor(FieldlineViewChassis *parent, int index) +: QWidget(parent), + m_pFieldlineViewChassis(parent), + m_sensorIndex(index), + m_sensorState(FieldlineSensorStatusType::SENSOR_OFF), + ledChangePeriod(600), + ledState(LedState::A), + m_pUi(new Ui::uiFieldlineViewSensor), + m_pScene(new QGraphicsScene) +{ + updateTimeStamp(); + m_pUi->setupUi(this); + + m_pUi->ledQGraphView->setStyleSheet("background:transparent"); + m_pUi->ledQGraphView->horizontalScrollBar()->hide(); + m_pUi->ledQGraphView->verticalScrollBar()->hide(); + m_pUi->label->setText(QString::number(index)); + m_pUi->ledQGraphView->setScene(m_pScene); + + m_pCircleLed = m_pScene->addEllipse(0, 0, this->width()/3., this->width()/3., + QPen(Qt::black), QBrush(QColor(200, 1, 1))); +} + +FieldlineViewSensor::~FieldlineViewSensor() +{ + // delete m_pUi; +} + +void FieldlineViewSensor::resizeEvent(QResizeEvent *event) +{ + auto bounds = m_pScene->itemsBoundingRect(); + bounds.setWidth(bounds.width() * 1.2); + bounds.setHeight(bounds.height() * 1.2); + m_pUi->ledQGraphView->fitInView(bounds, Qt::KeepAspectRatio); + + QWidget::resizeEvent(event); +} + +void FieldlineViewSensor::setState(FieldlineSensorStatusType state) { + m_sensorState = state; + updateTimeStamp(); +} + +FieldlineSensorStatusType FieldlineViewSensor::getState() const { + return m_sensorState; +} + +void FieldlineViewSensor::updateTimeStamp() +{ + timeStamp = std::chrono::steady_clock::now(); +} + +void FieldlineViewSensor::updateLedState() +{ + std::chrono::time_point now = std::chrono::steady_clock::now(); + std::chrono::milliseconds duration = std::chrono::duration_cast(now - timeStamp); + if (duration >= ledChangePeriod) { + switchLedState(); + } +} + +void FieldlineViewSensor::switchLedState() { + if (ledState == LedState::A) { + ledState = LedState::B; + } else { + ledState = LedState::B; + } + updateTimeStamp(); +} + +} // namespace FIELDLINEPLUGIN + + diff --git a/src/applications/mne_scan/plugins/fieldline/fieldline_view_sensor.h b/src/applications/mne_scan/plugins/fieldline/fieldline_view_sensor.h new file mode 100644 index 00000000000..d46d9820b3c --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/fieldline_view_sensor.h @@ -0,0 +1,111 @@ +//============================================================================================================= +/** + * @file fieldline_view_sensor.h + * @author Juan Garcia-Prieto + * Gabriel Motta + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineView Sensor class declaration. + * + */ + +#ifndef FIELDLINEPLUGIN_FIELDLINEVIEWSENSOR +#define FIELDLINEPLUGIN_FIELDLINEVIEWSENSOR + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include +#include "fieldline/fieldline_definitions.h" + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// FORWARD DECLARATIONS +//============================================================================================================= + +namespace Ui { +class uiFieldlineViewSensor; +} + +//============================================================================================================= +// DEFINE NAMESPACE DISPLIB +//============================================================================================================= + +class QGraphicsScene; +class QGraphicsEllipseItem; + +namespace FIELDLINEPLUGIN { + +class FieldlineViewChassis; + +//============================================================================================================= + +class FieldlineViewSensor: public QWidget +{ + Q_OBJECT + + public: + explicit FieldlineViewSensor(FieldlineViewChassis *parent, int index); + ~FieldlineViewSensor(); + void setState(FieldlineSensorStatusType state); + FieldlineSensorStatusType getState() const; + void updateLedState(); + + protected: + virtual void resizeEvent(QResizeEvent *event); + + private: + void updateTimeStamp(); + void switchLedState(); + + FieldlineViewChassis *m_pFieldlineViewChassis; + int m_sensorIndex; + FieldlineSensorStatusType m_sensorState; + std::chrono::time_point timeStamp; + std::chrono::milliseconds ledChangePeriod; + + enum class LedState { A, B }; + LedState ledState; + + Ui::uiFieldlineViewSensor *m_pUi; + QGraphicsScene *m_pScene; + QGraphicsEllipseItem *m_pCircleLed; +}; +} // namespace FIELDLINEPLUGIN + +#endif // FIELDLINEPLUGIN_FIELDLINEVIEWSENSOR_H + diff --git a/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view.ui b/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view.ui new file mode 100644 index 00000000000..ac80fd1000f --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view.ui @@ -0,0 +1,285 @@ + + + uiFieldlineView + + + + 0 + 0 + 948 + 665 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 600 + 120 + + + + + 1000 + 115 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + true + + + + 0 + 0 + + + + + 350 + 0 + + + + + 400 + 110 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + 0 + + + + + 330 + 100 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + 0 + 0 + + + + + 140 + 16777215 + + + + Disconnect + + + + + + + + 140 + 0 + + + + Connect + + + + + + + + 0 + 0 + + + + + 50 + 16777215 + + + + false + + + Qt::AlignCenter + + + false + + + 12 + + + + + + + Find IPs + + + + + + + + + + + + + + 0 + 0 + + + + + 1000 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Qt::Horizontal + + + + 597 + 20 + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Qt::Vertical + + + + 20 + 215 + + + + + + + + + + + + + + + diff --git a/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view_chassis.ui b/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view_chassis.ui new file mode 100644 index 00000000000..0bbfaaa5f55 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view_chassis.ui @@ -0,0 +1,115 @@ + + + uiFieldlineViewChassis + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + Form + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 2 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 20 + + + + FieldLine + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + diff --git a/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view_sensor.ui b/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view_sensor.ui new file mode 100644 index 00000000000..688cf361a3f --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/formfiles/fieldline_view_sensor.ui @@ -0,0 +1,133 @@ + + + uiFieldlineViewSensor + + + + 0 + 0 + 113 + 206 + + + + + 140 + 320 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 30 + 30 + + + + QFrame::NoFrame + + + QFrame::Plain + + + QAbstractScrollArea::AdjustToContents + + + + + + + + + + + 16777215 + 15 + + + + TextLabel + + + Qt::AlignCenter + + + + + + + PushButton 1 + + + + + + + PushButton 2 + + + + + + + PushButton 3 + + + + + + + PushButton 4 + + + + + + + + + + diff --git a/src/applications/mne_scan/plugins/fieldline/ipfinder.cpp b/src/applications/mne_scan/plugins/fieldline/ipfinder.cpp new file mode 100644 index 00000000000..937c13876d3 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/ipfinder.cpp @@ -0,0 +1,189 @@ +//============================================================================================================= +/** + * @file ipfinder.cpp + * @author Juan GarciaPrieto + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Contains the definition of the Fieldline class. + * + */ + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipfinder.h" +#include "ipfinder_network_unix.h" + +namespace IPFINDER { + +const char Defaults::regexIpStr[] = "[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}"; +const char Defaults::regexMacStr[] = "[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}"; +const char Defaults::tableFile[] = ".ipfinder_171921"; +const char Defaults::pingCommand[] = "ping"; +const char Defaults::pingCommandOpts[] = " -c 1 -w 0.002"; +const char Defaults::deleteFileCommand[] = "rm -f"; +const int Defaults::numRetriesMax = 3; +const char Defaults::arpTableFilePrefix[] = ".ipfinder_"; +const char Defaults::randomCharset[] = "abcdefghijklmpqrstuvwxyz0123456789"; +const int Defaults::randomStringLength = 8; +const char Defaults::defaultIpStr[] = "0.0.0.0"; + +void printMacIpList(const std::vector& macIpList) { + for (const MacIp& macip_i : macIpList) { + std::cout << "mac: " << macip_i.mac << " - ip: " + << (macip_i.ip.empty() ? "not found" : macip_i.ip) + << "\n"; + } +} + +void sendPingsAroundNetwork() { + std::string appStr(Defaults::pingCommand); + std::string appFlags(Defaults::pingCommandOpts); + std::vector threads; + std::vector networks = getNetworksClassC(); + + for (auto& netw_i : networks) { + for (auto& hostIp : netw_i.hostIPs) { + std::string commandStr = appStr + appFlags + " " + hostIp + " &>/dev/null"; + threads.emplace_back([=]{std::system(commandStr.c_str());}); + } + } + for (auto& thread : threads) { + thread.join(); + } +} + +void systemCalltoFile(const std::string& call, const std::string& filename) { + std::string callStr(call); + callStr.append(" > ").append(filename); + std::system(callStr.c_str()); +} + +void deleteFile(const std::string& filename) { + std::string command(Defaults::deleteFileCommand); + std::system(command.append(" ").append(filename).c_str()); +} + +IpFinder::IpFinder() + : numRetriesMax(Defaults::numRetriesMax), + numRetries(0) +{ + arp_table_filename = generateRandomArpTableFileName(); +} + +std::string IpFinder::generateRandomArpTableFileName() { + std::string newFileName(Defaults::arpTableFilePrefix); + const std::string charset(Defaults::randomCharset); + const int strLen(Defaults::randomStringLength); + + for (int i = 0; i < strLen; i++) { + int index = rand() % charset.length(); + newFileName += charset[index]; + } + return newFileName; +} + +void IpFinder::addMacAddress(const std::string& mac) { + std::regex macRegex(Defaults::regexMacStr); + std::smatch macParsed; + auto validMac = std::regex_search(mac, macParsed, macRegex); + if (validMac) { + if (macParsed.size() > 0) { + macIpList.emplace_back(MacIp(macParsed[0], "0.0.0.0", false)); + } else { + std::cerr << "Error: no matches found for MAC address " << mac << std::endl; + } + } else { + std::cerr << "Error: failed to match MAC address " << mac << " with regular expression." << std::endl; + } +} + +void IpFinder::findIps() { + numRetries += 1; + findIpsInARPTable(); + if (!allIpsFound() && (numRetries < numRetriesMax)) { + sendPingsAroundNetwork(); + findIps(); + } +} + +void IpFinder::findIpsInARPTable() { + systemCalltoFile("arp -a", arp_table_filename); + std::ifstream fp(arp_table_filename); + if (!fp.is_open()) { + std::cout << "Error: Unable to open arp file table.\n"; + std::cout.flush(); + return; + } + + std::string line; + for (MacIp& macip_i : macIpList) { + while (std::getline(fp, line)) { + std::smatch mac_found; + std::regex mac_i(macip_i.mac); + if (std::regex_search(line, mac_found, mac_i)) { + std::smatch ip_found; + std::regex regexIP(Defaults::regexIpStr); + if (std::regex_search(line, ip_found, regexIP)) { + macip_i.ip = ip_found[0]; + macip_i.valid = true; + } + } + } + } + fp.close(); + deleteFile(arp_table_filename); +} + +bool IpFinder::allIpsFound() const { + for (const MacIp& macip_i : macIpList) { + if (macip_i.valid) { + return false; + } + } + return true; +} + +} // namespace IPFINDER + diff --git a/src/applications/mne_scan/plugins/fieldline/ipfinder.h b/src/applications/mne_scan/plugins/fieldline/ipfinder.h new file mode 100644 index 00000000000..d32aa14835f --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/ipfinder.h @@ -0,0 +1,108 @@ +//============================================================================================================= +/** + * @file ipfinder.h + * @author Juan GarciaPrieto + * @since 0.1.0 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Juan G Prieto, Gabriel B Motta. 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 MNE-CPP authors 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. + * + * + * @brief Contains the declaration of the Fieldline plugin class. + * + */ + +#ifndef IPFINDER_H +#define IPFINDER_H + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include +#include +#include + +namespace IPFINDER { + +struct MacIp +{ + MacIp(const std::string& mac_, + const std::string& ip_, + bool value) + : mac(mac_), ip(ip_), valid(value) + {}; + MacIp() + : mac(""), ip(""), valid(false) + {}; + std::string mac; + std::string ip; + bool valid; +}; + +struct Defaults { + static const char regexIpStr[]; + static const char regexMacStr[]; + static const char tableFile[]; + static const char pingCommand[]; + static const char pingCommandOpts[]; + static const char deleteFileCommand[]; + static const int numRetriesMax; + static const char arpTableFilePrefix[]; + static const char randomCharset[]; + static const int randomStringLength; + static const char defaultIpStr[]; +}; + +void printMacIpList(const std::vector& macIpList); + +void sendPingsAroundNetwork(); + +void systemCalltoFile(const std::string& call, + const std::string& filename); + +void deleteFile(const std::string& filename); + +class IpFinder { + public: + IpFinder(); + void findIps(); + void addMacAddress(const std::string& mac); + bool allIpsFound() const; + + std::vector macIpList; + + private: + std::string generateRandomArpTableFileName(); + std::string arp_table_filename; + void findIpsInARPTable(); + int numRetriesMax; + int numRetries; +}; + +} // namespace IPFINDER + + + +#endif // IPFINDER_H + diff --git a/src/applications/mne_scan/plugins/fieldline/ipfinder_network_unix.cpp b/src/applications/mne_scan/plugins/fieldline/ipfinder_network_unix.cpp new file mode 100644 index 00000000000..b2988f35304 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/ipfinder_network_unix.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fieldline/ipfinder_network_unix.h" + +namespace IPFINDER { + +void printNetworks(const std::vector& networks) { + std::cout << "Networks:" << std::endl; + for (const auto& network : networks) { + std::cout << "Base Address: " << network.baseAddress << "\n"; + std::cout << "Number of Hosts: " << network.numOfHosts << "\n"; + std::cout << "Host IPs: "; + for (int i = 0; i < 10; i++) { + std::cout << " " << i << " " << network.hostIPs[i] << "\n"; + if (i == 10) break; + } + } +} + +std::vector getNetworksClassC() { + std::vector networks; + struct ifaddrs* ifAddrStruct = nullptr; + struct ifaddrs* ifa = nullptr; + void* tmpAddrPtr = nullptr; + + getifaddrs(&ifAddrStruct); + for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) continue; + if (ifa->ifa_addr->sa_family == AF_INET) { + tmpAddrPtr = &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr; + char addressBuffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); + std::string ip_address(addressBuffer); + + tmpAddrPtr = &((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr; + inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); + std::string subnet_mask(addressBuffer); + + // Calculate the network base address + struct in_addr network_addr; + inet_aton(ip_address.c_str(), &network_addr); + struct in_addr mask_addr; + inet_aton(subnet_mask.c_str(), &mask_addr); + network_addr.s_addr &= mask_addr.s_addr; + std::string network_address(inet_ntoa(network_addr)); + + // Calculate the number of possible hosts in the network + std::bitset<32> mask(mask_addr.s_addr); + int num_of_bits = 32 - mask.count(); + int num_of_hosts = (1 << num_of_bits) - 2; + + if (num_of_hosts > 255) + continue; + + std::vector host_ips; + for (int i = 1; i < num_of_hosts; ++i) { + struct in_addr host_addr; + host_addr.s_addr = network_addr.s_addr + htonl(i); + std::string host_ip(inet_ntoa(host_addr)); + host_ips.push_back(host_ip); + } + + Network network = { network_address, num_of_hosts, host_ips }; + networks.push_back(network); + } + } + if (ifAddrStruct != nullptr) freeifaddrs(ifAddrStruct); + return networks; +} + +} // namespace IPFINDER diff --git a/src/applications/mne_scan/plugins/fieldline/ipfinder_network_unix.h b/src/applications/mne_scan/plugins/fieldline/ipfinder_network_unix.h new file mode 100644 index 00000000000..c57775dcc3e --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/ipfinder_network_unix.h @@ -0,0 +1,22 @@ +#include +#include + +#ifndef IPFINDER_GETNETWORKS_H +#define IPFINDER_GETNETWORKS_H + +namespace IPFINDER { + +struct Network { + std::string baseAddress; + int numOfHosts; + std::vector hostIPs; +}; + +void printNetworks(const std::vector& networks); + +std::vector getNetworksClassC(); + +} // namespace IPFINDER + +#endif // IPFINDER_GETNETWORKS_H + diff --git a/src/applications/mne_scan/plugins/fieldline/notes.cpp b/src/applications/mne_scan/plugins/fieldline/notes.cpp new file mode 100644 index 00000000000..b90590ac452 --- /dev/null +++ b/src/applications/mne_scan/plugins/fieldline/notes.cpp @@ -0,0 +1,58 @@ + +//extern "C" { +// +//PyObject *restartFinished(PyObject *self, PyObject *args) { +// long chassis, sensor; +// if (PyArg_ParseTuple(args, "ii", &chassis, &sensor)) { +// std::cout << std::setfill('0') << std::setw(2) << chassis << ":" << sensor +// << " - restart finished;\n"; +// } else { +// std::cout << "A sensor has finished restarting!\n"; +// } +// +// return NULL; +//} +//PyObject *coarseZeroFinished(PyObject *self, PyObject *args) { +// long chassis, sensor; +// if (PyArg_ParseTuple(args, "ii", &chassis, &sensor)) { +// std::cout << std::setfill('0') << std::setw(2) << chassis << ":" << sensor +// << " - coarse zero finished;\n"; +// } else { +// std::cout << "A sensor has finished coarse zeroing!\n"; +// } +// +// return NULL; +//} +//PyObject *fineZeroFinished(PyObject *self, PyObject *args) { +// long chassis, sensor; +// if (PyArg_ParseTuple(args, "ii", &chassis, &sensor)) { +// std::cout << std::setfill('0') << std::setw(2) << chassis << ":" << sensor +// << " - fine zero finished;\n"; +// } else { +// std::cout << "A sensor has finished fine zeroing!\n"; +// } +// +// return NULL; +//} +//} +// +//static PyMethodDef my_module_methods[] = { +// {"restartFinished", restartFinished, METH_VARARGS, " "}, +// {"coarseZeroFinished", coarseZeroFinished, METH_VARARGS, " "}, +// {"fineZeroFinished", fineZeroFinished, METH_VARARGS, " "}, +// {NULL, NULL, 0, NULL}}; +// +//static PyModuleDef my_module_def = { +// PyModuleDef_HEAD_INIT, +// "mne_cpp_callbacks", +// "A module of callback functions for mne-cpp.", +// -1, +// my_module_methods, +// NULL, +// NULL, +// NULL, +// NULL}; +// +//PyMODINIT_FUNC PyInit_my_module(void) { +// return PyModule_Create(&my_module_def); +//} diff --git a/src/libraries/disp/CMakeLists.txt b/src/libraries/disp/CMakeLists.txt index e75f51feb10..513959302fc 100644 --- a/src/libraries/disp/CMakeLists.txt +++ b/src/libraries/disp/CMakeLists.txt @@ -56,6 +56,7 @@ set(SOURCES viewers/hpisettingsview.cpp viewers/covariancesettingsview.cpp viewers/bidsview.cpp + viewers/led_indicator.cpp viewers/helpers/rtfiffrawviewmodel.cpp viewers/helpers/rtfiffrawviewdelegate.cpp viewers/helpers/evokedsetmodel.cpp @@ -119,6 +120,7 @@ set(HEADERS viewers/hpisettingsview.h viewers/covariancesettingsview.h viewers/bidsview.h + viewers/led_indicator.h viewers/helpers/rtfiffrawviewdelegate.h viewers/helpers/rtfiffrawviewmodel.h viewers/helpers/evokedsetmodel.h diff --git a/src/libraries/disp/viewers/formfiles/led_indicator.ui b/src/libraries/disp/viewers/formfiles/led_indicator.ui new file mode 100644 index 00000000000..28af9347b34 --- /dev/null +++ b/src/libraries/disp/viewers/formfiles/led_indicator.ui @@ -0,0 +1,59 @@ + + + led_indicator + + + + 0 + 0 + 400 + 242 + + + + + 0 + 0 + + + + Form + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + + 0 + 0 + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + + diff --git a/src/libraries/disp/viewers/helpers/rtfiffrawviewdelegate.cpp b/src/libraries/disp/viewers/helpers/rtfiffrawviewdelegate.cpp index c5c9becb99b..f4c82296c0b 100644 --- a/src/libraries/disp/viewers/helpers/rtfiffrawviewdelegate.cpp +++ b/src/libraries/disp/viewers/helpers/rtfiffrawviewdelegate.cpp @@ -460,15 +460,24 @@ void RtFiffRawViewDelegate::createPlotPath(const QModelIndex &index, // The plot works as a rolling time-cursor, ploting data on top of previous // runs. You always plot one whole window of data, between first sample and - // numSamplesToPlot (or data.second) Even if the only change is a new block - // of samples to the left of the time-cursor. At any time, to the left of - // the time-cursor you have the most recent data (A part) corresponding to - // the current roll. To the right of the time-cursor, you have data - // corresponding to the previous roll. + // numSamplesToPlot (or data.second). Even if the only change is a new + // block of samples to the left of the time-cursor. At any time, to the + // left of the time-cursor you have the most recent data (A part) + // corresponding to the current roll. To the right of the time-cursor, you + // have data corresponding to the previous roll. for (int j = 0; j < data.second; ++j) { - dY = data.first[j]; - path.lineTo(calcPoint(path, dPixelsPerSample, dY, - dChannelOffset, dScaleY)); + if (j < iTimeCursorSample) { + // A part + dY = data.first[j]; // - data.first[0]; + } else { + // B part + dY = data.first[j]; // - firstValuePreviousPlot; + } + path.lineTo(calcPoint(path, + dPixelsPerSample, + dY, + dChannelOffset, + dScaleY)); } } diff --git a/src/libraries/disp/viewers/led_indicator.cpp b/src/libraries/disp/viewers/led_indicator.cpp new file mode 100644 index 00000000000..6228349a9d9 --- /dev/null +++ b/src/libraries/disp/viewers/led_indicator.cpp @@ -0,0 +1,172 @@ +//============================================================================================================= +/** + * @file led_indicator.cpp + * @author Gabriel Motta + * Juan Garcia-Prieto + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief LEDIndicator class definition. + * + */ + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include "led_indicator.h" +#include "ui_led_indicator.h" + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include +#include +#include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// USED NAMESPACES +//============================================================================================================= + +using DISPLIB::LEDIndicator; + +//============================================================================================================= +// DEFINE STATIC METHODS +//============================================================================================================= + +QColor LEDIndicator::default_color = Qt::red; +QString LEDIndicator::default_label = "XX"; + +//============================================================================================================= +// DEFINE MEMBER METHODS +//============================================================================================================= + +LEDIndicator::LEDIndicator(const QString& label, const QColor& led_color, QWidget *parent) +: QWidget(parent) +, ui(std::make_unique()) +, off_brush(QBrush(Qt::transparent)) +, on_brush(QBrush(led_color)) +, blink_time_ms(200) +, blink_state(false) +{ + m_pScene = std::make_unique(); + + ui->setupUi(this); + ui->graphicsView->setStyleSheet("background:transparent"); + ui->graphicsView->horizontalScrollBar()->hide(); + ui->graphicsView->verticalScrollBar()->hide(); + ui->label->setText(label); + ui->graphicsView->setScene(m_pScene.get()); + + circle_led = m_pScene->addEllipse(0,0,this->width()/3,this->width()/3, QPen(Qt::black), on_brush); + + this->setContextMenuPolicy(Qt::CustomContextMenu); +} + +//============================================================================================================= + +LEDIndicator::LEDIndicator(const QString& label, QWidget *parent) +: LEDIndicator(label, default_color, parent) +{ +} + +//============================================================================================================= + +LEDIndicator::LEDIndicator(QWidget *parent) +: LEDIndicator(default_label, default_color, parent) +{ +} + +//============================================================================================================= + +LEDIndicator::~LEDIndicator() +{ +} + +//============================================================================================================= + +void LEDIndicator::setLabel(const QString& label) +{ + ui->label->setText(label); +} + +//============================================================================================================= + +void LEDIndicator::setBlink(bool state) +{ + state ? turnOnBlink() : turnOffBlink(); +} + +//============================================================================================================= + +void LEDIndicator::setColor(const QColor& color) +{ + on_brush.setColor(color); +} + +//============================================================================================================= + +void LEDIndicator::resizeEvent(QResizeEvent *event) +{ + auto bounds = m_pScene->itemsBoundingRect(); + bounds.setWidth(bounds.width() * 1.2); + bounds.setHeight(bounds.height() * 1.2); + ui->graphicsView->fitInView(bounds, Qt::KeepAspectRatio); + + QWidget::resizeEvent(event); +} + +//============================================================================================================= + +void LEDIndicator::turnOnBlink() +{ + connect(&blink_timer, &QTimer::timeout, + this, &LEDIndicator::handleBlink, Qt::UniqueConnection); + blink_timer.start(blink_time_ms); +} + +//============================================================================================================= + +void LEDIndicator::turnOffBlink() +{ + blink_timer.stop(); + circle_led->setBrush(on_brush); +} + +//============================================================================================================= + +void LEDIndicator::handleBlink() +{ + blink_state ? circle_led->setBrush(off_brush) + : circle_led->setBrush(on_brush); + + blink_state = !blink_state; +} diff --git a/src/libraries/disp/viewers/led_indicator.h b/src/libraries/disp/viewers/led_indicator.h new file mode 100644 index 00000000000..391da06ad69 --- /dev/null +++ b/src/libraries/disp/viewers/led_indicator.h @@ -0,0 +1,113 @@ +//============================================================================================================= +/** + * @file led_indicator.h + * @author Gabriel Motta + * Juan Garcia-Prieto + * @since 0.1.9 + * @date February, 2023 + * + * @section LICENSE + * + * Copyright (C) 2023, Gabriel Motta, Juan Garcia-Prieto. 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 MNE-CPP authors 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. + * + * + * @brief FieldlineSensor class declaration. + * + */ + +#ifndef LED_INDICATOR_H +#define LED_INDICATOR_H + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include "../disp_global.h" + +#include + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include +#include +#include +#include + +//============================================================================================================= +// EIGEN INCLUDES +//============================================================================================================= + +//============================================================================================================= +// FORWARD DECLARATIONS +//============================================================================================================= + +namespace Ui { + class led_indicator; +} + +//============================================================================================================= +// DEFINE NAMESPACE DISPLIB +//============================================================================================================= + +namespace DISPLIB +{ + +class LEDIndicator : public QWidget +{ + Q_OBJECT + +public: + LEDIndicator(const QString& label, const QColor& led_color, QWidget *parent = nullptr); + LEDIndicator(const QString& label, QWidget *parent = nullptr); + explicit LEDIndicator(QWidget *parent = nullptr); + ~LEDIndicator(); + + void setLabel(const QString& label); + void setBlink(bool state); + void setColor(const QColor& color); + +protected: + virtual void resizeEvent(QResizeEvent *event); + +private: + void turnOnBlink(); + void turnOffBlink(); + void handleBlink(); + + std::unique_ptr ui; + std::unique_ptr m_pScene; + + int blink_time_ms; + QTimer blink_timer; + bool blink_state; + + static QColor default_color; + static QString default_label; + + QGraphicsEllipseItem* circle_led; + QBrush off_brush; + QBrush on_brush; +}; +}//namespace DISPLIB + +#endif // LED_INDICATOR_H diff --git a/testing.cpp b/testing.cpp new file mode 100644 index 00000000000..ca647ca0663 --- /dev/null +++ b/testing.cpp @@ -0,0 +1,208 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include +// #include "defaults.hpp" + +void dictParser() { + std::cout << "Hello from dictParser\n"; +} + +static PyObject* dict_parser_py_fastcall(PyObject* self, PyObject*const* args, Py_ssize_t argc) { + PyObject* dict = args[1]; + std::cout << "fastcall started.\n"; + std::cout << "num of args = " << argc << "\n"; + + if (!PyLong_Check(args[0])) { + std::cout << "Not int.\n"; + return NULL; + } else { + std::cout << "integer: " << PyLong_AsLong(args[0]) << "\n"; + } + if (!PyDict_Check(dict)) { + std::cout << "Not a dict.!\n"; + return NULL; + } else { + int typeDict = PyDict_Check(dict); + if (typeDict) + std::cout << "It is a boy!\n"; + + int s = PyDict_Size(dict); + std::cout << "Number of elements: " << s << "\n"; + + PyObject *key = PyUnicode_FromString("keyA"); + if (PyDict_Contains(dict, key) == 1) { + PyObject *value = PyDict_GetItem(dict, key); + long my_value = PyLong_AsLong(value); + std::cout + << "The value :" << PyUnicode_AsUTF8(key) + << " is " << my_value << "\n"; + } + } + return Py_None; +} + +static PyObject* dict_parser_py_varargs(PyObject* self, PyObject* args) { + // parse input args + int numArgs = 0; + int s = 0; + int var; + PyObject* dict; + + + if (!PyTuple_Check(args)) { + return NULL; + } + + numArgs = PyTuple_Size(args); + std::cout << "Num args: " << numArgs << "\n"; + // if (PyTuple_Size(args) != 1) + // std::cout << "Num args not correct.\n"; + // + if (!PyArg_ParseTuple(args, "iO", &var, &dict)) { + std::cout << "Error parsing arguments.\n"; + return NULL; + } + std::cout << "var = " << var << "\n"; + int typeDict = PyDict_Check(dict); + if (typeDict) + std::cout << "It is a boy!\n"; + + s = PyDict_Size(dict); + std::cout << "Number of elements: " << s << "\n"; + + PyObject *key = PyUnicode_FromString("keyA"); + if (PyDict_Contains(dict, key) == 1) { + PyObject *value = PyDict_GetItem(dict, key); + long my_value = PyLong_AsLong(value); + std::cout + << "The value :" << PyUnicode_AsUTF8(key) + << " is " << my_value << "\n"; + } + + return Py_None; +} + + +void get_item_from_dict(PyObject* item, PyObject* dict, const char* key) { + PyObject* key_obj = PyUnicode_FromString(key); + item = PyDict_GetItem(dict, key_obj); +} + + +static PyObject* dict_parser(PyObject* self, PyObject*const* args, Py_ssize_t argc) { + if (argc != 1) { + return NULL; + } + + if (!PyDict_Check(args[0])) { + return NULL; + } + + PyObject* dict = args[0]; + PyObject* data_frames; + PyObject* dataKeyObj = PyUnicode_FromString("data"); + PyObject* sensorNameKeyObj = PyUnicode_FromString("sensor"); + + PyObject* key_obj = PyUnicode_FromString("data_frames"); + + data_frames = PyDict_GetItem(dict, key_obj); + Py_DECREF(dict); + Py_DECREF(); + + int num_channels = PyDict_Size(data_frames); + + // std::cout << "Num channels: " << num_channels << "\n"; + // int* data = new int() + PyObject* key, *value; + Py_ssize_t pos = 0; + struct dataValue { + int value; + char label[5+1]; + }; + dataValue* dataValues = new dataValue[num_channels]; + + while (PyDict_Next(data_frames, &pos, &key, &value)) { + // int num_values = PyDict_Size(value); + // std::cout << "Num values: " << num_values << "\n"; + PyObject* data; + PyObject* sensorName; + sensorName = PyDict_GetItem(value, sensorNameKeyObj); + data = PyDict_GetItem(value, dataKeyObj); + // std::cout << "Sensor " << PyUnicode_AsUTF8(sensorName) + // << ": " << PyLong_AsLong(data) + // << " - pos: " << pos << "\n"; + memcpy((void*)dataValues[pos-1].label,(void*)PyUnicode_AsUTF8(sensorName), 5); + + dataValues[pos-1].label[5] = static_cast(0); + dataValues[pos-1].value = PyLong_AsLong(data); + } + + // for (int i = 0; i < num_channels; i++) { + // std::cout << "(" << dataValues[i].label << ") - " << dataValues[i].value << "\n"; + // } + + delete[] dataValues; + + + // important !!!!! + // we need to Py_DECREF a few PyObjects!!!! + + return Py_None; +} + +static struct PyMethodDef callbacksMethods[] = { + {"dict_parser_fastcall", _PyCFunction_CAST(dict_parser_py_fastcall), + METH_FASTCALL, "Parse dictionary fastcall"}, + {"dict_parser", _PyCFunction_CAST(dict_parser), + METH_FASTCALL, "Parse dictionary"}, + {"dict_parser_varargs", dict_parser_py_varargs, + METH_VARARGS, "Parse dictionary with varargs"}, + {NULL, NULL, 0, NULL} +}; + +static PyModuleDef CallModule = { + PyModuleDef_HEAD_INIT, "parsing", NULL, -1, callbacksMethods, + NULL, NULL, NULL, NULL +}; + + +static PyObject* PyInit_parsing(void) { + return PyModule_Create(&CallModule); +} + + + +int main(int argc, char* argv[]) { + // std::cout << "Starting execution\n"; + + PyImport_AppendInittab("parsing", &PyInit_parsing); + Py_Initialize(); + PyConfig config; + PyConfig_InitPythonConfig(&config); + config.module_search_paths_set = 1; + PyWideStringList_Append(&config.module_search_paths, L"."); + Py_InitializeFromConfig(&config); + + FILE* py_file; + if (argc != 2) { + std::cout << "Usage: " << argv[0] << ": main.py\n"; + exit(1); + } + py_file = fopen(argv[1], "r"); + PyObject* global_dict = PyDict_New(); + PyObject* local_dict = PyDict_New(); + PyObject* result = PyRun_File(py_file, + argv[1], + Py_file_input, + global_dict, + local_dict); + Py_DECREF(global_dict); + Py_DECREF(local_dict); + Py_DECREF(result); + fclose(py_file); + + Py_Finalize(); + + return 0; +}