Скачиваний:
2
Добавлен:
04.09.2023
Размер:
1.33 Mб
Скачать

МИНОБРНАУКИ РОССИИ

Санкт-Петербургский государственный

электротехнический университет

«ЛЭТИ» им. В.И. Ульянова (Ленина)

Кафедра БТС

отчет

по курсовой работе

по дисциплине «Технологии разработки программных комплексов медицинских информационных систем»

Тема: Виртуальный симулятор для обучения СЛР

Студент гр. 7503

Дегилевич А.А.

Преподаватель

Калиниченко А.Н.

Санкт-Петербург

2022

КУРСОВАЯ РАБОТА

«ВИРТУАЛЬНЫЙ СИМУЛЯТОР ДЛЯ ОБУЧЕНИЯ СЛР»

Описание работы:

Основное меню представлено на рисунке 1. Оно представляет собой окно, на которое попадает пользователь при включении симулятора. Через главное меню можно перейти в следующие разделы симулятора:

1) Меню выбора режима, представленное на рисунке 2. При наведении на кнопку режима изменяется изображение превью режима и его описание на соответствующее. Существует три основных режима:

- Симуляция – основной режим симулятора, который будет разработан в дальнейшем, в котором пользователь будет проводить СЛР виртуальному пострадавшему.

- Тренировка компрессий – режим, чтобы отработать практический навык компрессий, в отрыве от остального алгоритма (подробнее дальше).

Рисунок 1 – Главное меню

Рисунок 2 – Меню выбор режимов

Теоретический тест – режим для проверки теоретических знаний пользователя (рисунок 3).

Рисунок 3 – Режим теста

2) Теоретический раздел (рисунок 4). В этом разделе представлены основные теоретические знания о СЛР, а именно алгоритм, основные нюансы и ошибки проведения.

Рисунок 4 – Теоретический раздел

3) Меню настроек. В этом разделе содержаться настройки симулятора. На данный момент единственной регулируемой настройкой является язык симулятора.

Также разработан экран загрузки, представленный на рисунке 5. Он необходим для подгруздки необходимый объектов и данных при переходе между режимами. Во время загрузки пользователю демонстрируется советы и интересные факты о СЛР, которые помогут ему усвоить теоретические знания.

Рисунок 5 – Экран загрузки

Режим тренировки компрессиям работает следующим образом (рисунок 6). Справа налево по экрану двигаются полоски с частотой следования 110 раз/мин. Посередине экрана расположена область при нахождении полоски, в которой, пользователь может нажать на кнопку space, чтобы засчиталась компрессия. В зависимости от того, насколько точно пользователь попал в тайминг отображается различный визуальный эффект, и полоска окрашивается в соответственный цвет. Если полоска прошла зону, а компрессия не произошла то она становится красной.

Рисунок 6 – Режим оценки компрессий

В нижней части экрана расположены две динамические шкалы с цифровой расшифровкой. На этих маркерах после каждой компрессии отображается значение текущей частоты и глубины компрессий соответственно. Вместе с изменением числа перемещается указатель на шкале, который визуально объясняет пользователю в какую сторону ему следует изменить скорость или глубину компрессий.

Для дополнительной мотивации при обучении режим выполнен в игровом формате. За каждую успешно выполненную компрессию оператор получает очки, которые отображаются в верхнем левом углу.

После того как будут совершены 30 компрессий открывается окно результатов, в котором показывается итоговый счет, среднее значение частоты и глубины компрессий.

КОД ПРОГРАММЫ

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

using TMPro;

using System.Linq;

using System;

using UnityEngine.Localization.Settings;

public class BeatScroller : MonoBehaviour

