Skip to content

Commit

Permalink
Ping-Pong nodes for article.
Browse files Browse the repository at this point in the history
  • Loading branch information
graninas committed Dec 1, 2018
1 parent f8d8f6a commit e1968f4
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 101 deletions.
7 changes: 5 additions & 2 deletions app/Enecuum/App/Initialize.hs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ initialize configSrc = do
, runNode' $ Cfg.dispatchScenario @Tst.TstGenPoWNode configSrc
, runNode' $ Cfg.dispatchScenario @Tst.TstGenPoANode configSrc
, runNode' $ Cfg.dispatchScenario @Tst.TstRealPoWNode configSrc

, runNode' $ Cfg.dispatchScenario @Tst.PingServerNode configSrc
, runNode' $ Cfg.dispatchScenario @Tst.PongClientNode configSrc
]
sequence_ runners

Expand All @@ -110,11 +113,11 @@ runMultiNode configSrc = case Cfg.dispatchScenario @Prd.MultiNode configSrc of
startNodes "pow" startGenPoWNode
(Prd._routingGenPoWPorts $ Cfg.nodeConfig cfg)
(Prd._routingGenPoWConfig $ Cfg.nodeConfig cfg)

startNodes "poa" startGenPoaNode
(Prd._routingGenPoAPorts $ Cfg.nodeConfig cfg)
(Prd._routingGenPoAConfig $ Cfg.nodeConfig cfg)

startNodes "gn" startGrapNode
(Prd._routingGraphNodePorts $ Cfg.nodeConfig cfg)
(Prd._routingGraphNodeConfig $ Cfg.nodeConfig cfg)
Expand Down
16 changes: 16 additions & 0 deletions configs/tst_ping_server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"node": "PingServerNode",
"nodeScenario": "PingServer",
"nodeConfig": {
"tag": "PingServerNodeConfig",
"stopOnPing": 200,
"servingPort": 3000
},
"loggerConfig": {
"logFilePath": "",
"format": "$prio $loggername: $msg",
"logToFile": false,
"logToConsole": true,
"level": "Debug"
}
}
17 changes: 17 additions & 0 deletions configs/tst_pong_client1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"node": "PongClientNode",
"nodeScenario": "PongClient",
"nodeConfig": {
"tag": "PongClientNodeConfig",
"clientName": "Pong client #1",
"pingDelay": 1000000,
"pingServerAddress": {"host":"127.0.0.1","port":3000}
},
"loggerConfig": {
"logFilePath": "",
"format": "$prio $loggername: $msg",
"logToFile": false,
"logToConsole": true,
"level": "Debug"
}
}
17 changes: 17 additions & 0 deletions configs/tst_pong_client2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"node": "PongClientNode",
"nodeScenario": "PongClient",
"nodeConfig": {
"tag": "PongClientNodeConfig",
"clientName": "Pong client #2",
"pingDelay": 1000000,
"pingServerAddress": {"host":"127.0.0.1","port":3000}
},
"loggerConfig": {
"logFilePath": "",
"format": "$prio $loggername: $msg",
"logToFile": false,
"logToConsole": true,
"level": "Debug"
}
}
3 changes: 3 additions & 0 deletions src/Enecuum/Assets/Nodes/ConfigParsing.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ parseConfig configSrc = do
, runParser $ Cfg.tryParseConfig @Tst.TstGenPoWNode configSrc
, runParser $ Cfg.tryParseConfig @Tst.TstGenPoANode configSrc
, runParser $ Cfg.tryParseConfig @Tst.TstRealPoWNode configSrc

, runParser $ Cfg.tryParseConfig @Tst.PingServerNode configSrc
, runParser $ Cfg.tryParseConfig @Tst.PongClientNode configSrc
]

