First commit
This commit is contained in:
commit
97c6be4e8d
5
README.txt
Normal file
5
README.txt
Normal file
@ -0,0 +1,5 @@
|
||||
Для ознакомления с кодом проще всего запустить программу в PyCharm
|
||||
1. создать папку с проектом
|
||||
2. в папке с проектом создать папку project
|
||||
3. в папку project поместить файлы с кодом
|
||||
4. в папку с проектом разместить файл ui и ico
|
||||
2423
design_viewer.ui
Normal file
2423
design_viewer.ui
Normal file
File diff suppressed because it is too large
Load Diff
8
project/__init__.py
Normal file
8
project/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
"""Режимы работы, переменные, влияющие на работу, которые с некоторой вероятностью будут меняться."""
|
||||
|
||||
|
||||
DATA_RATE = 200 # скорость передачи данных
|
||||
|
||||
# mes = "AT+CONA11899AA1670B\r\n"
|
||||
mes = "AT+CONA11899AA21438\r\n" # для связи со стимулятором
|
||||
|
||||
472
project/controller.py
Normal file
472
project/controller.py
Normal file
@ -0,0 +1,472 @@
|
||||
"""Преобразование данных"""
|
||||
|
||||
import struct
|
||||
|
||||
import numpy as np
|
||||
|
||||
from project import DATA_RATE
|
||||
import model
|
||||
|
||||
NORMAL_LENGTH = 32 # размер пакета
|
||||
Start_byte = b'\x55' # стартовый байт, нужен для парсинга сообщения
|
||||
Stop_byte = b'\x77' # стоповый байт, нужен для парсинга сообщения
|
||||
|
||||
threshold_MIN = 100 # минимално возможная амплитуда выше которой ищем пики в базовом режиме
|
||||
threshold_MAX = 400 # максимально возможная амплитуда выше которой не сохраняем
|
||||
|
||||
|
||||
# методы чтения передаваемых данных
|
||||
def shift_or(one, two, three, four):
|
||||
"""Чтение 4-ёх байтного параметра"""
|
||||
a = one << 24 | two << 16 | three << 8 | four
|
||||
return a
|
||||
|
||||
|
||||
def shift_or_24(two, three, four):
|
||||
"""Чтение 3-ёх байтного параметра"""
|
||||
a = two << 16 | three << 8 | four
|
||||
return a
|
||||
|
||||
|
||||
def shift_or_16(three, four):
|
||||
"""Чтение 2-ух байтного параметра"""
|
||||
a = three << 8 | four
|
||||
return a
|
||||
|
||||
|
||||
def get_data_bin(file, data_in):
|
||||
"""
|
||||
Получение данных из бинарного файла - записанных данных
|
||||
:param file: файл для чтения
|
||||
:param data_in: объект для хранения модели данных
|
||||
"""
|
||||
chunk = list(model.get_data_bin(file, data_in))
|
||||
data_in.chunk = [item.to_bytes() for item in list(chunk)]
|
||||
|
||||
|
||||
def write_file():
|
||||
"""
|
||||
Открытие на запись файла
|
||||
"""
|
||||
return model.write_file()
|
||||
|
||||
|
||||
def write_in_file(file, rx):
|
||||
"""
|
||||
Запись данных в файл
|
||||
:param file: файл
|
||||
:param rx: серия данных
|
||||
"""
|
||||
model.write_in_file(file, rx)
|
||||
|
||||
|
||||
def close_file(file):
|
||||
"""
|
||||
Закрыть файл
|
||||
:param file: файл
|
||||
"""
|
||||
return model.close_file(file)
|
||||
|
||||
|
||||
def create_mess(send_mode, param_code, param, size, type_param):
|
||||
"""
|
||||
Создание сообщения для передачи параметров
|
||||
:param send_mode: режим передачи данных 433 - 0(Bluetooth - 1)
|
||||
:param param_code: код для идентификации параметра для передачи(задано протоколом взаимодействия)
|
||||
:param param: передаваемое значение
|
||||
:param size: размер передаваемого значения
|
||||
:param type_param: тип передаваемого значения
|
||||
"""
|
||||
index = 2
|
||||
if send_mode:
|
||||
index = 10
|
||||
init_mes = [0x41, 0x54, 0x2b, 0x44, 0x41, 0x54, 0x41, 0x31, 0x55, param_code, 0x77, 0x0D, 0x0A]
|
||||
else:
|
||||
init_mes = [0x55, param_code, 0x77]
|
||||
param_in_bytes = []
|
||||
if type_param == float:
|
||||
param = struct.unpack('i', struct.pack('f', param))[0]
|
||||
param_in_bytes = [int(((param & 0xFF000000)) >> 24), int(((param & 0xFF0000)) >> 16),
|
||||
int(((param & 0xFF00) >> 8)),
|
||||
int((param & 0xFF))]
|
||||
if type_param == int:
|
||||
if size == 4:
|
||||
param_in_bytes = [int(((param & 0xFF000000)) >> 24), int(((param & 0xFF0000)) >> 16),
|
||||
int(((param & 0xFF00) >> 8)),
|
||||
int((param & 0xFF))]
|
||||
if size == 3:
|
||||
param_in_bytes = [0, int(((param & 0xFF0000)) >> 16),
|
||||
int(((param & 0xFF00) >> 8)),
|
||||
int((param & 0xFF))]
|
||||
if size == 2:
|
||||
param_in_bytes = [0, 0, int(((param & 0xFF00) >> 8)),
|
||||
int((param & 0xFF))]
|
||||
if size == 1:
|
||||
param_in_bytes = [0, 0, 0, int((param & 0xFF))]
|
||||
|
||||
for _ in param_in_bytes[::-1]:
|
||||
init_mes.insert(index, _)
|
||||
|
||||
mes = bytes(init_mes)
|
||||
return mes
|
||||
|
||||
|
||||
def state_packet(send_mode, serial, param_code, param, size, type_param):
|
||||
"""
|
||||
Отправка пакета параметров
|
||||
:param send_mode: режим передачи данных 433 - 0(Bluetooth - 1)
|
||||
:param serial: канал передачи
|
||||
:param param_code: код для идентификации параметра для передачи(задано протоколом взаимодействия)
|
||||
:param param: передаваемое значение
|
||||
:param size: размер передаваемого значения
|
||||
:param type_param: тип передаваемого значения
|
||||
"""
|
||||
if serial.isOpen():
|
||||
serial.writeData(create_mess(send_mode, param_code, param, size, type_param))
|
||||
|
||||
|
||||
class DataClass:
|
||||
"""
|
||||
Класс модель данных для отображения на графике
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# переменные для работы с потоком данных с устройства
|
||||
self.data_rx = [] # список в котором храним данные
|
||||
self.draw_cnt = np.uint16(0) # x-координата точки в текущий момент
|
||||
self.data_length = 0 # кол-во отсчетов для отрисовки графика по x-координате
|
||||
self.pac_count = 0 # счетчик для распознавания пакетов
|
||||
self.counter = [] # счетчик небитых пакетов
|
||||
|
||||
# переменные для работы с записанным файлом
|
||||
self.i = 0 # счетчик для чтения файла
|
||||
self.chunk = [] # считанный массив из файла
|
||||
|
||||
# переменные для отрисовки графиков
|
||||
|
||||
# Сигнал по которому определяют ЧСС ЭКГ сигнал если у нас кардиограф
|
||||
# Правый желудочек (используется основной канал)
|
||||
self.ECG_RA = [] # np.zeros(self.data_length, np.int16)
|
||||
# позитивно отраженный сигнал
|
||||
self.ECG_RA_pos_sig = [] # np.zeros(self.data_length, np.int16)
|
||||
# динамический порог рассчитываемый в зависимости от нижнего и верхнего порогов
|
||||
self.ECG_RA_din_threshold = [] # np.zeros(self.data_length, np.int16)
|
||||
# нижний порог порог для метода треугольников при его пересечении считаем ЧСС событие
|
||||
self.ECG_RA_min_threshold = np.int16(threshold_MIN)
|
||||
# верхний порог для метода треугольников выше него мы не берём данные
|
||||
self.ECG_RA_max_threshold = np.int16(threshold_MAX)
|
||||
self.Last_RR_poz_rel = [] # время до прошлого события
|
||||
self.text_list = {} # список label на графике для динамического обновления
|
||||
self.last_period = [] # мгновенный период для отрисовки label
|
||||
self.rr_now = [] # есть ли удар сердца в текущий момент
|
||||
self.last_QRS = [] # тип последнего события Vsense = 0 нормальное сокращение
|
||||
# Vpace = 1 пришлось стимулировать сейчас не используем
|
||||
# Vnoise = 2 шумы в QRS комплексе(пока не реализовали)
|
||||
|
||||
# переменные для отрисовки label (обозначение на графике событий)
|
||||
self.V_print = 0 # необходимость отображения события
|
||||
self.draw_cnt_last = 0 # x-координата события
|
||||
self.draw_last_period = 0 # мгновенный период для отображения
|
||||
self.draw_last_QRS = 0 # тип последнего события для отображения
|
||||
|
||||
# переменные для передачи параметров стимулятору и отображения текущих значений
|
||||
self.dc_cut = 1 # вывод сигнала с постоянной составляющей или без
|
||||
self.channel = 3 # активный канал передачи
|
||||
self.signal = 0 # вывод сигнала с фильтром или без
|
||||
self.sd_card = 0 # запись на sd-карту
|
||||
self.hv_volt = 0 # напряжение на конденсаторе
|
||||
self.bat_volt = 0 # напряжение на батарее
|
||||
self.bat_pers = 0 # напряжение на батарее в процентах
|
||||
self.spi_pot_set = 0 # коэффициент усиления
|
||||
self.Work_Mode_pacemaker = 0 # текущий режим работы стимулятора
|
||||
self.redet_num = 0 # размер буфера редетекции
|
||||
self.redet_bad = 0 # порог буфера редетекции
|
||||
self.standby_timer = 0 # таймаут отключения
|
||||
self.hv_blind_time = 0 # время слепоты после разряда
|
||||
self.max_energy = 0 # максимальная энергия
|
||||
self.min_energy = 0 # минимальная энергия
|
||||
self.hv_step_number = 0 # кол-во ступеней высоковольтной терапии
|
||||
self.fibr_max_tres = 0 # порог корзины фибриляций
|
||||
self.tachy_2_tres = 0 # пороговое значение для тахикардии 2ст
|
||||
self.tachy_1_tres = 0 # пороговое значение для тахикардии 1ст
|
||||
self.fibr_tres = 0 # пороговое значение для фибрилляции
|
||||
self.max_search_time = 135 # длительность поиска максимума
|
||||
self.square_coef = 0.75 # порог чувствительности Т волны
|
||||
self.square_time = 350 # длительность защиты от T волны
|
||||
self.max_treshold = 8.0 # максимальная чувствительность
|
||||
self.min_treshold = 2.0 # минимальная чувствительность
|
||||
self.max_time = 1250 # максимальный размер RR интервала
|
||||
self.fibr_cnt = 0 # кол-во событий фибрилляции (прогресс-бар)
|
||||
self.norm_cnt = 0 # кол-во нормальных сокращений (прогресс-бар)
|
||||
self.tachy_1_cnt = 0 # кол-во событий тахикардии 1ст (прогресс-бар)
|
||||
self.tachy_2_cnt = 0 # кол-во событий тахикардии 2ст (прогресс-бар)
|
||||
self.hv_step_cnt = 0 # текущая ступень ВВ терапии
|
||||
self.last_period_stat = 0 # мгновенный период
|
||||
self.filt_period = 0 # усредненный период
|
||||
self.now_energy = 0 # текущая энергия
|
||||
self.Vn_cnt = 0 # счетчик Vn
|
||||
self.Vp_cnt = 0 # счетчик Vp
|
||||
self.Vs_cnt = 0 # счетчик Vs
|
||||
self.sub_mode = 0 # текущий подрежим терапии
|
||||
self.terapy_now = 0 # текущий режим терапии
|
||||
|
||||
def clear_data(self):
|
||||
"""
|
||||
Очистить модель хранения данных
|
||||
"""
|
||||
self.__init__()
|
||||
# self.data_rx.clear()
|
||||
# self.data_length = 0
|
||||
# self.counter = np.zeros(self.data_length, np.int16) # np.zeros(self.data_length, np.int16)
|
||||
# self.pac_count = 0
|
||||
# self.draw_cnt = 0
|
||||
#
|
||||
# self.i = 0
|
||||
# self.chunk = []
|
||||
# self.update_data(self.data_length)
|
||||
|
||||
# # Сигнал по которому определяют ЧСС ЭКГ сигнал если у нас кардиограф и производная фильрованного ФПГ если часы
|
||||
# # Правый желудочек (используется основной канал)
|
||||
# self.ECG_RA = []
|
||||
#
|
||||
# # позитивно отраженный сигнал
|
||||
# self.ECG_RA_pos_sig = [] # np.zeros(self.data_length, np.int16)
|
||||
#
|
||||
# # динамический порог рассчитываемый в зависимости от нижнего и верхнего порогов
|
||||
# self.ECG_RA_din_threshold = [] # np.zeros(self.data_length, np.int16)
|
||||
#
|
||||
# # нижний порог порог для метода треугольников при его пересечении считаем ЧСС событие
|
||||
# self.ECG_RA_min_threshold = np.int16(threshold_MIN)
|
||||
#
|
||||
# # верхний порог для метода треугольников выше него мы не берём данные
|
||||
# self.ECG_RA_max_threshold = np.int16(threshold_MAX)
|
||||
|
||||
# счетчик пакетов для вывод на график(визуальный контроль)
|
||||
|
||||
def update_data(self, data_length):
|
||||
"""
|
||||
Создание нулевых массивов для хранения в них даных для отображения
|
||||
:param data_length: длина массива (сколько значений поместится в окно)
|
||||
"""
|
||||
self.ECG_RA = np.zeros(data_length, np.float32)
|
||||
self.ECG_RA_pos_sig = np.zeros(data_length, np.float32)
|
||||
self.ECG_RA_din_threshold = np.zeros(data_length, np.float32)
|
||||
self.counter = np.zeros(data_length, np.float32)
|
||||
self.ECG_RA_min_threshold = np.zeros(data_length, np.float32)
|
||||
self.ECG_RA_max_threshold = np.zeros(data_length, np.float32)
|
||||
self.Last_RR_poz_rel = np.zeros(data_length, np.uint16)
|
||||
self.last_QRS = np.zeros(data_length, np.uint16)
|
||||
self.rr_now = np.zeros(data_length, np.uint16)
|
||||
self.last_period = np.zeros(data_length, np.uint16)
|
||||
|
||||
def parse_list(self, data_in):
|
||||
"""
|
||||
Расшифровка полученной серии данных
|
||||
:param data_in: ссылка на объект модели данных
|
||||
"""
|
||||
self.data_rx = self.data_rx + list(data_in)
|
||||
|
||||
if len(self.data_rx) >= NORMAL_LENGTH * 2: # парсить имеет смысл при длинне пакета больше минимальной
|
||||
while self.pac_count <= len(self.data_rx) - NORMAL_LENGTH:
|
||||
if (self.data_rx[self.pac_count] == Start_byte) and (
|
||||
self.data_rx[self.pac_count + NORMAL_LENGTH - 1] == Stop_byte) \
|
||||
and ((len(self.data_rx) - self.pac_count) >= NORMAL_LENGTH + 0):
|
||||
self.draw_cnt += 1
|
||||
if self.draw_cnt >= self.data_length:
|
||||
self.draw_cnt = 0
|
||||
self.text_list = {}
|
||||
|
||||
self.parse_part_from_1_to_20_byte()
|
||||
|
||||
if self.draw_cnt > 1:
|
||||
self.ECG_RA_min_threshold[self.draw_cnt] = self.ECG_RA_min_threshold[self.draw_cnt - 1]
|
||||
self.ECG_RA_max_threshold[self.draw_cnt] = self.ECG_RA_max_threshold[self.draw_cnt - 1]
|
||||
self.last_period[self.draw_cnt] = self.last_period[self.draw_cnt - 1]
|
||||
else:
|
||||
self.ECG_RA_min_threshold[self.draw_cnt] = self.ECG_RA_min_threshold[self.data_length - 1]
|
||||
self.ECG_RA_max_threshold[self.draw_cnt] = self.ECG_RA_max_threshold[self.data_length - 1]
|
||||
self.last_period[self.draw_cnt] = self.last_period[self.data_length - 1]
|
||||
|
||||
self.parse_part_from_23_to_30_byte()
|
||||
|
||||
if self.rr_now[self.draw_cnt] == 1:
|
||||
self.V_print = 1
|
||||
self.draw_cnt_last = self.draw_cnt
|
||||
self.draw_last_period = self.last_period[self.draw_cnt]
|
||||
|
||||
# в конце парсинга шагаем по массиву
|
||||
self.pac_count += NORMAL_LENGTH - 1
|
||||
|
||||
del self.data_rx[0:self.pac_count]
|
||||
self.pac_count = 0
|
||||
self.pac_count += 1
|
||||
|
||||
def parse_part_from_1_to_20_byte(self):
|
||||
"""
|
||||
Парсинг с 1 по 20 байт
|
||||
"""
|
||||
self.counter[self.draw_cnt] = shift_or(int.from_bytes(self.data_rx[self.pac_count + 1], "big"),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 2], "big"),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 3], "big"),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 4], "big"))
|
||||
|
||||
int1 = (shift_or(int.from_bytes(self.data_rx[self.pac_count + 5], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 6], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 7], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 8], "big", signed=False)))
|
||||
self.ECG_RA_din_threshold[self.draw_cnt] = struct.unpack('f', int1.to_bytes(4, 'big'))[0]
|
||||
|
||||
int1 = (shift_or(int.from_bytes(self.data_rx[self.pac_count + 9], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 10], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 11], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 12], "big", signed=False)))
|
||||
self.ECG_RA_pos_sig[self.draw_cnt] = struct.unpack('f', int1.to_bytes(4, 'big'))[0]
|
||||
|
||||
int1 = (shift_or(int.from_bytes(self.data_rx[self.pac_count + 13], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 14], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 15], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 16], "big", signed=False)))
|
||||
self.ECG_RA[self.draw_cnt] = struct.unpack('f', int1.to_bytes(4, 'big'))[0]
|
||||
|
||||
self.Last_RR_poz_rel[self.draw_cnt] = shift_or_16(
|
||||
int.from_bytes(self.data_rx[self.pac_count + 17], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 18], "big"))
|
||||
|
||||
int1 = int.from_bytes(self.data_rx[self.pac_count + 19], "big")
|
||||
self.terapy_now = int1 & 0x3
|
||||
self.sub_mode = (int1 & 0x1C) >> 2
|
||||
|
||||
int1 = int.from_bytes(self.data_rx[self.pac_count + 20], "big")
|
||||
self.last_QRS[self.draw_cnt] = int1 & 0x3
|
||||
self.draw_last_QRS = int1 & 0x3
|
||||
self.rr_now[self.draw_cnt] = (int1 & 0x4) >> 2
|
||||
self.Work_Mode_pacemaker = (int1 & 0x60) >> 5
|
||||
self.signal = (int1 & 0x80) >> 7
|
||||
|
||||
def parse_part_from_23_to_30_byte(self):
|
||||
"""
|
||||
Парсинг с 23 по 30 байт (передача значений в зависимости от остатка от деления счетчика)
|
||||
"""
|
||||
if self.counter[self.draw_cnt] % 10 == 0:
|
||||
int1 = (shift_or(int.from_bytes(self.data_rx[self.pac_count + 23], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 24], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 25], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 26], "big", signed=False)))
|
||||
self.ECG_RA_min_threshold[self.draw_cnt] = struct.unpack('f', int1.to_bytes(4, 'big'))[0]
|
||||
self.min_treshold = self.ECG_RA_min_threshold[self.draw_cnt]
|
||||
int1 = (shift_or(int.from_bytes(self.data_rx[self.pac_count + 27], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 28], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 29], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 30], "big", signed=False)))
|
||||
self.ECG_RA_max_threshold[self.draw_cnt] = struct.unpack('f', int1.to_bytes(4, 'big'))[0]
|
||||
self.max_treshold = self.ECG_RA_max_threshold[self.draw_cnt]
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 1:
|
||||
int1 = (
|
||||
shift_or(int.from_bytes(self.data_rx[self.pac_count + 23], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 24], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 25], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 26], "big", signed=False)))
|
||||
self.square_coef = struct.unpack('f', int1.to_bytes(4, 'big'))[0]
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 2:
|
||||
self.square_time = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 29], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 30], "big", signed=False)))
|
||||
self.max_search_time = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 27], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 28], "big", signed=False)))
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 3:
|
||||
int1 = int.from_bytes(self.data_rx[self.pac_count + 24], "big")
|
||||
self.sd_card = (int1 & 0x4) >> 2
|
||||
self.channel = int1 & 0x3
|
||||
self.dc_cut = (int1 & 0x8) >> 3
|
||||
self.spi_pot_set = int.from_bytes(self.data_rx[self.pac_count + 25], "big")
|
||||
self.bat_pers = int.from_bytes(self.data_rx[self.pac_count + 26], "big")
|
||||
self.bat_volt = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 27], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 28], "big", signed=False)))
|
||||
self.hv_volt = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 29], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 30], "big", signed=False)))
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 4:
|
||||
self.last_period[self.draw_cnt] = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 27], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 28], "big", signed=False)))
|
||||
self.last_period_stat = self.last_period[self.draw_cnt]
|
||||
self.max_time = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 25], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 26], "big", signed=False)))
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 5:
|
||||
self.Vs_cnt = (
|
||||
shift_or_24(int.from_bytes(self.data_rx[self.pac_count + 23], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 24], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 25], "big", signed=False)))
|
||||
self.Vp_cnt = (
|
||||
shift_or_24(int.from_bytes(self.data_rx[self.pac_count + 28], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 29], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 30], "big", signed=False)))
|
||||
self.Vn_cnt = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 26], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 27], "big", signed=False),
|
||||
))
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 6:
|
||||
self.fibr_tres = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 23], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 24], "big", signed=False)
|
||||
))
|
||||
self.tachy_2_tres = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 25], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 26], "big", signed=False)
|
||||
))
|
||||
self.tachy_1_tres = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 27], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 28], "big", signed=False)
|
||||
))
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 7:
|
||||
self.fibr_cnt = int.from_bytes(self.data_rx[self.pac_count + 25], "big")
|
||||
self.tachy_2_cnt = int.from_bytes(self.data_rx[self.pac_count + 26], "big")
|
||||
self.tachy_1_cnt = int.from_bytes(self.data_rx[self.pac_count + 27], "big")
|
||||
self.norm_cnt = int.from_bytes(self.data_rx[self.pac_count + 28], "big")
|
||||
self.fibr_max_tres = int.from_bytes(self.data_rx[self.pac_count + 29], "big")
|
||||
self.filt_period = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 23], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 24], "big", signed=False)))
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 8:
|
||||
self.now_energy = (shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 27], "big",
|
||||
signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 28], "big",
|
||||
signed=False))) / 10
|
||||
self.min_energy = (int.from_bytes(self.data_rx[self.pac_count + 26], "big",
|
||||
signed=False)) // 10
|
||||
self.max_energy = (shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 29], "big",
|
||||
signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 30], "big",
|
||||
signed=False))) // 10
|
||||
int1 = int.from_bytes(self.data_rx[self.pac_count + 23], "big")
|
||||
self.hv_step_cnt = int1 & 0xF
|
||||
self.hv_step_number = (int1 & 0xF0) >> 4
|
||||
|
||||
if self.counter[self.draw_cnt] % 10 == 9:
|
||||
self.hv_blind_time = (
|
||||
shift_or_16(int.from_bytes(self.data_rx[self.pac_count + 23], "big", signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 24], "big", signed=False)))
|
||||
self.standby_timer = (
|
||||
shift_or(int.from_bytes(self.data_rx[self.pac_count + 27], "big",
|
||||
signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 28], "big",
|
||||
signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 29], "big",
|
||||
signed=False),
|
||||
int.from_bytes(self.data_rx[self.pac_count + 30], "big",
|
||||
signed=False)
|
||||
)) // 1000
|
||||
int1 = int.from_bytes(self.data_rx[self.pac_count + 25], "big", signed=False)
|
||||
self.redet_bad = int1 & 0xF
|
||||
self.redet_num = (int1 & 0xF0) >> 4
|
||||
15
project/main.py
Normal file
15
project/main.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""
|
||||
Запуск программы
|
||||
Два режим работы:
|
||||
1. чтение из com-порта, отображение, запись данных, передача настраиваемых параметров в стимулятор,
|
||||
управелние перезагрузкой
|
||||
2. чтение данных из файла и отображение
|
||||
"""
|
||||
|
||||
import project.view
|
||||
|
||||
|
||||
app = project.view.app
|
||||
window = project.view.Ui()
|
||||
window.show()
|
||||
app.exec_()
|
||||
58
project/model.py
Normal file
58
project/model.py
Normal file
@ -0,0 +1,58 @@
|
||||
"""Получение сырых данных из различных источников. Работа с файлами"""
|
||||
import datetime
|
||||
import os.path
|
||||
|
||||
# from PyQt5.QtSerialPort import QSerialPort
|
||||
|
||||
|
||||
# def on_read():
|
||||
#
|
||||
#
|
||||
#
|
||||
# # создание соединения с устройством для передачи данных
|
||||
# serial = QSerialPort()
|
||||
# serial.setBaudRate(115200)
|
||||
# serial.readyRead.connect(on_read)
|
||||
|
||||
|
||||
def get_data_bin(file_name, data_in):
|
||||
"""
|
||||
Получение данных из бинарного файла
|
||||
:param file_name: файл
|
||||
:param data_in: объект модели данных для отрисовки
|
||||
:return: массив данных
|
||||
"""
|
||||
f = open(file_name, 'rb')
|
||||
size = os.path.getsize(file_name)
|
||||
chunk = f.read(size)
|
||||
data_in.data_length = int(size / 32)
|
||||
data_in.update_data(data_in.data_length)
|
||||
f.close()
|
||||
return chunk
|
||||
|
||||
|
||||
def write_file():
|
||||
"""
|
||||
Запись файла
|
||||
"""
|
||||
# создание папки data(если её нет)
|
||||
if not os.path.isdir("data"):
|
||||
os.mkdir("data")
|
||||
|
||||
file_name = datetime.datetime.now().strftime('%d%m%y%H%M%S')
|
||||
file = open('data/' + file_name + '.txt', 'wb')
|
||||
return file, file_name
|
||||
|
||||
|
||||
def write_in_file(file, rx):
|
||||
file.write(rx)
|
||||
|
||||
|
||||
def close_file(file):
|
||||
"""
|
||||
Закончить запись и закрыть файл
|
||||
"""
|
||||
if file != "":
|
||||
file.close()
|
||||
|
||||
return ""
|
||||
626
project/view.py
Normal file
626
project/view.py
Normal file
@ -0,0 +1,626 @@
|
||||
"""Отображение данных"""
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
import datetime
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
from PyQt5.QtCore import QIODevice
|
||||
from PyQt5.QtSerialPort import QSerialPortInfo, QSerialPort
|
||||
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5 import QtWidgets, uic, QtGui
|
||||
from PyQt5.QtWidgets import QFileDialog
|
||||
from PyQt5 import QtCore
|
||||
from pyqtgraph import TextItem
|
||||
import pyqtgraph as pg
|
||||
import project.controller as my_data
|
||||
from project import mes
|
||||
|
||||
# выбор режима чения из файла или из com-порта
|
||||
MODE_COM_PORT = False
|
||||
MODE_FILE = False
|
||||
|
||||
# объявили экземпляры класса, для обработки данных из файла или из com-порта
|
||||
data_IN_FILE = my_data.DataClass()
|
||||
data_IN_COM = my_data.DataClass()
|
||||
|
||||
# создание приложения
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
|
||||
class Ui(QtWidgets.QMainWindow):
|
||||
"""Класс отображения данных"""
|
||||
|
||||
def __init__(self):
|
||||
# создание графического отображения
|
||||
super(Ui, self).__init__()
|
||||
uic.loadUi('design_viewer.ui', self)
|
||||
self.setWindowTitle("Pacemaker viewer")
|
||||
self.setWindowIcon(QtGui.QIcon("logo.ICO"))
|
||||
|
||||
self.counter_com_port = [] # координата x для вывода графиков
|
||||
|
||||
self.point = 0 # координата, откуда начинать отрисовку, если используется полоса прокрутки??
|
||||
self.file = "" # файл
|
||||
self.file_name = "" # наименование файла
|
||||
|
||||
# Создание графиков
|
||||
self.graph_init()
|
||||
|
||||
def set_up_controls():
|
||||
"""
|
||||
Настройка элементов управления в визуале
|
||||
"""
|
||||
self.OpenB.setVisible(False)
|
||||
self.CloseB.setVisible(False)
|
||||
self.Play.setVisible(False)
|
||||
self.Pause.setVisible(False)
|
||||
self.horizontalScrollBar.setVisible(False)
|
||||
self.horizontalScrollBar.valueChanged.connect(self.on_scroll)
|
||||
self.radioButton_work_mode_com_port.toggled.connect(self.check_work_mode_viewer)
|
||||
self.OpenB.clicked.connect(self.on_open)
|
||||
self.CloseB.clicked.connect(self.on_close)
|
||||
self.Write_file.clicked.connect(self.write_file)
|
||||
self.Close_file.clicked.connect(self.close_file)
|
||||
self.Read.clicked.connect(self.on_start)
|
||||
self.Stop.clicked.connect(self.on_stop)
|
||||
self.Restart.clicked.connect(self.restart)
|
||||
self.SD_card_Box.currentTextChanged.connect(self.set_SD_card)
|
||||
self.Send_mode_Box.currentTextChanged.connect(self.set_send_mode)
|
||||
self.ComL.addItems(self.update_list())
|
||||
self.horizontalScrollBar.valueChanged.connect(self.on_scroll)
|
||||
self.RA_scale_BOX.currentTextChanged.connect(self.on_scale_change)
|
||||
self.Low_V_spinBox.valueChanged.connect(self.set_fibr_cnt_max)
|
||||
self.Low_V_spinBox_3.valueChanged.connect(self.set_tachy_2_cnt_max)
|
||||
self.Low_V_spinBox_4.valueChanged.connect(self.set_tachy_1_cnt_max)
|
||||
|
||||
self.RA_max_time_ms_BOX.editTextChanged.connect(self.set_RA_max_time_ms_BOX)
|
||||
self.RA_min_sensitivity_BOX.editTextChanged.connect(self.set_RA_min_sensitivity_BOX)
|
||||
self.RA_max_sensitivity_BOX.editTextChanged.connect(self.set_RA_max_sensitivity_BOX)
|
||||
self.RA_square_time_ms_BOX.editTextChanged.connect(self.set_RA_square_time_ms_BOX)
|
||||
self.RA_square_coeff_BOX.editTextChanged.connect(self.set_RA_square_coeff_BOX)
|
||||
self.RA_all_time_ms_BOX.editTextChanged.connect(self.set_RA_all_time_ms_BOX)
|
||||
self.Signal_Box.currentIndexChanged.connect(self.set_Signal_Box)
|
||||
self.Channel_Box.currentIndexChanged.connect(self.set_Channel_Box)
|
||||
self.DC_cut_Box.currentIndexChanged.connect(self.set_DC_cut_Box)
|
||||
|
||||
self.High_Tf_spinBox.valueChanged.connect(self.set_High_Tf_spinBox)
|
||||
self.High_Tt2_spinBox.valueChanged.connect(self.set_High_Tt2_spinBox)
|
||||
self.High_Tt1_spinBox.valueChanged.connect(self.set_High_Tt1_spinBox)
|
||||
self.Low_V_spinBox.valueChanged.connect(self.set_Low_V_spinBox)
|
||||
|
||||
self.Work_Mode_pacemaker.editTextChanged.connect(self.set_Work_Mode_pacemaker)
|
||||
self.hv_step_number_spinBox.valueChanged.connect(self.set_hv_step_number_spinBox)
|
||||
self.min_energy_spinBox.valueChanged.connect(self.set_min_energy_spinBox)
|
||||
self.max_energy_spinBox.valueChanged.connect(self.set_max_energy_spinBox)
|
||||
self.hv_blind_time_spinBox.valueChanged.connect(self.set_hv_blind_time_spinBox)
|
||||
self.standby_timer_spinBox.valueChanged.connect(self.set_standby_timer_spinBox)
|
||||
self.redet_num_spinBox.valueChanged.connect(self.set_redet_num_spinBox)
|
||||
self.redet_bad_spinBox.valueChanged.connect(self.set_redet_bad_spinBox)
|
||||
self.Spi_spot_set_spinBox.valueChanged.connect(self.set_Spi_spot_set_spinBox)
|
||||
|
||||
set_up_controls()
|
||||
|
||||
# создание соединения с устройством для передачи данных
|
||||
self.serial = QSerialPort()
|
||||
self.serial.setBaudRate(115200)
|
||||
self.serial.readyRead.connect(self.on_read)
|
||||
|
||||
self.send_mode = 0
|
||||
|
||||
# Создание таймера для отрисовки графиков
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.timerEvent)
|
||||
self.timer.start(20)
|
||||
|
||||
# Создание таймера для отображения параметров стимулятора
|
||||
self.timer_LS = QtCore.QTimer()
|
||||
self.timer_LS.timeout.connect(self.timerEvent_param)
|
||||
self.timer_LS.start(2000)
|
||||
|
||||
def write_file(self):
|
||||
"""
|
||||
Открытие на запись файла
|
||||
"""
|
||||
self.file, file_name = my_data.write_file()
|
||||
self.Write_file.setStyleSheet('background-color: green; color: white')
|
||||
self.Fname_w_label.setText(os.path.abspath(file_name))
|
||||
|
||||
def close_file(self):
|
||||
"""
|
||||
Закончить запись и закрыть файл
|
||||
"""
|
||||
self.Fname_w_label.setText('')
|
||||
self.Write_file.setStyleSheet('background-color: grey; color: black')
|
||||
self.file = my_data.close_file(self.file)
|
||||
|
||||
def restart(self):
|
||||
"""
|
||||
Перезагрузка устройства
|
||||
"""
|
||||
param = 1
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x1D, param, 1, int)
|
||||
|
||||
def timerEvent_param(self):
|
||||
"""Запуск по таймеру, для синхронизации параметров стимулятора"""
|
||||
if MODE_COM_PORT:
|
||||
self.synchronise_param(data_IN_COM)
|
||||
if MODE_FILE:
|
||||
self.synchronise_param(data_IN_FILE)
|
||||
# отрисовка
|
||||
app.processEvents()
|
||||
|
||||
def synchronise_param(self, data_in):
|
||||
"""Синхронизация параметров стимулятора"""
|
||||
self.SD_card_Box.setCurrentIndex(int(data_in.sd_card))
|
||||
self.Signal_Box.setCurrentIndex(int(data_in.signal))
|
||||
self.Channel_Box.setCurrentIndex(int(data_in.channel))
|
||||
self.DC_cut_Box.setCurrentIndex(int(data_in.dc_cut))
|
||||
self.RA_max_time_ms_BOX.setEditText(str(data_in.max_search_time))
|
||||
self.RA_min_sensitivity_BOX.setEditText(str(data_in.min_treshold))
|
||||
self.RA_max_sensitivity_BOX.setEditText(str(data_in.max_treshold))
|
||||
self.RA_square_time_ms_BOX.setEditText(str(data_in.square_time))
|
||||
self.RA_square_coeff_BOX.setEditText(f'{data_in.square_coef: .2f}')
|
||||
self.RA_all_time_ms_BOX.setEditText(str(data_in.max_time))
|
||||
self.High_Tf_spinBox.setValue(data_in.fibr_tres)
|
||||
self.High_Tt2_spinBox.setValue(data_in.tachy_2_tres)
|
||||
self.High_Tt1_spinBox.setValue(data_in.tachy_1_tres)
|
||||
self.Low_V_spinBox.setValue(data_in.fibr_max_tres)
|
||||
self.hv_step_number_spinBox.setValue(data_in.hv_step_number)
|
||||
self.min_energy_spinBox.setValue(data_in.min_energy)
|
||||
self.max_energy_spinBox.setValue(data_in.max_energy)
|
||||
self.hv_blind_time_spinBox.setValue(data_in.hv_blind_time)
|
||||
self.standby_timer_spinBox.setValue(data_in.standby_timer)
|
||||
self.redet_num_spinBox.setValue(data_in.redet_num)
|
||||
self.redet_bad_spinBox.setValue(data_in.redet_bad)
|
||||
self.Work_Mode_pacemaker.setCurrentIndex(data_in.Work_Mode_pacemaker)
|
||||
self.Spi_spot_set_spinBox.setValue(data_in.spi_pot_set)
|
||||
|
||||
def timerEvent(self):
|
||||
if MODE_COM_PORT:
|
||||
self.draw_data(data_IN_COM)
|
||||
if MODE_FILE:
|
||||
scale = self.get_scale()
|
||||
data_IN_FILE.data_length = scale * my_data.DATA_RATE
|
||||
data_IN_FILE.parse_list(data_IN_FILE.chunk[data_IN_FILE.i:data_IN_FILE.i + 640])
|
||||
data_IN_FILE.i += 640
|
||||
self.graph_1.setXRange(self.counter_com_port[0], self.counter_com_port[0] + scale)
|
||||
self.graph_2.setXRange(self.counter_com_port[0], self.counter_com_port[0] + scale)
|
||||
self.graph_3.setXRange(self.counter_com_port[0], self.counter_com_port[0] + scale)
|
||||
self.draw_data(data_IN_FILE)
|
||||
|
||||
# отрисовка
|
||||
app.processEvents()
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
Старт обменя с com-портом
|
||||
"""
|
||||
global MODE_COM_PORT
|
||||
data_IN_COM.clear_data()
|
||||
self.serial.setPortName(self.ComL.currentText())
|
||||
self.serial.open(QIODevice.ReadWrite)
|
||||
self.serial.flush()
|
||||
self.serial.write(mes.encode())
|
||||
self.on_close()
|
||||
MODE_COM_PORT = True
|
||||
self.graph_init()
|
||||
scale = self.get_scale()
|
||||
data_IN_COM.data_length = scale * my_data.DATA_RATE
|
||||
data_IN_COM.update_data(data_IN_COM.data_length)
|
||||
self.counter_com_port = list(map(lambda x: x / my_data.DATA_RATE, range(0, data_IN_COM.data_length)))
|
||||
|
||||
def graph_init(self):
|
||||
"""
|
||||
Создние графиков
|
||||
"""
|
||||
# Создание графиков
|
||||
self.curve10 = self.graph_1.plot(pen=(0, 0, 255))
|
||||
self.curve20 = self.graph_2.plot(pen=(0, 0, 0))
|
||||
pen_din_tres = pg.mkPen(color=(255, 0, 0), width=1)
|
||||
self.curve21 = self.graph_2.plot(pen=pen_din_tres)
|
||||
pen_peak = None
|
||||
self.curve22 = self.graph_2.plot(pen=pen_peak)
|
||||
self.curve23 = self.graph_2.plot(pen=pen_peak)
|
||||
pen_min_tres = pg.mkPen(color=(0, 255, 0), width=2, style=QtCore.Qt.DashLine)
|
||||
pen_max_tres = pg.mkPen(color=(0, 0, 255), width=2, style=QtCore.Qt.DashLine)
|
||||
self.curve24 = self.graph_2.plot(pen=pen_min_tres)
|
||||
self.curve25 = self.graph_2.plot(pen=pen_max_tres)
|
||||
self.curve26 = self.graph_2.plot(pen=(0, 0, 0))
|
||||
self.curve30 = self.graph_3.plot(pen=(0, 0, 255))
|
||||
|
||||
def edit_graph(graph):
|
||||
"""
|
||||
Форматирование полей графиков
|
||||
:param graph: поле графика
|
||||
"""
|
||||
graph.showGrid(x=True, y=True)
|
||||
graph.setBackground('w')
|
||||
# делаем поле графика нечувствительным к мышке
|
||||
graph.setMouseEnabled(x=False, y=False)
|
||||
graph.hideButtons()
|
||||
graph.getPlotItem().setMenuEnabled(False)
|
||||
graph.addLegend(enableMouse=False)
|
||||
|
||||
edit_graph(self.graph_1)
|
||||
edit_graph(self.graph_2)
|
||||
edit_graph(self.graph_3)
|
||||
|
||||
def graph_clear(self):
|
||||
"""
|
||||
Очистка графиков
|
||||
"""
|
||||
self.graph_1.clear()
|
||||
self.graph_2.clear()
|
||||
self.graph_3.clear()
|
||||
|
||||
def on_read(self):
|
||||
"""
|
||||
Чтение серии данных
|
||||
"""
|
||||
rx = self.serial.readAll()
|
||||
if self.file != "":
|
||||
my_data.write_in_file(self.file, rx)
|
||||
data_IN_COM.parse_list(rx)
|
||||
|
||||
def draw_data(self, data_in):
|
||||
"""
|
||||
Отрисовка данных
|
||||
:param data_in: модель данных
|
||||
"""
|
||||
self.curve10.setData(self.counter_com_port, data_in.ECG_RA)
|
||||
self.curve20.setData(self.counter_com_port, data_in.ECG_RA_pos_sig)
|
||||
self.curve21.setData(self.counter_com_port, data_in.ECG_RA_din_threshold)
|
||||
self.curve26.setData(self.counter_com_port, np.zeros(len(data_in.ECG_RA)))
|
||||
self.curve24.setData(self.counter_com_port, data_in.ECG_RA_min_threshold)
|
||||
self.curve25.setData(self.counter_com_port, data_in.ECG_RA_max_threshold)
|
||||
self.curve30.setData(self.counter_com_port, data_in.Last_RR_poz_rel)
|
||||
self.Vs_cnt_label.setText(str(data_in.Vs_cnt))
|
||||
self.Vp_cnt_label.setText(str(data_in.Vp_cnt))
|
||||
self.Vn_cnt_label.setText(str(data_in.Vn_cnt))
|
||||
self.U_batt_label.setText(str(float(data_in.bat_volt / 1000)))
|
||||
self.U_batt_p_label.setText(str(data_in.bat_pers))
|
||||
self.U_cap_label.setText(str(float(data_in.hv_volt / 100)))
|
||||
self.now_energy_label.setText(str(data_in.now_energy))
|
||||
self.filt_period_label.setText(str(data_in.filt_period))
|
||||
self.last_period_label.setText(str(data_in.last_period_stat))
|
||||
self.hv_step_cnt_label.setText(str(data_in.hv_step_cnt))
|
||||
|
||||
self.progressBar_f.setValue(data_in.fibr_cnt)
|
||||
self.progressBar_t2.setValue(data_in.tachy_2_cnt)
|
||||
self.progressBar_t1.setValue(data_in.tachy_1_cnt)
|
||||
self.progressBar_n.setValue(data_in.norm_cnt)
|
||||
|
||||
if data_in.terapy_now == 0:
|
||||
self.terapy_nowB.setStyleSheet('background-color: green; color: white')
|
||||
self.terapy_nowB.setText("Поиск корзин")
|
||||
if data_in.terapy_now == 1:
|
||||
self.terapy_nowB.setStyleSheet('background-color: red; color: white')
|
||||
self.terapy_nowB.setText("Терапия фибрилляции")
|
||||
|
||||
if data_in.terapy_now == 2:
|
||||
self.terapy_nowB.setStyleSheet('background-color: yellow; color: black')
|
||||
self.terapy_nowB.setText("Терапия сильной тахикардии")
|
||||
|
||||
if data_in.terapy_now == 3:
|
||||
self.terapy_nowB.setStyleSheet('background-color: yellow; color: black')
|
||||
self.terapy_nowB.setText("Терапия слабой тахикардии")
|
||||
|
||||
if data_in.sub_mode == 0:
|
||||
self.sub_modeB.setStyleSheet('background-color: green; color: white')
|
||||
self.sub_modeB.setText("Терапия неактивна")
|
||||
if data_in.sub_mode == 1:
|
||||
self.sub_modeB.setStyleSheet('background-color: blue; color: white')
|
||||
self.sub_modeB.setText("Зарядка конденсатора")
|
||||
|
||||
if data_in.sub_mode == 2:
|
||||
self.sub_modeB.setStyleSheet('background-color: yellow; color: black')
|
||||
self.sub_modeB.setText("Редетекция")
|
||||
|
||||
if data_in.sub_mode == 3:
|
||||
self.sub_modeB.setStyleSheet('background-color: red; color: white')
|
||||
self.sub_modeB.setText("Стимуляция")
|
||||
if data_in.sub_mode == 4:
|
||||
self.sub_modeB.setStyleSheet('background-color: grey; color: black')
|
||||
self.sub_modeB.setText("Терапия не сработала")
|
||||
|
||||
# так удаляются элементы
|
||||
# стирание элементов спереди
|
||||
all_items = self.graph_1.items() # Получаем все элементы
|
||||
text_items = [item for item in all_items if isinstance(item, TextItem)]
|
||||
for text_item in text_items:
|
||||
if data_in.draw_cnt / my_data.DATA_RATE - 0.02 < text_item.pos().x() and data_in.draw_cnt / my_data.DATA_RATE + 0.1 > text_item.pos().x():
|
||||
self.graph_1.removeItem(text_item)
|
||||
|
||||
if data_in.V_print:
|
||||
data_in.V_print = 0
|
||||
if data_in.draw_last_QRS == 0:
|
||||
self.text = pg.TextItem('Vs\nT:' + str(data_in.draw_last_period))
|
||||
self.text.setColor('green')
|
||||
self.text.setFont(QFont('Arial', 7))
|
||||
self.text.setPos(data_in.draw_cnt_last / my_data.DATA_RATE, (0 if data_in.dc_cut else 15))
|
||||
self.graph_1.addItem(self.text)
|
||||
if data_in.draw_last_QRS == 1:
|
||||
self.text = pg.TextItem('Vp\nT:' + str(data_in.draw_last_period))
|
||||
self.text.setColor('red')
|
||||
self.text.setPos(data_in.draw_cnt_last / my_data.DATA_RATE, (0 if data_in.dc_cut else 15))
|
||||
self.graph_1.addItem(self.text)
|
||||
|
||||
def on_stop(self):
|
||||
self.close_file()
|
||||
global MODE_COM_PORT
|
||||
MODE_COM_PORT = False
|
||||
self.graph_clear()
|
||||
# Создание графиков
|
||||
self.graph_init()
|
||||
self.serial.flush()
|
||||
self.serial.close()
|
||||
data_IN_COM.clear_data()
|
||||
self.terapy_nowB.setStyleSheet('background-color: lightGray; color: black')
|
||||
self.terapy_nowB.setText("")
|
||||
self.sub_modeB.setStyleSheet('background-color: lightGray; color: black')
|
||||
self.sub_modeB.setText("")
|
||||
self.last_period_label.setText("")
|
||||
self.filt_period_label.setText("")
|
||||
self.Vs_cnt_label.setText("")
|
||||
self.Vp_cnt_label.setText("")
|
||||
self.Vn_cnt_label.setText("")
|
||||
self.progressBar_f.setValue(0)
|
||||
self.progressBar_t2.setValue(0)
|
||||
self.progressBar_t1.setValue(0)
|
||||
self.progressBar_n.setValue(0)
|
||||
self.hv_step_cnt_label.setText("")
|
||||
self.now_energy_label.setText("")
|
||||
self.HR_label.setText("")
|
||||
self.OpenB.setEnabled(True)
|
||||
self.CloseB.setEnabled(True)
|
||||
|
||||
def on_open(self):
|
||||
"""
|
||||
Открыть файл для чтения
|
||||
"""
|
||||
global MODE_FILE
|
||||
self.on_close()
|
||||
file, _ = QFileDialog.getOpenFileName(self, 'Open File')
|
||||
if file:
|
||||
data_IN_FILE.clear_data()
|
||||
self.file_name = file
|
||||
self.Fname_label.setText(self.file_name)
|
||||
my_data.get_data_bin(self.file_name, data_IN_FILE)
|
||||
self.counter_com_port = list(map(lambda x: x / my_data.DATA_RATE, range(0, data_IN_FILE.data_length)))
|
||||
MODE_FILE = True
|
||||
|
||||
def draw_file(self):
|
||||
scale = self.get_scale()
|
||||
size = scale * my_data.DATA_RATE
|
||||
for x in range(self.point, data_IN_FILE.data_length, 20):
|
||||
self.curve10.setData(self.counter_com_port[x: x + size], data_IN_FILE.ECG_RA[x: x + size])
|
||||
self.curve20.setData(self.counter_com_port[x: x + size], data_IN_FILE.ECG_RA_pos_sig[x: x + size])
|
||||
self.curve21.setData(self.counter_com_port[x: x + size], data_IN_FILE.ECG_RA_din_threshold[x: x + size])
|
||||
self.curve26.setData(self.counter_com_port[x: x + size], np.zeros(len(self.counter_com_port[x: x + size])))
|
||||
self.curve24.setData(self.counter_com_port[x: x + size], data_IN_FILE.ECG_RA_min_threshold[x: x + size])
|
||||
self.curve25.setData(self.counter_com_port[x: x + size], data_IN_FILE.ECG_RA_max_threshold[x: x + size])
|
||||
self.curve30.setData(self.counter_com_port[x: x + size], data_IN_FILE.Last_RR_poz_rel[x: x + size])
|
||||
app.processEvents()
|
||||
time.sleep(0.05)
|
||||
|
||||
def re_scale(self):
|
||||
scale = self.get_scale()
|
||||
if len(data_IN_FILE.counter) != 0:
|
||||
position = self.horizontalScrollBar.value()
|
||||
self.point = int((len(data_IN_FILE.counter) / my_data.DATA_RATE) * (position / 1000))
|
||||
|
||||
def on_scroll(self):
|
||||
self.re_scale()
|
||||
|
||||
def on_close(self):
|
||||
"""
|
||||
Закрыть открытый для чтения файл
|
||||
"""
|
||||
global MODE_FILE
|
||||
MODE_FILE = False
|
||||
# очистка графиков
|
||||
self.graph_clear()
|
||||
# Пересоздание графиков
|
||||
self.graph_init()
|
||||
# очистка модели
|
||||
data_IN_FILE.clear_data()
|
||||
|
||||
self.Fname_label.setText('')
|
||||
|
||||
def get_scale(self):
|
||||
scale = int(self.RA_scale_BOX.currentText())
|
||||
return scale
|
||||
|
||||
def on_scale_change(self):
|
||||
scale = self.get_scale()
|
||||
data_IN_COM.data_length = scale * my_data.DATA_RATE
|
||||
data_IN_COM.update_data(data_IN_COM.data_length)
|
||||
|
||||
if MODE_FILE:
|
||||
self.draw_file()
|
||||
|
||||
all_items = self.graph_1.items() # Получаем все элементы
|
||||
text_items = [item for item in all_items if isinstance(item, TextItem)]
|
||||
for text_item in text_items:
|
||||
self.graph_1.removeItem(text_item)
|
||||
self.counter_com_port = list(map(lambda x: x / my_data.DATA_RATE, range(0, data_IN_COM.data_length)))
|
||||
|
||||
def update_list(self):
|
||||
"""Создание списка доступных для ввода/вывода портов"""
|
||||
portlist = [] # создадим пустой список в который и будем всё записывать
|
||||
ports = QSerialPortInfo().availablePorts() # доступные порты для обмена данными
|
||||
for port in ports:
|
||||
portlist.append(port.portName())
|
||||
return portlist
|
||||
|
||||
def check_work_mode_viewer(self):
|
||||
"""
|
||||
Выбор режима работы (чтение данных из файла/из com-порта), в случае чего некоторые элементы отображения
|
||||
становятся видимыми и доступными, а другие наоборот становятся недоступными.
|
||||
"""
|
||||
if self.radioButton_work_mode_com_port.isChecked():
|
||||
self.OpenB.setVisible(False)
|
||||
self.CloseB.setVisible(False)
|
||||
self.Play.setVisible(False)
|
||||
self.Pause.setVisible(False)
|
||||
self.Fname_label_com_port.setVisible(True)
|
||||
self.Fname_w_label.setVisible(True)
|
||||
self.ComL.setVisible(True)
|
||||
self.Read.setVisible(True)
|
||||
self.Stop.setVisible(True)
|
||||
self.Write_file.setVisible(True)
|
||||
self.Close_file.setVisible(True)
|
||||
self.Restart.setVisible(True)
|
||||
self.horizontalScrollBar.setVisible(False)
|
||||
else:
|
||||
self.on_stop()
|
||||
self.OpenB.setVisible(True)
|
||||
self.CloseB.setVisible(True)
|
||||
self.Play.setVisible(True)
|
||||
self.Pause.setVisible(True)
|
||||
self.Fname_label_com_port.setVisible(False)
|
||||
self.Fname_w_label.setVisible(False)
|
||||
self.ComL.setVisible(False)
|
||||
self.Read.setVisible(False)
|
||||
self.Stop.setVisible(False)
|
||||
self.Write_file.setVisible(False)
|
||||
self.Close_file.setVisible(False)
|
||||
self.Restart.setVisible(False)
|
||||
self.horizontalScrollBar.setVisible(True)
|
||||
|
||||
def set_send_mode(self):
|
||||
"""Установка режима передачи данных (Bluetooth/433)"""
|
||||
self.send_mode = int(self.Send_mode_Box.currentIndex())
|
||||
|
||||
# настройка параметров работы стимулятора с помощью программы
|
||||
def set_DC_cut_Box(self):
|
||||
"""Настройка вывода сигнала с/без постоянной составляющей"""
|
||||
param = int(self.DC_cut_Box.currentIndex())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x21, param, 1, int)
|
||||
|
||||
def set_Signal_Box(self):
|
||||
"""Настройка вывода сигнала с/без фильтром(а)"""
|
||||
param = int(self.Signal_Box.currentIndex())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x1E, param, 1, int)
|
||||
|
||||
def set_Channel_Box(self):
|
||||
"""Установка активного канала передачи"""
|
||||
param = int(self.Channel_Box.currentIndex())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x1F, param, 1, int)
|
||||
|
||||
def set_SD_card(self):
|
||||
"""Установка режима записи на карту памяти"""
|
||||
param = int(self.SD_card_Box.currentIndex())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x20, param, 1, int)
|
||||
|
||||
def set_Spi_spot_set_spinBox(self):
|
||||
"""Установка коэффициента усиления сигнала"""
|
||||
param = int(self.Spi_spot_set_spinBox.value())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x1C, param, 1, int)
|
||||
|
||||
def set_Work_Mode_pacemaker(self):
|
||||
"""Установка текущего режима работы стимулятора(без/с стимуляцией или в режиме принудительной стимуляции)"""
|
||||
param = int(self.Work_Mode_pacemaker.currentIndex()) # << 20
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x05, param, 2, int)
|
||||
|
||||
def set_RA_max_time_ms_BOX(self):
|
||||
"""Установка длительности поиска максимума"""
|
||||
param = int(self.RA_max_time_ms_BOX.currentText())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x0D, param, 2, int)
|
||||
|
||||
def set_RA_min_sensitivity_BOX(self):
|
||||
"""Установка минимальной чувствительности"""
|
||||
param = float(self.RA_min_sensitivity_BOX.currentText())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x01, param, 4, float)
|
||||
|
||||
def set_RA_max_sensitivity_BOX(self):
|
||||
"""Установка максимальной чувствительности"""
|
||||
param = float(self.RA_max_sensitivity_BOX.currentText())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x02, param, 4, float)
|
||||
|
||||
def set_RA_square_time_ms_BOX(self):
|
||||
"""Установка длительности защиты от Т волны"""
|
||||
param = int(self.RA_square_time_ms_BOX.currentText())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x07, param, 2, int)
|
||||
|
||||
def set_RA_square_coeff_BOX(self):
|
||||
"""Установка порога чувствительности Т волны"""
|
||||
param = float(self.RA_square_coeff_BOX.currentText())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x03, param, 4, float)
|
||||
|
||||
def set_RA_all_time_ms_BOX(self):
|
||||
"""Установка максимального размера RR-интервала"""
|
||||
param = int(self.RA_all_time_ms_BOX.currentText())
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x09, param, 2, int)
|
||||
|
||||
def set_High_Tf_spinBox(self):
|
||||
"""Установка длительности RR-интервала для определения события как фибрилляции"""
|
||||
param = self.High_Tf_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x10, param, 2, int)
|
||||
|
||||
def set_High_Tt2_spinBox(self):
|
||||
"""Установка длительности RR-интервала для определения события как тахикардии 2ст"""
|
||||
param = self.High_Tt2_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x11, param, 2, int)
|
||||
|
||||
def set_High_Tt1_spinBox(self):
|
||||
"""Установка длительности RR-интервала для определения события как тахикардии 1ст"""
|
||||
param = self.High_Tt1_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x12, param, 2, int)
|
||||
|
||||
def set_Low_V_spinBox(self):
|
||||
"""Установка порога корзины фибрилляции"""
|
||||
param = self.Low_V_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x13, param, 1, int)
|
||||
|
||||
def set_hv_step_number_spinBox(self):
|
||||
"""Установка количества ступеней BB - терапии"""
|
||||
param = self.hv_step_number_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x14, param, 1, int)
|
||||
|
||||
def set_min_energy_spinBox(self):
|
||||
"""Установка минимальной энергии стимулятора"""
|
||||
param = self.min_energy_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x15, param, 2, int)
|
||||
|
||||
def set_max_energy_spinBox(self):
|
||||
"""Установка максимальной энергии стимулятора"""
|
||||
param = self.max_energy_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x16, param, 2, int)
|
||||
|
||||
def set_hv_blind_time_spinBox(self):
|
||||
"""Установка времени слепоты после разряда"""
|
||||
param = self.hv_blind_time_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x18, param, 2, int)
|
||||
|
||||
def set_standby_timer_spinBox(self):
|
||||
"""Установка таймаута отключения"""
|
||||
param = self.standby_timer_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x1B, param, 4, int)
|
||||
|
||||
def set_redet_num_spinBox(self):
|
||||
"""Установка размера буффера редетекции"""
|
||||
param = self.redet_num_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x19, param, 1, int)
|
||||
|
||||
def set_redet_bad_spinBox(self):
|
||||
"""Установка порога буффера редетекции"""
|
||||
param = self.redet_bad_spinBox.value()
|
||||
my_data.state_packet(self.send_mode, self.serial, 0x1A, param, 1, int)
|
||||
|
||||
def set_fibr_cnt_max(self):
|
||||
"""Визуализация корзины фибрилляции"""
|
||||
# + отправить стимулятору
|
||||
self.progressBar_f.setMaximum(self.Low_V_spinBox.value())
|
||||
|
||||
def set_tachy_2_cnt_max(self):
|
||||
"""Визуализация корзины тахикардии 2ст"""
|
||||
self.progressBar_t2.setMaximum(self.Low_V_spinBox_3.value())
|
||||
|
||||
def set_tachy_1_cnt_max(self):
|
||||
"""Визуализация корзины тахикардии 1ст"""
|
||||
self.progressBar_t1.setMaximum(self.Low_V_spinBox_4.value())
|
||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# python12
|
||||
PyQt5~=5.15.11
|
||||
pyqtgraph~=0.13.7
|
||||
numpy~=2.2.3
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user