{

private GameObject BeatScrollerGO;

private GameObject Beat;

public float Tempo;

private float Speed;

public bool Started = false;

private float time;

private float timeLeft = 0f;

public List<float> ButtonPressTime;

public List<float> TimeBetween;

public List<int> ButtonPressClass;

public List<float> Depth;

public GameObject BeatSprite;

public TMP_Text ScoreText;

private string ScoreLocalize;

public static BeatScroller instance;

private int Score = 0;

private int ScorePerBeat = 50;

private int NumBeats = 0;

private int NumBeatsPassed = 0;

private int NumBeatsPressed = 0;

public GameObject ResultsUI, ScoreUI;

public TMP_Text FreqResultText;

public TMP_Text DepthResultText;

public double MeanFreq;

public double MeanDepth;

public TMP_Text BPMLevelText;

public TMP_Text DepthLevelText;

public GameObject BPMPointer;

public GameObject DepthPointer;

private double IdealDepth = 5;

private double BPMHigh;

private double BPMLow;

private double DepthHigh;

private double DepthLow;

void Start()

{

instance = this;

BeatScrollerGO = gameObject;

Speed = Tempo * 8 / 60f;

time = 1 / (Tempo / 60f);

ScoreLocalize = LocalizationSettings.StringDatabase.GetLocalizedString("BPMTrain", "Score");

ScoreText.text = ScoreLocalize + Score;

BPMHigh = Tempo + 30;

BPMLow = Tempo - 30;

DepthHigh = IdealDepth + 3;

DepthLow = IdealDepth - 3;

}

void Update()

{

if (Started)

{

if (timeLeft > 0)

{

timeLeft -= Time.deltaTime;

}

else

{

if (NumBeats < 30)

{

SpawnBeat();

NumBeats++;

}

else if (NumBeatsPassed < 30)

{

timeLeft = time;

NumBeats++;

}

else

{

ShowResults();

}

}

transform.position -= new Vector3(Speed * Time.deltaTime, 0f, 0f);

}

}

void SpawnBeat()

{

Beat = Instantiate(BeatSprite, new Vector3(22, 0, 0), transform.rotation);

Beat.transform.SetParent(BeatScrollerGO.transform);

timeLeft = time;

}

public void BeatHit(int Multiplier)

{

Score += ScorePerBeat * Multiplier;

ScoreText.text = ScoreLocalize + Score;

ButtonPressTime.Add(NumBeats * time - timeLeft);

ButtonPressClass.Add(Multiplier);

if (NumBeatsPressed > 0)

{

TimeBetween.Add(ButtonPressTime[NumBeatsPressed] - ButtonPressTime[NumBeatsPressed - 1]);

double CurrentBPM = Math.Round(60 / TimeBetween[NumBeatsPressed - 1], 0);

BPMLevelText.text = CurrentBPM.ToString();

double PercentBPM = (CurrentBPM - BPMLow)/(BPMHigh - BPMLow);

BPMPointer.GetComponent<Pointer>().ChangeDestination(PercentBPM);

}

else

BPMLevelText.text = "110";

NumBeatsPressed++;

NumBeatsPassed++;

}

public void DepthScore(float CurrentDepth)

{

DepthLevelText.text = Math.Round(CurrentDepth, 2).ToString();

Depth.Add(CurrentDepth);

double PercentDepth = (CurrentDepth - DepthLow) / (DepthHigh - DepthLow);

DepthPointer.GetComponent<Pointer>().ChangeDestination(PercentDepth);

}

public void BeatMiss()

{

NumBeatsPassed++;

}

void ShowResults()

{

MeanFreq = Math.Round(60 / TimeBetween.Average(), 2);

MeanDepth = Math.Round(Depth.Average(), 2);

BPMModeManager.instance.ShowResults();

}

}

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

public class BeatObject : MonoBehaviour

{

public bool CanBePressed;

public KeyCode KeyToPress;

private SpriteRenderer SR;

private bool Hit;

private float Depth = 0;

private AudioSource AudioSource;

public AudioClip BeatSound;

public GameObject GreatEffect, PerfectEffect, MissEffect;

// Start is called before the first frame update

void Start()

{

SR = GetComponent<SpriteRenderer>();

Destroy(gameObject, 3f);

}

// Update is called once per frame

void Update()

{

if(Input.GetKeyDown(KeyToPress))

{

if(CanBePressed)

{

Hit = true;

if (Mathf.Abs(transform.position.x) > 0.5)

{

BeatScroller.instance.BeatHit(1);

SR.color = new Color(0.4f, 1f, 0.4f);

Instantiate(GreatEffect, new Vector3(0, 0, 0), transform.rotation);

GetComponent<BoxCollider2D>().enabled = false;

}

else

{

BeatScroller.instance.BeatHit(2);

SR.color = new Color(1f, 1f, 0.4f);

Instantiate(PerfectEffect, new Vector3(0, 0, 0), transform.rotation);

GetComponent<BoxCollider2D>().enabled = false;

}

BeatScroller.instance.DepthScore(Depth);

//AudioSource.PlayOneShot(BeatSound, 1);

}

else

{

}

}

}

private void OnTriggerEnter2D(Collider2D other)

{

if (other.tag == "Activator")

{

CanBePressed = true;

}

}

private void OnTriggerExit2D(Collider2D other)

{

if (other.tag == "Activator")

{

CanBePressed = false;

if(!Hit)

{

BeatScroller.instance.BeatMiss();

SR.color = new Color(1f, 0.42f, 0.42f);

Instantiate(MissEffect, new Vector3(0, 0, 0), transform.rotation);

GetComponent<BoxCollider2D>().enabled = false;

}

}

}

}