sensenet-0.1.0.0: main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import System.ZMQ3.Monadic
import Control.Monad (forever)
import Control.Concurrent
import Control.Concurrent.STM
import qualified Data.Map.Strict as Map
import qualified Data.ByteString.Lazy.Char8 as LC
import Data.Aeson
import Control.Applicative
import Control.Monad
import System.Process
import System.Environment (getArgs)
-- alias for zmq uris
type ZmqUri = String
-- sensor data used to transport data over the network
data SensorData = SensorData {
hostName :: String,
sData :: Float }
instance FromJSON SensorData where
parseJSON (Object v) = SensorData <$>
v .: "hostname" <*>
v .: "data"
parseJSON _ = mzero
instance ToJSON SensorData where
toJSON (SensorData hname sdata) = object ["hostname" .= hname, "data" .= sdata]
-- map hostnames to a list of floats
type SensorMap = Map.Map String [Float]
-- STM Wrapper
type GlobalMap = TVar SensorMap
-- Helper function to atomically read a TVar
atomRead = atomically . readTVar
-- Function that adds some sensor data to the global sensor data map
mapUpdate :: SensorData -> SensorMap -> SensorMap
mapUpdate sd m = Map.update (\val-> Just (sData sd : val)) (hostName sd) m
-- Atomically add some data to the global data map
addSensorData :: GlobalMap -> SensorData -> IO ()
addSensorData gd sd = do
globular <- atomRead gd
atomically $ writeTVar gd $ mapUpdate sd globular
-- Atomically get all sensor data for a hostname
getSensorData :: GlobalMap -> String -> IO (Maybe [Float])
getSensorData gd hostname = do
globular <- atomRead gd
return $ Map.lookup hostname globular
-- Thread to listen for peeps over the network
remoteListener :: GlobalMap -> [ZmqUri] -> IO ()
remoteListener gd uris = runZMQ $ do
sub <- socket Sub
subscribe sub ""
mapM_ (connect sub) uris
return $ putStrLn "remote listener"
forever $ do
raw_line <- receive sub -- IO SB.ByteString
let bs_line = LC.fromStrict raw_line
case (decode bs_line :: Maybe SensorData) of
Just sdata -> return $ addSensorData gd sdata
Nothing -> return $ LC.putStrLn $ LC.append "Got invalid Sensor data: '" $ LC.append bs_line "'"
-- We are just going to fake it for now
-- by hardcoding this for my laptop
-- yes I know I'm a bad person
loadSensorData :: String -> IO SensorData
loadSensorData host = do
proc_data <- readProcess "/bin/cat" ["/sys/class/hwmon/hwmon2/temp3_input"] ""
return $ SensorData host (read proc_data :: Float)
-- Thread to send data to peers over the network
dataSender :: GlobalMap -> ZmqUri -> String -> IO ()
dataSender gd bind_uri hostname = runZMQ $ do
pub <- socket Pub
bind pub bind_uri
return $ putStrLn "remote listener"
ihatethis <- return $ forever $ do
sData <- loadSensorData hostname
let encoded_sdata = encode sData
return $ send pub [] $ LC.toStrict encoded_sdata
return ()
main :: IO ()
main = do
args <- getArgs
let hostname = args !! 0
let bind_uri = args !! 1
let connect_uri = args !! 2
putStrLn hostname
putStrLn bind_uri
putStrLn connect_uri
global_data <- atomically . newTVar $ (Map.empty :: SensorMap)
inThread <- forkIO $ remoteListener global_data [connect_uri]
dataSender global_data bind_uri hostname