{-# LANGUAGE Arrows, TypeOperators #-} -- | Module that can be used to generate UDP traffic using a signal -- function that outputs packets and displays information on the local -- console. module Nettle.FRPControl.TrafficGenerator ( TrafficGenerator , PacketSendCommand , ConsoleMessage , UDPPort , driveTrafficGenerator ) where import Network.Socket import Data.Word import Data.Bits import Control.Exception import Nettle.IPv4.IPAddress import Nettle.FRPControl.AFRP import Control.Concurrent.MVar import Control.Concurrent import Data.Time.Clock (getCurrentTime, diffUTCTime) import Control.Monad -- |The type of signal function that can be used to generate UDP traffic. type TrafficGenerator = SF () (Event PacketSendCommand) -- |A console message is simply a string. type ConsoleMessage = String -- |A packet send command is a list of triples including the destination address, -- the UDP port, and the payload (as a String). type PacketSendCommand = [(IPAddress, UDPPort, String)] -- |A UDP port is a 16 bit number. type UDPPort = Word16 -- |Drive (i.e. run) a traffic generator. driveTrafficGenerator :: SF () (Event PacketSendCommand, Event ConsoleMessage) -> IO () driveTrafficGenerator sf = do cntVar <- newMVar 0 lastSenseTimeVar <- newEmptyMVar inCh <- newChan let initiator = do forkIO $ genClockSignal inCh t <- getCurrentTime putMVar lastSenseTimeVar t return () let sensor _ = do ma <- readChan inCh t' <- getCurrentTime t <- swapMVar lastSenseTimeVar t' let delta = fromRational (toRational (diffUTCTime t' t)) return (delta, ma) let actuator sock _ (e1,e2) = do event (return ()) (mapM_ send) e1 event (return ()) putStr e2 return False where send (addr,port,msg) = let ipAddr = toHostAddress addr udpPortNum = PortNum (flipBytes port) dest = SockAddrInet udpPortNum ipAddr in do sendTo sock msg dest modifyMVar_ cntVar (return . (+1)) return () sock <- socket AF_INET Datagram defaultProtocol finally (reactimate initiator sensor (actuator sock) sf) (do sClose sock cnt <- readMVar cntVar putStrLn ("Number of packets sent: " ++ show cnt) ) where genClockSignal ch = forever (threadDelay clockCycle >> writeChan ch Nothing) where clockCycle = 1000 flipBytes :: Word16 -> Word16 flipBytes word = shiftL lbyte 8 + hbyte where hbyte = shiftR word 8 .&. (2^8 - 1) lbyte = word .&. (2^8 - 1) toHostAddress :: IPAddress -> HostAddress toHostAddress addr = shiftL (fromIntegral b4) 24 + shiftL (fromIntegral b3) 16 + shiftL (fromIntegral b2) 8 + fromIntegral b1 where (b1,b2,b3,b4) = addressToOctets addr {- -- |This function can be used to run a traffic generator. driveTrafficGenerator :: TrafficGenerator -> IO () driveTrafficGenerator sf = do cntVar <- newMVar 0 let senseChans = () sock <- socket AF_INET Datagram defaultProtocol let send (addr,port,msg) = let ipAddr = toHostAddress addr udpPortNum = PortNum (flipBytes port) dest = SockAddrInet udpPortNum ipAddr in do sendTo sock msg dest modifyMVar_ cntVar (return . (+1)) return () let packetActuator e = case e of Event cs -> mapM_ send cs NoEvent -> return () let actuators = (packetActuator, ()) let sf' = arr (const ()) >>> sf >>> arr (\e -> OutCons (e, (OutNil ()))) finally (sfDriver inputRep senseChans outputRep actuators sf') (sClose sock >> readMVar cntVar >>= \cnt -> putStrLn ("Number sent: " ++ show cnt)) type InputVector = TNil type ControllerInput = SFInput InputVector -- ESumNil () inputRep :: Rep InputVector inputRep = RepNil type OutputVector = Event PacketSendCommand ::: TNil type ControllerOutput = SFOutput OutputVector -- == OutCons (Event PacketSendCommand) (OutNil ()) outputRep :: Rep OutputVector outputRep = RepCons RepNil -}