module TinyMesh where
import Data.Char
import GHC.Generics
import Data.List (intercalate)
import Control.Monad
import Control.Applicative
import System.Environment (getArgs)
import System.Posix.Unistd
import Debug.Trace
import qualified Data.ByteString.Char8 as BS
import System.Hardware.Serialport as Serial
import Data.Hex
import Data.Attoparsec.ByteString.Char8 as Atto
import Packet.Parse
serialSettings :: SerialPortSettings
serialSettings = defaultSerialSettings { commSpeed = CS19200
, timeout = 1
, flowControl = NoFlowControl --Software
, parity = NoParity
, stopb = One
, bitsPerWord = 8
}
--http://tiny-mesh.com/mesh-network/pdf/RCxxxx%28HP%29-TM_Data_Sheet_1_42.pdf
queryCmd :: BS.ByteString
Right queryCmd = unhex $ BS.concat [
--Checksum:
"0A",
"FF", "FF", "FF", "FF",
"51",
"03", "11",
"00", "00"
]
readN :: SerialPort -> Int -> IO (Maybe BS.ByteString)
readN ser n = reader n []
where
finalize = Just . BS.concat . reverse
reader :: Int -> [BS.ByteString] -> IO (Maybe BS.ByteString)
reader m _ | m < 0 = error "FIXME: Read too many bytes!!!"
reader 0 acc = return $ finalize acc
reader i acc = do
rest <- Serial.recv ser i
if BS.length rest == 0 then do
print $ "Nothing in readN" ++ show (finalize acc)
return Nothing
else
reader (iBS.length rest) (rest:acc)
readPacket :: SerialPort -> IO (Maybe BS.ByteString)
readPacket ser = do
hdr <- Serial.recv ser 1
if BS.null hdr then
return Nothing
else
let packetSize = ord $ BS.head hdr
in do
rest <- readN ser (packetSize 1)
case rest of
Nothing -> return Nothing
Just payload -> return $ Just $ hdr `BS.append` payload
readPackets :: SerialPort -> IO [BS.ByteString]
readPackets ser = reverse <$> reader []
where
reader acc = do result <- readPacket ser
case result of
Nothing -> return $ reverse acc
Just pkt -> reader $ pkt:acc
data Header = Header {
len :: Int
, systemId :: NetAddr
, originId :: NetAddr
, originRSSI :: Int
, netLevel :: Int
, hops :: Int
, origMsgCnt :: Int
, latency :: Int
, packetType :: Int
}
deriving(Show, Generic)
netaddr :: Parser NetAddr
netaddr = parser
instance Parse Header where
parser = Header <$> byte
<*> netaddr
<*> netaddr
<*> byte
<*> byte
<*> byte
<*> word
<*> word
<*> byte
--endOfInput
headerLen :: Int
headerLen = 17
data Payload = Event {
}
| Serial {
blockCount :: Maybe Int
, serData :: BS.ByteString
}
| Unknown BS.ByteString
deriving Show
data Packet = Packet {
header :: Header
, payload :: Payload
} deriving(Show, Generic)
newtype NetAddr = NetAddr { netAddrAsTuple :: (Int, Int, Int, Int) }
deriving Generic
instance Show NetAddr where
show (NetAddr (d, c, b, a)) = "." `intercalate` map show [a, b, c, d]
instance Parse NetAddr where
parser = NetAddr <$> parser
byte :: Parser Int
byte = ord <$> anyChar
word :: Parser Int
word = compute <$> byte <*> byte
where
compute a b = a*256+b
instance Parse Packet where
parser = do
hdr <- parser
let bytesLeft = len hdr headerLen
Packet hdr <$> case packetType hdr of
2 -> parseEvent
16 -> parseSerial bytesLeft
otherType -> trace ("Unknown packet type: " ++ show otherType) $
Unknown <$> bytestringParser bytesLeft
parseEvent :: Parser Payload
parseEvent = return Event {}
parseSerial :: Int -> Parser Payload
parseSerial bytesLeft = do
blockCounter <- nothingIfZero <$> parser
Serial blockCounter <$> bytestringParser (bytesLeft 1)
where
nothingIfZero 0 = Nothing
nothingIfZero i = Just i
bytestringParser :: Int -> Parser BS.ByteString
bytestringParser i = BS.pack <$> count i anyChar
parsePacket :: BS.ByteString -> Packet
parsePacket = parseBS
main :: IO ()
main = do
serDevs <- getArgs
forM_ serDevs $ \serDev -> do
ser <- openSerial serDev serialSettings
flush ser
putStrLn $ "Opened serial on " ++ serDev
_ <- Serial.send ser queryCmd
usleep $ 1*1000000
result <- readPackets ser
putStrLn $ unlines $ map show result
mapM_ (print . parsePacket) result