results <- sequence runners
Expand Down
8 changes: 5 additions & 3 deletions src/Enecuum/Assets/Nodes/TstNodes/PingPong/Messages.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{-# LANGUAGE DeriveAnyClass #-}

module Enecuum.Assets.Nodes.TstNodes.PingPong.Messages where

import Enecuum.Prelude
import Enecuum.Prelude

data Ping = Ping Text
newtype Ping = Ping Text
deriving (Show, Eq, Generic, ToJSON, FromJSON)

data Pong = Pong Int
newtype Pong = Pong Int
deriving (Show, Eq, Generic, ToJSON, FromJSON)
69 changes: 31 additions & 38 deletions src/Enecuum/Assets/Nodes/TstNodes/PingPong/PingServer.hs
Original file line number Diff line number Diff line change
@@ -1,62 +1,55 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DuplicateRecordFields #-}

module Enecuum.Assets.Nodes.TstNodes.PingPong.PingServer where

import qualified Data.Aeson as A
import Enecuum.Assets.Nodes.TstNodes.PingPong.Messages
import Enecuum.Config
import qualified Enecuum.Domain as D
import qualified Enecuum.Language as L
import qualified Enecuum.Domain as D
import qualified Enecuum.Language as L
import Enecuum.Prelude
import Enecuum.Assets.Nodes.TstNodes.PingPong.Messages

data PingServerData = PingServerData
{ _pingsCount :: D.StateVar Int
}

makeFieldsNoPrefix ''PingServerData
data PingServerNode = PingServerNode
deriving (Show, Generic)

data instance NodeConfig PingServerNode = PingServerNode
{ _stopOnPing :: Int
data instance NodeConfig PingServerNode = PingServerNodeConfig
{ _stopOnPing :: Int
, _servingPort :: D.PortNumber
}
deriving (Show, Generic)

instance Node PingServerNode where
data NodeScenario PingServerNode = PingServer
deriving (Show, Generic)
getNodeScript _ = pingServerNode
getNodeScript _ = pingServerNode'
getNodeTag _ = PingServerNode

instance ToJSON (NodeScenario PingServerNode) where toJSON = J.genericToJSON nodeConfigJsonOptions
instance FromJSON (NodeScenario PingServerNode) where parseJSON = J.genericParseJSON nodeConfigJsonOptions
instance ToJSON PingServerNode where toJSON = A.genericToJSON nodeConfigJsonOptions
instance FromJSON PingServerNode where parseJSON = A.genericParseJSON nodeConfigJsonOptions
instance ToJSON (NodeConfig PingServerNode) where toJSON = A.genericToJSON nodeConfigJsonOptions
instance FromJSON (NodeConfig PingServerNode) where parseJSON = A.genericParseJSON nodeConfigJsonOptions
instance ToJSON (NodeScenario PingServerNode) where toJSON = A.genericToJSON nodeConfigJsonOptions
instance FromJSON (NodeScenario PingServerNode) where parseJSON = A.genericParseJSON nodeConfigJsonOptions

acceptPing :: PingServerNodeData -> Ping -> connection -> L.NodeL ()
acceptPing nodeData (Ping clientName) conn = do
acceptPing :: D.StateVar Int -> Ping -> D.Connection D.Udp -> L.NodeL ()
acceptPing pingsCount (Ping clientName) conn = do
pings <- L.atomically $ do
pings <- L.readVar $ nodeData ^. pingsCount
let newPings = pings + 1
L.writeVar (nodeData ^. pingsCount) newPings
pure newPings
L.modifyVar pingsCount (+1)
L.readVar pingsCount
L.send conn (Pong pings)
L.close conn
L.logInfo $ "Ping #" +|| pings ||+ " accepted from " <> clientName <> "."
L.logInfo $ "Ping #" +|| pings ||+ " accepted from " +|| clientName ||+ "."

pingServerNode :: NodeConfig PingServerNode -> L.NodeDefinitionL ()
pingServerNode cfg = do
nodeData <- initializePingServerNode
pingServerNode :: Int -> D.PortNumber -> L.NodeDefinitionL ()
pingServerNode threshold port = do
pingsCount <- L.newVarIO 0

L.serving D.Udp 3000 $ do
L.method $ acceptPing nodeData
L.serving D.Udp port $
L.handler $ acceptPing pingsCount

L.atomically $ do
pings <- readVar $ nodeData ^. pingsCount
when (pings < _stopOnPing cfg) L.retry
pings <- L.readVar pingsCount
when (pings < threshold) L.retry

initializePingServerNode :: NodeConfig PingServerNode -> L.NodeL PingServerNodeData
initializePingServerNode cfg = do
pingsCount <- L.newVarIO 0
pure PingServerNodeData
{ _pingsCount = pingsCount
}
pingServerNode' :: NodeConfig PingServerNode -> L.NodeDefinitionL ()
pingServerNode' cfg = pingServerNode (_stopOnPing cfg) (_servingPort cfg)
113 changes: 66 additions & 47 deletions src/Enecuum/Assets/Nodes/TstNodes/PingPong/PongClient.hs
Original file line number Diff line number Diff line change
@@ -1,63 +1,82 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DuplicateRecordFields #-}

module Enecuum.Assets.Nodes.TstNodes.PingPong.PongClient where

import qualified Data.Aeson as A
import Enecuum.Assets.Nodes.TstNodes.PingPong.Messages
import Enecuum.Config
import qualified Enecuum.Domain as D
import Enecuum.Framework.Language.Extra (HasStatus)
import qualified Enecuum.Language as L
import qualified Enecuum.Domain as D
import Enecuum.Framework.Language.Extra (HasStatus)
import qualified Enecuum.Language as L
import Enecuum.Prelude
import Enecuum.Assets.Nodes.TstNodes.PingPong.Messages

data PongClientData = PongClientData
{ _pingsCount :: D.StateVar Int
}

makeFieldsNoPrefix ''PongClientData
data PongClientNode = PongClientNode
deriving (Show, Generic)

data instance NodeConfig PongClientNode = PongClientNode
{ _clientName :: Text
data instance NodeConfig PongClientNode = PongClientNodeConfig
{ _clientName :: Text
, _pingDelay :: Int
, _pingServerAddress :: D.Address
}
deriving (Show, Generic)

instance Node PongClientNode where
data NodeScenario PongClientNode = PongClient
deriving (Show, Generic)
getNodeScript _ = PongClientNode
getNodeScript _ = pongClientNode'
getNodeTag _ = PongClientNode

instance ToJSON (NodeScenario PongClientNode) where toJSON = J.genericToJSON nodeConfigJsonOptions
instance FromJSON (NodeScenario PongClientNode) where parseJSON = J.genericParseJSON nodeConfigJsonOptions

acceptPing :: PongClientNodeData -> Ping -> connection -> L.NodeL ()
acceptPing nodeData (Ping clientName) conn = do
pings <- L.atomically $ do
pings <- L.readVar $ nodeData ^. pingsCount
let newPings = pings + 1
L.writeVar (nodeData ^. pingsCount) newPings
pure newPings
L.send conn (Pong pings)
L.close conn
L.logInfo $ "Ping #" +|| pings ||+ " accepted from " <> clientName <> "."

PongClientNode :: NodeConfig PongClientNode -> L.NodeDefinitionL ()
PongClientNode cfg = do
nodeData <- initializePongClientNode

L.serving D.Udp 3000 $ do
L.method $ acceptPing nodeData

L.atomically $ do
pings <- readVar $ nodeData ^. pingsCount
when (pings < _stopOnPing cfg) L.retry

initializePongClientNode :: NodeConfig PongClientNode -> L.NodeL PongClientNodeData
initializePongClientNode cfg = do
pingsCount <- L.newVarIO 0
pure PongClientNodeData
{ _pingsCount = pingsCount
}
instance ToJSON PongClientNode where toJSON = A.genericToJSON nodeConfigJsonOptions
instance FromJSON PongClientNode where parseJSON = A.genericParseJSON nodeConfigJsonOptions
instance ToJSON (NodeConfig PongClientNode) where toJSON = A.genericToJSON nodeConfigJsonOptions
instance FromJSON (NodeConfig PongClientNode) where parseJSON = A.genericParseJSON nodeConfigJsonOptions
instance ToJSON (NodeScenario PongClientNode) where toJSON = A.genericToJSON nodeConfigJsonOptions
instance FromJSON (NodeScenario PongClientNode) where parseJSON = A.genericParseJSON nodeConfigJsonOptions

acceptPong :: Pong -> connection -> L.NodeL ()
acceptPong (Pong pingsCount) _ =
L.logInfo $ "Pong accepted from server. Pings count: " <> show pingsCount

pingSending' :: NodeConfig PongClientNode -> D.Connection D.Udp -> L.NodeL ()
pingSending' cfg conn = do
L.delay $ _pingDelay cfg
L.logInfo "Sending Ping to the server."
eSent <- L.send conn (Ping $ _clientName cfg)
case eSent of
Right () -> pingSending' cfg conn
Left _ -> do
L.logInfo "Server is gone."
L.close conn


pongClientNode' :: NodeConfig PongClientNode -> L.NodeDefinitionL ()
pongClientNode' cfg = do

mbConn <- L.open D.Udp (_pingServerAddress cfg) $
L.handler acceptPong

case mbConn of
Nothing -> L.logError "Ping Server not found"
Just conn -> do
L.process (pingSending' cfg conn)
L.awaitNodeForever

pingSending :: Text -> D.Connection D.Udp -> L.NodeL ()
pingSending clientName conn = do
L.delay 1000000
L.logInfo "Sending Ping to the server."
eSent <- L.send conn (Ping clientName)
when (isLeft eSent) $ L.close conn
when (isRight eSent) $ pingSending clientName conn

pongClientNode :: Text -> D.Address -> L.NodeDefinitionL ()
pongClientNode clientName serverAddress = do

mbConn <- L.open D.Udp serverAddress $
L.handler acceptPong

when (isNothing mbConn) $ L.logError "Ping Server not found"
whenJust mbConn $ \conn -> do
L.process $ pingSending clientName conn
L.awaitNodeForever
25 changes: 14 additions & 11 deletions src/Enecuum/Assets/TstScenarios.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ module Enecuum.Assets.TstScenarios (
module X
) where

import Enecuum.Assets.Nodes.TestClient as X
import Enecuum.Assets.Nodes.TestServer as X
import Enecuum.Assets.Nodes.TestClient as X
import Enecuum.Assets.Nodes.TestServer as X

import Enecuum.Assets.Nodes.TstNodes.GenPoA.Config as X
import Enecuum.Assets.Nodes.TstNodes.GenPoA.Node as X
import Enecuum.Assets.Nodes.TstNodes.GenPoW.Config as X
import Enecuum.Assets.Nodes.TstNodes.GenPoW.Node as X
import Enecuum.Assets.Nodes.TstNodes.GraphNode.Config as X
import Enecuum.Assets.Nodes.TstNodes.GraphNode.Node as X
import Enecuum.Assets.Nodes.TstNodes.NetworkNode as X
import Enecuum.Assets.Nodes.TstNodes.RealPoW.Config as X
import Enecuum.Assets.Nodes.TstNodes.RealPoW.Node as X
import Enecuum.Assets.Nodes.TstNodes.GenPoA.Config as X
import Enecuum.Assets.Nodes.TstNodes.GenPoA.Node as X
import Enecuum.Assets.Nodes.TstNodes.GenPoW.Config as X
import Enecuum.Assets.Nodes.TstNodes.GenPoW.Node as X
import Enecuum.Assets.Nodes.TstNodes.GraphNode.Config as X
import Enecuum.Assets.Nodes.TstNodes.GraphNode.Node as X
import Enecuum.Assets.Nodes.TstNodes.NetworkNode as X
import Enecuum.Assets.Nodes.TstNodes.RealPoW.Config as X
import Enecuum.Assets.Nodes.TstNodes.RealPoW.Node as X

import Enecuum.Assets.Nodes.TstNodes.PingPong.PingServer as X
import Enecuum.Assets.Nodes.TstNodes.PingPong.PongClient as X

0 comments on commit e1968f4

Please sign in to comment.