diff --git a/commons/src/test/scala/io/github/tassiLuca/ChannelsContextTest.scala b/commons/src/test/scala/io/github/tassiLuca/ChannelsContextTest.scala index 245fc3c0..f47dfd41 100644 --- a/commons/src/test/scala/io/github/tassiLuca/ChannelsContextTest.scala +++ b/commons/src/test/scala/io/github/tassiLuca/ChannelsContextTest.scala @@ -24,7 +24,7 @@ class ChannelsContextTest extends AnyFunSpec with Matchers { case Right(_) => i = i + 1 }.run produceOn(channel).run.await // waiting for producer to finish - AsyncOperations.sleep(1_000) // making sure consumer finishes + AsyncOperations.sleep(1_000) // making sure consumer finish i shouldBe itemsProduced } } diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManager.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManager.scala index 104c562a..effb1aae 100644 --- a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManager.scala +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManager.scala @@ -2,23 +2,17 @@ package io.github.tassiLuca.smarthome.application import gears.async.{Async, AsyncOperations, ReadableChannel} import io.github.tassiLuca.rears.{Controller, bufferWithin} -import io.github.tassiLuca.smarthome.core.{ - AlertSystemComponent, - HVACControllerComponent, - SensorHealthCheckerComponent, - TemperatureEntry, - ThermostatComponent, - ThermostatScheduler, -} +import io.github.tassiLuca.smarthome.core.{AlertSystemComponent, DashboardComponent, HeaterComponent, SensorHealthCheckerComponent, TemperatureEntry, ThermostatComponent, ThermostatScheduler} import concurrent.duration.DurationInt import scala.language.postfixOps trait ThermostatHubManager extends ThermostatComponent - with HVACControllerComponent + with HeaterComponent with SensorHealthCheckerComponent[TemperatureEntry] - with AlertSystemComponent: + with AlertSystemComponent + with DashboardComponent: override val thermostat: Thermostat = Thermostat(ThermostatScheduler(19.0)) override val sensorHealthChecker: SensorHealthChecker = SensorHealthChecker() diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/DashboardComponent.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/DashboardComponent.scala new file mode 100644 index 00000000..35e7ba95 --- /dev/null +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/DashboardComponent.scala @@ -0,0 +1,13 @@ +package io.github.tassiLuca.smarthome.core + +trait DashboardComponent: + + enum HeaterState: + case ON, OFF + + val dashboard: Dashboard + + trait Dashboard: + def updateTemperature(entries: Seq[TemperatureEntry]): Unit + def newHeaterState(state: HeaterState): Unit + def newAlert(msg: String): Unit diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/HVACControllerComponent.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/HVACControllerComponent.scala deleted file mode 100644 index 63308436..00000000 --- a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/HVACControllerComponent.scala +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.tassiLuca.smarthome.core - -import gears.async.Async - -trait HVACControllerComponent: - - /** The instance of the HVAC controller, in charge of controlling actuators. */ - val hvacController: HVACController - - /** Heater, Ventilator, Air Conditioner (HVAC) actuators controller. */ - trait HVACController: - /** Turn on the heater. */ - def onHeater(using Async): Unit - - /** Turn off the heater. */ - def offHeather(using Async): Unit - - /** Turn on the air conditioner. */ - def onAirConditioner(using Async): Unit - - /** Turn on the air conditioner. */ - def offAirConditioner(using Async): Unit diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/HeaterComponent.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/HeaterComponent.scala new file mode 100644 index 00000000..bcdc7da1 --- /dev/null +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/HeaterComponent.scala @@ -0,0 +1,16 @@ +package io.github.tassiLuca.smarthome.core + +import gears.async.Async + +trait HeaterComponent: + + /** The instance in charge of controlling heater actuator. */ + val heater: Heater + + /** Heater actuator controller. */ + trait Heater: + /** Turn on the heater. */ + def on(using Async): Unit + + /** Turn off the heater. */ + def off(using Async): Unit diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/SensorHealthCheckerComponent.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/SensorHealthCheckerComponent.scala index ad7283f2..43bcf571 100644 --- a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/SensorHealthCheckerComponent.scala +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/SensorHealthCheckerComponent.scala @@ -6,7 +6,7 @@ import io.github.tassiLuca.rears.{Consumer, State} import scala.util.{Failure, Success, Try} trait SensorHealthCheckerComponent[E <: SensorEvent]: - context: AlertSystemComponent => + context: AlertSystemComponent with DashboardComponent => /** The [[SensorHealthChecker]] instance. */ val sensorHealthChecker: SensorHealthChecker @@ -23,6 +23,6 @@ trait SensorHealthCheckerComponent[E <: SensorEvent]: override protected def react(e: Try[Seq[E]])(using Async): Unit = e match case Success(es) => if state.isDefined && es.map(_.name) != state.map(_.map(_.name)).get then - println(s"[CHECKER] Detected a change: current state is $state, new e is $es") - else println("[CHECKER] Everything ok") + context.alertSystem.notify(s"Detected a change: current state is $state, new e is $es") + context.dashboard.newAlert(s"Detected a change: current state is $state, new e is $es") case Failure(es) => context.alertSystem.notify(es.getMessage) diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/ThermostatComponent.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/ThermostatComponent.scala index e898e425..252844f3 100644 --- a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/ThermostatComponent.scala +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/core/ThermostatComponent.scala @@ -6,7 +6,7 @@ import scala.util.Try import io.github.tassiLuca.rears.{Consumer, State} trait ThermostatComponent: - context: HVACControllerComponent => + context: HeaterComponent with DashboardComponent => /** The [[Thermostat]] instance. */ val thermostat: Thermostat @@ -21,3 +21,4 @@ trait ThermostatComponent: private class ThermostatImpl(override val scheduler: ThermostatScheduler) extends Thermostat: override protected def react(e: Try[Seq[TemperatureEntry]])(using Async): Unit = println(s"[THERMOSTAT] Received $e") + e.foreach(entries => context.dashboard.updateTemperature(entries)) diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedHubManager.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedHubManager.scala index b9e0a068..5296ca6d 100644 --- a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedHubManager.scala +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedHubManager.scala @@ -8,7 +8,7 @@ import io.github.tassiLuca.smarthome.core.TemperatureEntry object MockedHubManager: private val temperatureSource = GraphicalTemperatureSource() - private val thermostatHub = MockedThermostatHubManager() + private val thermostatHub = new MockedThermostatHubManager() with SwingDashboard() def run(using Async, AsyncOperations): Unit = val channelBySensor = temperatureSource.publishingChannel.groupBy(e => e.getClass) diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedThermostatHubManager.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedThermostatHubManager.scala index 2082dd0d..207564cd 100644 --- a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedThermostatHubManager.scala +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/MockedThermostatHubManager.scala @@ -2,13 +2,12 @@ package io.github.tassiLuca.smarthome.infrastructure import gears.async.Async import io.github.tassiLuca.smarthome.application.ThermostatHubManager +import io.github.tassiLuca.smarthome.core.DashboardComponent -class MockedThermostatHubManager extends ThermostatHubManager: - override val hvacController: HVACController = new HVACController: - override def onAirConditioner(using Async): Unit = println("[HVAC] Air conditioner turned on") - override def offAirConditioner(using Async): Unit = println("[HVAC] Air conditioner turned off") - override def offHeather(using Async): Unit = println("[HVAC] Heater turned off") - override def onHeater(using Async): Unit = println("[HVAC] Heater turned on") +trait MockedThermostatHubManager extends ThermostatHubManager with DashboardComponent: + override val heater: Heater = new Heater: + override def on(using Async): Unit = println("[HVAC] Air conditioner turned on") + override def off(using Async): Unit = println("[HVAC] Heater turned off") override val alertSystem: AlertSystem = new AlertSystem: override def notify(message: String)(using Async): Unit = println(s"[ALERT-SYSTEM] $message") diff --git a/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/SwingDashboard.scala b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/SwingDashboard.scala new file mode 100644 index 00000000..36f16625 --- /dev/null +++ b/smart-hub/src/main/scala/io/github/tassiLuca/smarthome/infrastructure/SwingDashboard.scala @@ -0,0 +1,47 @@ +package io.github.tassiLuca.smarthome.infrastructure + +import io.github.tassiLuca.smarthome.core.{DashboardComponent, TemperatureEntry} + +import java.awt.{BorderLayout, FlowLayout, Font, GridLayout} +import javax.swing.{JFrame, JLabel, JPanel, JScrollPane, JTextArea, SwingUtilities, WindowConstants} + +trait SwingDashboard extends DashboardComponent: + override val dashboard: Dashboard = new Dashboard: + + private val view: MainFrame = MainFrame() + + override def updateTemperature(entries: Seq[TemperatureEntry]): Unit = SwingUtilities.invokeLater { () => + view.temperatureLabel.setText("NEW TEMP") + } + + override def newHeaterState(state: HeaterState): Unit = SwingUtilities.invokeLater { () => + view.heaterLabel.setText(state.toString) + } + + override def newAlert(msg: String): Unit = SwingUtilities.invokeLater { () => + view.alertTextArea.setText(msg) + } + + private class MainFrame extends JFrame: + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) + setSize(300, 200) + setLayout(new GridLayout(3, 1)) + val temperatureLabel = new JLabel("00°C") + temperatureLabel.setFont(new Font("Arial", Font.BOLD, 20)) + val heaterLabel = new JLabel("Off") + heaterLabel.setFont(new Font("Arial", Font.BOLD, 20)) + val alertTextArea = new JTextArea(5, 20) + alertTextArea.setEditable(false) + val tempPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)) + tempPanel.add(new JLabel("Current Temperature:")) + tempPanel.add(temperatureLabel) + val heaterPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)) + heaterPanel.add(new JLabel("Heater Status:")) + heaterPanel.add(heaterLabel) + val alertPanel = new JPanel(new BorderLayout()) + alertPanel.add(new JLabel("Alerts:"), BorderLayout.NORTH) + alertPanel.add(new JScrollPane(alertTextArea), BorderLayout.CENTER) + add(tempPanel) + add(heaterPanel) + add(alertPanel) + setVisible(true) diff --git a/smart-hub/src/test/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManagerTest.scala b/smart-hub/src/test/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManagerTest.scala index 0dbc68a7..6f173b28 100644 --- a/smart-hub/src/test/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManagerTest.scala +++ b/smart-hub/src/test/scala/io/github/tassiLuca/smarthome/application/ThermostatHubManagerTest.scala @@ -13,11 +13,9 @@ import scala.util.Random class ThermostatHubManagerTest extends AnyFlatSpec with Matchers { val thermostatHubManager: ThermostatHubManager = new ThermostatHubManager: - override val hvacController: HVACController = new HVACController: - override def onHeater(using Async): Unit = ??? - override def offHeather(using Async): Unit = ??? - override def onAirConditioner(using Async): Unit = ??? - override def offAirConditioner(using Async): Unit = ??? + override val heater: Heater = new Heater: + override def on(using Async): Unit = ??? + override def off(using Async): Unit = ??? override val alertSystem: AlertSystem = new AlertSystem: override def notify(message: String)(using Async): Unit = ???