Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix optimisation #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 40 additions & 32 deletions src/NRNode.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveGeneric, OverloadedStrings, RecordWildCards #-}

module NRNode
( NRNode(..)
Expand All @@ -9,7 +9,9 @@ module NRNode
where

import Data.Aeson
import Control.Applicative
import Data.Maybe
import Data.Either
import GHC.Generics
import Data.List.Split
import qualified Striot.CompileIoT as C
Expand All @@ -18,34 +20,45 @@ import qualified Data.ByteString.Lazy as B
-- | NRNode - the Haskell data representation of Node-RED nodes
data NRNode =
NRNode { nrId :: String -- Node-RED ID - as it comes from Node-RED
, strId :: Maybe Int -- StrIoT ID - unique ID that can then be used for creating Vertices for StrIoT
, strId :: Int -- StrIoT ID - unique ID that can then be used for creating Vertices for StrIoT
, nodeType :: String
, func :: Maybe String
, func :: String
, input :: Maybe String
, output :: Maybe String
, wires :: Maybe [[String]]
, strWires :: Maybe [[Int]]
, imports :: Maybe String
, packages :: Maybe String
, optimise :: Maybe Bool
, wires :: [[String]]
, strWires :: [[Int]]
, imports :: String
, packages :: String
, optimise :: Bool
} deriving (Generic, Show, Eq)

-- Defines how to read the JSON object and convert into the NRNode
-- The optimise line is different because Node-RED outputs true (bool) for True, and '' (string) for False.
-- Aeson cannot easily handle both Bool and String for the same field, so using <|> (which acts as a catch in
-- a conversion failure) then having a field which is guaranteed to not be found is the best workaround for this
instance FromJSON NRNode where
parseJSON = genericParseJSON defaultOptions
{ fieldLabelModifier = \x -> case x of
"nrId" -> "id"
"nodeType" -> "type"
_ -> x
}
parseJSON = withObject "nrnode" $ \o -> do
nrId <- o .: "id"
strId <- o .:? "strId" .!= (-1)
nodeType <- o .: "type"
func <- o .:? "func" .!= ""
input <- o .:? "input" .!= Nothing
output <- o .:? "output" .!= Nothing
wires <- o .:? "wires" .!= []
strWires <- o .:? "strWires" .!= []
imports <- o .:? "imports" .!= ""
packages <- o .:? "packages" .!= ""
optimise <- o .:? "optimise" .!= False <|> o .:? "" .!= False
return NRNode { .. }


-- | Reads the specified file and converts into an array of NRNodes
nodesFromJSON :: FilePath -> IO [NRNode]
nodesFromJSON x =
addInputType
. addStrIds
. filterActualNodes
. fromJust
. fromMaybe (error "Unable to convert JSON file.")
. decode
<$> B.readFile x :: IO [NRNode]

Expand All @@ -55,7 +68,7 @@ filterActualNodes = filter (\n -> nodeType n `elem` striotTypes)

-- | Creates StrIoT compatible nodes for each ID, and also creates wires in the same format
addStrIds :: [NRNode] -> [NRNode]
addStrIds = updateStrWires . zipWith (\id x -> x { strId = Just id }) [1 ..]
addStrIds = updateStrWires . zipWith (\id x -> x { strId = id }) [1 ..]

addInputType :: [NRNode] -> [NRNode]
addInputType xs = map (\x -> x { input = getInputType xs x }) xs
Expand All @@ -64,33 +77,30 @@ addInputType xs = map (\x -> x { input = getInputType xs x }) xs
-- | Also removes any references which are not found in the list of IDs (0 or less)
updateStrWires :: [NRNode] -> [NRNode]
updateStrWires xs = map
(\x -> x
{ strWires = Just (map (filter (> 0) . map getId) (fromJust (wires x)))
}
)
(\x -> x { strWires = map (filter (> 0) . map getId) (wires x) })
xs
where getId = getStrId xs

-- | Tries to find the StrId for a Node given its NrID
getStrId :: [NRNode] -> String -> Int
getStrId xs id | null results = -1
| otherwise = fromJust . strId . head $ results
| otherwise = strId . head $ results
where results = filter (\x -> id == nrId x) xs

-- | Finds a specific node in an array of NRNodes which has the specified strID
-- TODO: add error handling for if node not found
getNodeByStrId :: [NRNode] -> Int -> NRNode
getNodeByStrId xs i = head . filter (\x -> fromJust (strId x) == i) $ xs
getNodeByStrId xs i = case results of
[] -> error ("No node found with ID " ++ show i)
(x : _) -> x
where results = filter ((i ==) . strId) xs

-- | Finds the input type for a specified node by finding the input node and returning the type
getInputType :: [NRNode] -> NRNode -> Maybe String
getInputType xs n = case inputs of
[] -> Nothing
_ -> output . head $ inputs
where
inputs = filter
(\x -> (fromJust . strId $ n) `elem` (concat . fromJust . strWires $ x))
xs
where inputs = filter (\x -> strId n `elem` (concat . strWires $ x)) xs

-- | Creates a GenerateOpts datatype from a list of NRNodes (where one is the 'generation-options node type')
getStrIoTGenerateOpts :: [NRNode] -> C.GenerateOpts
Expand All @@ -109,14 +119,12 @@ getGenerationOptsNode xs = case results of
toStrIoTGenerateOpts :: NRNode -> C.GenerateOpts
toStrIoTGenerateOpts x
| nodeType x == "generation-options" = C.GenerateOpts
(map (unwords . words) (endBy "," $ fromJust . imports $ x))
(map (unwords . words) (endBy "," $ fromJust . packages $ x))
(map (unwords . words) (endBy "," $ imports x))
(map (unwords . words) (endBy "," $ packages x))
ps
(fromMaybe False . optimise $ x)
(optimise x)
| otherwise = error "Node must be of type 'generation-options'"
where
ps = if preSource == "" then Nothing else Just preSource
preSource = fromJust (func x)
where ps = if func x == "" then Nothing else Just (func x)


striotTypes :: [String]
Expand Down
19 changes: 7 additions & 12 deletions src/ToStreamGraph.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ toStreamGraph = fromEdgeList . createEdgeList
createEdgeList :: [NRNode] -> [(StreamVertex, StreamVertex)]
createEdgeList [] = []
createEdgeList xs = concatMap
(\x ->
map (\w -> (toVertex x, toVertex (findNode w)))
. concat
. fromJust
. strWires
$ x
(\x -> map (\w -> (toVertex x, toVertex (findNode w))) . concat . strWires $ x
)
xs
where findNode = getNodeByStrId xs
Expand All @@ -36,14 +31,14 @@ fromEdgeList = foldr (overlay . edge) empty
-- | Converts the node to the correct type of Vertex depending on the node type
toVertex :: NRNode -> StreamVertex
toVertex n = case nodeType n of
"filter" -> toVertex' n Filter [fromMaybe "" (func n), "s"]
"generic-input" -> toVertex' n Source [fromMaybe "" (func n)]
"sink" -> toVertex' n Sink [fromMaybe "" (func n)]
"map" -> toVertex' n Map [fromMaybe "" (func n), "s"]
"filter" -> toVertex' n Filter [func n, "s"]
"generic-input" -> toVertex' n Source [func n]
"sink" -> toVertex' n Sink [func n]
"map" -> toVertex' n Map [func n, "s"]

-- | converts a node to a specific StreamVertex to be used by StrIoT
toVertex' :: NRNode -> StreamOperator -> [String] -> StreamVertex
toVertex' n o xs = StreamVertex (fromJust . strId $ n)
toVertex' n o xs = StreamVertex (strId n)
o
xs
(fromMaybe "String" (input n))
Expand All @@ -52,6 +47,6 @@ toVertex' n o xs = StreamVertex (fromJust . strId $ n)
partition :: [NRNode] -> [[Int]]
partition xs = [getIds partL, getIds partR]
where
getIds = map (fromJust . strId)
getIds = map strId
partL = filter (\x -> nodeType x `notElem` ["sink", "generation-options"]) xs
partR = filter (\x -> "sink" == nodeType x) xs
Loading