From 007c858061c59a411dbb8e37cd0f0247049732d6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 26 Nov 2024 02:15:17 +0000 Subject: [PATCH 1/8] ogma-core: Use template expansion to generate F' monitoring component. Refs #185. The F' backend uses a fixed template to generate the F' component. That template does not fit all use cases, so we are finding users heavily modifying the output (which is hard to keep up with when there are changes), and or not using ogma altogether for that reason. This commit modifies the ogma-core fprime command to use mustache to generate the F' monitoring application via a template and variable expansion. We adapt the template to also use those variables, and we modify the cabal file to include the new files as data files that are copied over during installation. --- ogma-core/ogma-core.cabal | 3 + ogma-core/src/Command/FPrimeApp.hs | 337 +++++-------------------- ogma-core/templates/fprime/Copilot.cpp | 78 ++++++ ogma-core/templates/fprime/Copilot.fpp | 71 ++++++ ogma-core/templates/fprime/Copilot.hpp | 66 +++++ 5 files changed, 286 insertions(+), 269 deletions(-) create mode 100644 ogma-core/templates/fprime/Copilot.cpp create mode 100644 ogma-core/templates/fprime/Copilot.fpp create mode 100644 ogma-core/templates/fprime/Copilot.hpp diff --git a/ogma-core/ogma-core.cabal b/ogma-core/ogma-core.cabal index 97743b34..e4930683 100644 --- a/ogma-core/ogma-core.cabal +++ b/ogma-core/ogma-core.cabal @@ -66,6 +66,9 @@ data-files: templates/copilot-cfs/CMakeLists.txt templates/ros/copilot/src/copilot_monitor.cpp templates/ros/copilot/package.xml templates/fprime/CMakeLists.txt + templates/fprime/Copilot.cpp + templates/fprime/Copilot.fpp + templates/fprime/Copilot.hpp templates/fprime/Dockerfile templates/fprime/instance-copilot data/formats/fcs_smv diff --git a/ogma-core/src/Command/FPrimeApp.hs b/ogma-core/src/Command/FPrimeApp.hs index 6020a5f2..d69c6b56 100644 --- a/ogma-core/src/Command/FPrimeApp.hs +++ b/ogma-core/src/Command/FPrimeApp.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} -- Copyright 2022 United States Government as represented by the Administrator -- of the National Aeronautics and Space Administration. All Rights Reserved. -- @@ -42,16 +43,17 @@ module Command.FPrimeApp import qualified Control.Exception as E import Control.Monad.Except ( ExceptT, liftEither, liftIO, runExceptT, throwError ) -import Data.Aeson ( eitherDecode ) +import Data.Aeson ( eitherDecode, object, (.=) ) import Data.Char ( toUpper ) import Data.List ( find, intercalate, nub, sort ) import Data.Maybe ( fromMaybe ) +import Data.Text.Lazy ( pack ) import System.FilePath ( () ) -- External imports: auxiliary import Data.ByteString.Extra as B ( safeReadFile ) import Data.String.Extra ( sanitizeLCIdentifier, sanitizeUCIdentifier ) -import System.Directory.Extra ( copyDirectoryRecursive ) +import System.Directory.Extra ( copyTemplate ) -- External imports: ogma import Data.OgmaSpec (Spec, externalVariableName, externalVariables, @@ -112,9 +114,6 @@ fprimeApp' targetDir varNames varDB monitors = dataDir <- getDataDir let templateDir = dataDir "templates" "fprime" - -- Expand template - copyDirectoryRecursive templateDir targetDir - let f n o@(oVars) = case variableMap varDB n of Nothing -> o @@ -123,29 +122,32 @@ fprimeApp' targetDir varNames varDB monitors = -- This is a Data.List.unzip4 let vars = foldr f [] varNames - let fprimeFileName = - targetDir "Copilot.fpp" - fprimeFileContents = - unlines $ - componentInterface vars monitors - - writeFile fprimeFileName fprimeFileContents - - let fprimeFileName = - targetDir "Copilot.hpp" - fprimeFileContents = - unlines $ - componentHeader vars monitors - - writeFile fprimeFileName fprimeFileContents - - let fprimeFileName = - targetDir "Copilot.cpp" - fprimeFileContents = - unlines $ - componentImpl vars monitors - - writeFile fprimeFileName fprimeFileContents + -- Copilot.fpp + (ifaceTypePorts, ifaceInputPorts, ifaceViolationEvents) = + componentInterface vars monitors + + -- Copilot.hpp + hdrHandlers = componentHeader vars monitors + + -- Copilot.cpp + (implInputs, implMonitorResults, implInputHandlers, + implTriggerResultReset, implTriggerChecks, implTriggers) = + componentImpl vars monitors + + subst = object $ + [ "ifaceTypePorts" .= pack ifaceTypePorts + , "ifaceInputPorts" .= pack ifaceInputPorts + , "ifaceViolationEvents" .= pack ifaceViolationEvents + , "hdrHandlers" .= pack hdrHandlers + , "implInputs" .= pack implInputs + , "implMonitorResults" .= pack implMonitorResults + , "implInputHandlers" .= pack implInputHandlers + , "implTriggerResultReset" .= pack implTriggerResultReset + , "implTriggerChecks" .= pack implTriggerChecks + , "implTriggers" .= pack implTriggers + ] + + copyTemplate templateDir subst targetDir return $ Right () @@ -280,93 +282,19 @@ data VarDecl = VarDecl -- | Return the contents of the FPrime component interface (.fpp) specification. componentInterface :: [VarDecl] -> [String] -- Monitors - -> [String] + -> (String, String, String) componentInterface variables monitors = - [ "module Ref {" - , "" - ] - ++ typePorts ++ - [ "" - , " @ Monitoring component" - , " queued component Copilot {" - , "" - , " # ----------------------------------------------------------------------" - , " # General ports" - , " # ----------------------------------------------------------------------" - , "" - ] - ++ inputPorts ++ - [ "" - , " # ----------------------------------------------------------------------" - , " # Special ports" - , " # ----------------------------------------------------------------------" - , "" - , " @ Command receive" - , " command recv port cmdIn" - , "" - , " @ Command registration" - , " command reg port cmdRegOut" - , "" - , " @ Command response" - , " command resp port cmdResponseOut" - , "" - , " @ Event" - , " event port eventOut" - , "" - , " @ Parameter get" - , " param get port prmGetOut" - , "" - , " @ Parameter set" - , " param set port prmSetOut" - , "" - , " @ Telemetry" - , " telemetry port tlmOut" - , "" - , " @ Text event" - , " text event port textEventOut" - , "" - , " @ Time get" - , " time get port timeGetOut" - , "" - , " # ----------------------------------------------------------------------" - , " # Parameters" - , " # ----------------------------------------------------------------------" - , "" - , " # This section intentionally left blank" - , "" - , " # ----------------------------------------------------------------------" - , " # Events" - , " # ----------------------------------------------------------------------" - , "" - ] - ++ violationEvents ++ - [ "" - , " # ----------------------------------------------------------------------" - , " # Commands" - , " # ----------------------------------------------------------------------" - , "" - , " sync command CHECK_MONITORS()" - , "" - , " # ----------------------------------------------------------------------" - , " # Telemetry" - , " # ----------------------------------------------------------------------" - , "" - , " # This section intentionally left blank" - , "" - , " }" - , "" - , "}" - ] + (typePorts, inputPorts, violationEvents) where - typePorts = nub $ sort $ map toTypePort variables + typePorts = unlines' $ nub $ sort $ map toTypePort variables toTypePort varDecl = " port " ++ fprimeVarDeclType varDecl ++ "Value(value: " ++ fprimeVarDeclType varDecl ++ ")" - inputPorts = map toInputPortDecl variables + inputPorts = unlines' $ map toInputPortDecl variables toInputPortDecl varDecl = " async input port " ++ varDeclName varDecl ++ "In : " ++ fprimeVarDeclType varDecl @@ -385,7 +313,8 @@ componentInterface variables monitors = "double" -> "F64" def -> def - violationEvents = intercalate [""] + violationEvents = unlines' + $ intercalate [""] $ map violationEvent monitors violationEvent monitor = [ " @ " ++ monitor ++ " violation" @@ -401,78 +330,11 @@ componentInterface variables monitors = -- | Return the contents of the FPrime component header file. componentHeader :: [VarDecl] -> [String] -- Monitors - -> [String] -componentHeader variables _monitors = - [ "// ======================================================================" - , "// \\title Copilot.hpp" - , "// \\author root" - , "// \\brief hpp file for Copilot component implementation class" - , "// ======================================================================" - , "" - , "#ifndef Copilot_HPP" - , "#define Copilot_HPP" - , "" - , "#include \"Ref/Copilot/CopilotComponentAc.hpp\"" - , "" - , "namespace Ref {" - , "" - , " class Copilot :" - , " public CopilotComponentBase" - , " {" - , "" - , " public:" - , "" - , " // ----------------------------------------------------------------------" - , " // Construction, initialization, and destruction" - , " // ----------------------------------------------------------------------" - , "" - , " //! Construct object Copilot" - , " //!" - , " Copilot(" - , " const char *const compName /*!< The component name*/" - , " );" - , "" - , " //! Initialize object Copilot" - , " //!" - , " void init(" - , " const NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/" - , " const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/" - , " );" - , "" - , " //! Destroy object Copilot" - , " //!" - , " ~Copilot();" - , "" - , " PRIVATE:" - , "" - , " // ----------------------------------------------------------------------" - , " // Handler implementations for user-defined typed input ports" - , " // ----------------------------------------------------------------------" - , "" - ] - ++ handlers ++ - [ "" - , " PRIVATE:" - , "" - , " // ----------------------------------------------------------------------" - , " // Command handler implementations" - , " // ----------------------------------------------------------------------" - , "" - , " //! Implementation for CHECK_MONITORS command handler" - , " //! " - , " void CHECK_MONITORS_cmdHandler(" - , " const FwOpcodeType opCode, /*!< The opcode*/" - , " const U32 cmdSeq /*!< The command sequence number*/" - , " );" - , "" - , " };" - , "" - , "} // end namespace Ref" - , "" - , "#endif" - ] + -> String +componentHeader variables _monitors = handlers where - handlers = intercalate [""] + handlers = unlines' + $ intercalate [""] $ map toInputHandler variables toInputHandler nm = [ " //! Handler implementation for " ++ varDeclName nm ++ "In" @@ -489,103 +351,29 @@ componentHeader variables _monitors = -- | Return the contents of the main FPrime component. componentImpl :: [VarDecl] -> [String] -- Monitors - -> [String] + -> (String, String, String, String, String, String) componentImpl variables monitors = - [ "// ======================================================================" - , "// \\title Copilot.cpp" - , "// \\author Ogma" - , "// \\brief cpp file for Copilot component implementation class" - , "// ======================================================================" - , "" - , "" - , "#include " - , "#include \"Fw/Types/BasicTypes.hpp\"" - , "" - , "#ifdef __cplusplus" - , "extern \"C\" {" - , "#endif" - , "" - , "#include \"copilot.h\"" - , "#include \"copilot_types.h\"" - , "" - , "#ifdef __cplusplus" - , "}" - , "#endif" - , "" - ] - ++ inputs - ++ monitorResults ++ - [ "" - , "namespace Ref {" - , "" - , " // ----------------------------------------------------------------------" - , " // Construction, initialization, and destruction" - , " // ----------------------------------------------------------------------" - , "" - , " Copilot ::" - , " Copilot(" - , " const char *const compName" - , " ) : CopilotComponentBase(compName)" - , " {" - , "" - , " }" - , "" - , " void Copilot ::" - , " init(" - , " const NATIVE_INT_TYPE queueDepth," - , " const NATIVE_INT_TYPE instance" - , " )" - , " {" - , " CopilotComponentBase::init(queueDepth, instance);" - , " }" - , "" - , " Copilot ::" - , " ~Copilot()" - , " {" - , "" - , " }" - , "" - , " // ----------------------------------------------------------------------" - , " // Handler implementations for user-defined typed input ports" - , " // ----------------------------------------------------------------------" - , "" - ] - ++ inputHandlers ++ - [ "" - , " // ----------------------------------------------------------------------" - , " // Command handler implementations" - , " // ----------------------------------------------------------------------" - , "" - , " void Copilot ::" - , " CHECK_MONITORS_cmdHandler(" - , " const FwOpcodeType opCode," - , " const U32 cmdSeq" - , " )" - , " {" - ] - ++ triggerResultReset ++ - [ " step();" - , " this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK);" - ] - ++ triggerChecks ++ - [ " }" - , "" - , "} // end namespace Ref" - , "" - ] - ++ triggers + ( inputs + , monitorResults + , inputHandlers + , triggerResultReset + , triggerChecks + , triggers + ) where - inputs = variablesS + inputs = unlines' variablesS - monitorResults = intercalate [""] + monitorResults = unlines' + $ intercalate [""] $ map monitorResult monitors monitorResult monitor = [ "bool " ++ monitor ++ "_result;" ] - inputHandlers = intercalate [""] + inputHandlers = unlines' + $ intercalate [""] $ map toInputHandler variables toInputHandler nm = [ " void Copilot :: " @@ -601,13 +389,15 @@ componentImpl variables monitors = portTy = varDeclType nm ty = varDeclType nm - triggerResultReset = intercalate [""] + triggerResultReset = unlines' + $ intercalate [""] $ map monitorResultReset monitors monitorResultReset monitor = [ " " ++ monitor ++ "_result = false;" ] - triggerChecks = intercalate [""] + triggerChecks = unlines' + $ intercalate [""] $ map triggerCheck monitors triggerCheck monitor = [ " if (" ++ monitor ++ "_result) {" @@ -617,8 +407,9 @@ componentImpl variables monitors = where ucMonitor = map toUpper monitor - triggers :: [String] - triggers = intercalate [""] + triggers :: String + triggers = unlines' + $ intercalate [""] $ map triggerImpl monitors triggerImpl monitor = [ "void " ++ monitor ++ "() {" @@ -753,3 +544,11 @@ fretFormat = JSONFormat , specRequirementDesc = Just ".fretish" , specRequirementExpr = ".ptLTL" } + +-- * Auxliary functions + +-- | Create a string from a list of strings, inserting new line characters +-- between them. Unlike 'Prelude.unlines', this function does not insert +-- an end of line character at the end of the last string. +unlines' :: [String] -> String +unlines' = intercalate "\n" diff --git a/ogma-core/templates/fprime/Copilot.cpp b/ogma-core/templates/fprime/Copilot.cpp new file mode 100644 index 00000000..a8730363 --- /dev/null +++ b/ogma-core/templates/fprime/Copilot.cpp @@ -0,0 +1,78 @@ +// ====================================================================== +// \title Copilot.cpp +// \author Ogma +// \brief cpp file for Copilot component implementation class +// ====================================================================== + + +#include +#include "Fw/Types/BasicTypes.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "copilot.h" +#include "copilot_types.h" + +#ifdef __cplusplus +} +#endif + +{{{implInputs}}} +{{{implMonitorResults}}} + +namespace Ref { + + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + Copilot :: + Copilot( + const char *const compName + ) : CopilotComponentBase(compName) + { + + } + + void Copilot :: + init( + const NATIVE_INT_TYPE queueDepth, + const NATIVE_INT_TYPE instance + ) + { + CopilotComponentBase::init(queueDepth, instance); + } + + Copilot :: + ~Copilot() + { + + } + + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + +{{{implInputHandlers}}} + + // ---------------------------------------------------------------------- + // Command handler implementations + // ---------------------------------------------------------------------- + + void Copilot :: + CHECK_MONITORS_cmdHandler( + const FwOpcodeType opCode, + const U32 cmdSeq + ) + { +{{{implTriggerResultReset}}} + step(); + this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK); +{{{implTriggerChecks}}} + } + +} // end namespace Ref + +{{{implTriggers}}} diff --git a/ogma-core/templates/fprime/Copilot.fpp b/ogma-core/templates/fprime/Copilot.fpp new file mode 100644 index 00000000..52cd16d6 --- /dev/null +++ b/ogma-core/templates/fprime/Copilot.fpp @@ -0,0 +1,71 @@ +module Ref { + +{{{ifaceTypePorts}}} + + @ Monitoring component + queued component Copilot { + + # ---------------------------------------------------------------------- + # General ports + # ---------------------------------------------------------------------- + +{{{ifaceInputPorts}}} + + # ---------------------------------------------------------------------- + # Special ports + # ---------------------------------------------------------------------- + + @ Command receive + command recv port cmdIn + + @ Command registration + command reg port cmdRegOut + + @ Command response + command resp port cmdResponseOut + + @ Event + event port eventOut + + @ Parameter get + param get port prmGetOut + + @ Parameter set + param set port prmSetOut + + @ Telemetry + telemetry port tlmOut + + @ Text event + text event port textEventOut + + @ Time get + time get port timeGetOut + + # ---------------------------------------------------------------------- + # Parameters + # ---------------------------------------------------------------------- + + # This section intentionally left blank + + # ---------------------------------------------------------------------- + # Events + # ---------------------------------------------------------------------- + +{{{ifaceViolationEvents}}} + + # ---------------------------------------------------------------------- + # Commands + # ---------------------------------------------------------------------- + + sync command CHECK_MONITORS() + + # ---------------------------------------------------------------------- + # Telemetry + # ---------------------------------------------------------------------- + + # This section intentionally left blank + + } + +} diff --git a/ogma-core/templates/fprime/Copilot.hpp b/ogma-core/templates/fprime/Copilot.hpp new file mode 100644 index 00000000..2e6d5d56 --- /dev/null +++ b/ogma-core/templates/fprime/Copilot.hpp @@ -0,0 +1,66 @@ +// ====================================================================== +// \title Copilot.hpp +// \author root +// \brief hpp file for Copilot component implementation class +// ====================================================================== + +#ifndef Copilot_HPP +#define Copilot_HPP + +#include "Ref/Copilot/CopilotComponentAc.hpp" + +namespace Ref { + + class Copilot : + public CopilotComponentBase + { + + public: + + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + //! Construct object Copilot + //! + Copilot( + const char *const compName /*!< The component name*/ + ); + + //! Initialize object Copilot + //! + void init( + const NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ + const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ + ); + + //! Destroy object Copilot + //! + ~Copilot(); + + PRIVATE: + + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + +{{{hdrHandlers}}} + + PRIVATE: + + // ---------------------------------------------------------------------- + // Command handler implementations + // ---------------------------------------------------------------------- + + //! Implementation for CHECK_MONITORS command handler + //! + void CHECK_MONITORS_cmdHandler( + const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq /*!< The command sequence number*/ + ); + + }; + +} // end namespace Ref + +#endif From a8279359f5c3f386d3bf2cf026124153658af0ea Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 26 Nov 2024 02:21:40 +0000 Subject: [PATCH 2/8] ogma-core: Enable customizing template directory in F' component generation. Refs #185. The F' backend uses a fixed template to generate the F' component. That template does not fit all use cases, so we are finding users heavily modifying the output (which is hard to keep up with when there are changes), and or not using ogma altogether for that reason. A recent commit introduced the ability to use mustache to expand variables in a template. This commit modifies the fprime command to accept an additional argument that points to a user-provided directory with a custom template. --- ogma-core/src/Command/FPrimeApp.hs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ogma-core/src/Command/FPrimeApp.hs b/ogma-core/src/Command/FPrimeApp.hs index d69c6b56..7b953d56 100644 --- a/ogma-core/src/Command/FPrimeApp.hs +++ b/ogma-core/src/Command/FPrimeApp.hs @@ -72,6 +72,7 @@ import Paths_ogma_core ( getDataDir ) -- | Generate a new FPrime component connected to Copilot. fprimeApp :: FilePath -- ^ Target directory where the component -- should be created. + -> Maybe FilePath -- ^ Directory where the template is to be found. -> Maybe FilePath -- ^ FRET Component specification file. -> Maybe FilePath -- ^ File containing a list of variables to make -- available to Copilot. @@ -82,7 +83,7 @@ fprimeApp :: FilePath -- ^ Target directory where the component -- Copilot specification. The handlers are assumed -- to receive no arguments. -> IO (Result ErrorCode) -fprimeApp targetDir fretCSFile varNameFile varDBFile handlersFile = +fprimeApp targetDir mTemplateDir fretCSFile varNameFile varDBFile handlersFile = processResult $ do cs <- parseOptionalFRETCS fretCSFile vs <- parseOptionalVariablesFile varNameFile @@ -94,13 +95,15 @@ fprimeApp targetDir fretCSFile varNameFile varDBFile handlersFile = let varNames = fromMaybe (fretCSExtractExternalVariables cs) vs monitors = fromMaybe (fretCSExtractHandlers cs) rs - e <- liftIO $ fprimeApp' targetDir varNames varDB monitors + e <- liftIO $ fprimeApp' targetDir mTemplateDir varNames varDB monitors liftEither e -- | Generate a new FPrime component connected to Copilot, by copying the -- template and filling additional necessary files. fprimeApp' :: FilePath -- ^ Target directory where the component -- should be created. + -> Maybe FilePath -- ^ Directory where the template is to be + -- found. -> [String] -- ^ List of variable names (data sources). -> [(String, String)] -- ^ List of variables with their types, and -- the message IDs (topics) they can be @@ -108,11 +111,14 @@ fprimeApp' :: FilePath -- ^ Target directory where the component -> [String] -- ^ List of handlers associated to the -- monitors (or requirements monitored). -> IO (Either ErrorTriplet ()) -fprimeApp' targetDir varNames varDB monitors = +fprimeApp' targetDir mTemplateDir varNames varDB monitors = E.handle (return . Left . cannotCopyTemplate) $ do -- Obtain template dir - dataDir <- getDataDir - let templateDir = dataDir "templates" "fprime" + templateDir <- case mTemplateDir of + Just x -> return x + Nothing -> do + dataDir <- getDataDir + return $ dataDir "templates" "fprime" let f n o@(oVars) = case variableMap varDB n of From aa3e900f09c547b9b45c2191d53382be2bcebb27 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 26 Nov 2024 02:26:21 +0000 Subject: [PATCH 3/8] ogma-cli: Provide CLI argument to pass an auxiliary template source directory to F' app command. Refs #185. The F' backend uses a fixed template to generate the F' component. That template does not fit all use cases, so we are finding users heavily modifying the output (which is hard to keep up with when there are changes), and or not using ogma altogether for that reason. A recent commit introduced into ogma-core the ability to use a custom provided template and expand variables using mustache. This commit exposes that new parameter to the user in the CLI. --- ogma-cli/src/CLI/CommandFPrimeApp.hs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/ogma-cli/src/CLI/CommandFPrimeApp.hs b/ogma-cli/src/CLI/CommandFPrimeApp.hs index e3f57883..5b3aab4a 100644 --- a/ogma-cli/src/CLI/CommandFPrimeApp.hs +++ b/ogma-cli/src/CLI/CommandFPrimeApp.hs @@ -56,11 +56,12 @@ import Command.FPrimeApp ( ErrorCode, fprimeApp ) -- | Options needed to generate the FPrime component. data CommandOpts = CommandOpts - { fprimeAppTarget :: String - , fprimeAppFRETFile :: Maybe String - , fprimeAppVarNames :: Maybe String - , fprimeAppVarDB :: Maybe String - , fprimeAppHandlers :: Maybe String + { fprimeAppTarget :: String + , fprimeAppTemplateDir :: Maybe String + , fprimeAppFRETFile :: Maybe String + , fprimeAppVarNames :: Maybe String + , fprimeAppVarDB :: Maybe String + , fprimeAppHandlers :: Maybe String } -- | Create component that subscribe @@ -72,6 +73,7 @@ command :: CommandOpts -> IO (Result ErrorCode) command c = fprimeApp (fprimeAppTarget c) + (fprimeAppTemplateDir c) (fprimeAppFRETFile c) (fprimeAppVarNames c) (fprimeAppVarDB c) @@ -94,6 +96,13 @@ commandOptsParser = CommandOpts <> value "fprime" <> help strFPrimeAppDirArgDesc ) + <*> optional + ( strOption + ( long "app-template-dir" + <> metavar "DIR" + <> help strFPrimeAppTemplateDirArgDesc + ) + ) <*> optional ( strOption ( long "fret-file-name" @@ -127,6 +136,11 @@ commandOptsParser = CommandOpts strFPrimeAppDirArgDesc :: String strFPrimeAppDirArgDesc = "Target directory" +-- | Argument template directory to FPrime component generation command +strFPrimeAppTemplateDirArgDesc :: String +strFPrimeAppTemplateDirArgDesc = + "Directory holding F' component source template" + -- | Argument FRET CS to FPrime component generation command strFPrimeAppFRETFileNameArgDesc :: String strFPrimeAppFRETFileNameArgDesc = From 0b9ef8df0d6aff4e89ceca84a6ebefbed38da984 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 27 Nov 2024 06:00:34 +0000 Subject: [PATCH 4/8] ogma-cli: Update README to document F' component template customization. Refs #185. The F' backend uses a fixed template to generate the F' component. That template does not fit all use cases, so we are finding users heavily modifying the output (which is hard to keep up with when there are changes), and or not using ogma altogether for that reason. Prior commits have expanded the command to allow for customization of the template using a user-provided directory and expanding variables in the template using mustache. This commit documents the new feature in the README. --- ogma-cli/README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/ogma-cli/README.md b/ogma-cli/README.md index b5d9ad11..457df82c 100644 --- a/ogma-cli/README.md +++ b/ogma-cli/README.md @@ -401,6 +401,7 @@ F' components are generated using the Ogma command `fprime`, which receives five main arguments: - `--app-target-dir DIR`: location where the F' application files must be stored. +- `--app-template-dir DIR`: directory holding F' component source template. - `--variable-file FILENAME`: a file containing a list of variables that must be made available to the monitor. - `--variable-db FILENAME`: a file containing a database of known variables, @@ -464,6 +465,67 @@ $ cat handlers handlerpropREQ_001 ``` +### Template Customization + +By default, Ogma uses a pre-defined template to generate the F' monitoring +component. It's possible to customize the output by providing a directory with +a set of files with an F' component template, which Ogma will use instead. + +To choose this feature, one must call Ogma's `fprime` command with the argument +`--app-template-dir DIR`, where `DIR` is the path to a directory containing an +F' component specification template. For example, assuming that the directory +`my_template` contains a custom F' component template, one can execute: + +``` +$ ogma fprime --app-template-dir my_template/ --handlers filename --variable-file variables --variable-db fprime-variable-db --app-target-dir fprime_demo +``` + +Ogma will copy the files in that directory to the target path, filling in +several holes with specific information. For the component interface, the +variables are: + +- {{{ifaceTypePorts}}}: Contain the type declarations for the types used by the + ports. + +- {{{ifaceInputPorts}}}: Contains the declarations of the `async input` port, + to provide information needed by the monitors. + +- {{{ifaceViolationEvents}}}: Contains the output port declarations, used to + emit property violations. + +For the monitor's header file, the variables are: + +- {{{hdrHandlers}}}: Contains the declarations of operations to execute when + new information is received in an input port, prior to re-evaluating the + monitors. + +For the monitor's implementation, the variables are: + +- {{{implInputs}}}: Contains the declarations of the variables with inputs + needed by the monitor. + +- {{{implMonitorResults}}}: Contains the declarations of boolean variables, + each holding the result of the evaluation of one of the monitors. + +- {{{implInputHandlers}}}: Contains the implementations of operations to + execute when new information is received in an input port, prior to + re-evaluating the monitors. + +- {{{implTriggerResultReset}}}: Contains instructions that reset the status of + the monitors. + +- {{{implTriggerChecks}}}: Contains instructions that check whether any monitor + has resulted in a violation and, if so, sends an event via the corresponding + port. + +- {{{implTriggers}}}: Contains the implementations of the functions that set + the result of a monitor evaluation to true when the Copilot implementation + finds a violation. + +We understand that this level of customization may be insufficient for your +application. If that is the case, feel free to reach out to our team to discuss +how we could make the template expansion system more versatile. + ### Current limitations The user must place the code generated by Copilot monitors in three files, From c1ecd8b3628d7454ec13f09da358b6a42ff1a37c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 5 Dec 2024 23:10:12 +0000 Subject: [PATCH 5/8] ogma-extra: Deprecate unnecessary functions. Refs #185. The functions copyDirectoryRecursive and copyFile' are no longer needed, now that all parts of ogma-core use the template expansion system. This commit deprecates both functions so that they can be removed in a future version of ogma-extra. --- ogma-extra/src/System/Directory/Extra.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ogma-extra/src/System/Directory/Extra.hs b/ogma-extra/src/System/Directory/Extra.hs index 01ebfdc4..2982ecf3 100644 --- a/ogma-extra/src/System/Directory/Extra.hs +++ b/ogma-extra/src/System/Directory/Extra.hs @@ -56,6 +56,7 @@ import Text.Microstache ( compileMustacheFile, compileMustacheText, renderMustache ) +{-# DEPRECATED copyDirectoryRecursive "This function is deprecated in ogma-extra-1.6.0." #-} -- | Copy all files from one directory to another. copyDirectoryRecursive :: FilePath -- ^ Source directory -> FilePath -- ^ Target directory @@ -74,6 +75,7 @@ copyDirectoryRecursive sourceDir targetDir = -- Copy all the files, replacing the top directory. mapM_ (copyFile' . sourceAndDest) files +{-# DEPRECATED copyFile' "This function is deprecated in ogma-extra-1.6.0." #-} -- | Copy file origin to dest, creating the target directory if it does not -- exist. copyFile' :: (FilePath, FilePath) -> IO () From a187017756be47f9840993cacf38c3136f551be7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 27 Nov 2024 06:02:16 +0000 Subject: [PATCH 6/8] ogma-core: Document changes in CHANGELOG. Refs #185. --- ogma-core/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ogma-core/CHANGELOG.md b/ogma-core/CHANGELOG.md index 47ebf797..cd51e531 100644 --- a/ogma-core/CHANGELOG.md +++ b/ogma-core/CHANGELOG.md @@ -3,6 +3,7 @@ ## [1.X.Y] - 2024-12-04 * Replace queueSize with QUEUE_SIZE in FPP file (#186). +* Use template expansion system to generate F' monitoring component (#185). ## [1.5.0] - 2024-11-21 From 42c9610fa08ba77bc5a54a7f138311f74b943e5b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 27 Nov 2024 06:02:48 +0000 Subject: [PATCH 7/8] ogma-cli: Document changes in CHANGELOG. Refs #185. --- ogma-cli/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ogma-cli/CHANGELOG.md b/ogma-cli/CHANGELOG.md index 721c2d2e..f005cbe0 100644 --- a/ogma-cli/CHANGELOG.md +++ b/ogma-cli/CHANGELOG.md @@ -1,8 +1,9 @@ # Revision history for ogma-cli -## [1.X.Y] - 2024-11-23 +## [1.X.Y] - 2024-11-26 * Update contribution guidelines (#161). +* Provide ability to customize template in fprime command (#185). ## [1.5.0] - 2024-11-21 From 56e24677f821ca31dc7af63a5d596ad4d55dca31 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 5 Dec 2024 23:10:53 +0000 Subject: [PATCH 8/8] ogma-extra: Document changes in CHANGELOG. Refs #185. --- ogma-extra/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ogma-extra/CHANGELOG.md b/ogma-extra/CHANGELOG.md index 8d4d22ce..37efb0f0 100644 --- a/ogma-extra/CHANGELOG.md +++ b/ogma-extra/CHANGELOG.md @@ -1,5 +1,9 @@ # Revision history for ogma-extra +## [1.X.Y] - 2024-12-05 + +* Deprecate unnecessary functions (#185). + ## [1.5.0] - 2024-11-21 * Version bump 1.5.0 (#178).