Compare commits

..

11 Commits

12 changed files with 368 additions and 63 deletions

View File

@@ -1,13 +1,15 @@
package ru.spbstu.telematics.java; package ru.spbstu.telematics.java;
/** import java.util.Locale;
* Hello world!
* public class App {
*/ public static void main(String[] args) {
public class App Locale.setDefault(Locale.ENGLISH);
{
public static void main( String[] args ) Room room = new Room();
{ Settings settings = new Settings(28, 0.4);
System.out.println( "Hello World!" ); Controller controller = new Controller(room, settings);
Thread controllerThread = new Thread(controller);
controllerThread.start();
} }
} }

View File

@@ -10,11 +10,93 @@ public class Controller implements Runnable {
private Room room; private Room room;
private Settings settings; private Settings settings;
private Sensor sensor; private Sensor sensor;
private Thread sensorThread;
private Heater heater; private Heater heater;
private Thread heaterThread;
private Fan fan; private Fan fan;
private Thread fanThread;
@Override public Controller(Room room, Settings settings) {
public void run() { 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();
}
}
} }

View File

@@ -1,15 +1,43 @@
package ru.spbstu.telematics.java; package ru.spbstu.telematics.java;
import java.util.Random;
/* /*
* Симулирует вентилятор, установленный в комнате. Может изменять поля комнаты, * Симулирует вентилятор, установленный в комнате. Может изменять поля комнаты,
* а именно - уменьшать влажность в ней. * а именно - уменьшать влажность в ней.
*/ */
public class Fan implements Runnable { public class Fan implements Runnable {
Room room; private Room room;
private boolean isOn;
@Override public Fan(Room room) {
public void run() { 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.03;
private long maxStepTimeMs = 3000;
@Override
public void run() {
while (!Thread.interrupted()) {
if (isOn)
room.adjustHumidity(-random.nextDouble() * humidityMaxStep);
Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
}
}
} }

View File

@@ -1,15 +1,43 @@
package ru.spbstu.telematics.java; package ru.spbstu.telematics.java;
import java.util.Random;
/* /*
* Симулирует нагреватель, установленный в комнате. Может изменять поля комнаты, * Симулирует нагреватель, установленный в комнате. Может изменять поля комнаты,
* а именно - увеличивать температуру в ней. * а именно - увеличивать температуру в ней.
*/ */
public class Heater implements Runnable { public class Heater implements Runnable {
Room room; private Room room;
private boolean isOn;
@Override public Heater(Room room) {
public void run() { 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 = 1;
private long maxStepTimeMs = 3000;
@Override
public void run() {
while (!Thread.interrupted()) {
if (isOn)
room.adjustTemperature(random.nextDouble() * temperatureMaxStep);
Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
}
}
} }

View File

@@ -6,6 +6,15 @@ import java.util.Random;
* Симулирует физические процессы, протекающие в команте. * Симулирует физические процессы, протекающие в команте.
*/ */
public class Room implements Runnable { 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; private volatile double temperature = 24.0;
@@ -13,6 +22,10 @@ public class Room implements Runnable {
return temperature; return temperature;
} }
public synchronized void adjustTemperature(double delta) {
this.temperature += delta;
}
// Относительная влажность в процентах // Относительная влажность в процентах
private volatile double humidity = 0.5; private volatile double humidity = 0.5;
@@ -20,7 +33,11 @@ public class Room implements Runnable {
return humidity; return humidity;
} }
// Параметры произвольного изменения температуры и влажности в комнате public synchronized void adjustHumidity(double delta) {
this.humidity += delta;
}
// Параметры произвольного изменения температуры и влажности в комнате
private Random random = new Random(); private Random random = new Random();
private double temperatureMaxStep = 1; private double temperatureMaxStep = 1;
private double humidityMaxStep = 0.05; private double humidityMaxStep = 0.05;
@@ -33,16 +50,7 @@ public class Room implements Runnable {
temperature += (random.nextDouble() - 0.5) * 2 * temperatureMaxStep; temperature += (random.nextDouble() - 0.5) * 2 * temperatureMaxStep;
humidity += (random.nextDouble() - 0.5) * 2 * humidityMaxStep; humidity += (random.nextDouble() - 0.5) * 2 * humidityMaxStep;
try { Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
Thread.sleep(getStepTime());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} }
} }
private long getStepTime() {
// Спим от 0.5 * maxStepTimeMs до maxSteTimeMs миллисекунд
return (long) (random.nextDouble() * 0.5 + 0.5) * maxStepTimeMs;
}
} }

View File

