Skip to content

Commit

Permalink
View multiple visualisations via their descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Quafadas committed Mar 27, 2024
1 parent fac66b0 commit bd07a70
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 41 deletions.
11 changes: 4 additions & 7 deletions core/jvm/src/main/scala/viz/PlotTargets.scala
Expand Up @@ -82,7 +82,7 @@ object PlotTargets extends SharedTargets:
end if
end outPath

lazy val port: Int = WebsocketVizServer.randomPort
lazy val port: Int = WebsocketVizServer.port

private def tempFileHtml(spec: String): String = raw"""<!DOCTYPE html>
<html>
Expand Down Expand Up @@ -169,15 +169,12 @@ object PlotTargets extends SharedTargets:
)
end show

given websocketNoBrowser: UnitTarget = new UnitTarget:
given publishToPort(using portI: Int): UnitTarget = new UnitTarget:
override def show(spec: String): Unit =
if WebsocketVizServer.firstTime then
println(s"starting local server on $port")
Thread.sleep(1000) // give undertow a chance to start
end if
requests.post(s"http://localhost:$port/viz", data = spec)
requests.post(s"http://localhost:$portI/viz", data = spec)
()
end show
end publishToPort

given websocket: UnitTarget = new UnitTarget:
override def show(spec: String): Unit =
Expand Down
117 changes: 83 additions & 34 deletions core/jvm/src/main/scala/viz/websockets/WebsocketViz.scala
Expand Up @@ -22,34 +22,35 @@ import io.undertow.websockets.spi.WebSocketHttpExchange
import scalatags.Text.all.*
import java.awt.Desktop
import scala.concurrent.Future
import io.undertow.server.handlers.BlockingHandler
import java.util.concurrent.Executors

// val executorService = Executors.newVirtualThreadExecutor()

implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global

object WebsocketVizServer extends WebsocketVizServer
@main
def serve(portIn: Int = 8085): Unit =
val s = new WebsocketVizServer(portIn) {}
// Start the server
s.main(Array())

trait WebsocketVizServer extends cask.MainRoutes:
// Keep the JVM alive until the server is stopped
while true do
try Thread.sleep(3000)
catch
case _: InterruptedException =>
// The sleep was interrupted, probably because the server is stopping
// Log the interruption and stop the server
println("Server interrupted, stopping...")
end while
end serve

val fixedPort: Option[Int] = Some(8085)
var firstTime: Boolean = true
object WebsocketVizServer extends WebsocketVizServer(8085)

lazy val randomPort: Int =
println("Generating random port and starting server")
Future {
initialize()
main(Array())
}
firstTime = false
val p = fixedPort match
case Some(port) => port
case None =>
8080 + scala.util.Random.nextInt(
40000
)
println(s"started viz server at http://localhost:$p")
p
end randomPort

override def port = randomPort
trait WebsocketVizServer(portIn: Int) extends cask.MainRoutes:
var firstTime: Boolean = true
override def port = portIn

def openBrowserWindow() =
if Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE) then
Expand All @@ -59,17 +60,61 @@ trait WebsocketVizServer extends cask.MainRoutes:
s"java.awt.Desktop claims the browse action is not supported in this environment. Consider opening a browser at https://localhost:$port , where you should find your plot"
)

private val headImports = head(
script(src := "https://cdn.jsdelivr.net/npm/vega@5"),
script(src := "https://cdn.jsdelivr.net/npm/vega-lite@5"),
script(src := "https://cdn.jsdelivr.net/npm/vega-embed@5")
)

private def divId = div(id := "vis", height := "95vmin", width := "95vmin")

@cask.get("/view/:description")
def filerViz(description: String) =
html(
headImports,
body(
// h1("viz"),
divId,
script(raw"""
let socket = new WebSocket('ws://localhost:$port/connect/viz');
socket.onopen = function(e) {
document.getElementById('vis').innerHTML = 'connected and waiting'
};
socket.onmessage = function(event) {
console.log(event.data)
const spec = JSON.parse(event.data)
if (spec.description === '$description') {
vegaEmbed('#vis', spec, {
renderer: 'canvas', // renderer (canvas or svg)
container: '#vis', // parent DOM container
hover: true, // enable hover processing
actions: true
})
}
};

socket.onclose = function(event) {
if (event.wasClean) {
console.log(`[close] Connection closed cleanly, code=$${event.code} reason=$${event.reason}`);
} else {
console.error('[close] Connection died');
}
};
socket.onerror = function(error) {
console.error(`[error] $${error.message}`);
};

""")
)
)

@cask.get("/")
def home() =
html(
head(
script(src := "https://cdn.jsdelivr.net/npm/vega@5"),
script(src := "https://cdn.jsdelivr.net/npm/vega-lite@5"),
script(src := "https://cdn.jsdelivr.net/npm/vega-embed@5")
),
headImports,
body(
// h1("viz"),
div(id := "vis", height := "95vmin", width := "95vmin"),
divId,
script(raw"""
let socket = new WebSocket('ws://localhost:$port/connect/viz');
socket.onopen = function(e) {
Expand Down Expand Up @@ -104,15 +149,16 @@ trait WebsocketVizServer extends cask.MainRoutes:
)
)

var channelCheat: Option[WebSocketChannel] = None
var channelCheat: List[WebSocketChannel] = List.empty

@cask.post("/viz")
def recievedSpec(request: cask.Request) =
val theBody = ujson.read(request.text())
channelCheat match
case None => cask.Response("no client is listening", statusCode = 418)
case Some(value) =>
WebSockets.sendTextBlocking(ujson.write(theBody), value)
case Nil => cask.Response("no client is listening", statusCode = 418)
case channels: List[WebSocketChannel] =>
channels.foreach { c =>
if c.isOpen() then WebSockets.sendTextBlocking(request.text(), c)
}
cask.Response("you should be looking at new viz")
end match
end recievedSpec
Expand All @@ -121,7 +167,7 @@ trait WebsocketVizServer extends cask.MainRoutes:
def setup(viz: String): cask.WebsocketResult =
new WebSocketConnectionCallback():
override def onConnect(exchange: WebSocketHttpExchange, channel: WebSocketChannel): Unit =
channelCheat = Some(channel)
channelCheat :+= channel
channel.getReceiveSetter.set(
new AbstractReceiveListener():
override def onFullTextMessage(channel: WebSocketChannel, message: BufferedTextMessage) =
Expand All @@ -131,4 +177,7 @@ trait WebsocketVizServer extends cask.MainRoutes:
)
channel.resumeReceives()
end onConnect
end setup

initialize()
end WebsocketVizServer

0 comments on commit bd07a70

Please sign in to comment.