module QuickPlot.IPC.Server (
runServer,
sendMessage
) where
import Network.WebSockets.Snap
import Network.WebSockets hiding (runServer)
import Data.ByteString.Lazy.Internal
import Snap
import Snap.Util.FileServe
import Control.Concurrent
import System.IO.Unsafe
import Control.Exception
import Control.Monad
import QuickPlot.IPC.Protocol
import Data.IORef
import Paths_QuickPlot
getFrontendDir :: IO FilePath
getFrontendDir = getDataFileName "src/frontend"
runServer :: FilePath
-> Int
-> IO ()
runServer userDir port = do
noClients <- isEmptyMVar clientThread
if noClients
then do
atomicWriteIORef serverRunning (True, port)
frontendDir <- getFrontendDir
void $ forkIO $ httpServe config (service frontendDir)
putStrLn $ "Find QuickPlot at \"localhost:" ++ show port ++ "\" in your browser"
else putStrLn "Start QuickPlot server only once per ghci session"
where config = setErrorLog ConfigNoLog
$ setAccessLog ConfigNoLog
$ setVerbose False
$ setPort port
defaultConfig
service :: FilePath
-> Snap ()
service staticDir = route [ ("/", serveDirectory staticDir)
, ("/ws", runWebSocketsSnap websocketHandler)
, ("", serveFile "src/404.html")
]
websocketHandler :: PendingConnection
-> IO ()
websocketHandler pending = do
connection <- acceptRequest pending
login
forkPingThread connection 20
handle close $ handleUntilNewClient connection
handleUntilNewClient :: Connection
-> IO ()
handleUntilNewClient connection = do
msg <- takeMVar channel
sendTextData connection msg
handleUntilNewClient connection
close :: ConnectionException
-> IO ()
close (CloseRequest _ _) = print "Exception: CloseRequest"
close ConnectionClosed = print "Exception: ConnectionClosed"
close exception = print $ "Exception: " ++ show exception
login :: IO ()
login = do
noClients <- isEmptyMVar clientThread
if noClients
then myThreadId >>= putMVar clientThread
else do
takeMVar clientThread >>= killThread
myThreadId >>= putMVar clientThread
channel :: MVar ByteString
channel = unsafePerformIO newEmptyMVar
clientThread :: MVar ThreadId
clientThread = unsafePerformIO newEmptyMVar
serverRunning :: IORef (Bool, Int)
serverRunning = unsafePerformIO (newIORef (False, 0))
sendRawMessage :: ByteString
-> IO ()
sendRawMessage message = do
noClients <- isEmptyMVar clientThread
(running, port) <- readIORef serverRunning
if running
then if noClients
then do
threadDelay 1000000
putStrLn $ "You need to go to: \"localhost:" ++ show port
++ "\" in your browser before plotting"
else putMVar channel message
else putStrLn "You need to start QuickPlot with \"runQuickPlot\" before plotting"
sendMessage :: QPMessage
-> IO ()
sendMessage = sendRawMessage . encode