@@ -11,22 +11,22 @@ public class Sensor implements Runnable {
public Sensor(Room room) { public Sensor(Room room) {
this.room = room; this.room = room;
} }
// Температура // Температура
private volatile double temperature; private volatile double temperature;
public double getTemperature() { public double getTemperature() {
return temperature; return temperature;
} }
// Влажность // Влажность
private volatile double humidity; private volatile double humidity;
public double getHumidity() { public double getHumidity() {
return humidity; return humidity;
} }
// Частота считывания значений с сенсоров // Частота считывания значений с сенсоров
private long updateIntervalMs = 1000; private long updateIntervalMs = 1000;
// Параметры произвольной ошибки измерений сенсоров // Параметры произвольной ошибки измерений сенсоров
@@ -40,11 +40,7 @@ public class Sensor implements Runnable {
temperature = room.getTemperature() + (random.nextDouble() - 0.5) * 2 * maxTemperatureError; temperature = room.getTemperature() + (random.nextDouble() - 0.5) * 2 * maxTemperatureError;
humidity = room.getHumidity() + (random.nextDouble() - 0.5) * 2 * maxHumidityError; humidity = room.getHumidity() + (random.nextDouble() - 0.5) * 2 * maxHumidityError;
try { Utils.sleep(updateIntervalMs);
Thread.sleep(updateIntervalMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} }
} }
} }

View File

@@ -1,14 +1,41 @@
package ru.spbstu.telematics.java; package ru.spbstu.telematics.java;
import java.util.Random;
/* /*
* Симулирует переодическое изменение настроек пользователем. * Симулирует переодическое изменение настроек пользователем.
*/ */
public class Settings implements Runnable { public class Settings implements Runnable {
private double temperature; private double temperature;
private double humidity;
@Override
public void run() {
} public double getTemperature() {
return temperature;
}
private double humidity;
public double getHumidity() {
return humidity;
}
public Settings(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
// Параметры произвольного изменения настроек температуры и влажности в комнате
private Random random = new Random();
private double temperatureMaxStep = 10;
private double humidityMaxStep = 0.15;
private long maxStepTimeMs = 20000;
@Override
public void run() {
while (!Thread.interrupted()) {
temperature += (random.nextDouble() - 0.5) * 2 * temperatureMaxStep;
humidity += (random.nextDouble() - 0.5) * 2 * humidityMaxStep;
Utils.sleepRandomTime((long) (maxStepTimeMs * 0.5), maxStepTimeMs);
}
}
} }

View 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();
}
}
}

View 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();
}
}

View 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 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();
}
}

View File

@@ -3,9 +3,8 @@ package ru.spbstu.telematics.java;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
public class RoomTests { public class RoomTests {
/* /*
* Проверяет, что температура и влажность изменяются со временем. * Проверяет, что температура и влажность изменяются со временем.
*/ */
@Test @Test
@@ -16,7 +15,7 @@ public class RoomTests {
Thread thread = new Thread(room); Thread thread = new Thread(room);
thread.start(); thread.start();
Thread.sleep(5000); Thread.sleep(5000);
assertNotEquals(initialTemperature, room.getTemperature()); assertNotEquals(initialTemperature, room.getTemperature());
assertNotEquals(initialHumidity, room.getHumidity()); assertNotEquals(initialHumidity, room.getHumidity());

View File

@@ -3,9 +3,8 @@ package ru.spbstu.telematics.java;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
public class SensorTests { public class SensorTests {
/* /*
* Моковый класс комнаты для упрощения тестирования сенсоров. * Моковый класс комнаты для упрощения тестирования сенсоров.
*/ */
private class MockRoom extends Room { private class MockRoom extends Room {
@@ -13,11 +12,11 @@ public class SensorTests {
double humidity; double humidity;
public MockRoom(double temperature, double humidity) { public MockRoom(double temperature, double humidity) {
this.temperature = temperature; this.temperature = temperature;
this.humidity = humidity; this.humidity = humidity;
} }
@Override @Override
public double getTemperature() { public double getTemperature() {
return temperature; return temperature;
} }
@@ -28,8 +27,9 @@ public class SensorTests {
} }
} }
/* /*
* Проверяет, что сенсоры выдают реальную температуру и влажность комнаты в пределах * Проверяет, что сенсоры выдают реальную температуру и влажность комнаты в
* пределах
* некоторой погрешности. * некоторой погрешности.
*/ */
@Test @Test
@@ -41,7 +41,7 @@ public class SensorTests {
Sensor sensor = new Sensor(room); Sensor sensor = new Sensor(room);
Thread thread = new Thread(sensor); Thread thread = new Thread(sensor);
thread.start(); thread.start();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
Thread.sleep(1000); Thread.sleep(1000);
assertTrue(Math.abs(initialTemperature - sensor.getTemperature()) <= 1); assertTrue(Math.abs(initialTemperature - sensor.getTemperature()) <= 1);