From 1e0f68182f5aefa38850e5721337391dfffc0dc2 Mon Sep 17 00:00:00 2001 From: Kiki Carter Date: Tue, 12 Nov 2019 11:28:52 -0500 Subject: [PATCH 1/4] removed exercises 001 and 016 --- build.sbt | 15 +- .../src/main/resources/application.conf | 71 ------ .../src/main/resources/logback.xml | 34 --- .../org/neopixel/ClusterStatusTracker.scala | 150 ------------ .../neopixel/ClusterStatusTrackerMain.scala | 67 ------ .../README.md | 0 .../src/main/resources/application.conf | 0 .../src/main/resources/logback.xml | 0 .../org/neopixel/ClusterStatusTracker.scala | 0 .../neopixel/ClusterStatusTrackerMain.scala | 0 .../neopixel/ConfigSettingsExtension.scala | 0 .../src/main/scala/org/neopixel/package.scala | 0 .../src/test/resources/logback-test.xml | 0 .../README.md | 60 ----- .../neopixel/ConfigSettingsExtension.scala | 117 --------- .../src/main/scala/org/neopixel/package.scala | 158 ------------- .../src/test/resources/logback-test.xml | 36 --- exercise_016_es_vizceral/README.md | 45 ---- exercise_016_es_vizceral/docker-compose.yml | 38 --- .../src/main/resources/application.conf | 150 ------------ .../src/main/resources/logback.xml | 34 --- .../src/main/resources/sudokuclient.conf | 32 --- .../scala/org/neopixel/CellMappings.scala | 25 -- .../org/neopixel/ClusterStatusTracker.scala | 214 ----------------- .../neopixel/ClusterStatusTrackerMain.scala | 72 ------ .../neopixel/ConfigSettingsExtension.scala | 123 ---------- .../org/neopixel/PiClusterSingleton.scala | 54 ----- .../org/neopixel/SudokuDetailProcessor.scala | 102 -------- .../main/scala/org/neopixel/SudokuIO.scala | 99 -------- .../org/neopixel/SudokuProgressTracker.scala | 41 ---- .../scala/org/neopixel/SudokuSolver.scala | 120 ---------- .../src/main/scala/org/neopixel/package.scala | 223 ------------------ .../src/test/resources/logback-test.xml | 36 --- 33 files changed, 2 insertions(+), 2114 deletions(-) delete mode 100644 exercise_001_cluster_base/src/main/resources/application.conf delete mode 100644 exercise_001_cluster_base/src/main/resources/logback.xml delete mode 100644 exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala delete mode 100644 exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala rename {exercise_001_cluster_base => exercise_002_cluster_base_a}/README.md (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base_a}/src/main/resources/application.conf (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base_a}/src/main/resources/logback.xml (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base_a}/src/main/scala/org/neopixel/ClusterStatusTracker.scala (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base_a}/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala (100%) rename {exercise_001_cluster_base => exercise_002_cluster_base_a}/src/main/scala/org/neopixel/ConfigSettingsExtension.scala (100%) rename {exercise_001_cluster_base => exercise_002_cluster_base_a}/src/main/scala/org/neopixel/package.scala (100%) rename {exercise_001_cluster_base => exercise_002_cluster_base_a}/src/test/resources/logback-test.xml (100%) delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/README.md delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml delete mode 100644 exercise_016_es_vizceral/README.md delete mode 100644 exercise_016_es_vizceral/docker-compose.yml delete mode 100644 exercise_016_es_vizceral/src/main/resources/application.conf delete mode 100644 exercise_016_es_vizceral/src/main/resources/logback.xml delete mode 100644 exercise_016_es_vizceral/src/main/resources/sudokuclient.conf delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala delete mode 100644 exercise_016_es_vizceral/src/test/resources/logback-test.xml diff --git a/build.sbt b/build.sbt index 91a178d..8f38e6e 100644 --- a/build.sbt +++ b/build.sbt @@ -3,8 +3,7 @@ lazy val pi_cluster_master = (project in file(".")) .aggregate( common, exercise_000_initial_state, - exercise_001_cluster_base, - exercise_002_cluster_base_move_to_artery_tcp, + exercise_002_cluster_base_a, exercise_003_cluster_weakly_up, exercise_004_cluster_singleton, exercise_005_cluster_the_perils_of_auto_downing, @@ -18,7 +17,6 @@ lazy val pi_cluster_master = (project in file(".")) exercise_013_clustered_sudoku_solver, exercise_014_add_cluster_client, exercise_015_clustered_sudoku_solver_cluster_client_enabled, - exercise_016_es_vizceral, exercise_017_es_opentracing, exercise_018_es_classic_console, exercise_050_cluster_cluster_singleton_akka_bootstrap_discovery_via_config, @@ -31,11 +29,7 @@ lazy val exercise_000_initial_state = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") -lazy val exercise_001_cluster_base = project - .configure(CommonSettings.configure) - .dependsOn(common % "test->test;compile->compile") - -lazy val exercise_002_cluster_base_move_to_artery_tcp = project +lazy val exercise_002_cluster_base_a = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") @@ -91,10 +85,6 @@ lazy val exercise_015_clustered_sudoku_solver_cluster_client_enabled = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") -lazy val exercise_016_es_vizceral = project - .configure(CommonSettings.configure) - .dependsOn(common % "test->test;compile->compile") - lazy val exercise_017_es_opentracing = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") @@ -110,4 +100,3 @@ lazy val exercise_050_cluster_cluster_singleton_akka_bootstrap_discovery_via_con lazy val exercise_051_cluster_singleton_akka_bootstrap_discovery_via_akka_dns = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") - diff --git a/exercise_001_cluster_base/src/main/resources/application.conf b/exercise_001_cluster_base/src/main/resources/application.conf deleted file mode 100644 index 0a5826a..0000000 --- a/exercise_001_cluster_base/src/main/resources/application.conf +++ /dev/null @@ -1,71 +0,0 @@ -akka { - - discovery.method = akka-dns - - management.http { - //host = 127.0.0.1 - port = 8558 - } - - loggers = [akka.event.slf4j.Slf4jLogger] - loglevel = debug - log-dead-letters = on - logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" - logger-startup-timeout = 30s - - actor { - - provider = cluster - - debug { - lifecycle = on - unhandled = on - } - } - - cluster { - - seed-node-timeout = 12 seconds - - seed-nodes = ["akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-1}":2550", - "akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-2}":2550", - "akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-3}":2550", - "akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-4}":2550"] - } - - remote { - - enabled-transports = [akka.remote.netty.tcp] - log-remote-lifecycle-events = off - - netty.tcp { - hostname = "127.0.0.1" - port = 2550 - } - } -} - -cinnamon { - -// akka.cluster { -// domain-events = on -// member-events = on -// singleton-events = on -// shard-region-info = on -// } - - akka.actors { - "/user/*" { - report-by = instance - } - } - - prometheus { - exporters += http-server - - http-server { - host = "0.0.0.0" - } - } -} - diff --git a/exercise_001_cluster_base/src/main/resources/logback.xml b/exercise_001_cluster_base/src/main/resources/logback.xml deleted file mode 100644 index 69db243..0000000 --- a/exercise_001_cluster_base/src/main/resources/logback.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - warn - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${neopixel.log-file:-neopixel.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala b/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala deleted file mode 100644 index 4760f6c..0000000 --- a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ActorLogging, Props, Timers} -import akka.cluster.Cluster -import akka.cluster.ClusterEvent._ -import akka.cluster.MemberStatus.{Up, WeaklyUp} - -object ClusterStatusTracker { - - def resetAllLeds(strip: Adafruit_NeoPixel.type): Unit = { - for { - pixel <- 0 until strip.numPixels() - } strip.setPixelColor(pixel, Black) - strip.show() - } - - case object Heartbeat - - def props(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int): Props = - Props(new ClusterStatusTracker(strip, logicalToPhysicalLEDMapping)) -} - -class ClusterStatusTracker(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int) extends Actor with ActorLogging with SettingsActor with Timers { - import ClusterStatusTracker._ - - private val thisHost = context.system.settings.config.getString("akka.remote.netty.tcp.hostname") - log.debug(s"Starting ClusterStatus Actor on ${thisHost}:${context.system.settings.config.getString("akka.remote.netty.tcp.port")}") - - import settings._ - - private val LeaderLedNumber = 5 - private val HeartbeatLedNumber = 7 - - override def receive: Receive = idle - - def idle: Receive = akka.actor.Actor.emptyBehavior - - def running(hearbeatLEDOn: Boolean): Receive = { - case Heartbeat if hearbeatLEDOn => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), Black) - context.become(running(hearbeatLEDOn = false)) - - case Heartbeat => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), heartbeartIndicatorColor) - context.become(running(hearbeatLEDOn = true)) - - case msg @ MemberUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - log.debug(s"$msg") - - case msg @ MemberLeft(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeLeftColor) - log.debug(s"$msg") - - case msg @ MemberExited(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeExitedColor) - log.debug(s"$msg") - - case msg @ MemberJoined(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeJoinedColor) - log.debug(s"$msg") - - case msg @ MemberRemoved(member, previousStatus) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeDownColor) - log.debug(s"$msg") - - case msg @ MemberWeaklyUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - log.debug(s"$msg") - - case msg @ ReachableMember(member) if member.status == Up => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - log.debug(s"$msg") - - case msg @ ReachableMember(member) if member.status == WeaklyUp => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - log.debug(s"$msg") - - case msg @ UnreachableMember(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUnreachableColor) - log.debug(s"$msg") - - case msg @ LeaderChanged(Some(leader)) if leader.host.getOrElse("") == thisHost => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), leaderIndicatorColor) - log.debug(s"$msg") - - case msg @ LeaderChanged(Some(leader)) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - log.debug(s"$msg") - - case msg @ LeaderChanged(None) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - log.debug(s"$msg") - - case event => - log.debug(s"~~~> UNHANDLED CLUSTER DOMAIN EVENT: $event") - - } - - override def preStart(): Unit = { - strip.begin() - resetAllLeds(strip) - Cluster(context.system) - .subscribe(self, - InitialStateAsEvents, - classOf[LeaderChanged], - classOf[ReachabilityEvent], - classOf[MemberEvent] - ) - timers.startPeriodicTimer("heartbeat-timer", Heartbeat, heartbeatIndicatorInterval) - context.become(running(hearbeatLEDOn = false)) - - } - - override def postStop(): Unit = { - resetAllLeds(strip) - Cluster(context.system).unsubscribe(self) - } - - private def setPixelColorAndShow(strip: Adafruit_NeoPixel.type , - ledNumber: Int, - ledColor: Long): Unit = { - strip.setPixelColor(ledNumber, ledColor) - strip.show() - } - - def mapHostToLED(hostName: String): Int = - logicalToPhysicalLEDMapping(HostToLedMapping(hostName)) -} - diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala b/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala deleted file mode 100644 index cd58e98..0000000 --- a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.ActorSystem -import akka.management.scaladsl.AkkaManagement -import com.typesafe.config.{ConfigFactory, ConfigValueFactory} -import neopixel.{rpi_ws281xConstants => wsC} - -object ClusterStatusTrackerMain { - def main(args: Array[String]): Unit = { - System.loadLibrary("rpi_ws281x") - - val baseConfig = ConfigFactory.load() - - val nodeHostname = baseConfig.getString("cluster-node-configuration.node-hostname") - - val config = baseConfig.withValue("akka.remote.netty.tcp.hostname", ConfigValueFactory.fromAnyRef(nodeHostname)) - - val actorSystemName = s"pi-${config.getString("cluster-node-configuration.cluster-id")}-system" - - val system = ActorSystem(actorSystemName, config) - - val settings = ConfigSettingsExtension(system) - - import settings.LedStripConfig._ - - val strip = Adafruit_NeoPixel(ledCount, ledPin, ledFreqHz, ledDma, ledInvert, ledBrightness, ledChannel, wsC.WS2811_STRIP_RGB) - - val ledStripType = config.getString("cluster-status-indicator.led-strip-type") - - val logicalToPhysicalLEDMapping: Int => Int = ledStripType match { - case "eight-led-reversed-order" => - (n: Int) => scala.math.abs(n - 7) % 8 - case "ten-led-non-reversed-order" => - identity - case _ => - system.terminate() - println(s"Unknown LED strip type: $ledStripType") - System.exit(-1) - identity - } - - val clusterStatusTracker = system.actorOf(ClusterStatusTracker.props(strip, logicalToPhysicalLEDMapping), "cluster-status-tracker") - - // Start Akka HTTP Management extension - AkkaManagement(system).start - } -} diff --git a/exercise_001_cluster_base/README.md b/exercise_002_cluster_base_a/README.md similarity index 100% rename from exercise_001_cluster_base/README.md rename to exercise_002_cluster_base_a/README.md diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/application.conf b/exercise_002_cluster_base_a/src/main/resources/application.conf similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/application.conf rename to exercise_002_cluster_base_a/src/main/resources/application.conf diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/logback.xml b/exercise_002_cluster_base_a/src/main/resources/logback.xml similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/logback.xml rename to exercise_002_cluster_base_a/src/main/resources/logback.xml diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTracker.scala b/exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTracker.scala similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTracker.scala rename to exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTracker.scala diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala b/exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala rename to exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala b/exercise_002_cluster_base_a/src/main/scala/org/neopixel/ConfigSettingsExtension.scala similarity index 100% rename from exercise_001_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala rename to exercise_002_cluster_base_a/src/main/scala/org/neopixel/ConfigSettingsExtension.scala diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/package.scala b/exercise_002_cluster_base_a/src/main/scala/org/neopixel/package.scala similarity index 100% rename from exercise_001_cluster_base/src/main/scala/org/neopixel/package.scala rename to exercise_002_cluster_base_a/src/main/scala/org/neopixel/package.scala diff --git a/exercise_001_cluster_base/src/test/resources/logback-test.xml b/exercise_002_cluster_base_a/src/test/resources/logback-test.xml similarity index 100% rename from exercise_001_cluster_base/src/test/resources/logback-test.xml rename to exercise_002_cluster_base_a/src/test/resources/logback-test.xml diff --git a/exercise_002_cluster_base_move_to_artery_tcp/README.md b/exercise_002_cluster_base_move_to_artery_tcp/README.md deleted file mode 100644 index 85e0c10..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Switch remoting transport from default to Artery-TCP - -`Akka 2.5.11` introduced an new remoting transport named `Artery TCP`. - -`Artery TCP` is built on top of `Akka Streams` and provides an alternative -for the existing TCP based transport. - -Apart from giving lower latency and higher throughput, `Artery TCP` -also utilises separate TCP connections for user- and system traffic. -This is important as it prevents user traffic from 'pushing out' -system messages which in turn may lead to pushing out `Akka Cluster` -gossip messages. - -It is expected that `Artery TCP` will become the default remoting -transport protocol in a future `Akka` release. - -In this example, we take the code from the previous exercise and -switch the transport to `Artery TCP` - -# Instructions - -- `Artery TCP` is enabled by swapping the remote transport setting - which uses `Netty TCP` for one that uses the new transport. Look - at the slide deck for more details - -- Note that, in contrast with the default remoting transport, `Artery TCP` - uses `akka` instead of `akka.tcp` in the URI's protocol field. - - For example, the following URI valid for the default transport: - -```scala - akka.tcp://my-favorite-cluster-system@some-node:2550 -``` - - will change to the following version when using `Artery TCP`: - -```scala - akka://my-favorite-cluster-system@some-node:2550 -``` - -- Adapt any URIs in the configuration file the reflect this change - in the protocol field - -- Build and transfer the new version of the code to the nodes -- Verify that everything continues to work as in the previous exercise - -# LED Legend - -- LEDs 1 to 5 show the status of each node as seen by a node - - Green: Node is Up - - Red: Node is Down - - Cyan: Node in Leaving - - Magenta: Node is Exiting - - White: Node is Unreachable - - Dark Green: Node is WeaklyUp - The LED is blinking - -- LED number 6 indicates is Cyan when the node has a leader role -- LED number 7 is unused -- LED number 8 is the cluster liveliness indicator: when it blinks, we know -that the cluster node software is actually running \ No newline at end of file diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala b/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala deleted file mode 100644 index 555ee87..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider} -import com.typesafe.config.Config - -import scala.collection.JavaConverters._ -import scala.concurrent.duration.{Duration, FiniteDuration, MILLISECONDS => Millis} - -object ConfigSettingsExtension extends ExtensionId[ConfigSettingsImpl] with ExtensionIdProvider { - override def lookup = ConfigSettingsExtension - - override def createExtension(system: ExtendedActorSystem) = new ConfigSettingsImpl(system) -} - -class ConfigSettingsImpl(system: ExtendedActorSystem) extends Extension { - - private def validateColor(colorSetting: String)(implicit config: Config, colorMap: Map[String, Long]): Long = { - val color = config.getString(colorSetting).toUpperCase - if (colorMap.contains(color)) { - colorMap(color) - } else throw new Exception(s"$color: invalid color for $colorSetting") - } - - implicit val colorMap: Map[String, Long] = availableColorMap.map { case (x, y) => (x.toUpperCase, y)} - - private implicit val config: Config = system.settings.config - - private val clusterNodeConfig = config.getConfig("cluster-node-configuration") - - private val clusterId = clusterNodeConfig.getString("cluster-id") - - private val clusterNodeToLedMapping = clusterNodeConfig.getConfig(s"cluster-node-to-led-mapping.$clusterId") - - val HostToLedMapping: Map[String, Int] = (for { - mapping <- clusterNodeToLedMapping.entrySet().asScala - } yield (mapping.getKey, clusterNodeToLedMapping.getInt(mapping.getKey))).toMap - - val nodeUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-up-color") - - val nodeWeaklyUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-weakly-up-color") - - val nodeDownColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-down-color") - - val nodeLeftColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-left-color") - - val nodeExitedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-exited-color") - - val nodeUnreachableColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-unreachable-color") - - val nodeJoinedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-joined-color") - - val leaderIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-leader-indicator-color") - - val heartbeartIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-heartbeat-indicator-color") - - val heartbeatIndicatorInterval: FiniteDuration = - Duration(system.settings.config.getDuration("cluster-status-indicator.cluster-heartbeat-indicator-interval", Millis), Millis) - - object LedStripConfig { - - val ledBrightness: Short = - config.getInt("cluster-status-indicator.led-brightness").toShort - - val ledCount: Int = - config.getInt("cluster-status-indicator.led-count") - - val ledPin: Int = - config.getInt("cluster-status-indicator.led-pin") - - val ledFreqHz: Int = - config.getInt("cluster-status-indicator.led-freq-hz") - - val ledDma: Int = - config.getInt("cluster-status-indicator.led-dma") - - val ledInvert: Boolean = - config.getBoolean("cluster-status-indicator.led-invert") - - val ledChannel: Int = - config.getInt("cluster-status-indicator.led-channel") - } -} - -trait SettingsActor { - this: Actor => - - val settings: ConfigSettingsImpl = ConfigSettingsExtension(context.system) -} diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala b/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala deleted file mode 100644 index d625665..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org - -import _root_.neopixel.{rpi_ws281x => ws} -import _root_.neopixel.ws2811_t -import _root_.neopixel.{rpi_ws281xConstants => wsC} -import _root_.neopixel.{rpi_ws281xJNI => wsJ} -import _root_.neopixel.ws2811_channel_t -import _root_.neopixel.{ws2811_return_t => wsRet} - -import scala.collection.mutable.ListBuffer - -package object neopixel { - - val Black = color(0, 0, 0) - val Green = color(255, 0, 0) - val MediumGreen = color(95, 0, 0) - val DarkGreen = color(60, 0, 0) - val Orange = color(255, 165, 0) - val DarkOrange = color(255, 140, 0) - val Red = color(0, 255, 0) - val DarkRed = color(0, 100, 0) - val Blue = color(0, 0, 255) - val DarkBlue = color(0, 0, 100) - val Yellow = color(255, 255, 0) - val Cyan = color(255, 0, 255) - val Magenta = color(0, 255, 255) - val White = color(255, 255, 255) - val WhiteLow = color(100, 100, 100) - - val availableColorMap: Map[String, Long] = - Map( - "Black" -> Black, - "Green" -> Green, - "MediumGreen" -> MediumGreen, - "DarkGreen" -> DarkGreen, - "Orange" -> Orange, - "DarkOrange" -> DarkOrange, - "Red" -> Red, - "DarkRed" -> DarkRed, - "Blue" -> Blue, - "DarkBlue" -> DarkBlue, - "Yellow" -> Yellow, - "Cyan" -> Cyan, - "Magenta" -> Magenta, - "White" -> White, - "WhiteLow" -> WhiteLow - ) - - def color(red: Int, green: Int, blue: Int, white: Int = 0): Long = - (white << 24) | (red << 16)| (green << 8) | blue - - case class LED_Data (channel: ws2811_channel_t, size: Int) { -// val ledData: Array[Long] = Array.fill[Long](size)(0L) - val ledData = ListBuffer.fill[Long](size)(0L) - - def getitem(pos: Int): Long = ws.ws2811_led_get(channel, pos) - - def setitem(pos: Int, value: Long) = { - ledData(pos) = value - ws.ws2811_led_set(channel, pos, value) - } - } - - object Adafruit_NeoPixel { - - var leds: ws2811_t = _ - var led_data: LED_Data = _ - var thisChannel: ws2811_channel_t = _ - - def apply(num: Int, pin: Int, freq_hz: Long = 800000, dma: Int = 5, invert: Boolean = false, - brightness: Short = 255, channel: Int = 0, strip_type: Int = wsC.WS2811_STRIP_RGB): Adafruit_NeoPixel.type = { - - // Initialize the channels to zero - leds = new ws2811_t() - for { channum <- 0 to 1 } { - val chan = ws.ws2811_channel_get(leds, channum) - chan.setCount(0) - chan.setGpionum(0) - chan.setInvert(0) - chan.setBrightness(0) - } - - // Initialize the channel in use - thisChannel = ws.ws2811_channel_get(leds, channel) - thisChannel.setCount(num) - thisChannel.setGpionum(pin) - thisChannel.setInvert(if (invert) 1 else 0) - thisChannel.setBrightness(brightness) - - // Initialize the controller - leds.setFreq(freq_hz) - leds.setDmanum(dma) - - led_data = LED_Data(thisChannel, num) - - this - } - - def begin(): Unit = { - val resp = ws.ws2811_init(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_init failed with code $resp ($message)") - System.exit(-1) - } - } - - def show(): Unit = { - val resp = ws.ws2811_render(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_render failed with code $resp ($message)") - } - } - - def setPixelColor(n: Int, color: Long): Unit = { - led_data.setitem(n, color) - } - - def setPixelColorRGB(n: Int, red: Int, green: Int, blue: Int, white: Int): Unit = { - led_data.setitem(n, color(red, green, blue, white)) - } - - def setBrightness(brightness: Short): Unit = { - thisChannel.setBrightness(brightness) - } - - def numPixels(): Int = thisChannel.getCount - - def getPixelColor(n: Int): Long = led_data.ledData(n) - - def cleanup(): Unit = - if (leds != null) { - ws.ws2811_fini(leds) - } - } - -} diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml b/exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml deleted file mode 100644 index addb019..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - warn - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${coffee-house.log-file:-coffee-house.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - - - diff --git a/exercise_016_es_vizceral/README.md b/exercise_016_es_vizceral/README.md deleted file mode 100644 index e4bfa19..0000000 --- a/exercise_016_es_vizceral/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# How to demonstrate Enterprise Suite with Raspberry Pi Akka Cluster - -## Exercise - Vizceral Demo - -### Demo Setup Pre-requisite steps - -1. Ensure you have passwordless ssh setup between your laptop and your Raspberry Pi Cluster. - 2. Steps found [here:](https://github.com/lightbend/Pi-Akka-Cluster/blob/master/Hypriot-OS-Customisation-Instructions.md#configure-password-less-login-1) -2. ssh into each of the Rasperry Pi nodes in the cluster with user `akkapi` -> Hint 1: Use a multi-input terminal to make this easier. For example with a mac you can use iTerm2 [link to iTerm2](https://www.iterm2.com) - -### Demo Steps - -1. From the project root assemble the jar using command: - `sbt exercise_015_es_vizceral/assembly` -2. Copy the assembled jar to the raspberry pi's using the command: `CLUSTER_NR=0 ./copy 18` -3. start vizceral docker machine on your laptop using the command: - `docker-compose -f exercise_015_es_vizceral/docker-compose.yml up` -4. Since you'll be using the Akka Http client to send sudoku problems to the cluster, you'll need to start that up -locally on your machine. Run exercise 16 locally by typing: - `sbt exercise_013_add_cluster_client/run` - > HINT: The project code may be configured to use port 8080 for the Akka Http server; you'll need to change this to something - different so it does not conflict with the default vizceral port. -5. Start vizceral visualization exercise 18 on the pi cluster [*from within the terminal window logged into the pi node*] by typing: - `CLUSTER_NR=0 ./run 18` -6. Send a sudoko problem to the cluster by typing in a new terminal [*from within the project root directory on your laptop*]: - `./postSudoku 22` (you can choose any number between 1 and 77) - > HINT: In case you had to change the Akka HTTP Port, ensure you also changed the script - `postSudoku` to use that port. - - > Post a few of these problems so that vizceral will have data inside of kibana -7. Check out your cool vizceral screens by browsing to `http://localhost:8080/` - -#### troubleshooting - -1. In case you are stuck on the loading screen, here you can troubleshoot - * Open up kibana dashboard to check the metrics - * Open the Kibana page at `http://localhost:5601` - * First log in to Kibana. The default `user/password` is `elastic/changeme`: - * Before you can start exploring data in Kibana, you must set up an index. The *Index Patterns* page should be - the default page after logging in, when there are no indices already defined. You can navigate to this page by - selecting `Management` then `Kibana > Index Patterns`. - * Enter `cinnamon-metrics-YYYY-MM` for the Index pattern, where `YYYY` is the current year and `MM` is the - current month. - \ No newline at end of file diff --git a/exercise_016_es_vizceral/docker-compose.yml b/exercise_016_es_vizceral/docker-compose.yml deleted file mode 100644 index 10b94d7..0000000 --- a/exercise_016_es_vizceral/docker-compose.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: '3' -services: - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:5.6.3 - ports: - - "9200:9200" - - "9300:9300" - environment: - - discovery.type=single-node - networks: - - cinnamon - kibana: - image: docker.elastic.co/kibana/kibana:5.6.3 - ports: - - "5601:5601" - networks: - - cinnamon - depends_on: - - elasticsearch - backend: - image: lightbend-docker-registry.bintray.io/cinnamon/cinnamon-vizceral:0.1 - ports: - - "8999:8999" - networks: - - cinnamon - depends_on: - - elasticsearch - web: - image: lightbend-docker-registry.bintray.io/cinnamon/cinnamon-vizceral-frontend:0.1 - ports: - - "8080:8080" - networks: - - cinnamon - depends_on: - - backend - -networks: - cinnamon: diff --git a/exercise_016_es_vizceral/src/main/resources/application.conf b/exercise_016_es_vizceral/src/main/resources/application.conf deleted file mode 100644 index b8f30f3..0000000 --- a/exercise_016_es_vizceral/src/main/resources/application.conf +++ /dev/null @@ -1,150 +0,0 @@ -akka { - - discovery.method = config - - management.http { - // This will allow one to alter the state of a cluster using Akka HTTP Management - // Setting this option to false may create a security exposure in production - // environments - route-providers-read-only = false - - //host = 127.0.0.1 - port = 8558 - } - - extensions = ["akka.cluster.client.ClusterClientReceptionist"] - - loggers = [akka.event.slf4j.Slf4jLogger] - loglevel = debug - log-dead-letters = on - logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" - logger-startup-timeout = 30s - - coordinated-shutdown.exit-jvm = on - - actor { - - provider = cluster - - debug { - - lifecycle = on - unhandled = on - } - } - - cluster { - - seed-node-timeout = 12 seconds - - seed-nodes = ["akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-1}":2550", - "akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-2}":2550", - "akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-3}":2550", - "akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-4}":2550"] - - downing-provider-class = "com.lightbend.akka.sbr.SplitBrainResolverProvider" - - split-brain-resolver { - # Select one of the available strategies (see descriptions below): - # static-quorum, keep-majority, keep-oldest, keep-referee - # if left "off" when the downing provider is enabled cluster startup will fail. - active-strategy = static-quorum - - static-quorum { - quorum-size = 3 - } - - # Time margin after which shards or singletons that belonged to a downed/removed - # partition are created in surviving partition. The purpose of this margin is that - # in case of a network partition the persistent actors in the non-surviving partitions - # must be stopped before corresponding persistent actors are started somewhere else. - # This is useful if you implement downing strategies that handle network partitions, - # e.g. by keeping the larger side of the partition and shutting down the smaller side. - # Decision is taken by the strategy when there has been no membership or - # reachability changes for this duration, i.e. the cluster state is stable. - stable-after = 7s - } - } - - remote { - - artery { - transport = tcp - - enabled = on - - canonical { - hostname = "127.0.0.1" - port = 2550 - } - } - } -} - -cinnamon { - akka.cluster { - domain-events = on - member-events = on - node-events = on - singleton-events = on - shard-region-info = on - } - - akka.actors { - "/user/*" { - report-by = instance - traceable = on - excludes = ["akka.http.*", "akka.stream.*"] - } - } - - akka.http { - servers { - "*:*" { - paths { - "*" { - metrics = on - traceable = on - } - } - } - } - - clients { - "*:*" { - paths { - "*" { - metrics = on - traceable = on - } - } - } - } - } - - opentracing { - # Const sampler trace only for demo purposes -> never in production! - tracer { - sampler = const-sampler - const-sampler { - decision = true - } - } - - akka.trace-system-messages = yes - - zipkin { - url-connection { - # FIXME : THE IP NEEDS TO POINT TO THE MACHINE WHERE ZIPKIN IS RUNNING - endpoint = "http://192.168.0.28:9411/api/v1/spans" - } - } - } - - prometheus { - exporters += http-server - http-server { - host = "0.0.0.0" - } - } -} diff --git a/exercise_016_es_vizceral/src/main/resources/logback.xml b/exercise_016_es_vizceral/src/main/resources/logback.xml deleted file mode 100644 index 51061c9..0000000 --- a/exercise_016_es_vizceral/src/main/resources/logback.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - warn - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${neopixel.log-file:-neopixel.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - diff --git a/exercise_016_es_vizceral/src/main/resources/sudokuclient.conf b/exercise_016_es_vizceral/src/main/resources/sudokuclient.conf deleted file mode 100644 index f1d7a33..0000000 --- a/exercise_016_es_vizceral/src/main/resources/sudokuclient.conf +++ /dev/null @@ -1,32 +0,0 @@ -akka { - - loggers = [akka.event.slf4j.Slf4jLogger] - loglevel = info - log-dead-letters = on - logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" - logger-startup-timeout = 30s - - actor.provider = "akka.remote.RemoteActorRefProvider" - - remote { - - artery { - transport = tcp - - enabled = on - - canonical { - hostname = "0.0.0.0" - port = 4000 - } - } - } -} - -contact-points = [ - "akka://pi-cluster-0-system@node-0:2550", - "akka://pi-cluster-0-system@node-1:2550", - "akka://pi-cluster-0-system@node-2:2550", - "akka://pi-cluster-0-system@node-3:2550", - "akka://pi-cluster-0-system@node-4:2550" -] \ No newline at end of file diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala deleted file mode 100644 index 50d8650..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala +++ /dev/null @@ -1,25 +0,0 @@ -package org.neopixel - -object CellMappings { - - def rowToColumnCoordinates(rowNr: Int, cellNr: Int): (Int, Int) = - (cellNr, rowNr) - - def rowToBlockCoordinates(rowNr: Int, cellNr: Int): (Int, Int) = { - ((rowNr / 3) * 3 + cellNr / 3, (rowNr % 3) * 3 + cellNr % 3) - } - - def columnToRowCoordinates(columnNr: Int, cellNr: Int): (Int, Int) = - (cellNr, columnNr) - - def columnToBlockCoordinates(columnNr: Int, cellNr: Int): (Int, Int) = { - rowToBlockCoordinates(cellNr, columnNr) - } - - def blockToRowCoordinates(blockNr: Int, cellNr: Int): (Int, Int) = - ((blockNr / 3) * 3 + cellNr / 3 , (blockNr % 3) * 3 + cellNr % 3) - - def blockToColumnCoordinates(blockNr: Int, cellNr: Int): (Int, Int) = - ((blockNr % 3) * 3 + cellNr % 3 , (blockNr / 3) * 3 + cellNr / 3) - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala deleted file mode 100644 index b186294..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ActorLogging, PoisonPill, Props, Timers} -import akka.cluster.ClusterEvent._ -import akka.cluster.MemberStatus.{Up, WeaklyUp} -import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings} -import akka.cluster.{Cluster, Member} - -object ClusterStatusTracker { - - def resetAllLeds(strip: Adafruit_NeoPixel.type): Unit = { - for { - pixel <- 0 until strip.numPixels() - } strip.setPixelColor(pixel, Black) - strip.show() - } - - case object Heartbeat - case object WeaklyUpBeat - - def props(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int): Props = - Props(new ClusterStatusTracker(strip, logicalToPhysicalLEDMapping)) -} - -class ClusterStatusTracker(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int) - extends Actor with ActorLogging with SettingsActor with Timers { - import ClusterStatusTracker._ - - private val thisHost = context.system.settings.config.getString("akka.remote.artery.canonical.hostname") - log.debug(s"Starting ClusterStatus Actor on $thisHost") - - import settings._ - - private val LeaderLedNumber = 5 - private val HeartbeatLedNumber = 7 - - override def receive: Receive = idle - - def idle: Receive = akka.actor.Actor.emptyBehavior - - def running(heartbeatLEDOn: Boolean, weaklyUpIndicatorOn: Boolean, weaklyUpMembers: Set[Member]): Receive = { - case Heartbeat if heartbeatLEDOn => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), Black) - - context.become(running(heartbeatLEDOn = false, weaklyUpIndicatorOn, weaklyUpMembers)) - - case Heartbeat => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), heartbeartIndicatorColor) - - context.become(running(heartbeatLEDOn = true, weaklyUpIndicatorOn, weaklyUpMembers)) - - case WeaklyUpBeat if weaklyUpIndicatorOn => - for { - weaklyUpMember <- weaklyUpMembers - } setPixelColorAndShow(strip, mapHostToLED(weaklyUpMember.address.host.get), Black) - - context.become(running(heartbeatLEDOn, ! weaklyUpIndicatorOn, weaklyUpMembers)) - - case WeaklyUpBeat => - for { - weaklyUpMember <- weaklyUpMembers - } setPixelColorAndShow(strip, mapHostToLED(weaklyUpMember.address.host.get), nodeWeaklyUpColor) - - context.become(running(heartbeatLEDOn, ! weaklyUpIndicatorOn, weaklyUpMembers)) - - case msg @ MemberUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberLeft(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeLeftColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberExited(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeExitedColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberJoined(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeJoinedColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberRemoved(member, previousStatus) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeDownColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberWeaklyUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - - log.info(s"$msg\n${weaklyUpMembers + member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers + member)) - - case msg @ ReachableMember(member) if member.status == Up => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ ReachableMember(member) if member.status == WeaklyUp => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - - log.info(s"$msg\n${weaklyUpMembers + member }") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers + member)) - - case msg @ UnreachableMember(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUnreachableColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ LeaderChanged(Some(leader)) if leader.host.getOrElse("") == thisHost => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), leaderIndicatorColor) - - log.debug(s"$msg") - - case msg @ LeaderChanged(Some(leader)) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - - log.info(s"$msg") - - case msg @ LeaderChanged(None) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - - log.info(s"$msg") - - case event => - - log.info(s"~~~> UNHANDLED CLUSTER DOMAIN EVENT: $event") - - } - - override def preStart(): Unit = { - - log.info(s"LED brightness = ${settings.ledBrightness}") - strip.begin() - resetAllLeds(strip) - - Cluster(context.system) - .subscribe(self, - InitialStateAsEvents, - classOf[LeaderChanged], - classOf[ReachabilityEvent], - classOf[MemberEvent] - ) - - timers.startPeriodicTimer("heartbeat-timer", Heartbeat, heartbeatIndicatorInterval) - timers.startPeriodicTimer("weakly-up-beat", WeaklyUpBeat, weaklyUpIndicatorInterval) - context.become(running(heartbeatLEDOn = false, weaklyUpIndicatorOn = false, weaklyUpMembers = Set.empty[Member])) - - val piClusterSingleton = - context.actorOf( - ClusterSingletonManager.props( - PiClusterSingleton.props(strip, logicalToPhysicalLEDMapping), - PoisonPill, - ClusterSingletonManagerSettings(context.system) - ), - "pi-cluster-singleton" - ) - } - - override def postStop(): Unit = { - resetAllLeds(strip) - Cluster(context.system).unsubscribe(self) - } - - private def setPixelColorAndShow(strip: Adafruit_NeoPixel.type , - ledNumber: Int, - ledColor: Long): Unit = { - strip.setPixelColor(ledNumber, ledColor) - strip.show() - } - - def mapHostToLED(hostName: String): Int = - logicalToPhysicalLEDMapping(HostToLedMapping(hostName)) -} - diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala deleted file mode 100644 index c29f55f..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.ActorSystem -import akka.cluster.client.ClusterClientReceptionist -import com.typesafe.config.{ConfigFactory, ConfigValueFactory} -import akka.management.scaladsl.AkkaManagement -import neopixel.{rpi_ws281xConstants => wsC} - -object ClusterStatusTrackerMain { - def main(args: Array[String]): Unit = { - System.loadLibrary("rpi_ws281x") - - val baseConfig = ConfigFactory.load() - - val nodeHostname = baseConfig.getString("cluster-node-configuration.node-hostname") - - val config = baseConfig.withValue("akka.remote.artery.canonical.hostname", ConfigValueFactory.fromAnyRef(nodeHostname)) - - val actorSystemName = s"pi-${config.getString("cluster-node-configuration.cluster-id")}-system" - - val system = ActorSystem(actorSystemName, config) - - val settings = ConfigSettingsExtension(system) - - import settings.LedStripConfig._ - - val strip = Adafruit_NeoPixel(ledCount, ledPin, ledFreqHz, ledDma, ledInvert, ledBrightness, ledChannel, wsC.WS2811_STRIP_RGB) - - val ledStripType = config.getString("cluster-status-indicator.led-strip-type") - - val logicalToPhysicalLEDMapping: Int => Int = ledStripType match { - case "eight-led-reversed-order" => - (n: Int) => scala.math.abs(n - 7) % 8 - case "ten-led-non-reversed-order" => - identity - case _ => - system.terminate() - println(s"Unknown LED strip type: $ledStripType") - System.exit(-1) - identity - } - - val clusterStatusTracker = system.actorOf(ClusterStatusTracker.props(strip, logicalToPhysicalLEDMapping), "cluster-status-tracker") - - val sudokuSolver = system.actorOf(SudokuSolver.props(), "sudoku-solver") - - ClusterClientReceptionist(system).registerService(sudokuSolver) - - // Start Akka HTTP Management extension - AkkaManagement(system).start - } -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala deleted file mode 100644 index 58bc77d..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider} -import com.typesafe.config.Config - -import scala.collection.JavaConverters._ -import scala.concurrent.duration.{Duration, FiniteDuration, MILLISECONDS => Millis} - -object ConfigSettingsExtension extends ExtensionId[ConfigSettingsImpl] with ExtensionIdProvider { - override def lookup = ConfigSettingsExtension - - override def createExtension(system: ExtendedActorSystem) = new ConfigSettingsImpl(system) -} - -class ConfigSettingsImpl(system: ExtendedActorSystem) extends Extension { - - private def validateColor(colorSetting: String)(implicit config: Config, colorMap: Map[String, Long]): Long = { - val color = config.getString(colorSetting).toUpperCase - if (colorMap.contains(color)) { - colorMap(color) - } else throw new Exception(s"$color: invalid color for $colorSetting") - } - - implicit val colorMap: Map[String, Long] = availableColorMap.map { case (x, y) => (x.toUpperCase, y)} - - private implicit val config: Config = system.settings.config - - private val clusterNodeConfig = config.getConfig("cluster-node-configuration") - - private val clusterId = clusterNodeConfig.getString("cluster-id") - - private val clusterNodeToLedMapping = clusterNodeConfig.getConfig(s"cluster-node-to-led-mapping.$clusterId") - - val HostToLedMapping: Map[String, Int] = (for { - mapping <- clusterNodeToLedMapping.entrySet().asScala - } yield (mapping.getKey, clusterNodeToLedMapping.getInt(mapping.getKey))).toMap - - val ledBrightness: Byte = config.getInt("cluster-status-indicator.led-brightness").toByte - - val nodeUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-up-color") - - val nodeWeaklyUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-weakly-up-color") - - val nodeDownColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-down-color") - - val nodeLeftColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-left-color") - - val nodeExitedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-exited-color") - - val nodeUnreachableColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-unreachable-color") - - val nodeJoinedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-joined-color") - - val leaderIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-leader-indicator-color") - - val heartbeartIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-heartbeat-indicator-color") - - val heartbeatIndicatorInterval: FiniteDuration = - Duration(system.settings.config.getDuration("cluster-status-indicator.cluster-heartbeat-indicator-interval", Millis), Millis) - - val weaklyUpIndicatorInterval: FiniteDuration = - Duration(system.settings.config.getDuration("cluster-status-indicator.cluster-weakly-up-indicator-interval", Millis), Millis) - - object LedStripConfig { - - val ledBrightness: Short = - config.getInt("cluster-status-indicator.led-brightness").toShort - - val ledCount: Int = - config.getInt("cluster-status-indicator.led-count") - - val ledPin: Int = - config.getInt("cluster-status-indicator.led-pin") - - val ledFreqHz: Int = - config.getInt("cluster-status-indicator.led-freq-hz") - - val ledDma: Int = - config.getInt("cluster-status-indicator.led-dma") - - val ledInvert: Boolean = - config.getBoolean("cluster-status-indicator.led-invert") - - val ledChannel: Int = - config.getInt("cluster-status-indicator.led-channel") - } - -} - -trait SettingsActor { - this: Actor => - - val settings: ConfigSettingsImpl = ConfigSettingsExtension(context.system) -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala deleted file mode 100644 index 4417f7d..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ActorLogging, Props} - -object PiClusterSingleton { - - def props(strip: Adafruit_NeoPixel.type,logicalToPhysicalLEDMapping: Int => Int): Props = - Props(new PiClusterSingleton(strip, logicalToPhysicalLEDMapping)) -} - -class PiClusterSingleton(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int) extends Actor with ActorLogging { - - override def receive: Receive = akka.actor.Actor.emptyBehavior - - override def preStart(): Unit = { - log.info(s"ClusterSingleton started") - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(6), LightBlue) - super.preStart() - } - - override def postStop(): Unit = { - log.info(s"ClusterSingleton stopped") - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(6), Black) - super.postStop() - } - - private def setPixelColorAndShow(strip: Adafruit_NeoPixel.type , - ledNumber: Int, - ledColor: Long): Unit = { - strip.setPixelColor(ledNumber, ledColor) - strip.show() - } - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala deleted file mode 100644 index 735ff46..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala +++ /dev/null @@ -1,102 +0,0 @@ -package org.neopixel - -import akka.actor.{Actor, ActorLogging, ActorRef, Props} -import org.neopixel.SudokuDetailProcessor.UpdateSender - -object SudokuDetailProcessor { - - case class Update(cellUpdates: CellUpdates) - case class RowUpdate(id: Int, cellUpdates: CellUpdates) - case class ColumnUpdate(id: Int, cellUpdates: CellUpdates) - case class BlockUpdate(id: Int, cellUpdates: CellUpdates) - case object SudokuDetailUnchanged - case object GetSudokuDetailState - case object ResetSudokuDetailState - case class SudokuDetailState(index: Int, state: ReductionSet) - - val InitialDetailState: ReductionSet = cellIndexesVector.map(_ => initialCell) - - def props[DetailType <: SudokoDetailType](id: Int, state: ReductionSet = InitialDetailState)(implicit updateSender: UpdateSender[DetailType]): Props = - Props(new SudokuDetailProcessor[DetailType](id, state)(updateSender)) - - trait UpdateSender[A] { - def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit - - def processorName(id: Int): String - } - - implicit val rowUpdateSender: UpdateSender[Row] = new UpdateSender[Row] { - override def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit = { - sender ! RowUpdate(id, cellUpdates) - } - override def processorName(id: Int): String = s"row-processor-$id" - } - - implicit val columnUpdateSender: UpdateSender[Column] = new UpdateSender[Column] { - override def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit = - sender ! ColumnUpdate(id, cellUpdates) - override def processorName(id: Int): String = s"col-processor-$id" - } - - implicit val blockUpdateSender: UpdateSender[Block] = new UpdateSender[Block] { - override def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit = - sender ! BlockUpdate(id, cellUpdates) - override def processorName(id: Int): String = s"blk-processor-$id" - } -} - -class SudokuDetailProcessor[DetailType <: SudokoDetailType : UpdateSender](id: Int, state: ReductionSet) - extends Actor with ActorLogging { - - import ReductionRules.{reductionRuleOne, reductionRuleTwo} - import SudokuDetailProcessor._ - - override def receive: Receive = operational(id, state) - - def operational(id: Int, state: ReductionSet, fullyReduced: Boolean = false): Receive = { - - case Update(cellUpdates) if ! fullyReduced => - val updatedState = mergeState(state, cellUpdates) - val transformedUpdatedState = reductionRuleTwo(reductionRuleOne(updatedState)) - if (transformedUpdatedState == state) { - sender ! SudokuDetailUnchanged - } else { - val updateSender = implicitly[UpdateSender[DetailType]] - updateSender.sendUpdate(id, stateChanges(state, transformedUpdatedState))(sender) - context.become(operational(id, transformedUpdatedState, isFullyReduced(transformedUpdatedState))) - } - - case Update(cellUpdates) => - sender ! SudokuDetailUnchanged - - case GetSudokuDetailState => - sender() ! SudokuDetailState(id, state) - - case ResetSudokuDetailState => - context.become(operational(id, InitialDetailState)) - - } - - private def mergeState(state: ReductionSet, cellUpdates: CellUpdates): ReductionSet = { - (cellUpdates foldLeft state) { - case (stateTally, (index, updatedCellContent)) => - stateTally.updated(index, stateTally(index) & updatedCellContent) - } - } - - private def stateChanges(state: ReductionSet, updatedState: ReductionSet): CellUpdates = { - ((state zip updatedState).zipWithIndex foldRight cellUpdatesEmpty) { - case (((previousCellContent, updatedCellContent), index), cellUpdates) - if updatedCellContent != previousCellContent => - (index, updatedCellContent) +: cellUpdates - - case (_, cellUpdates) => cellUpdates - } - } - - private def isFullyReduced(state: ReductionSet): Boolean = { - val allValuesInState = state.flatten - allValuesInState == allValuesInState.distinct - } - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala deleted file mode 100644 index 6f8a7d4..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala +++ /dev/null @@ -1,99 +0,0 @@ -package org.neopixel - -object SudokuIO { - - def printRow( row: ReductionSet): String = { - def printSubRow( subRowNo: Int): String = { - val printItems = List(1,2,3) map( x => x + subRowNo * 3) - (for { elem <- row } - yield { - (printItems map (item => if ((elem & printItems.toSet).contains(item)) item.toString else " ")).mkString("") - }).mkString("| ", " | ", " |") - } - (for { subRow <- 0 until 3 } yield printSubRow(subRow)).mkString("\n") - } - - def printRowShort( row: ReductionSet): String = { - (for { - elem <- row - } yield { - if (elem.size == 1) elem.head.toString else " " - }).mkString("|","|","|") - - } - - private def sudokuCellRepresentation(content: CellContent): String = { - content.toList match { - case Nil => "x" - case singleValue +: Nil => singleValue.toString - case _ => " " - } - } - - private def sudokuRowPrinter(threeRows: Vector[ReductionSet]): String = { - val rowSubBlocks = for { - row <- threeRows - rowSubBlock <- row.map(el => sudokuCellRepresentation(el)).sliding(3,3) - rPres = rowSubBlock.mkString - - } yield rPres - rowSubBlocks.sliding(3,3).map(_.mkString("", "|", "")).mkString("|", "|\n|", "|\n") - } - - def sudokuPrinter(result: SudokuSolver.Result): String = { - result.sudoku - .sliding(3,3) - .map(sudokuRowPrinter) - .mkString("+---+---+---+\n", "+---+---+---+\n", "+---+---+---+") - } - - /* - * FileLineTraversable code taken from "Scala in Depth" by Joshua Suereth - */ - - import java.io.{BufferedReader, File, FileReader} - - import scala.language.postfixOps - class FileLineTraversable(file: File) extends Traversable[String] { - override def foreach[U](f: String => U): Unit = { - val input = new BufferedReader(new FileReader(file)) - try { - var line = input.readLine - while (line != null) { - f(line) - line = input.readLine - } - } finally { - input.close() - } - } - - override def toString: String = - "{Lines of " + file.getAbsolutePath + "}" - } - - def convertFromCellsToComplete(cellsIn: List[(String, Int)]): Seq[(Int, CellUpdates)] = - for { - (rowCells, row) <- cellsIn - updates = (rowCells.zipWithIndex foldLeft cellUpdatesEmpty) { - case (cellUpdates, (c, index)) if c != ' ' => - (index, Set(c.toString.toInt)) +: cellUpdates - case (cellUpdates, _) => cellUpdates - } - - } yield (row, updates) - - - def readSudokuFromFile(sudokuInputFile: java.io.File): Seq[(Int, CellUpdates)] = { - val dataLines = new FileLineTraversable(sudokuInputFile).toList - val cellsIn = - dataLines - .map { inputLine => """\|""".r replaceAllIn(inputLine, "")} // Remove 3x3 separator character - .filter (_ != "---+---+---") // Remove 3x3 line separator - .map ("""^[1-9 ]{9}$""".r findFirstIn(_)) // Input data should only contain values 1-9 or ' ' - .collect { case Some(x) => x} - .zipWithIndex - - convertFromCellsToComplete(cellsIn) - } -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala deleted file mode 100644 index 061fe61..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala +++ /dev/null @@ -1,41 +0,0 @@ -package org.neopixel - -import akka.actor.{Actor, ActorLogging, ActorRef, Props} - -object SudokuProgressTracker { - - case class NewUpdatesInFlight(count: Int) - - def props(rowDetailProcessors: Map[Int, ActorRef]): Props = - Props(new SudokuProgressTracker(rowDetailProcessors)) - -} - -class SudokuProgressTracker(rowDetailProcessors: Map[Int, ActorRef]) extends Actor with ActorLogging { - - import SudokuProgressTracker._ - - override def receive: Receive = trackProgress(updatesInFlight = 0) - - def trackProgress(updatesInFlight: Int) : Receive = { - case NewUpdatesInFlight(updateCount) => - context.become(trackProgress(updatesInFlight + updateCount)) - - case SudokuDetailProcessor.SudokuDetailUnchanged if updatesInFlight - 1 == 0 => - rowDetailProcessors.foreach { case (_, processor) => processor ! SudokuDetailProcessor.GetSudokuDetailState } - context.become(collectEndState()) - - case SudokuDetailProcessor.SudokuDetailUnchanged => - context.become(trackProgress(updatesInFlight - 1)) - } - - def collectEndState(remainingRows: Int = 9, endState: Vector[SudokuDetailProcessor.SudokuDetailState] = Vector.empty[SudokuDetailProcessor.SudokuDetailState]): Receive = { - case detail @ SudokuDetailProcessor.SudokuDetailState(index, state) if remainingRows == 1 => - context.parent ! SudokuSolver.Result((detail +: endState).sortBy { case SudokuDetailProcessor.SudokuDetailState(idx, _) => idx }.map { case SudokuDetailProcessor.SudokuDetailState(_, state) => state}) - context.become(trackProgress(updatesInFlight = 0)) - - case detail @ SudokuDetailProcessor.SudokuDetailState(index, state) => - context.become(collectEndState(remainingRows = remainingRows - 1, detail +: endState)) - } - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala deleted file mode 100644 index 046f8b7..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala +++ /dev/null @@ -1,120 +0,0 @@ -package org.neopixel - -import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Props, Stash} -import org.neopixel.SudokuDetailProcessor.UpdateSender - -object SudokuSolver { - - case class SetInitialCells(updates: CellUpdates) - - case class InitialRowUpdates(rowUpdates: Seq[SudokuDetailProcessor.RowUpdate]) - case class Result(sudoku: Sudoku) - - def genDetailProcessors[A <: SudokoDetailType : UpdateSender](context: ActorContext): Map[Int, ActorRef] = { - - cellIndexesVector.map { - index => - val detailProcessorName = implicitly[UpdateSender[A]].processorName(index) - val detailProcessor = context.actorOf(SudokuDetailProcessor.props[A](index), detailProcessorName) - (index, detailProcessor) - }.toMap - } - - def props(): Props = Props(new SudokuSolver) -} - -class SudokuSolver extends Actor with ActorLogging with Stash { - - import SudokuSolver._ - - private val rowDetailProcessors = genDetailProcessors[Row](context) - private val columnDetailProcessors = genDetailProcessors[Column](context) - private val blockDetailProcessors = genDetailProcessors[Block](context) - private val allDetailProcessors = List(rowDetailProcessors, columnDetailProcessors, blockDetailProcessors) - - private val progressTracker = context.actorOf(SudokuProgressTracker.props(rowDetailProcessors), "sudoku-progress-tracker") - - import org.neopixel.CellMappings._ - - override def receive: Receive = idle - - def idle: Receive = { - - case initialUpdate @ InitialRowUpdates(rowUpdates) => - rowUpdates.foreach { - case SudokuDetailProcessor.RowUpdate(row, cellUpdates) => - rowDetailProcessors(row) ! SudokuDetailProcessor.Update(cellUpdates) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(rowUpdates.size) - context.become(processRequest(Some(sender()))) - log.info(s"Received sudo problem: $initialUpdate") - - } - - def processRequest(requestor: Option[ActorRef]): Receive = { - case SudokuDetailProcessor.RowUpdate(rowNr, updates) => - updates.foreach { - case (rowCellNr, newCellContent) => - - val (columnNr, columnCellNr) = rowToColumnCoordinates(rowNr, rowCellNr) - val columnUpdate = List((columnCellNr, newCellContent)) - columnDetailProcessors(columnNr) ! SudokuDetailProcessor.Update(columnUpdate) - - val (blockNr, blockCellNr) = rowToBlockCoordinates(rowNr, rowCellNr) - val blockUpdate = List((blockCellNr, newCellContent)) - blockDetailProcessors(blockNr) ! SudokuDetailProcessor.Update(blockUpdate) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(2 * updates.size - 1) - - case SudokuDetailProcessor.ColumnUpdate(columnNr, updates) => - updates.foreach { - case (colCellNr, newCellContent) => - - val (rowNr, rowCellNr) = columnToRowCoordinates(columnNr, colCellNr) - val rowUpdate = List((rowCellNr, newCellContent)) - rowDetailProcessors(rowNr) ! SudokuDetailProcessor.Update(rowUpdate) - - val (blockNr, blockCellNr) = columnToBlockCoordinates(columnNr, colCellNr) - val blockUpdate = List((blockCellNr, newCellContent)) - blockDetailProcessors(blockNr) ! SudokuDetailProcessor.Update(blockUpdate) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(2 * updates.size - 1) - - case SudokuDetailProcessor.BlockUpdate(blockNr, updates) => - updates.foreach { - case (blockCellNr, newCellContent) => - - val (rowNr, rowCellNr) = blockToRowCoordinates(blockNr, blockCellNr) - val rowUpdate = List((rowCellNr, newCellContent)) - rowDetailProcessors(rowNr) ! SudokuDetailProcessor.Update(rowUpdate) - - val (columnNr, columnCellNr) = blockToColumnCoordinates(blockNr, blockCellNr) - val columnUpdate = List((columnCellNr, newCellContent)) - columnDetailProcessors(columnNr) ! SudokuDetailProcessor.Update(columnUpdate) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(2 * updates.size - 1) - - case unchanged @ SudokuDetailProcessor.SudokuDetailUnchanged => - progressTracker ! unchanged - - case result @ Result(sudoku) => - requestor.get ! result - resetAllDetailProcessors() - unstashAll() - context.become(idle) - log.info( - s"""Result: - |${result.sudoku.mkString("\n ", "\n ", "")} - """.stripMargin) - - case _ => - stash() - } - - private def resetAllDetailProcessors(): Unit = { - for { - processors <- allDetailProcessors - (_, processor) <- processors - } processor ! SudokuDetailProcessor.ResetSudokuDetailState - } -} \ No newline at end of file diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala deleted file mode 100644 index d2543b8..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org - -import _root_.neopixel.{rpi_ws281x => ws} -import _root_.neopixel.ws2811_t -import _root_.neopixel.{rpi_ws281xConstants => wsC} -import _root_.neopixel.{rpi_ws281xJNI => wsJ} -import _root_.neopixel.ws2811_channel_t -import _root_.neopixel.{ws2811_return_t => wsRet} - -import scala.collection.mutable.ListBuffer - -package object neopixel { - - sealed trait SudokoDetailType - case class Row(id: Int) extends SudokoDetailType - case class Column(id: Int) extends SudokoDetailType - case class Block(id: Int) extends SudokoDetailType - - type Seq[+A] = scala.collection.immutable.Seq[A] - val Seq = scala.collection.immutable.Seq - - private val N = 9 - val CELLPossibleValues: Vector[Int] = (1 to N).toVector - val cellIndexesVector: Vector[Int] = (0 until N).toVector - val initialCell: Set[Int] = Set(1 to N: _*) - - type CellContent = Set[Int] - type ReductionSet = Vector[CellContent] - type Sudoku = Vector[ReductionSet] - - type CellUpdates = Seq[(Int, Set[Int])] - val cellUpdatesEmpty = Seq.empty[(Int, Set[Int])] - - object ReductionRules { - - def reductionRuleOne(reductionSet: ReductionSet): ReductionSet = { - val inputCellsGrouped = reductionSet filter {_.size <= 7} groupBy identity - val completeInputCellGroups = inputCellsGrouped filter { - case (set, setOccurrences) => set.size == setOccurrences.length - } - val completeAndIsolatedValueSets = completeInputCellGroups.keys.toList - (completeAndIsolatedValueSets foldLeft reductionSet) { - case (cells, caivSet) => cells map { - cell => if (cell != caivSet) cell &~ caivSet else cell - } - } - } - - def reductionRuleTwo(reductionSet: ReductionSet): ReductionSet = { - val valueOccurrences = CELLPossibleValues map { value => - (cellIndexesVector zip reductionSet foldLeft Vector.empty[Int]) { - case (acc, (index, cell)) => - if (cell contains value) index +: acc else acc - } - } - - val cellIndexesToValues = - (CELLPossibleValues zip valueOccurrences) - .groupBy { case (value, occurrence) => occurrence} - .filter { case (loc, occ) => loc.length == occ.length && loc.length <= 6 } - - val cellIndexListToReducedValue = cellIndexesToValues map { - case (index, seq) => (index, (seq map { case (value, _) => value }).toSet) - } - - val cellIndexToReducedValue = cellIndexListToReducedValue flatMap { - case (cellIndexList, reducedValue) => - cellIndexList map { cellIndex => cellIndex -> reducedValue } - } - - (reductionSet.zipWithIndex foldRight Vector.empty[CellContent]) { - case ((cellValue, cellIndex), acc) => - cellIndexToReducedValue.getOrElse(cellIndex, cellValue) +: acc - } - } - } - - val Black = color(0, 0, 0) - val Green = color(255, 0, 0) - val MediumGreen = color(95, 0, 0) - val DarkGreen = color(60, 0, 0) - val Orange = color(255, 165, 0) - val DarkOrange = color(255, 140, 0) - val Red = color(0, 255, 0) - val DarkRed = color(0, 100, 0) - val Blue = color(0, 0, 255) - val LightBlue = color(40, 40, 255) - val DarkBlue = color(0, 0, 100) - val Yellow = color(255, 255, 0) - val Cyan = color(255, 0, 255) - val Magenta = color(0, 255, 255) - val White = color(255, 255, 255) - val WhiteLow = color(100, 100, 100) - - val availableColorMap: Map[String, Long] = - Map( - "Black" -> Black, - "Green" -> Green, - "MediumGreen" -> MediumGreen, - "DarkGreen" -> DarkGreen, - "Orange" -> Orange, - "DarkOrange" -> DarkOrange, - "Red" -> Red, - "DarkRed" -> DarkRed, - "Blue" -> Blue, - "DarkBlue" -> DarkBlue, - "Yellow" -> Yellow, - "Cyan" -> Cyan, - "Magenta" -> Magenta, - "White" -> White, - "WhiteLow" -> WhiteLow - ) - - def color(red: Int, green: Int, blue: Int, white: Int = 0): Long = - (white << 24) | (red << 16)| (green << 8) | blue - - case class LED_Data (channel: ws2811_channel_t, size: Int) { -// val ledData: Array[Long] = Array.fill[Long](size)(0L) - val ledData = ListBuffer.fill[Long](size)(0L) - - def getitem(pos: Int): Long = ws.ws2811_led_get(channel, pos) - - def setitem(pos: Int, value: Long) = { - ledData(pos) = value - ws.ws2811_led_set(channel, pos, value) - } - } - - object Adafruit_NeoPixel { - - var leds: ws2811_t = _ - var led_data: LED_Data = _ - var thisChannel: ws2811_channel_t = _ - - def apply(num: Int, pin: Int, freq_hz: Long = 800000, dma: Int = 5, invert: Boolean = false, - brightness: Short = 255, channel: Int = 0, strip_type: Int = wsC.WS2811_STRIP_RGB): Adafruit_NeoPixel.type = { - - // Initialize the channels to zero - leds = new ws2811_t() - for { channum <- 0 to 1 } { - val chan = ws.ws2811_channel_get(leds, channum) - chan.setCount(0) - chan.setGpionum(0) - chan.setInvert(0) - chan.setBrightness(0) - } - - // Initialize the channel in use - thisChannel = ws.ws2811_channel_get(leds, channel) - thisChannel.setCount(num) - thisChannel.setGpionum(pin) - thisChannel.setInvert(if (invert) 1 else 0) - thisChannel.setBrightness(brightness) - - // Initialize the controller - leds.setFreq(freq_hz) - leds.setDmanum(dma) - - led_data = LED_Data(thisChannel, num) - - this - } - - def begin(): Unit = { - val resp = ws.ws2811_init(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_init failed with code $resp ($message)") - System.exit(-1) - } - } - - def show(): Unit = { - val resp = ws.ws2811_render(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_render failed with code $resp ($message)") - } - } - - def setPixelColor(n: Int, color: Long): Unit = { - led_data.setitem(n, color) - } - - def setPixelColorRGB(n: Int, red: Int, green: Int, blue: Int, white: Int): Unit = { - led_data.setitem(n, color(red, green, blue, white)) - } - - def setBrightness(brightness: Short): Unit = { - thisChannel.setBrightness(brightness) - } - - def numPixels(): Int = thisChannel.getCount - - def getPixelColor(n: Int): Long = led_data.ledData(n) - - def cleanup(): Unit = - if (leds != null) { - ws.ws2811_fini(leds) - } - } - -} diff --git a/exercise_016_es_vizceral/src/test/resources/logback-test.xml b/exercise_016_es_vizceral/src/test/resources/logback-test.xml deleted file mode 100644 index cb28fac..0000000 --- a/exercise_016_es_vizceral/src/test/resources/logback-test.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - info - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${coffee-house.log-file:-coffee-house.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - - - From 4da0a331c9d865229050589e0fb5bdd02b7d5402 Mon Sep 17 00:00:00 2001 From: Kiki Carter Date: Tue, 12 Nov 2019 11:58:51 -0500 Subject: [PATCH 2/4] remove trailing _a_ on exercise_002 name --- build.sbt | 4 ++-- .../README.md | 0 .../src/main/resources/application.conf | 0 .../src/main/resources/logback.xml | 0 .../src/main/scala/org/neopixel/ClusterStatusTracker.scala | 0 .../main/scala/org/neopixel/ClusterStatusTrackerMain.scala | 0 .../src/main/scala/org/neopixel/ConfigSettingsExtension.scala | 0 .../src/main/scala/org/neopixel/package.scala | 0 .../src/test/resources/logback-test.xml | 0 9 files changed, 2 insertions(+), 2 deletions(-) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/README.md (100%) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/src/main/resources/application.conf (100%) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/src/main/resources/logback.xml (100%) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/src/main/scala/org/neopixel/ClusterStatusTracker.scala (100%) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala (100%) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/src/main/scala/org/neopixel/ConfigSettingsExtension.scala (100%) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/src/main/scala/org/neopixel/package.scala (100%) rename {exercise_002_cluster_base_a => exercise_002_cluster_base}/src/test/resources/logback-test.xml (100%) diff --git a/build.sbt b/build.sbt index 8f38e6e..01d0dbd 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ lazy val pi_cluster_master = (project in file(".")) .aggregate( common, exercise_000_initial_state, - exercise_002_cluster_base_a, + exercise_002_cluster_base, exercise_003_cluster_weakly_up, exercise_004_cluster_singleton, exercise_005_cluster_the_perils_of_auto_downing, @@ -29,7 +29,7 @@ lazy val exercise_000_initial_state = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") -lazy val exercise_002_cluster_base_a = project +lazy val exercise_002_cluster_base = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") diff --git a/exercise_002_cluster_base_a/README.md b/exercise_002_cluster_base/README.md similarity index 100% rename from exercise_002_cluster_base_a/README.md rename to exercise_002_cluster_base/README.md diff --git a/exercise_002_cluster_base_a/src/main/resources/application.conf b/exercise_002_cluster_base/src/main/resources/application.conf similarity index 100% rename from exercise_002_cluster_base_a/src/main/resources/application.conf rename to exercise_002_cluster_base/src/main/resources/application.conf diff --git a/exercise_002_cluster_base_a/src/main/resources/logback.xml b/exercise_002_cluster_base/src/main/resources/logback.xml similarity index 100% rename from exercise_002_cluster_base_a/src/main/resources/logback.xml rename to exercise_002_cluster_base/src/main/resources/logback.xml diff --git a/exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTracker.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala similarity index 100% rename from exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTracker.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala diff --git a/exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala similarity index 100% rename from exercise_002_cluster_base_a/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala diff --git a/exercise_002_cluster_base_a/src/main/scala/org/neopixel/ConfigSettingsExtension.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala similarity index 100% rename from exercise_002_cluster_base_a/src/main/scala/org/neopixel/ConfigSettingsExtension.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala diff --git a/exercise_002_cluster_base_a/src/main/scala/org/neopixel/package.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/package.scala similarity index 100% rename from exercise_002_cluster_base_a/src/main/scala/org/neopixel/package.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/package.scala diff --git a/exercise_002_cluster_base_a/src/test/resources/logback-test.xml b/exercise_002_cluster_base/src/test/resources/logback-test.xml similarity index 100% rename from exercise_002_cluster_base_a/src/test/resources/logback-test.xml rename to exercise_002_cluster_base/src/test/resources/logback-test.xml From d6080ce88c1a03256666827402189882944c1597 Mon Sep 17 00:00:00 2001 From: kikiya Date: Wed, 13 Nov 2019 12:01:04 -0500 Subject: [PATCH 3/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 180c620..d5f73b5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Description -This code is used on a Raspberry-Pi based Akka Cluster to demonstrate cluster formation, split-brain occurrence and Split Brain Resolver in a visual manner. +This code is used on a Raspberry-Pi based Akka Cluster to visually demonstrate cluster formation, split-brain occurrence and Split Brain Resolver. Each node is equipped with an 10-LED RGB strip where different LED's are used to show each node's state (`Joining`, `Up`, `Weakly-up`, `Unreachable`, `Leaving`, `Exiting` and `Down`/`Removed`). In addition, it shows if a node has a so-called _leader_ role and whether a node is running an Akka Cluster Singleton (if one is created on the cluster). From 920aeed116c2208a7bb6582426e1079b77d9aea5 Mon Sep 17 00:00:00 2001 From: Kiki Carter Date: Tue, 12 Nov 2019 11:28:52 -0500 Subject: [PATCH 4/4] removed exercises 001 and 016 remove trailing _a_ on exercise_002 name --- build.sbt | 15 +- .../src/main/resources/application.conf | 71 ------ .../src/main/resources/logback.xml | 34 --- .../org/neopixel/ClusterStatusTracker.scala | 150 ------------ .../neopixel/ClusterStatusTrackerMain.scala | 67 ------ .../README.md | 0 .../src/main/resources/application.conf | 0 .../src/main/resources/logback.xml | 0 .../org/neopixel/ClusterStatusTracker.scala | 0 .../neopixel/ClusterStatusTrackerMain.scala | 0 .../neopixel/ConfigSettingsExtension.scala | 0 .../src/main/scala/org/neopixel/package.scala | 0 .../src/test/resources/logback-test.xml | 0 .../README.md | 60 ----- .../neopixel/ConfigSettingsExtension.scala | 117 --------- .../src/main/scala/org/neopixel/package.scala | 158 ------------- .../src/test/resources/logback-test.xml | 36 --- exercise_016_es_vizceral/README.md | 45 ---- exercise_016_es_vizceral/docker-compose.yml | 38 --- .../src/main/resources/application.conf | 150 ------------ .../src/main/resources/logback.xml | 34 --- .../src/main/resources/sudokuclient.conf | 32 --- .../scala/org/neopixel/CellMappings.scala | 25 -- .../org/neopixel/ClusterStatusTracker.scala | 214 ----------------- .../neopixel/ClusterStatusTrackerMain.scala | 72 ------ .../neopixel/ConfigSettingsExtension.scala | 123 ---------- .../org/neopixel/PiClusterSingleton.scala | 54 ----- .../org/neopixel/SudokuDetailProcessor.scala | 102 -------- .../main/scala/org/neopixel/SudokuIO.scala | 99 -------- .../org/neopixel/SudokuProgressTracker.scala | 41 ---- .../scala/org/neopixel/SudokuSolver.scala | 120 ---------- .../src/main/scala/org/neopixel/package.scala | 223 ------------------ .../src/test/resources/logback-test.xml | 36 --- 33 files changed, 2 insertions(+), 2114 deletions(-) delete mode 100644 exercise_001_cluster_base/src/main/resources/application.conf delete mode 100644 exercise_001_cluster_base/src/main/resources/logback.xml delete mode 100644 exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala delete mode 100644 exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala rename {exercise_001_cluster_base => exercise_002_cluster_base}/README.md (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base}/src/main/resources/application.conf (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base}/src/main/resources/logback.xml (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base}/src/main/scala/org/neopixel/ClusterStatusTracker.scala (100%) rename {exercise_002_cluster_base_move_to_artery_tcp => exercise_002_cluster_base}/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala (100%) rename {exercise_001_cluster_base => exercise_002_cluster_base}/src/main/scala/org/neopixel/ConfigSettingsExtension.scala (100%) rename {exercise_001_cluster_base => exercise_002_cluster_base}/src/main/scala/org/neopixel/package.scala (100%) rename {exercise_001_cluster_base => exercise_002_cluster_base}/src/test/resources/logback-test.xml (100%) delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/README.md delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala delete mode 100644 exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml delete mode 100644 exercise_016_es_vizceral/README.md delete mode 100644 exercise_016_es_vizceral/docker-compose.yml delete mode 100644 exercise_016_es_vizceral/src/main/resources/application.conf delete mode 100644 exercise_016_es_vizceral/src/main/resources/logback.xml delete mode 100644 exercise_016_es_vizceral/src/main/resources/sudokuclient.conf delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala delete mode 100644 exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala delete mode 100644 exercise_016_es_vizceral/src/test/resources/logback-test.xml diff --git a/build.sbt b/build.sbt index 91a178d..01d0dbd 100644 --- a/build.sbt +++ b/build.sbt @@ -3,8 +3,7 @@ lazy val pi_cluster_master = (project in file(".")) .aggregate( common, exercise_000_initial_state, - exercise_001_cluster_base, - exercise_002_cluster_base_move_to_artery_tcp, + exercise_002_cluster_base, exercise_003_cluster_weakly_up, exercise_004_cluster_singleton, exercise_005_cluster_the_perils_of_auto_downing, @@ -18,7 +17,6 @@ lazy val pi_cluster_master = (project in file(".")) exercise_013_clustered_sudoku_solver, exercise_014_add_cluster_client, exercise_015_clustered_sudoku_solver_cluster_client_enabled, - exercise_016_es_vizceral, exercise_017_es_opentracing, exercise_018_es_classic_console, exercise_050_cluster_cluster_singleton_akka_bootstrap_discovery_via_config, @@ -31,11 +29,7 @@ lazy val exercise_000_initial_state = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") -lazy val exercise_001_cluster_base = project - .configure(CommonSettings.configure) - .dependsOn(common % "test->test;compile->compile") - -lazy val exercise_002_cluster_base_move_to_artery_tcp = project +lazy val exercise_002_cluster_base = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") @@ -91,10 +85,6 @@ lazy val exercise_015_clustered_sudoku_solver_cluster_client_enabled = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") -lazy val exercise_016_es_vizceral = project - .configure(CommonSettings.configure) - .dependsOn(common % "test->test;compile->compile") - lazy val exercise_017_es_opentracing = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") @@ -110,4 +100,3 @@ lazy val exercise_050_cluster_cluster_singleton_akka_bootstrap_discovery_via_con lazy val exercise_051_cluster_singleton_akka_bootstrap_discovery_via_akka_dns = project .configure(CommonSettings.configure) .dependsOn(common % "test->test;compile->compile") - diff --git a/exercise_001_cluster_base/src/main/resources/application.conf b/exercise_001_cluster_base/src/main/resources/application.conf deleted file mode 100644 index 0a5826a..0000000 --- a/exercise_001_cluster_base/src/main/resources/application.conf +++ /dev/null @@ -1,71 +0,0 @@ -akka { - - discovery.method = akka-dns - - management.http { - //host = 127.0.0.1 - port = 8558 - } - - loggers = [akka.event.slf4j.Slf4jLogger] - loglevel = debug - log-dead-letters = on - logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" - logger-startup-timeout = 30s - - actor { - - provider = cluster - - debug { - lifecycle = on - unhandled = on - } - } - - cluster { - - seed-node-timeout = 12 seconds - - seed-nodes = ["akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-1}":2550", - "akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-2}":2550", - "akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-3}":2550", - "akka.tcp://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-4}":2550"] - } - - remote { - - enabled-transports = [akka.remote.netty.tcp] - log-remote-lifecycle-events = off - - netty.tcp { - hostname = "127.0.0.1" - port = 2550 - } - } -} - -cinnamon { - -// akka.cluster { -// domain-events = on -// member-events = on -// singleton-events = on -// shard-region-info = on -// } - - akka.actors { - "/user/*" { - report-by = instance - } - } - - prometheus { - exporters += http-server - - http-server { - host = "0.0.0.0" - } - } -} - diff --git a/exercise_001_cluster_base/src/main/resources/logback.xml b/exercise_001_cluster_base/src/main/resources/logback.xml deleted file mode 100644 index 69db243..0000000 --- a/exercise_001_cluster_base/src/main/resources/logback.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - warn - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${neopixel.log-file:-neopixel.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala b/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala deleted file mode 100644 index 4760f6c..0000000 --- a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ActorLogging, Props, Timers} -import akka.cluster.Cluster -import akka.cluster.ClusterEvent._ -import akka.cluster.MemberStatus.{Up, WeaklyUp} - -object ClusterStatusTracker { - - def resetAllLeds(strip: Adafruit_NeoPixel.type): Unit = { - for { - pixel <- 0 until strip.numPixels() - } strip.setPixelColor(pixel, Black) - strip.show() - } - - case object Heartbeat - - def props(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int): Props = - Props(new ClusterStatusTracker(strip, logicalToPhysicalLEDMapping)) -} - -class ClusterStatusTracker(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int) extends Actor with ActorLogging with SettingsActor with Timers { - import ClusterStatusTracker._ - - private val thisHost = context.system.settings.config.getString("akka.remote.netty.tcp.hostname") - log.debug(s"Starting ClusterStatus Actor on ${thisHost}:${context.system.settings.config.getString("akka.remote.netty.tcp.port")}") - - import settings._ - - private val LeaderLedNumber = 5 - private val HeartbeatLedNumber = 7 - - override def receive: Receive = idle - - def idle: Receive = akka.actor.Actor.emptyBehavior - - def running(hearbeatLEDOn: Boolean): Receive = { - case Heartbeat if hearbeatLEDOn => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), Black) - context.become(running(hearbeatLEDOn = false)) - - case Heartbeat => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), heartbeartIndicatorColor) - context.become(running(hearbeatLEDOn = true)) - - case msg @ MemberUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - log.debug(s"$msg") - - case msg @ MemberLeft(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeLeftColor) - log.debug(s"$msg") - - case msg @ MemberExited(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeExitedColor) - log.debug(s"$msg") - - case msg @ MemberJoined(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeJoinedColor) - log.debug(s"$msg") - - case msg @ MemberRemoved(member, previousStatus) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeDownColor) - log.debug(s"$msg") - - case msg @ MemberWeaklyUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - log.debug(s"$msg") - - case msg @ ReachableMember(member) if member.status == Up => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - log.debug(s"$msg") - - case msg @ ReachableMember(member) if member.status == WeaklyUp => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - log.debug(s"$msg") - - case msg @ UnreachableMember(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUnreachableColor) - log.debug(s"$msg") - - case msg @ LeaderChanged(Some(leader)) if leader.host.getOrElse("") == thisHost => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), leaderIndicatorColor) - log.debug(s"$msg") - - case msg @ LeaderChanged(Some(leader)) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - log.debug(s"$msg") - - case msg @ LeaderChanged(None) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - log.debug(s"$msg") - - case event => - log.debug(s"~~~> UNHANDLED CLUSTER DOMAIN EVENT: $event") - - } - - override def preStart(): Unit = { - strip.begin() - resetAllLeds(strip) - Cluster(context.system) - .subscribe(self, - InitialStateAsEvents, - classOf[LeaderChanged], - classOf[ReachabilityEvent], - classOf[MemberEvent] - ) - timers.startPeriodicTimer("heartbeat-timer", Heartbeat, heartbeatIndicatorInterval) - context.become(running(hearbeatLEDOn = false)) - - } - - override def postStop(): Unit = { - resetAllLeds(strip) - Cluster(context.system).unsubscribe(self) - } - - private def setPixelColorAndShow(strip: Adafruit_NeoPixel.type , - ledNumber: Int, - ledColor: Long): Unit = { - strip.setPixelColor(ledNumber, ledColor) - strip.show() - } - - def mapHostToLED(hostName: String): Int = - logicalToPhysicalLEDMapping(HostToLedMapping(hostName)) -} - diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala b/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala deleted file mode 100644 index cd58e98..0000000 --- a/exercise_001_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.ActorSystem -import akka.management.scaladsl.AkkaManagement -import com.typesafe.config.{ConfigFactory, ConfigValueFactory} -import neopixel.{rpi_ws281xConstants => wsC} - -object ClusterStatusTrackerMain { - def main(args: Array[String]): Unit = { - System.loadLibrary("rpi_ws281x") - - val baseConfig = ConfigFactory.load() - - val nodeHostname = baseConfig.getString("cluster-node-configuration.node-hostname") - - val config = baseConfig.withValue("akka.remote.netty.tcp.hostname", ConfigValueFactory.fromAnyRef(nodeHostname)) - - val actorSystemName = s"pi-${config.getString("cluster-node-configuration.cluster-id")}-system" - - val system = ActorSystem(actorSystemName, config) - - val settings = ConfigSettingsExtension(system) - - import settings.LedStripConfig._ - - val strip = Adafruit_NeoPixel(ledCount, ledPin, ledFreqHz, ledDma, ledInvert, ledBrightness, ledChannel, wsC.WS2811_STRIP_RGB) - - val ledStripType = config.getString("cluster-status-indicator.led-strip-type") - - val logicalToPhysicalLEDMapping: Int => Int = ledStripType match { - case "eight-led-reversed-order" => - (n: Int) => scala.math.abs(n - 7) % 8 - case "ten-led-non-reversed-order" => - identity - case _ => - system.terminate() - println(s"Unknown LED strip type: $ledStripType") - System.exit(-1) - identity - } - - val clusterStatusTracker = system.actorOf(ClusterStatusTracker.props(strip, logicalToPhysicalLEDMapping), "cluster-status-tracker") - - // Start Akka HTTP Management extension - AkkaManagement(system).start - } -} diff --git a/exercise_001_cluster_base/README.md b/exercise_002_cluster_base/README.md similarity index 100% rename from exercise_001_cluster_base/README.md rename to exercise_002_cluster_base/README.md diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/application.conf b/exercise_002_cluster_base/src/main/resources/application.conf similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/application.conf rename to exercise_002_cluster_base/src/main/resources/application.conf diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/logback.xml b/exercise_002_cluster_base/src/main/resources/logback.xml similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/resources/logback.xml rename to exercise_002_cluster_base/src/main/resources/logback.xml diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTracker.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTracker.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTracker.scala diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala similarity index 100% rename from exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala similarity index 100% rename from exercise_001_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/ConfigSettingsExtension.scala diff --git a/exercise_001_cluster_base/src/main/scala/org/neopixel/package.scala b/exercise_002_cluster_base/src/main/scala/org/neopixel/package.scala similarity index 100% rename from exercise_001_cluster_base/src/main/scala/org/neopixel/package.scala rename to exercise_002_cluster_base/src/main/scala/org/neopixel/package.scala diff --git a/exercise_001_cluster_base/src/test/resources/logback-test.xml b/exercise_002_cluster_base/src/test/resources/logback-test.xml similarity index 100% rename from exercise_001_cluster_base/src/test/resources/logback-test.xml rename to exercise_002_cluster_base/src/test/resources/logback-test.xml diff --git a/exercise_002_cluster_base_move_to_artery_tcp/README.md b/exercise_002_cluster_base_move_to_artery_tcp/README.md deleted file mode 100644 index 85e0c10..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Switch remoting transport from default to Artery-TCP - -`Akka 2.5.11` introduced an new remoting transport named `Artery TCP`. - -`Artery TCP` is built on top of `Akka Streams` and provides an alternative -for the existing TCP based transport. - -Apart from giving lower latency and higher throughput, `Artery TCP` -also utilises separate TCP connections for user- and system traffic. -This is important as it prevents user traffic from 'pushing out' -system messages which in turn may lead to pushing out `Akka Cluster` -gossip messages. - -It is expected that `Artery TCP` will become the default remoting -transport protocol in a future `Akka` release. - -In this example, we take the code from the previous exercise and -switch the transport to `Artery TCP` - -# Instructions - -- `Artery TCP` is enabled by swapping the remote transport setting - which uses `Netty TCP` for one that uses the new transport. Look - at the slide deck for more details - -- Note that, in contrast with the default remoting transport, `Artery TCP` - uses `akka` instead of `akka.tcp` in the URI's protocol field. - - For example, the following URI valid for the default transport: - -```scala - akka.tcp://my-favorite-cluster-system@some-node:2550 -``` - - will change to the following version when using `Artery TCP`: - -```scala - akka://my-favorite-cluster-system@some-node:2550 -``` - -- Adapt any URIs in the configuration file the reflect this change - in the protocol field - -- Build and transfer the new version of the code to the nodes -- Verify that everything continues to work as in the previous exercise - -# LED Legend - -- LEDs 1 to 5 show the status of each node as seen by a node - - Green: Node is Up - - Red: Node is Down - - Cyan: Node in Leaving - - Magenta: Node is Exiting - - White: Node is Unreachable - - Dark Green: Node is WeaklyUp - The LED is blinking - -- LED number 6 indicates is Cyan when the node has a leader role -- LED number 7 is unused -- LED number 8 is the cluster liveliness indicator: when it blinks, we know -that the cluster node software is actually running \ No newline at end of file diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala b/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala deleted file mode 100644 index 555ee87..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/ConfigSettingsExtension.scala +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider} -import com.typesafe.config.Config - -import scala.collection.JavaConverters._ -import scala.concurrent.duration.{Duration, FiniteDuration, MILLISECONDS => Millis} - -object ConfigSettingsExtension extends ExtensionId[ConfigSettingsImpl] with ExtensionIdProvider { - override def lookup = ConfigSettingsExtension - - override def createExtension(system: ExtendedActorSystem) = new ConfigSettingsImpl(system) -} - -class ConfigSettingsImpl(system: ExtendedActorSystem) extends Extension { - - private def validateColor(colorSetting: String)(implicit config: Config, colorMap: Map[String, Long]): Long = { - val color = config.getString(colorSetting).toUpperCase - if (colorMap.contains(color)) { - colorMap(color) - } else throw new Exception(s"$color: invalid color for $colorSetting") - } - - implicit val colorMap: Map[String, Long] = availableColorMap.map { case (x, y) => (x.toUpperCase, y)} - - private implicit val config: Config = system.settings.config - - private val clusterNodeConfig = config.getConfig("cluster-node-configuration") - - private val clusterId = clusterNodeConfig.getString("cluster-id") - - private val clusterNodeToLedMapping = clusterNodeConfig.getConfig(s"cluster-node-to-led-mapping.$clusterId") - - val HostToLedMapping: Map[String, Int] = (for { - mapping <- clusterNodeToLedMapping.entrySet().asScala - } yield (mapping.getKey, clusterNodeToLedMapping.getInt(mapping.getKey))).toMap - - val nodeUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-up-color") - - val nodeWeaklyUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-weakly-up-color") - - val nodeDownColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-down-color") - - val nodeLeftColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-left-color") - - val nodeExitedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-exited-color") - - val nodeUnreachableColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-unreachable-color") - - val nodeJoinedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-joined-color") - - val leaderIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-leader-indicator-color") - - val heartbeartIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-heartbeat-indicator-color") - - val heartbeatIndicatorInterval: FiniteDuration = - Duration(system.settings.config.getDuration("cluster-status-indicator.cluster-heartbeat-indicator-interval", Millis), Millis) - - object LedStripConfig { - - val ledBrightness: Short = - config.getInt("cluster-status-indicator.led-brightness").toShort - - val ledCount: Int = - config.getInt("cluster-status-indicator.led-count") - - val ledPin: Int = - config.getInt("cluster-status-indicator.led-pin") - - val ledFreqHz: Int = - config.getInt("cluster-status-indicator.led-freq-hz") - - val ledDma: Int = - config.getInt("cluster-status-indicator.led-dma") - - val ledInvert: Boolean = - config.getBoolean("cluster-status-indicator.led-invert") - - val ledChannel: Int = - config.getInt("cluster-status-indicator.led-channel") - } -} - -trait SettingsActor { - this: Actor => - - val settings: ConfigSettingsImpl = ConfigSettingsExtension(context.system) -} diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala b/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala deleted file mode 100644 index d625665..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/src/main/scala/org/neopixel/package.scala +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org - -import _root_.neopixel.{rpi_ws281x => ws} -import _root_.neopixel.ws2811_t -import _root_.neopixel.{rpi_ws281xConstants => wsC} -import _root_.neopixel.{rpi_ws281xJNI => wsJ} -import _root_.neopixel.ws2811_channel_t -import _root_.neopixel.{ws2811_return_t => wsRet} - -import scala.collection.mutable.ListBuffer - -package object neopixel { - - val Black = color(0, 0, 0) - val Green = color(255, 0, 0) - val MediumGreen = color(95, 0, 0) - val DarkGreen = color(60, 0, 0) - val Orange = color(255, 165, 0) - val DarkOrange = color(255, 140, 0) - val Red = color(0, 255, 0) - val DarkRed = color(0, 100, 0) - val Blue = color(0, 0, 255) - val DarkBlue = color(0, 0, 100) - val Yellow = color(255, 255, 0) - val Cyan = color(255, 0, 255) - val Magenta = color(0, 255, 255) - val White = color(255, 255, 255) - val WhiteLow = color(100, 100, 100) - - val availableColorMap: Map[String, Long] = - Map( - "Black" -> Black, - "Green" -> Green, - "MediumGreen" -> MediumGreen, - "DarkGreen" -> DarkGreen, - "Orange" -> Orange, - "DarkOrange" -> DarkOrange, - "Red" -> Red, - "DarkRed" -> DarkRed, - "Blue" -> Blue, - "DarkBlue" -> DarkBlue, - "Yellow" -> Yellow, - "Cyan" -> Cyan, - "Magenta" -> Magenta, - "White" -> White, - "WhiteLow" -> WhiteLow - ) - - def color(red: Int, green: Int, blue: Int, white: Int = 0): Long = - (white << 24) | (red << 16)| (green << 8) | blue - - case class LED_Data (channel: ws2811_channel_t, size: Int) { -// val ledData: Array[Long] = Array.fill[Long](size)(0L) - val ledData = ListBuffer.fill[Long](size)(0L) - - def getitem(pos: Int): Long = ws.ws2811_led_get(channel, pos) - - def setitem(pos: Int, value: Long) = { - ledData(pos) = value - ws.ws2811_led_set(channel, pos, value) - } - } - - object Adafruit_NeoPixel { - - var leds: ws2811_t = _ - var led_data: LED_Data = _ - var thisChannel: ws2811_channel_t = _ - - def apply(num: Int, pin: Int, freq_hz: Long = 800000, dma: Int = 5, invert: Boolean = false, - brightness: Short = 255, channel: Int = 0, strip_type: Int = wsC.WS2811_STRIP_RGB): Adafruit_NeoPixel.type = { - - // Initialize the channels to zero - leds = new ws2811_t() - for { channum <- 0 to 1 } { - val chan = ws.ws2811_channel_get(leds, channum) - chan.setCount(0) - chan.setGpionum(0) - chan.setInvert(0) - chan.setBrightness(0) - } - - // Initialize the channel in use - thisChannel = ws.ws2811_channel_get(leds, channel) - thisChannel.setCount(num) - thisChannel.setGpionum(pin) - thisChannel.setInvert(if (invert) 1 else 0) - thisChannel.setBrightness(brightness) - - // Initialize the controller - leds.setFreq(freq_hz) - leds.setDmanum(dma) - - led_data = LED_Data(thisChannel, num) - - this - } - - def begin(): Unit = { - val resp = ws.ws2811_init(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_init failed with code $resp ($message)") - System.exit(-1) - } - } - - def show(): Unit = { - val resp = ws.ws2811_render(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_render failed with code $resp ($message)") - } - } - - def setPixelColor(n: Int, color: Long): Unit = { - led_data.setitem(n, color) - } - - def setPixelColorRGB(n: Int, red: Int, green: Int, blue: Int, white: Int): Unit = { - led_data.setitem(n, color(red, green, blue, white)) - } - - def setBrightness(brightness: Short): Unit = { - thisChannel.setBrightness(brightness) - } - - def numPixels(): Int = thisChannel.getCount - - def getPixelColor(n: Int): Long = led_data.ledData(n) - - def cleanup(): Unit = - if (leds != null) { - ws.ws2811_fini(leds) - } - } - -} diff --git a/exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml b/exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml deleted file mode 100644 index addb019..0000000 --- a/exercise_002_cluster_base_move_to_artery_tcp/src/test/resources/logback-test.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - warn - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${coffee-house.log-file:-coffee-house.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - - - diff --git a/exercise_016_es_vizceral/README.md b/exercise_016_es_vizceral/README.md deleted file mode 100644 index e4bfa19..0000000 --- a/exercise_016_es_vizceral/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# How to demonstrate Enterprise Suite with Raspberry Pi Akka Cluster - -## Exercise - Vizceral Demo - -### Demo Setup Pre-requisite steps - -1. Ensure you have passwordless ssh setup between your laptop and your Raspberry Pi Cluster. - 2. Steps found [here:](https://github.com/lightbend/Pi-Akka-Cluster/blob/master/Hypriot-OS-Customisation-Instructions.md#configure-password-less-login-1) -2. ssh into each of the Rasperry Pi nodes in the cluster with user `akkapi` -> Hint 1: Use a multi-input terminal to make this easier. For example with a mac you can use iTerm2 [link to iTerm2](https://www.iterm2.com) - -### Demo Steps - -1. From the project root assemble the jar using command: - `sbt exercise_015_es_vizceral/assembly` -2. Copy the assembled jar to the raspberry pi's using the command: `CLUSTER_NR=0 ./copy 18` -3. start vizceral docker machine on your laptop using the command: - `docker-compose -f exercise_015_es_vizceral/docker-compose.yml up` -4. Since you'll be using the Akka Http client to send sudoku problems to the cluster, you'll need to start that up -locally on your machine. Run exercise 16 locally by typing: - `sbt exercise_013_add_cluster_client/run` - > HINT: The project code may be configured to use port 8080 for the Akka Http server; you'll need to change this to something - different so it does not conflict with the default vizceral port. -5. Start vizceral visualization exercise 18 on the pi cluster [*from within the terminal window logged into the pi node*] by typing: - `CLUSTER_NR=0 ./run 18` -6. Send a sudoko problem to the cluster by typing in a new terminal [*from within the project root directory on your laptop*]: - `./postSudoku 22` (you can choose any number between 1 and 77) - > HINT: In case you had to change the Akka HTTP Port, ensure you also changed the script - `postSudoku` to use that port. - - > Post a few of these problems so that vizceral will have data inside of kibana -7. Check out your cool vizceral screens by browsing to `http://localhost:8080/` - -#### troubleshooting - -1. In case you are stuck on the loading screen, here you can troubleshoot - * Open up kibana dashboard to check the metrics - * Open the Kibana page at `http://localhost:5601` - * First log in to Kibana. The default `user/password` is `elastic/changeme`: - * Before you can start exploring data in Kibana, you must set up an index. The *Index Patterns* page should be - the default page after logging in, when there are no indices already defined. You can navigate to this page by - selecting `Management` then `Kibana > Index Patterns`. - * Enter `cinnamon-metrics-YYYY-MM` for the Index pattern, where `YYYY` is the current year and `MM` is the - current month. - \ No newline at end of file diff --git a/exercise_016_es_vizceral/docker-compose.yml b/exercise_016_es_vizceral/docker-compose.yml deleted file mode 100644 index 10b94d7..0000000 --- a/exercise_016_es_vizceral/docker-compose.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: '3' -services: - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:5.6.3 - ports: - - "9200:9200" - - "9300:9300" - environment: - - discovery.type=single-node - networks: - - cinnamon - kibana: - image: docker.elastic.co/kibana/kibana:5.6.3 - ports: - - "5601:5601" - networks: - - cinnamon - depends_on: - - elasticsearch - backend: - image: lightbend-docker-registry.bintray.io/cinnamon/cinnamon-vizceral:0.1 - ports: - - "8999:8999" - networks: - - cinnamon - depends_on: - - elasticsearch - web: - image: lightbend-docker-registry.bintray.io/cinnamon/cinnamon-vizceral-frontend:0.1 - ports: - - "8080:8080" - networks: - - cinnamon - depends_on: - - backend - -networks: - cinnamon: diff --git a/exercise_016_es_vizceral/src/main/resources/application.conf b/exercise_016_es_vizceral/src/main/resources/application.conf deleted file mode 100644 index b8f30f3..0000000 --- a/exercise_016_es_vizceral/src/main/resources/application.conf +++ /dev/null @@ -1,150 +0,0 @@ -akka { - - discovery.method = config - - management.http { - // This will allow one to alter the state of a cluster using Akka HTTP Management - // Setting this option to false may create a security exposure in production - // environments - route-providers-read-only = false - - //host = 127.0.0.1 - port = 8558 - } - - extensions = ["akka.cluster.client.ClusterClientReceptionist"] - - loggers = [akka.event.slf4j.Slf4jLogger] - loglevel = debug - log-dead-letters = on - logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" - logger-startup-timeout = 30s - - coordinated-shutdown.exit-jvm = on - - actor { - - provider = cluster - - debug { - - lifecycle = on - unhandled = on - } - } - - cluster { - - seed-node-timeout = 12 seconds - - seed-nodes = ["akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-1}":2550", - "akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-2}":2550", - "akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-3}":2550", - "akka://pi-"${cluster-node-configuration.cluster-id}"-system@"${cluster-node-configuration.seed-node-4}":2550"] - - downing-provider-class = "com.lightbend.akka.sbr.SplitBrainResolverProvider" - - split-brain-resolver { - # Select one of the available strategies (see descriptions below): - # static-quorum, keep-majority, keep-oldest, keep-referee - # if left "off" when the downing provider is enabled cluster startup will fail. - active-strategy = static-quorum - - static-quorum { - quorum-size = 3 - } - - # Time margin after which shards or singletons that belonged to a downed/removed - # partition are created in surviving partition. The purpose of this margin is that - # in case of a network partition the persistent actors in the non-surviving partitions - # must be stopped before corresponding persistent actors are started somewhere else. - # This is useful if you implement downing strategies that handle network partitions, - # e.g. by keeping the larger side of the partition and shutting down the smaller side. - # Decision is taken by the strategy when there has been no membership or - # reachability changes for this duration, i.e. the cluster state is stable. - stable-after = 7s - } - } - - remote { - - artery { - transport = tcp - - enabled = on - - canonical { - hostname = "127.0.0.1" - port = 2550 - } - } - } -} - -cinnamon { - akka.cluster { - domain-events = on - member-events = on - node-events = on - singleton-events = on - shard-region-info = on - } - - akka.actors { - "/user/*" { - report-by = instance - traceable = on - excludes = ["akka.http.*", "akka.stream.*"] - } - } - - akka.http { - servers { - "*:*" { - paths { - "*" { - metrics = on - traceable = on - } - } - } - } - - clients { - "*:*" { - paths { - "*" { - metrics = on - traceable = on - } - } - } - } - } - - opentracing { - # Const sampler trace only for demo purposes -> never in production! - tracer { - sampler = const-sampler - const-sampler { - decision = true - } - } - - akka.trace-system-messages = yes - - zipkin { - url-connection { - # FIXME : THE IP NEEDS TO POINT TO THE MACHINE WHERE ZIPKIN IS RUNNING - endpoint = "http://192.168.0.28:9411/api/v1/spans" - } - } - } - - prometheus { - exporters += http-server - http-server { - host = "0.0.0.0" - } - } -} diff --git a/exercise_016_es_vizceral/src/main/resources/logback.xml b/exercise_016_es_vizceral/src/main/resources/logback.xml deleted file mode 100644 index 51061c9..0000000 --- a/exercise_016_es_vizceral/src/main/resources/logback.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - warn - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${neopixel.log-file:-neopixel.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - diff --git a/exercise_016_es_vizceral/src/main/resources/sudokuclient.conf b/exercise_016_es_vizceral/src/main/resources/sudokuclient.conf deleted file mode 100644 index f1d7a33..0000000 --- a/exercise_016_es_vizceral/src/main/resources/sudokuclient.conf +++ /dev/null @@ -1,32 +0,0 @@ -akka { - - loggers = [akka.event.slf4j.Slf4jLogger] - loglevel = info - log-dead-letters = on - logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" - logger-startup-timeout = 30s - - actor.provider = "akka.remote.RemoteActorRefProvider" - - remote { - - artery { - transport = tcp - - enabled = on - - canonical { - hostname = "0.0.0.0" - port = 4000 - } - } - } -} - -contact-points = [ - "akka://pi-cluster-0-system@node-0:2550", - "akka://pi-cluster-0-system@node-1:2550", - "akka://pi-cluster-0-system@node-2:2550", - "akka://pi-cluster-0-system@node-3:2550", - "akka://pi-cluster-0-system@node-4:2550" -] \ No newline at end of file diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala deleted file mode 100644 index 50d8650..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/CellMappings.scala +++ /dev/null @@ -1,25 +0,0 @@ -package org.neopixel - -object CellMappings { - - def rowToColumnCoordinates(rowNr: Int, cellNr: Int): (Int, Int) = - (cellNr, rowNr) - - def rowToBlockCoordinates(rowNr: Int, cellNr: Int): (Int, Int) = { - ((rowNr / 3) * 3 + cellNr / 3, (rowNr % 3) * 3 + cellNr % 3) - } - - def columnToRowCoordinates(columnNr: Int, cellNr: Int): (Int, Int) = - (cellNr, columnNr) - - def columnToBlockCoordinates(columnNr: Int, cellNr: Int): (Int, Int) = { - rowToBlockCoordinates(cellNr, columnNr) - } - - def blockToRowCoordinates(blockNr: Int, cellNr: Int): (Int, Int) = - ((blockNr / 3) * 3 + cellNr / 3 , (blockNr % 3) * 3 + cellNr % 3) - - def blockToColumnCoordinates(blockNr: Int, cellNr: Int): (Int, Int) = - ((blockNr % 3) * 3 + cellNr % 3 , (blockNr / 3) * 3 + cellNr / 3) - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala deleted file mode 100644 index b186294..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTracker.scala +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ActorLogging, PoisonPill, Props, Timers} -import akka.cluster.ClusterEvent._ -import akka.cluster.MemberStatus.{Up, WeaklyUp} -import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings} -import akka.cluster.{Cluster, Member} - -object ClusterStatusTracker { - - def resetAllLeds(strip: Adafruit_NeoPixel.type): Unit = { - for { - pixel <- 0 until strip.numPixels() - } strip.setPixelColor(pixel, Black) - strip.show() - } - - case object Heartbeat - case object WeaklyUpBeat - - def props(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int): Props = - Props(new ClusterStatusTracker(strip, logicalToPhysicalLEDMapping)) -} - -class ClusterStatusTracker(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int) - extends Actor with ActorLogging with SettingsActor with Timers { - import ClusterStatusTracker._ - - private val thisHost = context.system.settings.config.getString("akka.remote.artery.canonical.hostname") - log.debug(s"Starting ClusterStatus Actor on $thisHost") - - import settings._ - - private val LeaderLedNumber = 5 - private val HeartbeatLedNumber = 7 - - override def receive: Receive = idle - - def idle: Receive = akka.actor.Actor.emptyBehavior - - def running(heartbeatLEDOn: Boolean, weaklyUpIndicatorOn: Boolean, weaklyUpMembers: Set[Member]): Receive = { - case Heartbeat if heartbeatLEDOn => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), Black) - - context.become(running(heartbeatLEDOn = false, weaklyUpIndicatorOn, weaklyUpMembers)) - - case Heartbeat => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(HeartbeatLedNumber), heartbeartIndicatorColor) - - context.become(running(heartbeatLEDOn = true, weaklyUpIndicatorOn, weaklyUpMembers)) - - case WeaklyUpBeat if weaklyUpIndicatorOn => - for { - weaklyUpMember <- weaklyUpMembers - } setPixelColorAndShow(strip, mapHostToLED(weaklyUpMember.address.host.get), Black) - - context.become(running(heartbeatLEDOn, ! weaklyUpIndicatorOn, weaklyUpMembers)) - - case WeaklyUpBeat => - for { - weaklyUpMember <- weaklyUpMembers - } setPixelColorAndShow(strip, mapHostToLED(weaklyUpMember.address.host.get), nodeWeaklyUpColor) - - context.become(running(heartbeatLEDOn, ! weaklyUpIndicatorOn, weaklyUpMembers)) - - case msg @ MemberUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberLeft(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeLeftColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberExited(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeExitedColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberJoined(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeJoinedColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberRemoved(member, previousStatus) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeDownColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ MemberWeaklyUp(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - - log.info(s"$msg\n${weaklyUpMembers + member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers + member)) - - case msg @ ReachableMember(member) if member.status == Up => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUpColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ ReachableMember(member) if member.status == WeaklyUp => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeWeaklyUpColor) - - log.info(s"$msg\n${weaklyUpMembers + member }") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers + member)) - - case msg @ UnreachableMember(member) => - setPixelColorAndShow(strip, mapHostToLED(member.address.host.get), nodeUnreachableColor) - - log.info(s"$msg\n${weaklyUpMembers - member}") - - context.become(running(heartbeatLEDOn, weaklyUpIndicatorOn, weaklyUpMembers - member)) - - case msg @ LeaderChanged(Some(leader)) if leader.host.getOrElse("") == thisHost => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), leaderIndicatorColor) - - log.debug(s"$msg") - - case msg @ LeaderChanged(Some(leader)) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - - log.info(s"$msg") - - case msg @ LeaderChanged(None) => - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(LeaderLedNumber), Black) - - log.info(s"$msg") - - case event => - - log.info(s"~~~> UNHANDLED CLUSTER DOMAIN EVENT: $event") - - } - - override def preStart(): Unit = { - - log.info(s"LED brightness = ${settings.ledBrightness}") - strip.begin() - resetAllLeds(strip) - - Cluster(context.system) - .subscribe(self, - InitialStateAsEvents, - classOf[LeaderChanged], - classOf[ReachabilityEvent], - classOf[MemberEvent] - ) - - timers.startPeriodicTimer("heartbeat-timer", Heartbeat, heartbeatIndicatorInterval) - timers.startPeriodicTimer("weakly-up-beat", WeaklyUpBeat, weaklyUpIndicatorInterval) - context.become(running(heartbeatLEDOn = false, weaklyUpIndicatorOn = false, weaklyUpMembers = Set.empty[Member])) - - val piClusterSingleton = - context.actorOf( - ClusterSingletonManager.props( - PiClusterSingleton.props(strip, logicalToPhysicalLEDMapping), - PoisonPill, - ClusterSingletonManagerSettings(context.system) - ), - "pi-cluster-singleton" - ) - } - - override def postStop(): Unit = { - resetAllLeds(strip) - Cluster(context.system).unsubscribe(self) - } - - private def setPixelColorAndShow(strip: Adafruit_NeoPixel.type , - ledNumber: Int, - ledColor: Long): Unit = { - strip.setPixelColor(ledNumber, ledColor) - strip.show() - } - - def mapHostToLED(hostName: String): Int = - logicalToPhysicalLEDMapping(HostToLedMapping(hostName)) -} - diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala deleted file mode 100644 index c29f55f..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ClusterStatusTrackerMain.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.ActorSystem -import akka.cluster.client.ClusterClientReceptionist -import com.typesafe.config.{ConfigFactory, ConfigValueFactory} -import akka.management.scaladsl.AkkaManagement -import neopixel.{rpi_ws281xConstants => wsC} - -object ClusterStatusTrackerMain { - def main(args: Array[String]): Unit = { - System.loadLibrary("rpi_ws281x") - - val baseConfig = ConfigFactory.load() - - val nodeHostname = baseConfig.getString("cluster-node-configuration.node-hostname") - - val config = baseConfig.withValue("akka.remote.artery.canonical.hostname", ConfigValueFactory.fromAnyRef(nodeHostname)) - - val actorSystemName = s"pi-${config.getString("cluster-node-configuration.cluster-id")}-system" - - val system = ActorSystem(actorSystemName, config) - - val settings = ConfigSettingsExtension(system) - - import settings.LedStripConfig._ - - val strip = Adafruit_NeoPixel(ledCount, ledPin, ledFreqHz, ledDma, ledInvert, ledBrightness, ledChannel, wsC.WS2811_STRIP_RGB) - - val ledStripType = config.getString("cluster-status-indicator.led-strip-type") - - val logicalToPhysicalLEDMapping: Int => Int = ledStripType match { - case "eight-led-reversed-order" => - (n: Int) => scala.math.abs(n - 7) % 8 - case "ten-led-non-reversed-order" => - identity - case _ => - system.terminate() - println(s"Unknown LED strip type: $ledStripType") - System.exit(-1) - identity - } - - val clusterStatusTracker = system.actorOf(ClusterStatusTracker.props(strip, logicalToPhysicalLEDMapping), "cluster-status-tracker") - - val sudokuSolver = system.actorOf(SudokuSolver.props(), "sudoku-solver") - - ClusterClientReceptionist(system).registerService(sudokuSolver) - - // Start Akka HTTP Management extension - AkkaManagement(system).start - } -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala deleted file mode 100644 index 58bc77d..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/ConfigSettingsExtension.scala +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider} -import com.typesafe.config.Config - -import scala.collection.JavaConverters._ -import scala.concurrent.duration.{Duration, FiniteDuration, MILLISECONDS => Millis} - -object ConfigSettingsExtension extends ExtensionId[ConfigSettingsImpl] with ExtensionIdProvider { - override def lookup = ConfigSettingsExtension - - override def createExtension(system: ExtendedActorSystem) = new ConfigSettingsImpl(system) -} - -class ConfigSettingsImpl(system: ExtendedActorSystem) extends Extension { - - private def validateColor(colorSetting: String)(implicit config: Config, colorMap: Map[String, Long]): Long = { - val color = config.getString(colorSetting).toUpperCase - if (colorMap.contains(color)) { - colorMap(color) - } else throw new Exception(s"$color: invalid color for $colorSetting") - } - - implicit val colorMap: Map[String, Long] = availableColorMap.map { case (x, y) => (x.toUpperCase, y)} - - private implicit val config: Config = system.settings.config - - private val clusterNodeConfig = config.getConfig("cluster-node-configuration") - - private val clusterId = clusterNodeConfig.getString("cluster-id") - - private val clusterNodeToLedMapping = clusterNodeConfig.getConfig(s"cluster-node-to-led-mapping.$clusterId") - - val HostToLedMapping: Map[String, Int] = (for { - mapping <- clusterNodeToLedMapping.entrySet().asScala - } yield (mapping.getKey, clusterNodeToLedMapping.getInt(mapping.getKey))).toMap - - val ledBrightness: Byte = config.getInt("cluster-status-indicator.led-brightness").toByte - - val nodeUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-up-color") - - val nodeWeaklyUpColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-weakly-up-color") - - val nodeDownColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-down-color") - - val nodeLeftColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-left-color") - - val nodeExitedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-exited-color") - - val nodeUnreachableColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-unreachable-color") - - val nodeJoinedColor: Long = - validateColor("cluster-status-indicator.cluster-node-colors.cluster-node-joined-color") - - val leaderIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-leader-indicator-color") - - val heartbeartIndicatorColor: Long = - validateColor("cluster-status-indicator.cluster-heartbeat-indicator-color") - - val heartbeatIndicatorInterval: FiniteDuration = - Duration(system.settings.config.getDuration("cluster-status-indicator.cluster-heartbeat-indicator-interval", Millis), Millis) - - val weaklyUpIndicatorInterval: FiniteDuration = - Duration(system.settings.config.getDuration("cluster-status-indicator.cluster-weakly-up-indicator-interval", Millis), Millis) - - object LedStripConfig { - - val ledBrightness: Short = - config.getInt("cluster-status-indicator.led-brightness").toShort - - val ledCount: Int = - config.getInt("cluster-status-indicator.led-count") - - val ledPin: Int = - config.getInt("cluster-status-indicator.led-pin") - - val ledFreqHz: Int = - config.getInt("cluster-status-indicator.led-freq-hz") - - val ledDma: Int = - config.getInt("cluster-status-indicator.led-dma") - - val ledInvert: Boolean = - config.getBoolean("cluster-status-indicator.led-invert") - - val ledChannel: Int = - config.getInt("cluster-status-indicator.led-channel") - } - -} - -trait SettingsActor { - this: Actor => - - val settings: ConfigSettingsImpl = ConfigSettingsExtension(context.system) -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala deleted file mode 100644 index 4417f7d..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/PiClusterSingleton.scala +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.neopixel - -import akka.actor.{Actor, ActorLogging, Props} - -object PiClusterSingleton { - - def props(strip: Adafruit_NeoPixel.type,logicalToPhysicalLEDMapping: Int => Int): Props = - Props(new PiClusterSingleton(strip, logicalToPhysicalLEDMapping)) -} - -class PiClusterSingleton(strip: Adafruit_NeoPixel.type, logicalToPhysicalLEDMapping: Int => Int) extends Actor with ActorLogging { - - override def receive: Receive = akka.actor.Actor.emptyBehavior - - override def preStart(): Unit = { - log.info(s"ClusterSingleton started") - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(6), LightBlue) - super.preStart() - } - - override def postStop(): Unit = { - log.info(s"ClusterSingleton stopped") - setPixelColorAndShow(strip, logicalToPhysicalLEDMapping(6), Black) - super.postStop() - } - - private def setPixelColorAndShow(strip: Adafruit_NeoPixel.type , - ledNumber: Int, - ledColor: Long): Unit = { - strip.setPixelColor(ledNumber, ledColor) - strip.show() - } - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala deleted file mode 100644 index 735ff46..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuDetailProcessor.scala +++ /dev/null @@ -1,102 +0,0 @@ -package org.neopixel - -import akka.actor.{Actor, ActorLogging, ActorRef, Props} -import org.neopixel.SudokuDetailProcessor.UpdateSender - -object SudokuDetailProcessor { - - case class Update(cellUpdates: CellUpdates) - case class RowUpdate(id: Int, cellUpdates: CellUpdates) - case class ColumnUpdate(id: Int, cellUpdates: CellUpdates) - case class BlockUpdate(id: Int, cellUpdates: CellUpdates) - case object SudokuDetailUnchanged - case object GetSudokuDetailState - case object ResetSudokuDetailState - case class SudokuDetailState(index: Int, state: ReductionSet) - - val InitialDetailState: ReductionSet = cellIndexesVector.map(_ => initialCell) - - def props[DetailType <: SudokoDetailType](id: Int, state: ReductionSet = InitialDetailState)(implicit updateSender: UpdateSender[DetailType]): Props = - Props(new SudokuDetailProcessor[DetailType](id, state)(updateSender)) - - trait UpdateSender[A] { - def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit - - def processorName(id: Int): String - } - - implicit val rowUpdateSender: UpdateSender[Row] = new UpdateSender[Row] { - override def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit = { - sender ! RowUpdate(id, cellUpdates) - } - override def processorName(id: Int): String = s"row-processor-$id" - } - - implicit val columnUpdateSender: UpdateSender[Column] = new UpdateSender[Column] { - override def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit = - sender ! ColumnUpdate(id, cellUpdates) - override def processorName(id: Int): String = s"col-processor-$id" - } - - implicit val blockUpdateSender: UpdateSender[Block] = new UpdateSender[Block] { - override def sendUpdate(id: Int, cellUpdates: CellUpdates)(implicit sender: ActorRef): Unit = - sender ! BlockUpdate(id, cellUpdates) - override def processorName(id: Int): String = s"blk-processor-$id" - } -} - -class SudokuDetailProcessor[DetailType <: SudokoDetailType : UpdateSender](id: Int, state: ReductionSet) - extends Actor with ActorLogging { - - import ReductionRules.{reductionRuleOne, reductionRuleTwo} - import SudokuDetailProcessor._ - - override def receive: Receive = operational(id, state) - - def operational(id: Int, state: ReductionSet, fullyReduced: Boolean = false): Receive = { - - case Update(cellUpdates) if ! fullyReduced => - val updatedState = mergeState(state, cellUpdates) - val transformedUpdatedState = reductionRuleTwo(reductionRuleOne(updatedState)) - if (transformedUpdatedState == state) { - sender ! SudokuDetailUnchanged - } else { - val updateSender = implicitly[UpdateSender[DetailType]] - updateSender.sendUpdate(id, stateChanges(state, transformedUpdatedState))(sender) - context.become(operational(id, transformedUpdatedState, isFullyReduced(transformedUpdatedState))) - } - - case Update(cellUpdates) => - sender ! SudokuDetailUnchanged - - case GetSudokuDetailState => - sender() ! SudokuDetailState(id, state) - - case ResetSudokuDetailState => - context.become(operational(id, InitialDetailState)) - - } - - private def mergeState(state: ReductionSet, cellUpdates: CellUpdates): ReductionSet = { - (cellUpdates foldLeft state) { - case (stateTally, (index, updatedCellContent)) => - stateTally.updated(index, stateTally(index) & updatedCellContent) - } - } - - private def stateChanges(state: ReductionSet, updatedState: ReductionSet): CellUpdates = { - ((state zip updatedState).zipWithIndex foldRight cellUpdatesEmpty) { - case (((previousCellContent, updatedCellContent), index), cellUpdates) - if updatedCellContent != previousCellContent => - (index, updatedCellContent) +: cellUpdates - - case (_, cellUpdates) => cellUpdates - } - } - - private def isFullyReduced(state: ReductionSet): Boolean = { - val allValuesInState = state.flatten - allValuesInState == allValuesInState.distinct - } - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala deleted file mode 100644 index 6f8a7d4..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuIO.scala +++ /dev/null @@ -1,99 +0,0 @@ -package org.neopixel - -object SudokuIO { - - def printRow( row: ReductionSet): String = { - def printSubRow( subRowNo: Int): String = { - val printItems = List(1,2,3) map( x => x + subRowNo * 3) - (for { elem <- row } - yield { - (printItems map (item => if ((elem & printItems.toSet).contains(item)) item.toString else " ")).mkString("") - }).mkString("| ", " | ", " |") - } - (for { subRow <- 0 until 3 } yield printSubRow(subRow)).mkString("\n") - } - - def printRowShort( row: ReductionSet): String = { - (for { - elem <- row - } yield { - if (elem.size == 1) elem.head.toString else " " - }).mkString("|","|","|") - - } - - private def sudokuCellRepresentation(content: CellContent): String = { - content.toList match { - case Nil => "x" - case singleValue +: Nil => singleValue.toString - case _ => " " - } - } - - private def sudokuRowPrinter(threeRows: Vector[ReductionSet]): String = { - val rowSubBlocks = for { - row <- threeRows - rowSubBlock <- row.map(el => sudokuCellRepresentation(el)).sliding(3,3) - rPres = rowSubBlock.mkString - - } yield rPres - rowSubBlocks.sliding(3,3).map(_.mkString("", "|", "")).mkString("|", "|\n|", "|\n") - } - - def sudokuPrinter(result: SudokuSolver.Result): String = { - result.sudoku - .sliding(3,3) - .map(sudokuRowPrinter) - .mkString("+---+---+---+\n", "+---+---+---+\n", "+---+---+---+") - } - - /* - * FileLineTraversable code taken from "Scala in Depth" by Joshua Suereth - */ - - import java.io.{BufferedReader, File, FileReader} - - import scala.language.postfixOps - class FileLineTraversable(file: File) extends Traversable[String] { - override def foreach[U](f: String => U): Unit = { - val input = new BufferedReader(new FileReader(file)) - try { - var line = input.readLine - while (line != null) { - f(line) - line = input.readLine - } - } finally { - input.close() - } - } - - override def toString: String = - "{Lines of " + file.getAbsolutePath + "}" - } - - def convertFromCellsToComplete(cellsIn: List[(String, Int)]): Seq[(Int, CellUpdates)] = - for { - (rowCells, row) <- cellsIn - updates = (rowCells.zipWithIndex foldLeft cellUpdatesEmpty) { - case (cellUpdates, (c, index)) if c != ' ' => - (index, Set(c.toString.toInt)) +: cellUpdates - case (cellUpdates, _) => cellUpdates - } - - } yield (row, updates) - - - def readSudokuFromFile(sudokuInputFile: java.io.File): Seq[(Int, CellUpdates)] = { - val dataLines = new FileLineTraversable(sudokuInputFile).toList - val cellsIn = - dataLines - .map { inputLine => """\|""".r replaceAllIn(inputLine, "")} // Remove 3x3 separator character - .filter (_ != "---+---+---") // Remove 3x3 line separator - .map ("""^[1-9 ]{9}$""".r findFirstIn(_)) // Input data should only contain values 1-9 or ' ' - .collect { case Some(x) => x} - .zipWithIndex - - convertFromCellsToComplete(cellsIn) - } -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala deleted file mode 100644 index 061fe61..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuProgressTracker.scala +++ /dev/null @@ -1,41 +0,0 @@ -package org.neopixel - -import akka.actor.{Actor, ActorLogging, ActorRef, Props} - -object SudokuProgressTracker { - - case class NewUpdatesInFlight(count: Int) - - def props(rowDetailProcessors: Map[Int, ActorRef]): Props = - Props(new SudokuProgressTracker(rowDetailProcessors)) - -} - -class SudokuProgressTracker(rowDetailProcessors: Map[Int, ActorRef]) extends Actor with ActorLogging { - - import SudokuProgressTracker._ - - override def receive: Receive = trackProgress(updatesInFlight = 0) - - def trackProgress(updatesInFlight: Int) : Receive = { - case NewUpdatesInFlight(updateCount) => - context.become(trackProgress(updatesInFlight + updateCount)) - - case SudokuDetailProcessor.SudokuDetailUnchanged if updatesInFlight - 1 == 0 => - rowDetailProcessors.foreach { case (_, processor) => processor ! SudokuDetailProcessor.GetSudokuDetailState } - context.become(collectEndState()) - - case SudokuDetailProcessor.SudokuDetailUnchanged => - context.become(trackProgress(updatesInFlight - 1)) - } - - def collectEndState(remainingRows: Int = 9, endState: Vector[SudokuDetailProcessor.SudokuDetailState] = Vector.empty[SudokuDetailProcessor.SudokuDetailState]): Receive = { - case detail @ SudokuDetailProcessor.SudokuDetailState(index, state) if remainingRows == 1 => - context.parent ! SudokuSolver.Result((detail +: endState).sortBy { case SudokuDetailProcessor.SudokuDetailState(idx, _) => idx }.map { case SudokuDetailProcessor.SudokuDetailState(_, state) => state}) - context.become(trackProgress(updatesInFlight = 0)) - - case detail @ SudokuDetailProcessor.SudokuDetailState(index, state) => - context.become(collectEndState(remainingRows = remainingRows - 1, detail +: endState)) - } - -} diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala deleted file mode 100644 index 046f8b7..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/SudokuSolver.scala +++ /dev/null @@ -1,120 +0,0 @@ -package org.neopixel - -import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Props, Stash} -import org.neopixel.SudokuDetailProcessor.UpdateSender - -object SudokuSolver { - - case class SetInitialCells(updates: CellUpdates) - - case class InitialRowUpdates(rowUpdates: Seq[SudokuDetailProcessor.RowUpdate]) - case class Result(sudoku: Sudoku) - - def genDetailProcessors[A <: SudokoDetailType : UpdateSender](context: ActorContext): Map[Int, ActorRef] = { - - cellIndexesVector.map { - index => - val detailProcessorName = implicitly[UpdateSender[A]].processorName(index) - val detailProcessor = context.actorOf(SudokuDetailProcessor.props[A](index), detailProcessorName) - (index, detailProcessor) - }.toMap - } - - def props(): Props = Props(new SudokuSolver) -} - -class SudokuSolver extends Actor with ActorLogging with Stash { - - import SudokuSolver._ - - private val rowDetailProcessors = genDetailProcessors[Row](context) - private val columnDetailProcessors = genDetailProcessors[Column](context) - private val blockDetailProcessors = genDetailProcessors[Block](context) - private val allDetailProcessors = List(rowDetailProcessors, columnDetailProcessors, blockDetailProcessors) - - private val progressTracker = context.actorOf(SudokuProgressTracker.props(rowDetailProcessors), "sudoku-progress-tracker") - - import org.neopixel.CellMappings._ - - override def receive: Receive = idle - - def idle: Receive = { - - case initialUpdate @ InitialRowUpdates(rowUpdates) => - rowUpdates.foreach { - case SudokuDetailProcessor.RowUpdate(row, cellUpdates) => - rowDetailProcessors(row) ! SudokuDetailProcessor.Update(cellUpdates) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(rowUpdates.size) - context.become(processRequest(Some(sender()))) - log.info(s"Received sudo problem: $initialUpdate") - - } - - def processRequest(requestor: Option[ActorRef]): Receive = { - case SudokuDetailProcessor.RowUpdate(rowNr, updates) => - updates.foreach { - case (rowCellNr, newCellContent) => - - val (columnNr, columnCellNr) = rowToColumnCoordinates(rowNr, rowCellNr) - val columnUpdate = List((columnCellNr, newCellContent)) - columnDetailProcessors(columnNr) ! SudokuDetailProcessor.Update(columnUpdate) - - val (blockNr, blockCellNr) = rowToBlockCoordinates(rowNr, rowCellNr) - val blockUpdate = List((blockCellNr, newCellContent)) - blockDetailProcessors(blockNr) ! SudokuDetailProcessor.Update(blockUpdate) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(2 * updates.size - 1) - - case SudokuDetailProcessor.ColumnUpdate(columnNr, updates) => - updates.foreach { - case (colCellNr, newCellContent) => - - val (rowNr, rowCellNr) = columnToRowCoordinates(columnNr, colCellNr) - val rowUpdate = List((rowCellNr, newCellContent)) - rowDetailProcessors(rowNr) ! SudokuDetailProcessor.Update(rowUpdate) - - val (blockNr, blockCellNr) = columnToBlockCoordinates(columnNr, colCellNr) - val blockUpdate = List((blockCellNr, newCellContent)) - blockDetailProcessors(blockNr) ! SudokuDetailProcessor.Update(blockUpdate) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(2 * updates.size - 1) - - case SudokuDetailProcessor.BlockUpdate(blockNr, updates) => - updates.foreach { - case (blockCellNr, newCellContent) => - - val (rowNr, rowCellNr) = blockToRowCoordinates(blockNr, blockCellNr) - val rowUpdate = List((rowCellNr, newCellContent)) - rowDetailProcessors(rowNr) ! SudokuDetailProcessor.Update(rowUpdate) - - val (columnNr, columnCellNr) = blockToColumnCoordinates(blockNr, blockCellNr) - val columnUpdate = List((columnCellNr, newCellContent)) - columnDetailProcessors(columnNr) ! SudokuDetailProcessor.Update(columnUpdate) - } - progressTracker ! SudokuProgressTracker.NewUpdatesInFlight(2 * updates.size - 1) - - case unchanged @ SudokuDetailProcessor.SudokuDetailUnchanged => - progressTracker ! unchanged - - case result @ Result(sudoku) => - requestor.get ! result - resetAllDetailProcessors() - unstashAll() - context.become(idle) - log.info( - s"""Result: - |${result.sudoku.mkString("\n ", "\n ", "")} - """.stripMargin) - - case _ => - stash() - } - - private def resetAllDetailProcessors(): Unit = { - for { - processors <- allDetailProcessors - (_, processor) <- processors - } processor ! SudokuDetailProcessor.ResetSudokuDetailState - } -} \ No newline at end of file diff --git a/exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala b/exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala deleted file mode 100644 index d2543b8..0000000 --- a/exercise_016_es_vizceral/src/main/scala/org/neopixel/package.scala +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Copyright © 2016-2019 Lightbend, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * NO COMMERCIAL SUPPORT OR ANY OTHER FORM OF SUPPORT IS OFFERED ON - * THIS SOFTWARE BY LIGHTBEND, Inc. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org - -import _root_.neopixel.{rpi_ws281x => ws} -import _root_.neopixel.ws2811_t -import _root_.neopixel.{rpi_ws281xConstants => wsC} -import _root_.neopixel.{rpi_ws281xJNI => wsJ} -import _root_.neopixel.ws2811_channel_t -import _root_.neopixel.{ws2811_return_t => wsRet} - -import scala.collection.mutable.ListBuffer - -package object neopixel { - - sealed trait SudokoDetailType - case class Row(id: Int) extends SudokoDetailType - case class Column(id: Int) extends SudokoDetailType - case class Block(id: Int) extends SudokoDetailType - - type Seq[+A] = scala.collection.immutable.Seq[A] - val Seq = scala.collection.immutable.Seq - - private val N = 9 - val CELLPossibleValues: Vector[Int] = (1 to N).toVector - val cellIndexesVector: Vector[Int] = (0 until N).toVector - val initialCell: Set[Int] = Set(1 to N: _*) - - type CellContent = Set[Int] - type ReductionSet = Vector[CellContent] - type Sudoku = Vector[ReductionSet] - - type CellUpdates = Seq[(Int, Set[Int])] - val cellUpdatesEmpty = Seq.empty[(Int, Set[Int])] - - object ReductionRules { - - def reductionRuleOne(reductionSet: ReductionSet): ReductionSet = { - val inputCellsGrouped = reductionSet filter {_.size <= 7} groupBy identity - val completeInputCellGroups = inputCellsGrouped filter { - case (set, setOccurrences) => set.size == setOccurrences.length - } - val completeAndIsolatedValueSets = completeInputCellGroups.keys.toList - (completeAndIsolatedValueSets foldLeft reductionSet) { - case (cells, caivSet) => cells map { - cell => if (cell != caivSet) cell &~ caivSet else cell - } - } - } - - def reductionRuleTwo(reductionSet: ReductionSet): ReductionSet = { - val valueOccurrences = CELLPossibleValues map { value => - (cellIndexesVector zip reductionSet foldLeft Vector.empty[Int]) { - case (acc, (index, cell)) => - if (cell contains value) index +: acc else acc - } - } - - val cellIndexesToValues = - (CELLPossibleValues zip valueOccurrences) - .groupBy { case (value, occurrence) => occurrence} - .filter { case (loc, occ) => loc.length == occ.length && loc.length <= 6 } - - val cellIndexListToReducedValue = cellIndexesToValues map { - case (index, seq) => (index, (seq map { case (value, _) => value }).toSet) - } - - val cellIndexToReducedValue = cellIndexListToReducedValue flatMap { - case (cellIndexList, reducedValue) => - cellIndexList map { cellIndex => cellIndex -> reducedValue } - } - - (reductionSet.zipWithIndex foldRight Vector.empty[CellContent]) { - case ((cellValue, cellIndex), acc) => - cellIndexToReducedValue.getOrElse(cellIndex, cellValue) +: acc - } - } - } - - val Black = color(0, 0, 0) - val Green = color(255, 0, 0) - val MediumGreen = color(95, 0, 0) - val DarkGreen = color(60, 0, 0) - val Orange = color(255, 165, 0) - val DarkOrange = color(255, 140, 0) - val Red = color(0, 255, 0) - val DarkRed = color(0, 100, 0) - val Blue = color(0, 0, 255) - val LightBlue = color(40, 40, 255) - val DarkBlue = color(0, 0, 100) - val Yellow = color(255, 255, 0) - val Cyan = color(255, 0, 255) - val Magenta = color(0, 255, 255) - val White = color(255, 255, 255) - val WhiteLow = color(100, 100, 100) - - val availableColorMap: Map[String, Long] = - Map( - "Black" -> Black, - "Green" -> Green, - "MediumGreen" -> MediumGreen, - "DarkGreen" -> DarkGreen, - "Orange" -> Orange, - "DarkOrange" -> DarkOrange, - "Red" -> Red, - "DarkRed" -> DarkRed, - "Blue" -> Blue, - "DarkBlue" -> DarkBlue, - "Yellow" -> Yellow, - "Cyan" -> Cyan, - "Magenta" -> Magenta, - "White" -> White, - "WhiteLow" -> WhiteLow - ) - - def color(red: Int, green: Int, blue: Int, white: Int = 0): Long = - (white << 24) | (red << 16)| (green << 8) | blue - - case class LED_Data (channel: ws2811_channel_t, size: Int) { -// val ledData: Array[Long] = Array.fill[Long](size)(0L) - val ledData = ListBuffer.fill[Long](size)(0L) - - def getitem(pos: Int): Long = ws.ws2811_led_get(channel, pos) - - def setitem(pos: Int, value: Long) = { - ledData(pos) = value - ws.ws2811_led_set(channel, pos, value) - } - } - - object Adafruit_NeoPixel { - - var leds: ws2811_t = _ - var led_data: LED_Data = _ - var thisChannel: ws2811_channel_t = _ - - def apply(num: Int, pin: Int, freq_hz: Long = 800000, dma: Int = 5, invert: Boolean = false, - brightness: Short = 255, channel: Int = 0, strip_type: Int = wsC.WS2811_STRIP_RGB): Adafruit_NeoPixel.type = { - - // Initialize the channels to zero - leds = new ws2811_t() - for { channum <- 0 to 1 } { - val chan = ws.ws2811_channel_get(leds, channum) - chan.setCount(0) - chan.setGpionum(0) - chan.setInvert(0) - chan.setBrightness(0) - } - - // Initialize the channel in use - thisChannel = ws.ws2811_channel_get(leds, channel) - thisChannel.setCount(num) - thisChannel.setGpionum(pin) - thisChannel.setInvert(if (invert) 1 else 0) - thisChannel.setBrightness(brightness) - - // Initialize the controller - leds.setFreq(freq_hz) - leds.setDmanum(dma) - - led_data = LED_Data(thisChannel, num) - - this - } - - def begin(): Unit = { - val resp = ws.ws2811_init(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_init failed with code $resp ($message)") - System.exit(-1) - } - } - - def show(): Unit = { - val resp = ws.ws2811_render(leds) - if (resp != wsRet.WS2811_SUCCESS) { - val message = ws.ws2811_get_return_t_str(resp) - println(s"ws2811_render failed with code $resp ($message)") - } - } - - def setPixelColor(n: Int, color: Long): Unit = { - led_data.setitem(n, color) - } - - def setPixelColorRGB(n: Int, red: Int, green: Int, blue: Int, white: Int): Unit = { - led_data.setitem(n, color(red, green, blue, white)) - } - - def setBrightness(brightness: Short): Unit = { - thisChannel.setBrightness(brightness) - } - - def numPixels(): Int = thisChannel.getCount - - def getPixelColor(n: Int): Long = led_data.ledData(n) - - def cleanup(): Unit = - if (leds != null) { - ws.ws2811_fini(leds) - } - } - -} diff --git a/exercise_016_es_vizceral/src/test/resources/logback-test.xml b/exercise_016_es_vizceral/src/test/resources/logback-test.xml deleted file mode 100644 index cb28fac..0000000 --- a/exercise_016_es_vizceral/src/test/resources/logback-test.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - info - - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - ${coffee-house.log-file:-coffee-house.log} - - %date{HH:mm:ss} %-5level [%X{akkaSource}] - %msg%n - - - - - - - - - - - - - - - - - - - - -