Compare commits
40 Commits
7fd4b38c38
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c6f7893725 | |||
| 5365327d2f | |||
| 548619a0e5 | |||
| eb3fa2fbad | |||
| 9e5b33d600 | |||
| f26810f2f8 | |||
| 0f2f9532ef | |||
| 7868add638 | |||
| 31dcf88642 | |||
| 50fc3ceaa7 | |||
| dbfb3b6bb0 | |||
| 5fc07ef69f | |||
| 8049646a8a | |||
| 0e4b212f99 | |||
| 0c4644e6b7 | |||
| ee8988056a | |||
| aef1113061 | |||
| f03ac31ae4 | |||
| 1c00338d2d | |||
| d8a6c256d5 | |||
| 5b753c1dda | |||
| 289935499b | |||
| db840afffa | |||
| 0f3e71f775 | |||
| 23de6a3d93 | |||
| 5c1a148ffa | |||
| f12bac21b1 | |||
| 29c367e3f7 | |||
| 47427ae6fe | |||
| b40778d10c | |||
| c5277dd0dc | |||
| b0a214bedb | |||
| 0b5f8e6aba | |||
| 65f7586e24 | |||
| a87c13dc42 | |||
| 4bb503b70f | |||
| dbdd2396ca | |||
| b23c8ac9cd | |||
| 6100f1becb | |||
| 27737cf7b4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
target
|
||||
target
|
||||
tmp
|
||||
39
lab2/pom.xml
Normal file
39
lab2/pom.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ru.spbstu.telematics.java</groupId>
|
||||
<artifactId>lab2</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<name>lab2</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.9.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>ru.spbstu.telematics.java.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
82
lab2/report/report.txt
Normal file
82
lab2/report/report.txt
Normal file
@@ -0,0 +1,82 @@
|
||||
h1. Отчёт по лабораторной работе №2 (Тищенко Артём)
|
||||
|
||||
h2. Задание
|
||||
|
||||
* Исследовать *TreeSet*, посмотреть абстрактные классы и интерфейсы, которые данный тип реализует.
|
||||
* Реализовать собственный класс *MyTreeSet*, который реализует интерфейсы *Iterable* и поддерживает основные операции множества: добавление, удаление, проверка наличия элемента, получение размера, а также итерацию по элементам в отсортированном порядке.
|
||||
* Написать тесты, демонстрирующие аналогичное поведение стандартного *TreeSet* и реализованного класса.
|
||||
|
||||
h2. Исследование TreeSet
|
||||
|
||||
!treeset.png!
|
||||
|
||||
Класс *TreeSet* в Java реализует интерфейс *NavigableSet*, основанный на структуре данных **красно-черного дерева**. Он обеспечивает хранение элементов в отсортированном порядке и эффективное выполнение операций.
|
||||
|
||||
h3. Реализуемые интерфейсы:
|
||||
* *NavigableSet*: Поддерживает навигацию по ближайшим элементам.
|
||||
* *SortedSet*: Гарантирует порядок элементов согласно их естественному упорядочиванию.
|
||||
* *Set*: Запрещает дублирование элементов.
|
||||
* *Serializable*: Позволяет сериализацию объекта.
|
||||
* *Cloneable*: Поддерживает клонирование.
|
||||
|
||||
h3. Основные характеристики:
|
||||
* **Структура данных**: Красно-черное дерево (сбалансированное бинарное дерево поиска).
|
||||
* **Время операций**: Добавление, удаление и поиск выполняются за *O(log n)*.
|
||||
* **Порядок элементов**: Элементы хранятся в отсортированном порядке (natural ordering или через *Comparator*).
|
||||
* **Итерация**: Итератор возвращает элементы в порядке возрастания.
|
||||
|
||||
h3. Недостатки:
|
||||
* **Производительность**: Медленнее *HashSet* для операций добавления/удаления из-за необходимости поддержания баланса дерева.
|
||||
* **Ограничения**: Элементы должны быть сравнимыми (реализовывать *Comparable*) или требовать внешнего *Comparator*.
|
||||
|
||||
h2. Реализация MyTreeSet
|
||||
|
||||
h3. Описание кода
|
||||
|
||||
Класс *MyTreeSet* реализует интерфейс *Iterable* и поддерживает основные операции множества с использованием **бинарного дерева поиска** (без балансировки).
|
||||
|
||||
Основные методы:
|
||||
* **add(E e)**: Добавляет элемент в дерево, сохраняя порядок. Возвращает *true*, если элемент был добавлен.
|
||||
* **contains(E e)**: Проверяет наличие элемента в дереве.
|
||||
* **remove(E e)**: Удаляет элемент, перестраивая дерево.
|
||||
* **size()**: Возвращает количество элементов.
|
||||
* **iterator()**: Возвращает итератор для обхода элементов в порядке возрастания (in-order traversal).
|
||||
|
||||
Поля класса:
|
||||
* **root**: Корень дерева (тип *Node*).
|
||||
* **size**: Текущее количество элементов.
|
||||
|
||||
Вспомогательные структуры:
|
||||
* **Класс Node**: Содержит значение, ссылки на левое и правое поддерево.
|
||||
* **Итератор**: Реализован через стек для симуляции рекурсивного in-order обхода.
|
||||
|
||||
h3. Тесты
|
||||
|
||||
Для проверки корректности работы *MyTreeSet* написаны следующие тесты:
|
||||
* **testAdd**: Проверяет добавление элементов и обработку дубликатов.
|
||||
* **testContains**: Проверяет поиск элементов.
|
||||
* **testRemove**: Проверяет удаление элементов.
|
||||
* **testSize**: Проверяет корректность подсчёта элементов.
|
||||
* **testIterator**: Проверяет порядок обхода элементов и работу методов *hasNext()* и *next()*.
|
||||
|
||||
Каждый тест сравнивает поведение *MyTreeSet* со стандартным *TreeSet*.
|
||||
|
||||
|
||||
h2. Результаты работы
|
||||
|
||||
*Исходный код доступен на "GitHub":https://github.com/Arity-T/java_labs/tree/main/lab2 .*
|
||||
|
||||
Склонировать репозиторий и запустить тесты можно с помощью команд:
|
||||
<pre>
|
||||
git clone https://github.com/Arity-T/java_labs.git
|
||||
cd lab2
|
||||
mvn test
|
||||
</pre>
|
||||
|
||||
!tests.png!
|
||||
|
||||
h2. Заключение
|
||||
|
||||
В ходе работы был исследован класс *TreeSet* и реализован упрощённый аналог *MyTreeSet* на основе бинарного дерева поиска. Класс поддерживает основные операции множества и корректно работает с итератором. Тесты подтвердили соответствие поведения *MyTreeSet* стандартной реализации для базовых сценариев. Основное отличие — отсутствие балансировки дерева, что может влиять на производительность в худших случаях.
|
||||
|
||||
*Исходный код доступен на "GitHub":https://github.com/Arity-T/java_labs/tree/main/lab2 .*
|
||||
BIN
lab2/report/tests.png
Normal file
BIN
lab2/report/tests.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
lab2/report/treeset.png
Normal file
BIN
lab2/report/treeset.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
17
lab2/src/main/java/ru/spbstu/telematics/java/App.java
Normal file
17
lab2/src/main/java/ru/spbstu/telematics/java/App.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
public class App
|
||||
{
|
||||
public static void main( String[] args )
|
||||
{
|
||||
MyTreeSet<Integer> tree = new MyTreeSet<>();
|
||||
System.out.println(tree.add(10));
|
||||
System.out.println(tree.add(20));
|
||||
System.out.println(tree.add(10));
|
||||
|
||||
System.out.println("Содержимое дерева:");
|
||||
for (Integer val: tree) {
|
||||
System.out.println(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
163
lab2/src/main/java/ru/spbstu/telematics/java/MyTreeSet.java
Normal file
163
lab2/src/main/java/ru/spbstu/telematics/java/MyTreeSet.java
Normal file
@@ -0,0 +1,163 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Stack;
|
||||
|
||||
public class MyTreeSet<E extends Comparable<E>> implements Iterable<E> {
|
||||
// Класс для представления узла дерева
|
||||
private class Node {
|
||||
E value;
|
||||
Node left;
|
||||
Node right;
|
||||
|
||||
Node(E value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Node root;
|
||||
private int size;
|
||||
|
||||
public MyTreeSet() {
|
||||
root = null;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean add(E element) {
|
||||
if (element == null) {
|
||||
throw new NullPointerException("Нельзя добавить null в TreeSet!");
|
||||
}
|
||||
|
||||
if (root == null) {
|
||||
root = new Node(element);
|
||||
size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
Node currentNode = root;
|
||||
Node parentNode = null;
|
||||
|
||||
while (currentNode != null) {
|
||||
int cmp = element.compareTo(currentNode.value);
|
||||
if (cmp == 0) return false;
|
||||
parentNode = currentNode;
|
||||
currentNode = cmp < 0 ? currentNode.left : currentNode.right;
|
||||
}
|
||||
|
||||
Node newNode = new Node(element);
|
||||
|
||||
if (element.compareTo(parentNode.value) < 0) {
|
||||
parentNode.left = newNode;
|
||||
} else {
|
||||
parentNode.right = newNode;
|
||||
}
|
||||
|
||||
size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean contains(E element) {
|
||||
if (element == null) {
|
||||
throw new NullPointerException("Нельзя добавить null в TreeSet!");
|
||||
}
|
||||
|
||||
return contains(root, element);
|
||||
}
|
||||
|
||||
private boolean contains(Node node, E element) {
|
||||
if (node == null) return false;
|
||||
|
||||
int cmp = element.compareTo(node.value);
|
||||
if (cmp == 0) return true;
|
||||
return cmp < 0 ? contains(node.left, element) : contains(node.right, element);
|
||||
}
|
||||
|
||||
public boolean remove(E element) {
|
||||
if (element == null) {
|
||||
throw new NullPointerException("Null элементы не допускаются");
|
||||
}
|
||||
if (!contains(element)) return false;
|
||||
root = remove(root, element);
|
||||
size--;
|
||||
return true;
|
||||
}
|
||||
|
||||
private Node remove(Node node, E element) {
|
||||
if (node == null) return null;
|
||||
int cmp = element.compareTo(node.value);
|
||||
if (cmp < 0) {
|
||||
node.left = remove(node.left, element);
|
||||
} else if (cmp > 0) {
|
||||
node.right = remove(node.right, element);
|
||||
} else {
|
||||
// Узел для удаления найден
|
||||
if (node.left == null && node.right == null) {
|
||||
// Если он является листом, то просто удаляем его
|
||||
return null;
|
||||
} else if (node.left == null) {
|
||||
// Если у него нету левого поддерева, то заменяем правым узлом
|
||||
return node.right;
|
||||
} else if (node.right == null) {
|
||||
// Если у него нету правого поддерева, то заменяем левым узлом
|
||||
return node.left;
|
||||
} else {
|
||||
// Если есть оба поддерева, то заменяем самым наименьшим элементом правого поддерева
|
||||
Node successor = minValueNode(node.right);
|
||||
node.value = successor.value;
|
||||
node.right = remove(node.right, successor.value);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node minValueNode(Node node) {
|
||||
Node current = node;
|
||||
while (current.left != null) {
|
||||
current = current.left;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new MyTreeSetIterator();
|
||||
}
|
||||
|
||||
private class MyTreeSetIterator implements Iterator<E> {
|
||||
// На вершине стека всегда будет лежать наименьший элемент в дереве
|
||||
Stack<Node> nodeStack = new Stack<>();
|
||||
|
||||
MyTreeSetIterator() {
|
||||
pushAllLeftToStack(root);
|
||||
}
|
||||
|
||||
void pushAllLeftToStack(Node current) {
|
||||
if (current == null) return;
|
||||
nodeStack.push(current);
|
||||
pushAllLeftToStack(current.left);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nodeStack.size() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (!hasNext()) throw new NoSuchElementException();
|
||||
|
||||
Node minNode = nodeStack.pop();
|
||||
|
||||
if (minNode.right != null) {
|
||||
pushAllLeftToStack(minNode.right);
|
||||
}
|
||||
|
||||
return minNode.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
lab2/src/test/java/ru/spbstu/telematics/java/AppTest.java
Normal file
99
lab2/src/test/java/ru/spbstu/telematics/java/AppTest.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.TreeSet;
|
||||
|
||||
class MyTreeSetTests {
|
||||
MyTreeSet<Integer> myTreeSet;
|
||||
TreeSet<Integer> treeSet;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
myTreeSet = new MyTreeSet<>();
|
||||
treeSet = new TreeSet<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdd() {
|
||||
assertThrows(NullPointerException.class, () -> myTreeSet.add(null));
|
||||
assertThrows(NullPointerException.class, () -> treeSet.add(null));
|
||||
|
||||
assertEquals(myTreeSet.add(150), treeSet.add(150));
|
||||
assertEquals(myTreeSet.add(200), treeSet.add(200));
|
||||
assertEquals(myTreeSet.add(150), treeSet.add(150));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> myTreeSet.add(null));
|
||||
assertThrows(NullPointerException.class, () -> treeSet.add(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContains() {
|
||||
assertEquals(myTreeSet.contains(15), treeSet.contains(15));
|
||||
treeSet.add(15);
|
||||
myTreeSet.add(15);
|
||||
assertEquals(myTreeSet.contains(15), treeSet.contains(15));
|
||||
assertEquals(myTreeSet.contains(50), treeSet.contains(50));
|
||||
treeSet.add(50);
|
||||
myTreeSet.add(50);
|
||||
assertEquals(myTreeSet.contains(50), treeSet.contains(50));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> myTreeSet.contains(null));
|
||||
assertThrows(NullPointerException.class, () -> treeSet.contains(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemove() {
|
||||
assertEquals(myTreeSet.remove(15), treeSet.remove(15));
|
||||
treeSet.add(15);
|
||||
myTreeSet.add(15);
|
||||
assertEquals(myTreeSet.remove(15), treeSet.remove(15));
|
||||
assertEquals(myTreeSet.remove(50), treeSet.remove(50));
|
||||
treeSet.add(50);
|
||||
myTreeSet.add(50);
|
||||
assertEquals(myTreeSet.remove(50), treeSet.remove(50));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> myTreeSet.remove(null));
|
||||
assertThrows(NullPointerException.class, () -> treeSet.remove(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSize() {
|
||||
assertEquals(myTreeSet.size(), treeSet.size());
|
||||
treeSet.add(15);
|
||||
myTreeSet.add(15);
|
||||
assertEquals(myTreeSet.size(), treeSet.size());
|
||||
treeSet.add(50);
|
||||
myTreeSet.add(50);
|
||||
assertEquals(myTreeSet.size(), treeSet.size());
|
||||
assertEquals(myTreeSet.remove(50), treeSet.remove(50));
|
||||
assertEquals(myTreeSet.size(), treeSet.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIterator() {
|
||||
treeSet.add(15);
|
||||
myTreeSet.add(15);
|
||||
treeSet.add(250);
|
||||
myTreeSet.add(250);
|
||||
treeSet.add(50);
|
||||
myTreeSet.add(50);
|
||||
Iterator<Integer> itMyTreeSet = myTreeSet.iterator();
|
||||
Iterator<Integer> itTreeSet = treeSet.iterator();
|
||||
|
||||
assertEquals(itMyTreeSet.hasNext(), itTreeSet.hasNext());
|
||||
assertEquals(itMyTreeSet.next(), itTreeSet.next());
|
||||
assertEquals(itMyTreeSet.hasNext(), itTreeSet.hasNext());
|
||||
assertEquals(itMyTreeSet.next(), itTreeSet.next());
|
||||
assertEquals(itMyTreeSet.hasNext(), itTreeSet.hasNext());
|
||||
assertEquals(itMyTreeSet.next(), itTreeSet.next());
|
||||
|
||||
assertEquals(itMyTreeSet.hasNext(), itTreeSet.hasNext());
|
||||
assertThrows(NoSuchElementException.class, () -> itMyTreeSet.next());
|
||||
assertThrows(NoSuchElementException.class, () -> itTreeSet.next());
|
||||
}
|
||||
}
|
||||
5
lab3/README.md
Normal file
5
lab3/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## Задание
|
||||
В данной работе необходимо написать многопоточное приложение, которое эмулирует заданную модель. Студент сам должен спроектировать потоки, которые отвечают за поведение сущностей из полученного задания. Взаимодействие потоков должно быть синхронизировано и приложение должно быть протестирование на наличие dead locks и race conditions. Приложение не должно переставать работать из-за изменения задержек и модель не должна быть полностью синхронной.
|
||||
|
||||
### Мой вариант
|
||||
Обогреватель — вентилятор. Каждая комната в здании имеет управляющий терминал для наблюдения и контролирования за окружающей средой. Каждый терминал измеряет и выводит текущую температуру и влажность. В каждой комнаты кроме того установлена предпочтительная температура и влажность (пара чисел). Если текущие значения температуры или влажности вышли за пределы предпочтительных настроек более чем на 1%, тогда станция включает/выключает обогреватель или вентилятор. Должны быть следующие процессы: сенсоры, настройки, обогреватель — вентилятор, контроллер.
|
||||
39
lab3/pom.xml
Normal file
39
lab3/pom.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ru.spbstu.telematics.java</groupId>
|
||||
<artifactId>lab3</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<name>lab3</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.9.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>ru.spbstu.telematics.java.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
97
lab3/report/report.txt
Normal file
97
lab3/report/report.txt
Normal file
@@ -0,0 +1,97 @@
|
||||
h1. Отчёт по лабораторной работе №3 (Тищенко Артём)
|
||||
|
||||
|
||||
h2. Задание
|
||||
|
||||
В данной работе необходимо написать многопоточное приложение, которое эмулирует заданную модель. Студент сам должен спроектировать потоки, которые отвечают за поведение сущностей из полученного задания. Взаимодействие потоков должно быть синхронизировано и приложение должно быть протестирование на наличие dead locks и race conditions. Приложение не должно переставать работать из-за изменения задержек и модель не должна быть полностью синхронной.
|
||||
|
||||
h2. Мой вариант
|
||||
|
||||
Обогреватель — вентилятор. Каждая комната в здании имеет управляющий терминал для наблюдения и контролирования за окружающей средой. Каждый терминал измеряет и выводит текущую температуру и влажность. В каждой комнаты кроме того установлена предпочтительная температура и влажность (пара чисел). Если текущие значения температуры или влажности вышли за пределы предпочтительных настроек более чем на 1%, тогда станция включает/выключает обогреватель или вентилятор. Должны быть следующие процессы: сенсоры, настройки, обогреватель — вентилятор, контроллер.
|
||||
|
||||
h2. Архитектура и компоненты реализации
|
||||
|
||||
Приложение состоит из нескольких основных классов:
|
||||
|
||||
# *App*
|
||||
#* Точка входа в программу.
|
||||
#* Создаёт объект _Room_, объекты _Settings_ и _Controller_, затем запускает поток с контроллером.
|
||||
#* При необходимости может выступать стартовой площадкой для других потоков (например, если вы решите запускать класс _Room_ как отдельный поток).
|
||||
# *Room* (реализует _Runnable_)
|
||||
#* Симулирует физические процессы в комнате.
|
||||
#* Хранит внутренние поля:
|
||||
#** _temperature_ – текущая температура в градусах Цельсия.
|
||||
#** _humidity_ – текущая относительная влажность (0..1).
|
||||
#* Периодически изменяет температуру и влажность случайным образом.
|
||||
#* Методы _adjustTemperature()_ и _adjustHumidity()_ помечены как _synchronized_, чтобы избежать гонок данных.
|
||||
#* При запуске в потоке (метод _run()_) постоянно вносит небольшие случайные изменения в температуру и влажность.
|
||||
# *Sensor* (реализует _Runnable_)
|
||||
#* Представляет набор датчиков в комнате.
|
||||
#* Периодически (с заданным интервалом) считывает актуальную _temperature_ и _humidity_ из _Room_, добавляя некоторую случайную погрешность.
|
||||
#* Хранит результат измерений во внутренних полях (например, _temperature_, _humidity_).
|
||||
#* Эти значения затем читает _Controller_.
|
||||
# *Heater* (реализует _Runnable_)
|
||||
#* Нагреватель комнаты.
|
||||
#* При включённом состоянии (_isOn_ = true) повышает температуру в комнате на случайную величину.
|
||||
#* Работает в собственном потоке, периодически внося изменения в _Room_ (через _adjustTemperature()_).
|
||||
# *Fan* (реализует _Runnable_)
|
||||
#* Вентилятор, уменьшающий влажность.
|
||||
#* При включённом состоянии (_isOn_ = true) понижает влажность в комнате на случайную величину.
|
||||
#* Работает в собственном потоке, периодически внося изменения в _Room_ (через _adjustHumidity()_).
|
||||
# *Controller* (реализует _Runnable_)
|
||||
#* Логический контроллер, который:
|
||||
#** Читает измерения с _Sensor_ (температура и влажность).
|
||||
#** Сравнивает их с желаемыми параметрами из _Settings_.
|
||||
#** Включает/выключает _Heater_ и _Fan_, если показатели выходят за пределы допустимых значений.
|
||||
#* Запускается в отдельном потоке и периодически (по заданному интервалу) проверяет состояние сенсоров и управляет нагревателем/вентилятором.
|
||||
#* При завершении (прерывании) аккуратно останавливает все подчинённые потоки (_Sensor_, _Heater_, _Fan_).
|
||||
# *Settings* (реализует _Runnable_)
|
||||
#* Хранит желаемую температуру и влажность, заданные пользователем.
|
||||
#* Периодически (в методе _run()_) может изменять эти настройки, симулируя поведение человека, корректирующего параметры.
|
||||
#* Хранится и используется в _Controller_ для решения, когда включать/выключать нагреватель и вентилятор.
|
||||
# *Utils*
|
||||
#* Вспомогательный класс с методами:
|
||||
#** _sleep(...)_ – безопасная пауза (учитывает _InterruptedException_).
|
||||
#** _sleepRandomTime(...)_ – пауза случайной длины (диапазон задаётся параметрами).
|
||||
#* Используется всеми потоками для имитации задержек, дабы избежать полного синхронного шага у всех объектов.
|
||||
|
||||
|
||||
h2. Тестирование
|
||||
|
||||
Ниже приведён краткий обзор тестовых классов и методов, покрывающих основные аспекты работы приложения:
|
||||
|
||||
# *AppTest*
|
||||
#* *testNoDeadlock()* – проверяет отсутствие взаимной блокировки (deadlock), когда одновременно запущены _Controller_ и _Room_.
|
||||
#* *testNoRaceCondition()* – тест на отсутствие гонок (race conditions), в ходе которого периодически проверяются значения температуры и влажности в _Room_.
|
||||
# *FanTests*
|
||||
#* *testFanOn()* – проверяет, что при включённом вентиляторе влажность в комнате уменьшается, а температура остаётся без изменений.
|
||||
#* *testFanOff()* – проверяет, что при выключенном вентиляторе ни температура, ни влажность не меняются.
|
||||
# *HeaterTests*
|
||||
#* *testHeaterOn()* – убеждается, что при включённом нагревателе температура в комнате увеличивается, а влажность не меняется.
|
||||
#* *testHeaterOff()* – при выключенном нагревателе показатели комнаты не меняются.
|
||||
# *RoomTests*
|
||||
#* *testTemperatureAndHumidityChange()* – запускает _Room_ в отдельном потоке и проверяет, что температура и влажность действительно меняются со временем.
|
||||
# *SensorTests*
|
||||
#* *testSensor()* – эмулирует работу сенсора с помощью макета комнаты (_MockRoom_), сверяет реальное значение температуры и влажности с тем, что возвращает сенсор, учитывая допустимую погрешность.
|
||||
|
||||
h3. Результаты тестов
|
||||
|
||||
Ниже представлена иллюстрация (вывод в консоли) после успешного запуска _mvn test_, где видно, что все тесты проходят:
|
||||
|
||||
!tests-results.png!
|
||||
|
||||
h2. Результаты работы программы
|
||||
|
||||
Результаты 60 секунд симуляции представлены на скриншоте ниже.
|
||||
|
||||
<pre>
|
||||
mvn package
|
||||
java -jar .\target\lab3-1.0-SNAPSHOT.jar
|
||||
</pre>
|
||||
|
||||
!results.png!
|
||||
|
||||
|
||||
h2. Исходный код
|
||||
|
||||
*Исходный код доступен на "GitHub":https://github.com/Arity-T/java_labs/tree/main/lab3 .*
|
||||
20
lab3/src/main/java/ru/spbstu/telematics/java/App.java
Normal file
20
lab3/src/main/java/ru/spbstu/telematics/java/App.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
Locale.setDefault(Locale.ENGLISH);
|
||||
|
||||
Room room = new Room();
|
||||
Thread roomThread = new Thread(room);
|
||||
Settings settings = new Settings(room, 28, 0.4);
|
||||
Thread settingsThread = new Thread(settings);
|
||||
Controller controller = new Controller(room, settings);
|
||||
Thread controllerThread = new Thread(controller);
|
||||
|
||||
roomThread.start();
|
||||
settingsThread.start();
|
||||
controllerThread.start();
|
||||
}
|
||||
}
|
||||
102
lab3/src/main/java/ru/spbstu/telematics/java/Controller.java
Normal file
102
lab3/src/main/java/ru/spbstu/telematics/java/Controller.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
/*
|
||||
* Симулирует работу контроллера, установленного в комнате. Контроллер может снимать
|
||||
* показания с сенсоров и управлять работой нагревателя и вентилятора, в соответствии
|
||||
* с указанными настройками. При этом сам контроллер не читает и не изменяет поля
|
||||
* комнаты напрямую.
|
||||
*/
|
||||
public class Controller implements Runnable {
|
||||
private Room room;
|
||||
private Settings settings;
|
||||
private Sensor sensor;
|
||||
private Thread sensorThread;
|
||||
private Heater heater;
|
||||
private Thread heaterThread;
|
||||
private Fan fan;
|
||||
private Thread fanThread;
|
||||
|
||||
public Controller(Room room, Settings settings) {
|
||||
this.room = room;
|
||||
this.settings = settings;
|
||||
|
||||
sensor = new Sensor(room);
|
||||
sensorThread = new Thread(sensor);
|
||||
|
||||
heater = new Heater(room);
|
||||
heaterThread = new Thread(heater);
|
||||
|
||||
fan = new Fan(room);
|
||||
fanThread = new Thread(fan);
|
||||
}
|
||||
|
||||
private long updateIntervalMs = 500;
|
||||
private double tolerance = 0.01;
|
||||
|
||||
private void log(String string) {
|
||||
System.out.printf("[Controller in room %s] %s\n", room.name, string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sensorThread.start();
|
||||
heaterThread.start();
|
||||
fanThread.start();
|
||||
log("Started sensor, heater and fan threads");
|
||||
|
||||
while (!Thread.interrupted()) {
|
||||
double realTemperature = room.getTemperature();
|
||||
double sensorTemperature = sensor.getTemperature();
|
||||
double desiredTemperature = settings.getTemperature();
|
||||
|
||||
if (sensorTemperature < desiredTemperature * (1 - tolerance)) {
|
||||
if (!heater.isOn()) {
|
||||
log(String.format(
|
||||
"Turning heater ON (real - %.2fC°, sensor - %.2fC°, desired %.2fC°)",
|
||||
realTemperature, sensorTemperature, desiredTemperature));
|
||||
heater.turnOn();
|
||||
}
|
||||
} else {
|
||||
if (heater.isOn()) {
|
||||
log(String.format(
|
||||
"Turning heater OFF (real - %.2fC°, sensor - %.2fC°, desired %.2fC°)",
|
||||
realTemperature, sensorTemperature, desiredTemperature));
|
||||
heater.turnOff();
|
||||
}
|
||||
}
|
||||
|
||||
double realHumidity = room.getHumidity();
|
||||
double sensorHumidity = sensor.getHumidity();
|
||||
double desiredHumidity = settings.getHumidity();
|
||||
if (sensorHumidity > desiredHumidity * (1 + tolerance)) {
|
||||
if (!fan.isOn()) {
|
||||
log(String.format(
|
||||
"Turning fan ON (real - %.2f%%, sensor - %.2f%%, desired %.2f%%)",
|
||||
realHumidity * 100, sensorHumidity * 100, desiredHumidity * 100));
|
||||
fan.turnOn();
|
||||
}
|
||||
} else {
|
||||
if (fan.isOn()) {
|
||||
log(String.format(
|
||||
"Turning fan OFF (real - %.2f%%, sensor - %.2f%%, desired %.2f%%)",
|
||||
realHumidity * 100, sensorHumidity * 100, desiredHumidity * 100));
|
||||
fan.turnOff();
|
||||
}
|
||||
}
|
||||
|
||||
Utils.sleep(updateIntervalMs);
|
||||
}
|
||||
|
||||
sensorThread.interrupt();
|
||||
heaterThread.interrupt();
|
||||
fanThread.interrupt();
|
||||
|
||||
try {
|
||||
sensorThread.join();
|
||||
heaterThread.join();
|
||||
fanThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
43
lab3/src/main/java/ru/spbstu/telematics/java/Fan.java
Normal file
43
lab3/src/main/java/ru/spbstu/telematics/java/Fan.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/*
|
||||
* Симулирует вентилятор, установленный в комнате. Может изменять поля комнаты,
|
||||
* а именно - уменьшать влажность в ней.
|
||||
*/
|
||||
public class Fan implements Runnable {
|
||||
private Room room;
|
||||
|
||||
public Fan(Room room) {
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
private volatile boolean isOn;
|
||||
|
||||
public boolean isOn() {
|
||||
return isOn;
|
||||
}
|
||||
|
||||
public void turnOn() {
|
||||
this.isOn = true;
|
||||
}
|
||||
|
||||
public void turnOff() {
|
||||
this.isOn = false;
|
||||
}
|
||||
|
||||
private Random random = new Random();
|
||||
private double humidityMaxStep = 0.005;
|
||||
private long maxStepTimeMs = 500;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.interrupted()) {
|
||||
if (isOn)
|
||||
room.adjustHumidity(-random.nextDouble() * humidityMaxStep);
|
||||
|
||||
Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
lab3/src/main/java/ru/spbstu/telematics/java/Heater.java
Normal file
43
lab3/src/main/java/ru/spbstu/telematics/java/Heater.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/*
|
||||
* Симулирует нагреватель, установленный в комнате. Может изменять поля комнаты,
|
||||
* а именно - увеличивать температуру в ней.
|
||||
*/
|
||||
public class Heater implements Runnable {
|
||||
private Room room;
|
||||
|
||||
public Heater(Room room) {
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
private volatile boolean isOn;
|
||||
|
||||
public boolean isOn() {
|
||||
return isOn;
|
||||
}
|
||||
|
||||
public void turnOn() {
|
||||
this.isOn = true;
|
||||
}
|
||||
|
||||
public void turnOff() {
|
||||
this.isOn = false;
|
||||
}
|
||||
|
||||
private Random random = new Random();
|
||||
private double temperatureMaxStep = 0.25;
|
||||
private long maxStepTimeMs = 500;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.interrupted()) {
|
||||
if (isOn)
|
||||
room.adjustTemperature(random.nextDouble() * temperatureMaxStep);
|
||||
|
||||
Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
lab3/src/main/java/ru/spbstu/telematics/java/Room.java
Normal file
58
lab3/src/main/java/ru/spbstu/telematics/java/Room.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/*
|
||||
* Симулирует физические процессы, протекающие в команте.
|
||||
*/
|
||||
public class Room implements Runnable {
|
||||
static private int roomCounter;
|
||||
|
||||
public final String name;
|
||||
|
||||
public Room() {
|
||||
roomCounter++;
|
||||
this.name = "#" + roomCounter;
|
||||
}
|
||||
|
||||
// Температура измеряется в градусах цельсия
|
||||
private volatile double temperature = 24.0;
|
||||
|
||||
public double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public synchronized void adjustTemperature(double delta) {
|
||||
this.temperature += delta;
|
||||
}
|
||||
|
||||
// Относительная влажность в процентах
|
||||
private volatile double humidity = 0.5;
|
||||
|
||||
public double getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
|
||||
public synchronized void adjustHumidity(double delta) {
|
||||
this.humidity += delta;
|
||||
}
|
||||
|
||||
// Параметры произвольного изменения температуры и влажности в комнате
|
||||
private Random random = new Random();
|
||||
private double temperatureMaxStep = 0.12; // примерно 0.5% от средних значений
|
||||
private double humidityMaxStep = 0.003;
|
||||
private long maxStepTimeMs = 500;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Пусть температура и влажность почти произвольно изменяются со временем,
|
||||
// но со временем становится немного холоднее (комната остывает), а влажность
|
||||
// немного растёт (потому что нужно иногда проветривать).
|
||||
while (!Thread.interrupted()) {
|
||||
temperature += (random.nextDouble() - 0.6) * 2 * temperatureMaxStep;
|
||||
humidity += (random.nextDouble() - 0.4) * 2 * humidityMaxStep;
|
||||
|
||||
Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
lab3/src/main/java/ru/spbstu/telematics/java/Sensor.java
Normal file
46
lab3/src/main/java/ru/spbstu/telematics/java/Sensor.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/*
|
||||
* Симулирует работу сенсоров, установленных в комнате.
|
||||
*/
|
||||
public class Sensor implements Runnable {
|
||||
private Room room;
|
||||
|
||||
public Sensor(Room room) {
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
// Температура
|
||||
private volatile double temperature;
|
||||
|
||||
public double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
// Влажность
|
||||
private volatile double humidity;
|
||||
|
||||
public double getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
|
||||
// Частота считывания значений с сенсоров
|
||||
private long updateIntervalMs = 1000;
|
||||
|
||||
// Параметры произвольной ошибки измерений сенсоров
|
||||
private Random random = new Random();
|
||||
private double maxTemperatureError = 0.5;
|
||||
private double maxHumidityError = 0.02;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.interrupted()) {
|
||||
temperature = room.getTemperature() + (random.nextDouble() - 0.5) * 2 * maxTemperatureError;
|
||||
humidity = room.getHumidity() + (random.nextDouble() - 0.5) * 2 * maxHumidityError;
|
||||
|
||||
Utils.sleep(updateIntervalMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
lab3/src/main/java/ru/spbstu/telematics/java/Settings.java
Normal file
52
lab3/src/main/java/ru/spbstu/telematics/java/Settings.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/*
|
||||
* Симулирует переодическое изменение настроек пользователем.
|
||||
*/
|
||||
public class Settings implements Runnable {
|
||||
private Room room;
|
||||
|
||||
private double temperature;
|
||||
|
||||
public double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
private double humidity;
|
||||
|
||||
public double getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
|
||||
public Settings(Room room, double temperature, double humidity) {
|
||||
this.room = room;
|
||||
this.temperature = temperature;
|
||||
this.humidity = humidity;
|
||||
}
|
||||
|
||||
// Параметры произвольного изменения настроек температуры и влажности в комнате
|
||||
private Random random = new Random();
|
||||
private double temperatureMaxStep = 6;
|
||||
private double humidityMaxStep = 0.10;
|
||||
private long maxStepTimeMs = 30000;
|
||||
|
||||
private void log(String string) {
|
||||
System.out.printf("[Settings in room %s] %s\n", room.name, string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.interrupted()) {
|
||||
Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
|
||||
|
||||
temperature += (random.nextDouble() - 0.5) * 2 * temperatureMaxStep;
|
||||
humidity += (random.nextDouble() - 0.5) * 2 * humidityMaxStep;
|
||||
|
||||
log(String.format(
|
||||
"Changed to temperature %.2fC°, humidity %.2f%%",
|
||||
temperature, humidity));
|
||||
}
|
||||
}
|
||||
}
|
||||
21
lab3/src/main/java/ru/spbstu/telematics/java/Utils.java
Normal file
21
lab3/src/main/java/ru/spbstu/telematics/java/Utils.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class Utils {
|
||||
static public void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
static public void sleepRandomTime(long from, long to) {
|
||||
try {
|
||||
Thread.sleep(ThreadLocalRandom.current().nextLong(from, to));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
78
lab3/src/test/java/ru/spbstu/telematics/java/AppTest.java
Normal file
78
lab3/src/test/java/ru/spbstu/telematics/java/AppTest.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
|
||||
public class AppTest {
|
||||
|
||||
/**
|
||||
* Тест проверяет, что при параллельном запуске всех потоков
|
||||
* не возникает взаимной блокировки (deadlock).
|
||||
*/
|
||||
@Test
|
||||
void testNoDeadlock() throws InterruptedException {
|
||||
Room room = new Room();
|
||||
Settings settings = new Settings(room, 28.0, 0.4);
|
||||
Controller controller = new Controller(room, settings);
|
||||
Thread controllerThread = new Thread(controller);
|
||||
Thread roomThread = new Thread(room);
|
||||
|
||||
controllerThread.start();
|
||||
roomThread.start();
|
||||
|
||||
// Будем проверять наличие deadlock несколько раз с периодом 1 сек
|
||||
// Общая длительность теста 10 секунд
|
||||
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Thread.sleep(1000);
|
||||
|
||||
long[] threadIds = threadBean.findDeadlockedThreads();
|
||||
// Если возвращается не null, значит обнаружен deadlock
|
||||
assertNull(threadIds, "Обнаружен deadlock.");
|
||||
}
|
||||
|
||||
controllerThread.interrupt();
|
||||
roomThread.interrupt();
|
||||
|
||||
controllerThread.join();
|
||||
roomThread.join();
|
||||
}
|
||||
|
||||
/**
|
||||
* Тест проверяет отсутствие гонок (race conditions),
|
||||
* когда значения становятся очевидно некорректными или программа "падает".
|
||||
*/
|
||||
@Test
|
||||
void testNoRaceCondition() throws InterruptedException {
|
||||
Room room = new Room();
|
||||
Settings settings = new Settings(room, 25.0, 0.4);
|
||||
Controller controller = new Controller(room, settings);
|
||||
Thread controllerThread = new Thread(controller, "Controller-Thread");
|
||||
Thread roomThread = new Thread(room, "Room-Thread");
|
||||
|
||||
controllerThread.start();
|
||||
roomThread.start();
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
long testDuration = 10000;
|
||||
|
||||
while (System.currentTimeMillis() - startTime < testDuration) {
|
||||
double t = room.getTemperature();
|
||||
double h = room.getHumidity();
|
||||
|
||||
assertTrue(t > -50 && t < 100, "Температура вышла за пределы допустимых значений, возможна гонка");
|
||||
assertTrue(h >= -0.5 && h <= 1.5, "Влажность вышла за пределы допустимых значений, возможна гонка");
|
||||
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
controllerThread.interrupt();
|
||||
roomThread.interrupt();
|
||||
|
||||
controllerThread.join();
|
||||
roomThread.join();
|
||||
}
|
||||
}
|
||||
57
lab3/src/test/java/ru/spbstu/telematics/java/FanTests.java
Normal file
57
lab3/src/test/java/ru/spbstu/telematics/java/FanTests.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class FanTests {
|
||||
Room room;
|
||||
double initialTemperature;
|
||||
double initialHumidity;
|
||||
Fan fan;
|
||||
Thread fanThread;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
room = new Room();
|
||||
initialTemperature = room.getTemperature();
|
||||
initialHumidity = room.getHumidity();
|
||||
fan = new Fan(room);
|
||||
fanThread = new Thread(fan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Проверяет, что включенный вентилятор уменьшает влажность в комнате
|
||||
* и при этом не изменяет температуру.
|
||||
*/
|
||||
@Test
|
||||
public void testFanOn() throws InterruptedException {
|
||||
fan.turnOn();
|
||||
fanThread.start();
|
||||
|
||||
Thread.sleep(5000);
|
||||
|
||||
assertEquals(initialTemperature, room.getTemperature());
|
||||
assertTrue(initialHumidity > room.getHumidity());
|
||||
|
||||
fanThread.interrupt();
|
||||
fanThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* Проверяет, что выключенный вентилятор не изменяет температуру и влажность
|
||||
* в комнате.
|
||||
*/
|
||||
@Test
|
||||
public void testFanOff() throws InterruptedException {
|
||||
fanThread.start();
|
||||
|
||||
Thread.sleep(5000);
|
||||
|
||||
assertEquals(initialHumidity, room.getHumidity());
|
||||
assertEquals(initialTemperature, room.getTemperature());
|
||||
|
||||
fanThread.interrupt();
|
||||
fanThread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class HeaterTests {
|
||||
Room room;
|
||||
double initialTemperature;
|
||||
double initialHumidity;
|
||||
Heater heater;
|
||||
Thread heaterThread;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
room = new Room();
|
||||
initialTemperature = room.getTemperature();
|
||||
initialHumidity = room.getHumidity();
|
||||
heater = new Heater(room);
|
||||
heaterThread = new Thread(heater);
|
||||
}
|
||||
|
||||
/*
|
||||
* Проверяет, что включенный нагреватель увеличивает температуру в комнате
|
||||
* и при этом не изменяет влажность.
|
||||
*/
|
||||
@Test
|
||||
public void testHeaterOn() throws InterruptedException {
|
||||
heater.turnOn();
|
||||
heaterThread.start();
|
||||
|
||||
Thread.sleep(5000);
|
||||
|
||||
assertEquals(initialHumidity, room.getHumidity());
|
||||
assertTrue(initialTemperature < room.getTemperature());
|
||||
|
||||
heaterThread.interrupt();
|
||||
heaterThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* Проверяет, что выключенный нагреватель не изменяет температуру и влажность
|
||||
* в комнате.
|
||||
*/
|
||||
@Test
|
||||
public void testHeaterOff() throws InterruptedException {
|
||||
heaterThread.start();
|
||||
|
||||
Thread.sleep(5000);
|
||||
|
||||
assertEquals(initialHumidity, room.getHumidity());
|
||||
assertEquals(initialTemperature, room.getTemperature());
|
||||
|
||||
heaterThread.interrupt();
|
||||
heaterThread.join();
|
||||
}
|
||||
}
|
||||
26
lab3/src/test/java/ru/spbstu/telematics/java/RoomTests.java
Normal file
26
lab3/src/test/java/ru/spbstu/telematics/java/RoomTests.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class RoomTests {
|
||||
/*
|
||||
* Проверяет, что температура и влажность изменяются со временем.
|
||||
*/
|
||||
@Test
|
||||
public void testTemperatureAndHumidityChange() throws InterruptedException {
|
||||
Room room = new Room();
|
||||
double initialTemperature = room.getTemperature();
|
||||
double initialHumidity = room.getHumidity();
|
||||
|
||||
Thread thread = new Thread(room);
|
||||
thread.start();
|
||||
Thread.sleep(5000);
|
||||
|
||||
assertNotEquals(initialTemperature, room.getTemperature());
|
||||
assertNotEquals(initialHumidity, room.getHumidity());
|
||||
|
||||
thread.interrupt();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package ru.spbstu.telematics.java;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class SensorTests {
|
||||
/*
|
||||
* Моковый класс комнаты для упрощения тестирования сенсоров.
|
||||
*/
|
||||
private class MockRoom extends Room {
|
||||
double temperature;
|
||||
double humidity;
|
||||
|
||||
public MockRoom(double temperature, double humidity) {
|
||||
this.temperature = temperature;
|
||||
this.humidity = humidity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Проверяет, что сенсоры выдают реальную температуру и влажность комнаты в
|
||||
* пределах
|
||||
* некоторой погрешности.
|
||||
*/
|
||||
@Test
|
||||
public void testSensor() throws InterruptedException {
|
||||
double initialTemperature = 30.0;
|
||||
double initialHumidity = 0.8;
|
||||
Room room = new MockRoom(initialTemperature, initialHumidity);
|
||||
|
||||
Sensor sensor = new Sensor(room);
|
||||
Thread thread = new Thread(sensor);
|
||||
thread.start();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Thread.sleep(1000);
|
||||
assertTrue(Math.abs(initialTemperature - sensor.getTemperature()) <= 1);
|
||||
assertTrue(Math.abs(initialHumidity - sensor.getHumidity()) <= 0.05);
|
||||
}
|
||||
|
||||
thread.interrupt();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user