LSCS Simulator and JPL Simulator
The JVM simulator server is implemented in the SocketServerStream
class. It implements the same low level protocol as the JPL C library, located in the m1cs-lscs-sim
subproject. The main difference in the current version is that the Scala version of the server supports a “DELAY ms” command, which is used to simulate a command that takes ms
milliseconds to complete. For testing, it also takes a q
command that causes the server to shut down.
Starting the Socket Server
For testing, the JVM socket server can be started with:
new SocketServerStream()(actorSystem)
The C version of the server can be started with the CmdSrvSim
command. See the README.md in the m1cs-lscs-sim
subproject for details on the C version.
Creating a Socket Client
In order to create a socket client, you need either an ActorSystem[SpawnProtocol] or an ActorContext, since the client needs to create an actor internally, in order to manage responses.
To use an ActorSystem, use the SocketClientStream.withSystem()
method:
implicit val system: ActorSystem[SpawnProtocol.Command] = ActorSystem(SpawnProtocol(), "SocketServerStream")
val client1 = SocketClientStream.withSystem("client1")
If you are already in an actor, you can use the ActorContext instead (used only in the same thread to create a child actor):
Behaviors.setup[Command] { ctx =>
val io = SocketClientStream(ctx, name)
// ...
}
Both methods have optional arguments for the host and port to use to connect to the server. The deault is localhost:8023, which is the same default used by the C library.
Sending Messages to the Server
The class used to access the socket server is SocketClientStream
and it can talk to either the C or the Scala version of the server.
The API for the client is basically just: send(message)
, which returns a non-blocking Future response:
- Scala
-
source
implicit val system: ActorSystem[SpawnProtocol.Command] = ActorSystem(SpawnProtocol(), "SocketServerStream") implicit val ece: ExecutionContextExecutor = system.executionContext implicit val timout: Timeout = Timeout(30.seconds) // Start the server new SocketServerStream()(system) test("Basic test") { val client1 = SocketClientStream.withSystem("client1") val client2 = SocketClientStream.withSystem("client2") val client3 = SocketClientStream.withSystem("client3") def showResult(msg: SocketMessage): SocketMessage = { println(s"XXX showResult: $msg") msg } val f0 = client1.send("IMMEDIATE arg1 arg2").map(showResult) val f1 = client1.send("DELAY 2000").map(showResult) val f2 = client2.send("DELAY 1000").map(showResult) val f3 = client3.send("DELAY 500").map(showResult) val f4 = client1.send("DELAY 200").map(showResult) val f5 = client2.send("IMMEDIATE arg1 arg2 arg3").map(showResult) val f = for { resp0 <- f0 resp1 <- f1 resp2 <- f2 resp3 <- f3 resp4 <- f4 resp5 <- f5 } yield { client1.terminate() client2.terminate() client3.terminate() List(resp0, resp1, resp2, resp3, resp4, resp5) } val list = Await.result(f, 30.seconds) println(s"XXX test1 result = $list") assert(list.forall(_.cmd.endsWith(" Completed."))) }
The type of the message sent is SocketMessage
, which has the same layout as the JPL C socket message:
- Scala
-
source
/** * The type of a message sent to the server (also used for the reply). * * @param hdr message header * @param cmd the actual text of the command */ case class SocketMessage(hdr: MsgHdr, cmd: String) {
In most cases you don’t need to provide the message header, since it is generated automatically from the message text. It contains information, such as the size of the message and a sequence number, which is also returned as part of the response. The header is represented by the MsgHdr class, below:
- Scala
-
source
/** * @param msgId message type * @param srcId sender application id * @param msgLen message length including header(bytes * @param seqNo sequence number */ case class MsgHdr(msgId: MessageId, srcId: SourceId, msgLen: Int, seqNo: Int)
The type of the response to sending a command is the same (SocketMessage).
Stopping the Server
For testing, you can use the terminate()
method on the client to cause the server to shutdown, ending the connection. This is not supported in the C version.
Wire Format
The wire format for a socket message is based on the C library version and uses the same header and byte order.