summaryrefslogtreecommitdiff
path: root/Network/EBus/Layer2.hs
blob: b194ee60f1431743ea237b6cbea1599a0b044160 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
module Network.EBus.Layer2(
  ebusParserLayer2,
  EbusPacket) where

import Control.Applicative ((<|>))
import Data.Attoparsec (anyWord8, count, skipMany, try, Parser, word8)
import Data.Word (Word8)

-- | Ebus Layer2 Constants
ebusConstant :: String -> Word8
ebusConstant "SYN" = 0xaa
ebusConstant "ACK" = 0x00
ebusConstant "BROADCAST" = 0xff
ebusConstant "ESCAPE" = 0xa9
ebusConstant "ESCAPE_ESCAPE" = 0x00
ebusConstant "ESCAPE_SYN" = 0x01
ebusConstant _ = 0x00

-- | Ebus Packet Types
data EbusType = EbusBroadcast 
              | EbusMasterMaster 
              | EbusMasterSlave
              deriving (Show)

-- | Ebus Packet representation
data EbusPacket = EbusPacket { 
  ebusPacketType :: EbusType,
  ebusPacketSource :: Word8,
  ebusPacketDestination :: Word8,
  ebusPacketPrimaryCommand :: Word8,
  ebusPacketSecondaryCommand :: Word8,
  ebusPacketPayload :: [Word8],
  ebusPacketPayloadSlave :: Maybe [Word8]
  } deriving (Show)

-- | Ebus Binary Escape Sequences for payload data
parserPayload :: Parser Word8
parserPayload = do{ word8 $ ebusConstant "ESCAPE"
                  ; word8 $ ebusConstant "ESCAPE_ESCAPE"
                  ; return $ ebusConstant "ESCAPE"}
                <|>
                do{ word8 $ ebusConstant "ESCAPE"
                  ; word8 $ ebusConstant "ESCAPE_SYN"
                  ; return $ ebusConstant "SYN"}
                <|>
                anyWord8;

-- | Parse one Ebus Packet, skips leading SYNs
ebusParserLayer2 :: Parser EbusPacket
ebusParserLayer2 = do{ 
  skipMany $ word8 $ ebusConstant "SYN"
  ; source <- anyWord8
  ; destination <- anyWord8
  ; primaryCommand <- anyWord8
  ; secondaryCommand <- anyWord8
  ; payloadLength <- anyWord8
  ; payload <- count (fromIntegral payloadLength) parserPayload
  ; followup <-
    if destination == 0xfe then
      -- Broadcast
      do{ crc <- anyWord8
        ; {- syn -} word8 $ ebusConstant "SYN"
        ; if True then -- CHECK CRC
            return $ EbusPacket EbusMasterMaster source destination primaryCommand secondaryCommand payload Nothing
          else
            fail "Broadcast: CRC Check failed"}
      <|>
      fail "Failed to parse Broadcast Paket"
    else
      -- Master-Master
      try( 
        do{ crc <- anyWord8
          ; {- ack -} word8 $ ebusConstant "ACK"
          ; {- syn -} word8 $ ebusConstant "SYN"
          ; if True then -- CHECK CRC
              return $ EbusPacket EbusMasterMaster source destination primaryCommand secondaryCommand payload Nothing
            else
              fail "Master-Master: CRC Check failed"})
      <|> 
      -- Master Slave
      try(
        do{ crc <- anyWord8
          ; {- ack -} word8 $ ebusConstant "ACK"
          ; payloadSlaveLength <- anyWord8
          ; payloadSlave <- count (fromIntegral payloadSlaveLength) parserPayload
          ; crcSlave <- anyWord8
          ; {- ackSlave -} word8 $ ebusConstant "ACK"
          ; {- synSlave -} word8 $ ebusConstant "SYN"
          ; if True then -- Check CRC
              return $ EbusPacket EbusMasterSlave source destination primaryCommand secondaryCommand payload (Just payloadSlave)
            else
              fail "Master-Slave: CRC Check failed"})

      <|> 
      fail "Failed to parse Master-Master/Master-Slave Packet"
  ; return followup
    <|> fail "Failed to parse packet"
  }