diff --git a/scala/src/edu/cwru/eecs293/ttf10/uxb/AbstractDevice.scala b/scala/src/edu/cwru/eecs293/ttf10/uxb/AbstractDevice.scala index 9dbc07b..53065d6 100644 --- a/scala/src/edu/cwru/eecs293/ttf10/uxb/AbstractDevice.scala +++ b/scala/src/edu/cwru/eecs293/ttf10/uxb/AbstractDevice.scala @@ -80,6 +80,19 @@ abstract class AbstractDevice[T <: AbstractDevice.Builder[T]](private val builde throw new IllegalArgumentException("Message not received: connector does not belong to this device.") } + protected def send(message: Message, connectors: List[Connector]) { + if (message == null || connectors == null || connectors.contains(null)) + throw new NullPointerException("Message not sent: null argument") + if (connectors.exists(_.getDevice != this)) + throw new IllegalArgumentException("Message not sent: connector does not belong to this device") + connectors.foreach(_.getPeer + .foreach(_.recv(message))) // `foreach` passes over an undefined optional value + } + + def broadcast(message: Message) { + send(message, connectors) + } + } diff --git a/scala/src/edu/cwru/eecs293/ttf10/uxb/Connector.scala b/scala/src/edu/cwru/eecs293/ttf10/uxb/Connector.scala index 432a182..8888edb 100644 --- a/scala/src/edu/cwru/eecs293/ttf10/uxb/Connector.scala +++ b/scala/src/edu/cwru/eecs293/ttf10/uxb/Connector.scala @@ -58,7 +58,12 @@ final class Connector(private val device: Device, private val index: Int, privat if (peer.isReachable(device)) throw new ConnectionException(this, ConnectionException.ErrorCode.CONNECTION_CYCLE) + if (peer.getPeer.nonEmpty) + throw new ConnectionException(peer, ConnectionException.ErrorCode.CONNECTOR_BUSY) + // TODO: ^ or `this`? + this.peer = Option(peer) + peer.peer = Option(this) } /** diff --git a/scala/src/edu/cwru/eecs293/ttf10/uxb/Device.scala b/scala/src/edu/cwru/eecs293/ttf10/uxb/Device.scala index fb74634..6ff9a51 100644 --- a/scala/src/edu/cwru/eecs293/ttf10/uxb/Device.scala +++ b/scala/src/edu/cwru/eecs293/ttf10/uxb/Device.scala @@ -147,4 +147,27 @@ trait Device { @throws[IllegalArgumentException] def recv(message: BinaryMessage, connector: Connector) + /** + * Sends a message on the specified (sub)list of connectors belonging to this device. + * + * @param message the message + * @param connectors the connectors on which the message will be sent + * @throws NullPointerException if either argument is null, or if `connectors` contains a null connector + * @throws IllegalArgumentException if any of the connectors do not belong to this device + */ + @throws[NullPointerException] + @throws[IllegalArgumentException] + protected def send(message: Message, connectors: List[Connector]) + + /** + * Broadcasts a message on all connectors belonging to this hub. + * + * @param message the message to send + * @throws NullPointerException if the message is null + * @throws IllegalArgumentException if any of the connectors employed in this broadcast do not belong to this device + */ + @throws[NullPointerException] + @throws[IllegalArgumentException] + def broadcast(message: Message) + } diff --git a/scala/src/edu/cwru/eecs293/ttf10/uxb/GoAmateur.scala b/scala/src/edu/cwru/eecs293/ttf10/uxb/GoAmateur.scala index 8db35f0..1cc7882 100644 --- a/scala/src/edu/cwru/eecs293/ttf10/uxb/GoAmateur.scala +++ b/scala/src/edu/cwru/eecs293/ttf10/uxb/GoAmateur.scala @@ -31,7 +31,9 @@ class GoAmateur(private val builder: GoAmateur.Builder) extends AbstractVideo[Go @throws[IllegalArgumentException] def recv(message: BinaryMessage, connector: Connector) { validateRecv(message, connector) - println("[Log] >> " + "GoAmateur is not yet active: " + message.getValue) + send(BinaryMessage(293), connectors) // respond by broadcasting the binary message: 293 + println("[Log] >> " + "GoAmateur has responded to the binary message: " + message.getValue) + println(" " + "by sending on all of its connectors the binary message: 293") } } diff --git a/scala/src/edu/cwru/eecs293/ttf10/uxb/Hub.scala b/scala/src/edu/cwru/eecs293/ttf10/uxb/Hub.scala index cc1123e..7a92e65 100644 --- a/scala/src/edu/cwru/eecs293/ttf10/uxb/Hub.scala +++ b/scala/src/edu/cwru/eecs293/ttf10/uxb/Hub.scala @@ -24,12 +24,38 @@ class Hub(private val builder: Hub.Builder) extends AbstractDevice(builder) { def recv(message: StringMessage, connector: Connector) { validateRecv(message, connector) - println("[Log] >> " + "recv not yet supported") + sendFrom(message, connector) + println("[Log] >> " + "Hub has forwarded on the string message: " + message.getString) } def recv(message: BinaryMessage, connector: Connector) { validateRecv(message, connector) - println("[Log] >> " + "recv not yet supported") + sendFrom(message, connector) + println("[Log] >> " + "Hub has forwarded on the binary message: " + message.getValue) + } + + /** + * Forwards the received message on all connectors except the one from which the message was received. + * + * @param message the received message + * @param connector the connector from which the message was received + */ + @throws[NullPointerException] + @throws[IllegalArgumentException] + private def sendFrom(message: Message, connector: Connector) { + send(message, connectors diff List(connector)) + } + + /** + * Sends a message from this hub along one of its connectors. + * + * @param message the message to send + * @param connector the connector to which the message is being sent + */ + @throws[NullPointerException] + @throws[IllegalArgumentException] + def sendTo(message: Message, connector: Connector) { + send(message, List(connector)) } } diff --git a/scala/test/edu/cwru/eecs293/ttf10/uxb/SystemTest.scala b/scala/test/edu/cwru/eecs293/ttf10/uxb/SystemTest.scala new file mode 100644 index 0000000..4d0f95f --- /dev/null +++ b/scala/test/edu/cwru/eecs293/ttf10/uxb/SystemTest.scala @@ -0,0 +1,93 @@ +/* *\ +** Case Western Reserve University ** +** ** +** EECS 293 ** +** Software Craftsmanship ** +** 2016 Fall Semester ** +\* */ + +package edu.cwru.eecs293.ttf10.uxb + +import org.junit.{Before, Test} + +/** + * This test creates a UXB system containing two hubs, three printers (not all of the same type), and a Webcam. These UXB devices will all be connected with each other, directly or indirectly. Then, the following scenarios will be explored: + * + * @since Programming Assignment 5 + * @author Ted Frohlich + */ +class SystemTest { + + private var hub1, hub2 : Hub = _ + private var printer1, printer2 : SisterPrinter = _ + private var printer3 : CannonPrinter = _ + private var webcam: GoAmateur = _ + + @Before + def setUp() { + webcam = new GoAmateur.Builder(2) + .connectors(List( + Connector.Type.PERIPHERAL)) // to hub1 + .build() + hub1 = new Hub.Builder(1) + .connectors(List( + Connector.Type.COMPUTER, // to webcam + Connector.Type.COMPUTER, // to printer3 + Connector.Type.COMPUTER, // to hub2 + Connector.Type.PERIPHERAL)) // hub needs both types of connectors + .build() + printer3 = new CannonPrinter.Builder(2) + .connectors(List( + Connector.Type.PERIPHERAL)) // to hub1 + .build() + hub2 = new Hub.Builder(2) + .connectors(List( + Connector.Type.PERIPHERAL, // to hub1 + Connector.Type.COMPUTER, // to printer1 + Connector.Type.COMPUTER)) // to printer2 + .build() + val sisterPrinterBuilder = new SisterPrinter.Builder(3) + .connectors(List( + Connector.Type.PERIPHERAL)) // to hub2 + printer1 = sisterPrinterBuilder.build() + printer2 = sisterPrinterBuilder.build() + } + + /** + * Tests the scenario in which a string message is broadcast from a hub. + * + * @throws Exception if the test fails + */ + @Test + @throws[Exception] + def broadcastStringMessage() { + hub1.broadcast(StringMessage("A string message is broadcast from a hub.")) + } + + /** + * Tests the scenario in which a binary message is sent from a hub along a connector that links the hub to a Webcam. + * + * @throws Exception if the test fails + */ + @Test + @throws[Exception] + def sendBinaryMessage() { + hub1.sendTo(BinaryMessage(132), hub1.getConnector(0)) + } + + /** + * Tests the scenario in which a binary message is broadcast from a hub. + * + * @throws Exception if the test fails + */ + @Test + @throws[Exception] + def broadcastBinaryMessage() { + hub2.broadcast(BinaryMessage(132465798)) + } + +}