2013-08-26 00:18:30 -07:00
|
|
|
import BasicPrelude
|
|
|
|
import Control.Concurrent
|
|
|
|
import Data.Aeson
|
|
|
|
|
|
|
|
import qualified Data.Map as Map
|
2013-08-26 14:47:27 -07:00
|
|
|
import qualified Data.UUID.V4 as UUID (nextRandom)
|
|
|
|
import qualified Data.ByteString.Lazy as ByteString
|
2013-08-26 00:18:30 -07:00
|
|
|
|
|
|
|
import IHaskell.Types
|
2013-08-26 14:47:27 -07:00
|
|
|
import IHaskell.ZeroMQ
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-09-28 21:29:54 -07:00
|
|
|
data KernelState = KernelState
|
|
|
|
{ getExecutionCounter :: Int
|
|
|
|
}
|
2013-08-26 00:18:30 -07:00
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Read the profile JSON file from the argument list.
|
|
|
|
[profileSrc] <- getArgs
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Parse the profile file.
|
|
|
|
Just profile <- liftM decode $ ByteString.readFile (textToString profileSrc)
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Serve on all sockets and ports defined in the profile.
|
|
|
|
interface <- serveProfile profile
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
state <- initialKernelState
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Receive and reply to all messages on the shell socket.
|
|
|
|
forever $ do
|
|
|
|
-- Read the request from the request channel.
|
|
|
|
request <- readChan $ shellRequestChannel interface
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Create a header for the reply.
|
|
|
|
replyHeader <- createReplyHeader (header request)
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Create the reply, possibly modifying kernel state.
|
|
|
|
reply <- modifyMVar state $ replyTo interface request replyHeader
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Write the reply to the reply channel.
|
|
|
|
writeChan (shellReplyChannel interface) reply
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- Initial kernel state.
|
|
|
|
initialKernelState :: IO (MVar KernelState)
|
2013-09-28 21:29:54 -07:00
|
|
|
initialKernelState = newMVar KernelState {
|
|
|
|
getExecutionCounter = 1
|
|
|
|
}
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
-- | Create a new message header, given a parent message header.
|
|
|
|
createReplyHeader :: MessageHeader -> IO MessageHeader
|
|
|
|
createReplyHeader parent = do
|
|
|
|
-- Generate a new message UUID.
|
|
|
|
newMessageId <- UUID.nextRandom
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
return MessageHeader {
|
|
|
|
identifiers = identifiers parent,
|
|
|
|
parentHeader = Just parent,
|
|
|
|
metadata = Map.fromList [],
|
|
|
|
messageId = newMessageId,
|
|
|
|
sessionId = sessionId parent,
|
|
|
|
username = username parent,
|
|
|
|
msgType = replyType $ msgType parent
|
|
|
|
}
|
2013-08-26 00:18:30 -07:00
|
|
|
|
2013-08-26 14:47:27 -07:00
|
|
|
replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> IO (KernelState, Message)
|
|
|
|
replyTo _ KernelInfoRequest{} replyHeader state = return (state, KernelInfoReply { header = replyHeader })
|
2013-08-26 16:14:48 -07:00
|
|
|
|
2013-08-27 09:46:31 -07:00
|
|
|
replyTo interface ExecuteRequest{} replyHeader state = do
|
|
|
|
-- Queue up a response on the iopub socket
|
|
|
|
newMessageId <- UUID.nextRandom
|
2013-10-02 21:13:42 -07:00
|
|
|
newMessageId2 <- UUID.nextRandom
|
2013-08-27 09:46:31 -07:00
|
|
|
let header = MessageHeader {
|
|
|
|
identifiers = identifiers replyHeader,
|
|
|
|
parentHeader = Nothing,
|
|
|
|
metadata = Map.fromList [],
|
|
|
|
messageId = newMessageId,
|
|
|
|
sessionId = sessionId replyHeader,
|
|
|
|
username = username replyHeader,
|
|
|
|
msgType = "status"
|
|
|
|
}
|
|
|
|
let statusMsg = IopubStatus {
|
|
|
|
header = header,
|
|
|
|
executionState = Idle
|
|
|
|
}
|
2013-10-02 21:13:42 -07:00
|
|
|
let streamHeader = MessageHeader {
|
|
|
|
identifiers = identifiers replyHeader,
|
|
|
|
parentHeader = Nothing,
|
|
|
|
metadata = Map.fromList [],
|
|
|
|
messageId = newMessageId2,
|
|
|
|
sessionId = sessionId replyHeader,
|
|
|
|
username = username replyHeader,
|
|
|
|
msgType = "stream"
|
|
|
|
}
|
|
|
|
let streamMsg = IopubStream streamHeader Stdout $ textToString $ "Hello! " ++ show (getExecutionCounter state)
|
|
|
|
writeChan (iopubChannel interface) streamMsg
|
2013-08-27 09:46:31 -07:00
|
|
|
writeChan (iopubChannel interface) statusMsg
|
|
|
|
|
2013-09-28 21:29:54 -07:00
|
|
|
let counter = getExecutionCounter state
|
|
|
|
newState = state { getExecutionCounter = getExecutionCounter state + 1 }
|
|
|
|
return (newState, ExecuteReply {
|
2013-08-27 09:46:31 -07:00
|
|
|
header = replyHeader,
|
2013-09-28 21:29:54 -07:00
|
|
|
executionCounter = counter,
|
2013-08-27 09:46:31 -07:00
|
|
|
status = "ok"
|
|
|
|
})
|