Skip to content

Commit

Permalink
refactor(hub): add lum
Browse files Browse the repository at this point in the history
  • Loading branch information
tassiluca committed Feb 23, 2024
1 parent 8188cdb commit 8ff4b2a
Show file tree
Hide file tree
Showing 18 changed files with 179 additions and 72 deletions.
3 changes: 3 additions & 0 deletions rears/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
api(project(":commons"))
}
12 changes: 12 additions & 0 deletions rears/src/main/scala/io/github/tassiLuca/rears/Controller.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package io.github.tassiLuca.rears

import gears.async.TaskSchedule.RepeatUntilFailure
import gears.async.{Async, ChannelMultiplexer, ReadableChannel, Task}
import io.github.tassiLuca.utils.ChannelsPimping.tryable

object Controller:

def oneToOne[T, R](
publisherChannel: ReadableChannel[T],
consumer: Consumer[R, ?],
transformation: PipelineTransformation[T, R] = identity,
): Task[Unit] =
val tranformedChannel = transformation(publisherChannel)
Task {
consumer.listeningChannel.send(tranformedChannel.read().tryable)
}.schedule(RepeatUntilFailure())

def oneToMany[T, R](
publisherChannel: ReadableChannel[T],
consumers: Set[Consumer[R, ?]],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.tassiLuca.hub.application

import scala.concurrent.duration.DurationInt
import gears.async.{Async, AsyncOperations, ReadableChannel}
import io.github.tassiLuca.hub.core.{LightingSystemComponent, LuminosityEntry}
import io.github.tassiLuca.hub.core.ports.{DashboardServiceComponent, LampsComponent}
import io.github.tassiLuca.rears.Controller
import io.github.tassiLuca.rears.bufferWithin

trait LightingHubManager extends LightingSystemComponent with LampsComponent with DashboardServiceComponent:
override val lightingSystem: LightingSystem = LightingSystem()

def run(source: ReadableChannel[LuminosityEntry])(using Async, AsyncOperations): Unit =
lightingSystem.asRunnable.run
Controller.oneToOne(
publisherChannel = source,
consumer = lightingSystem,
transformation = r => r.bufferWithin(10.seconds),
).run
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
package io.github.tassiLuca.hub.application

import gears.async.{Async, AsyncOperations, ReadableChannel}
import io.github.tassiLuca.hub.core.{AlertSystemComponent, DashboardComponent, HeaterComponent, SensorHealthCheckerComponent, TemperatureEntry, ThermostatComponent, ThermostatScheduler}
import io.github.tassiLuca.hub.core.ports.{AlertSystemComponent, DashboardServiceComponent, HeaterComponent}
import io.github.tassiLuca.hub.core.{
SensorHealthCheckerComponent,
TemperatureEntry,
ThermostatComponent,
ThermostatScheduler,
}
import io.github.tassiLuca.rears.{Controller, bufferWithin}

import concurrent.duration.DurationInt
import scala.language.postfixOps

trait ThermostatHubManager
extends ThermostatComponent
with HeaterComponent
with SensorHealthCheckerComponent[TemperatureEntry]
with HeaterComponent
with AlertSystemComponent
with DashboardComponent:
override val thermostat: Thermostat = Thermostat(ThermostatScheduler.byHour(19.0))
with DashboardServiceComponent:
override val thermostat: Thermostat = ???
override val sensorHealthChecker: SensorHealthChecker = SensorHealthChecker()

def run(source: ReadableChannel[TemperatureEntry])(using Async, AsyncOperations): Unit =
thermostat.asRunnable.run
sensorHealthChecker.asRunnable.run
Controller.oneToMany(
source,
publisherChannel = source,
consumers = Set(thermostat, sensorHealthChecker),
transformation = r => r.bufferWithin(10.seconds),
).run

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.github.tassiLuca.hub.core

import gears.async.Async
import io.github.tassiLuca.hub.core.ports.{DashboardServiceComponent, LampsComponent}
import io.github.tassiLuca.rears.{Consumer, State}

import scala.util.Try

trait LightingSystemComponent:
context: LampsComponent & DashboardServiceComponent =>

val lightingSystem: LightingSystem

trait LightingSystem
extends Consumer[Seq[LuminosityEntry], Seq[LuminosityEntry]]
with State[Seq[LuminosityEntry], Seq[LuminosityEntry]]

object LightingSystem:
def apply(): LightingSystem = LightingSystemImpl()

private class LightingSystemImpl extends LightingSystem:
override protected def react(e: Try[Seq[LuminosityEntry]])(using Async): Seq[LuminosityEntry] =
println(s"[LIGHTING SYSTEM] Received $e")
e.getOrElse(Seq())
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package io.github.tassiLuca.hub.core

import gears.async.Async
import io.github.tassiLuca.hub.core.ports.{AlertSystemComponent, DashboardServiceComponent}
import io.github.tassiLuca.rears.{Consumer, State}

import scala.util.{Failure, Success, Try}

trait SensorHealthCheckerComponent[E <: SensorEvent]:
context: AlertSystemComponent & DashboardComponent =>
context: AlertSystemComponent & DashboardServiceComponent =>

/** The [[SensorHealthChecker]] instance. */
val sensorHealthChecker: SensorHealthChecker

/** A generic consumer of [[SensorEvent]] that detects */
/** A generic consumer of [[SensorEvent]] that detects probable sensing unit malfunctioning. */
trait SensorHealthChecker extends Consumer[Seq[E], Seq[E]] with State[Seq[E], Seq[E]]

object SensorHealthChecker:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ package io.github.tassiLuca.hub.core

import io.github.tassiLuca.rears.Publisher

/** The temperature value, in a certain moment in time, expressed in °C. */
type Temperature = Double

/** The value of luminosity, in a certain moment in time. */
type Luminosity = Double

/** A generic source of [[SensorEvent]]. */
trait SensorSource extends Publisher[SensorEvent]

/** A detection performed by a sensing unit. */
sealed trait SensorEvent(val name: String)

/** A temperature detection. */
case class TemperatureEntry(sensorName: String, temperature: Temperature) extends SensorEvent(sensorName)

/** A luminosity detection. */
case class LuminosityEntry(sensorName: String, luminosity: Temperature) extends SensorEvent(sensorName)
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.github.tassiLuca.hub.core

import gears.async.Async
import io.github.tassiLuca.hub.core.ports.{DashboardServiceComponent, HeaterComponent}

import scala.util.Try
import io.github.tassiLuca.rears.{Consumer, State}

/** The component encapsulating the thermostat. */
trait ThermostatComponent:
context: HeaterComponent & DashboardComponent =>
context: HeaterComponent & DashboardServiceComponent =>

/** The [[Thermostat]] instance. */
val thermostat: Thermostat
Expand All @@ -25,9 +27,10 @@ trait ThermostatComponent:
private val hysteresis = 1.5

override protected def react(e: Try[Seq[TemperatureEntry]])(using Async): Option[Temperature] =
e.map { entries => entries.map(_.temperature).sum / entries.size }
.map { avg => avg.evaluate(); avg }
.toOption
for
averageTemperature <- e.map { entries => entries.map(_.temperature).sum / entries.size }.toOption
_ = averageTemperature.evaluate()
yield averageTemperature

extension (t: Temperature)
private def evaluate()(using Async): Unit =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.github.tassiLuca.hub.core
package io.github.tassiLuca.hub.core.ports

import gears.async.Async

/** The component encapsulating the alert system. */
trait AlertSystemComponent:

/** The alert system instance. */
val alertSystem: AlertSystem

/** The alert system port though which is possible to notify alerts. */
trait AlertSystem:
/** Notify an alert with the given [[message]]. */
def notify(message: String)(using Async): Unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.github.tassiLuca.hub.core.ports

import io.github.tassiLuca.hub.core.Temperature

/** The component encapsulating the dashboard. */
trait DashboardServiceComponent:

/** The [[DashboardService]] instance. */
val dashboard: DashboardService

/** The dashboard boundary. */
trait DashboardService:
def temperatureUpdated(newTemperature: Temperature): Unit
def onHeaterNotified(): Unit
def offHeaterNotified(): Unit
def alertNotified(msg: String): Unit
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.github.tassiLuca.hub.core
package io.github.tassiLuca.hub.core.ports

import gears.async.Async

/** The component encapsulating the heater actuator. */
trait HeaterComponent:

/** The instance in charge of controlling heater actuator. */
Expand All @@ -11,12 +12,12 @@ trait HeaterComponent:
trait Heater:
enum HeaterState:
case ON, OFF

/** Turn on the heater. */
def on()(using Async): Unit

/** Turn off the heater. */
def off()(using Async): Unit

/** The current state of the heater, i.e. [[HeaterState]]- */
def state: HeaterState
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.github.tassiLuca.hub.core.ports

import gears.async.Async

trait LampsComponent:

val lamps: Lamps

trait Lamps:
def on()(using Async): Unit
def off()(using Async): Unit
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ package io.github.tassiLuca.hub.infrastructure

import gears.async.TaskSchedule.RepeatUntilFailure
import gears.async.{Async, AsyncOperations, ReadableChannel, Task}
import io.github.tassiLuca.hub.core.TemperatureEntry
import io.github.tassiLuca.hub.core.{LuminosityEntry, TemperatureEntry}
import io.github.tassiLuca.rears.groupBy

class MockedHubManager(using Async, AsyncOperations):

private val temperatureSource = GraphicalTemperatureSource()
private val thermostatHub = new MockedThermostatHubManager() with SwingDashboard()
private val lightingHub = new MockedLightingHubManager() with SwingDashboard()

def run(): Unit =
val channelBySensor = temperatureSource.publishingChannel.groupBy(e => e.getClass)
Task {
channelBySensor.read() match
case Right((clazz, c)) if clazz == classOf[TemperatureEntry] =>
thermostatHub.run(c.asInstanceOf[ReadableChannel[TemperatureEntry]])
case Right((clazz, c)) if clazz == classOf[LuminosityEntry] =>
lightingHub.run(c.asInstanceOf[ReadableChannel[LuminosityEntry]])
case _ => ()
}.schedule(RepeatUntilFailure()).run
temperatureSource.asRunnable.run.await
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.tassiLuca.hub.infrastructure

import gears.async.Async
import io.github.tassiLuca.hub.application.LightingHubManager

trait MockedLightingHubManager extends LightingHubManager:
override val lamps: Lamps = new Lamps:
override def on()(using Async): Unit = println("[Lamp] Turning On...")
override def off()(using Async): Unit = println("[Lamp] Turning off...")
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package io.github.tassiLuca.hub.infrastructure

import gears.async.Async
import io.github.tassiLuca.hub.application.ThermostatHubManager
import io.github.tassiLuca.hub.core.DashboardComponent
import io.github.tassiLuca.hub.core.ports.DashboardServiceComponent

trait MockedThermostatHubManager extends ThermostatHubManager with DashboardComponent:
trait MockedThermostatHubManager extends ThermostatHubManager with DashboardServiceComponent:
override val heater: Heater = new Heater:
private var _state = HeaterState.OFF
override def state: HeaterState = _state
override def on()(using Async): Unit = println("[Heater] Heater turned on"); _state = HeaterState.ON
override def off()(using Async): Unit = println("[Heater] Heater turned off"); _state = HeaterState.OFF

override val alertSystem: AlertSystem = new AlertSystem:
override def notify(message: String)(using Async): Unit = println(s"[ALERT-SYSTEM] $message")
override def notify(message: String)(using Async): Unit = println(s"[Alert-System] $message")
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package io.github.tassiLuca.hub.infrastructure

import io.github.tassiLuca.hub.core.DashboardComponent
import io.github.tassiLuca.hub.infrastructure.ui.DashboardUI
import io.github.tassiLuca.hub.core.Temperature
import io.github.tassiLuca.hub.core.ports.DashboardServiceComponent

import javax.swing.SwingUtilities

trait SwingDashboard extends DashboardComponent:
override val dashboard: Dashboard = new Dashboard:
trait SwingDashboard extends DashboardServiceComponent:
override val dashboard: DashboardService = new DashboardService:

private val view = DashboardUI()

Expand All @@ -18,11 +18,11 @@ trait SwingDashboard extends DashboardComponent:
override def offHeaterNotified(): Unit = SwingUtilities.invokeLater { () =>
view.heaterLabel.setText("Off")
}

override def onHeaterNotified(): Unit = SwingUtilities.invokeLater { () =>
view.heaterLabel.setText("On")
}

override def alertNotified(msg: String): Unit = SwingUtilities.invokeLater { () =>
view.alertTextArea.setText(msg)
}
Loading

0 comments on commit 8ff4b2a

Please sign in to comment.