sensenet (empty) → 0.1.0.0
raw patch · 4 files changed
+129/−0 lines, 4 filesdep +aesondep +basedep +bytestringsetup-changed
Dependencies added: aeson, base, bytestring, containers, process, stm, zeromq3-haskell
Files
- LICENSE +0/−0
- Setup.hs +2/−0
- main.hs +106/−0
- sensenet.cabal +21/−0
+ LICENSE view
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ main.hs view
@@ -0,0 +1,106 @@+{-# 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
+ sensenet.cabal view
@@ -0,0 +1,21 @@+-- Initial sensenet.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: sensenet+version: 0.1.0.0+synopsis: Distributed sensor network for the raspberry pi+-- description: +homepage: https://github.com/rossdylan/sensenet+license: MIT+license-file: LICENSE+author: Ross Delinger+maintainer: rossdylan@csh.rit.edu+-- copyright: +-- category: +build-type: Simple+cabal-version: >=1.8++executable sensenet+ main-is: main.hs+ -- other-modules: + build-depends: base ==4.6.*, containers, bytestring, stm, aeson, zeromq3-haskell, process