ogma-core 1.4.1 → 1.5.0
raw patch · 19 files changed
+516/−772 lines, 19 filesdep +textdep ~ogma-extradep ~ogma-language-cdep ~ogma-language-cocospec
Dependencies added: text
Dependency ranges changed: ogma-extra, ogma-language-c, ogma-language-cocospec, ogma-language-copilot, ogma-language-jsonspec, ogma-language-smv, ogma-spec
Files
- CHANGELOG.md +11/−0
- data/formats/fcs_cocospec +13/−0
- data/formats/fcs_smv +13/−0
- data/formats/fdb_cocospec +13/−0
- data/formats/fdb_smv +13/−0
- ogma-core.cabal +16/−11
- src/Command/CFSApp.hs +34/−167
- src/Command/FRETComponentSpec2Copilot.hs +0/−204
- src/Command/FRETReqsDB2Copilot.hs +0/−213
- src/Command/ROSApp.hs +55/−123
- src/Command/Standalone.hs +223/−0
- src/Language/Trans/SMV2Copilot.hs +14/−14
- src/Language/Trans/Spec2Copilot.hs +11/−3
- templates/copilot-cfs/fsw/src/copilot_cfs.c +4/−17
- templates/ros/Dockerfile +2/−2
- templates/ros/copilot/src/.keep +0/−0
- templates/ros/copilot/src/copilot_logger.cpp +27/−0
- templates/ros/copilot/src/copilot_monitor.cpp +52/−0
- tests/Main.hs +15/−18
CHANGELOG.md view
@@ -1,5 +1,16 @@ # Revision history for ogma-core +## [1.5.0] - 2024-11-21++* Version bump 1.5.0 (#178).+* Fix incorrect path when using Space ROS humble-2024.10.0 (#158).+* Use template expansion system to generate cFS monitoring application (#157).+* Use template expansion system to generate ROS monitoring application (#162).+* Fix comment in generated Copilot spec (#164).+* Add missing notPreviousNot to generated spec (#168).+* Introduce new standalone command (#170).+* Correct name in documentation (#176).+ ## [1.4.1] - 2024-09-21 * Version bump 1.4.1 (#155).
+ data/formats/fcs_cocospec view
@@ -0,0 +1,13 @@+JSONFormat+ { specInternalVars = Just "..Internal_variables[*]"+ , specInternalVarId = ".name"+ , specInternalVarExpr = ".assignmentCopilot"+ , specInternalVarType = Just ".type"+ , specExternalVars = Just "..Other_variables[*]"+ , specExternalVarId = ".name"+ , specExternalVarType = Just ".type"+ , specRequirements = "..Requirements[*]"+ , specRequirementId = ".name"+ , specRequirementDesc = Just ".fretish"+ , specRequirementExpr = ".CoCoSpecCode"+ }
+ data/formats/fcs_smv view
@@ -0,0 +1,13 @@+JSONFormat+ { specInternalVars = Just "..Internal_variables[*]"+ , specInternalVarId = ".name"+ , specInternalVarExpr = ".assignmentCopilot"+ , specInternalVarType = Just ".type"+ , specExternalVars = Just "..Other_variables[*]"+ , specExternalVarId = ".name"+ , specExternalVarType = Just ".type"+ , specRequirements = "..Requirements[*]"+ , specRequirementId = ".name"+ , specRequirementDesc = Just ".fretish"+ , specRequirementExpr = ".ptLTL"+ }
+ data/formats/fdb_cocospec view
@@ -0,0 +1,13 @@+JSONFormat+ { specInternalVars = Nothing+ , specInternalVarId = ""+ , specInternalVarExpr = ""+ , specInternalVarType = Nothing+ , specExternalVars = Just ".semantics.variables..*.*"+ , specExternalVarId = ""+ , specExternalVarType = Nothing+ , specRequirements = "$[:]"+ , specRequirementId = ".reqid"+ , specRequirementDesc = Just ".fulltext"+ , specRequirementExpr = ".semantics.CoCoSpecCode"+ }
+ data/formats/fdb_smv view
@@ -0,0 +1,13 @@+JSONFormat+ { specInternalVars = Nothing+ , specInternalVarId = ""+ , specInternalVarExpr = ""+ , specInternalVarType = Nothing+ , specExternalVars = Just ".semantics.variables..*.*"+ , specExternalVarId = ""+ , specExternalVarType = Nothing+ , specRequirements = "$[:]"+ , specRequirementId = ".reqid"+ , specRequirementDesc = Just ".fulltext"+ , specRequirementExpr = ".semantics.ptExpanded"+ }
ogma-core.cabal view
@@ -32,7 +32,7 @@ build-type: Simple name: ogma-core-version: 1.4.1+version: 1.5.0 homepage: https://github.com/nasa/ogma license: OtherLicense license-file: LICENSE.pdf@@ -62,11 +62,16 @@ templates/copilot-cfs/fsw/src/copilot_cfs_events.h templates/ros/Dockerfile templates/ros/copilot/CMakeLists.txt- templates/ros/copilot/src/.keep+ templates/ros/copilot/src/copilot_logger.cpp+ templates/ros/copilot/src/copilot_monitor.cpp templates/ros/copilot/package.xml templates/fprime/CMakeLists.txt templates/fprime/Dockerfile templates/fprime/instance-copilot+ data/formats/fcs_smv+ data/formats/fcs_cocospec+ data/formats/fdb_smv+ data/formats/fdb_cocospec -- Ogma packages should be uncurated so that only the official maintainers make -- changes.@@ -82,10 +87,9 @@ Command.CStructs2Copilot Command.CStructs2MsgHandlers Command.FPrimeApp- Command.FRETComponentSpec2Copilot- Command.FRETReqsDB2Copilot Command.Result Command.ROSApp+ Command.Standalone Data.Location @@ -108,14 +112,15 @@ , bytestring , filepath , mtl+ , text >= 1.2.3.1 && < 2.1 - , ogma-extra >= 1.4.1 && < 1.5- , ogma-language-c >= 1.4.1 && < 1.5- , ogma-language-cocospec >= 1.4.1 && < 1.5- , ogma-language-copilot >= 1.4.1 && < 1.5- , ogma-language-jsonspec >= 1.4.1 && < 1.5- , ogma-language-smv >= 1.4.1 && < 1.5- , ogma-spec >= 1.4.1 && < 1.5+ , ogma-extra >= 1.5.0 && < 1.6+ , ogma-language-c >= 1.5.0 && < 1.6+ , ogma-language-cocospec >= 1.5.0 && < 1.6+ , ogma-language-copilot >= 1.5.0 && < 1.6+ , ogma-language-jsonspec >= 1.5.0 && < 1.6+ , ogma-language-smv >= 1.5.0 && < 1.6+ , ogma-spec >= 1.5.0 && < 1.6 hs-source-dirs: src
src/Command/CFSApp.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- Copyright 2020 United States Government as represented by the Administrator -- of the National Aeronautics and Space Administration. All Rights Reserved. --@@ -45,16 +46,17 @@ where -- External imports-import qualified Control.Exception as E-import Data.List ( find )-import System.FilePath ( (</>) )---- External imports: auxiliary-import System.Directory.Extra ( copyDirectoryRecursive )+import qualified Control.Exception as E+import Data.Aeson (decode, object, (.=))+import Data.List (find)+import Data.Text (Text)+import Data.Text.Lazy (pack, unpack)+import System.FilePath ( (</>) ) -- Internal imports: auxiliary-import Command.Result ( Result (..) )-import Data.Location ( Location (..) )+import Command.Result ( Result (..) )+import Data.Location ( Location (..) )+import System.Directory.Extra ( copyTemplate ) -- Internal imports import Paths_ogma_core ( getDataDir )@@ -62,13 +64,14 @@ -- | Generate a new CFS application connected to Copilot. cFSApp :: FilePath -- ^ Target directory where the application -- should be created.+ -> Maybe FilePath -- ^ Directory where the template is to be found. -> FilePath -- ^ File containing a list of variables to make -- available to Copilot. -> Maybe FilePath -- ^ File containing a list of known variables -- with their types and the message IDs they -- can be obtained from. -> IO (Result ErrorCode)-cFSApp targetDir varNameFile varDBFile = do+cFSApp targetDir mTemplateDir varNameFile varDBFile = do -- We first try to open the two files we need to fill in details in the CFS -- app template.@@ -97,12 +100,13 @@ Right varNames -> do -- Obtain template dir- dataDir <- getDataDir- let templateDir = dataDir </> "templates" </> "copilot-cfs"+ templateDir <- case mTemplateDir of+ Just x -> return x+ Nothing -> do+ dataDir <- getDataDir+ return $ dataDir </> "templates" </> "copilot-cfs" E.handle (return . cannotCopyTemplate) $ do- -- Expand template- copyDirectoryRecursive templateDir targetDir let f n o@(oVars, oIds, oInfos, oDatas) = case variableMap varDB n of@@ -113,10 +117,18 @@ -- This is a Data.List.unzip4 let (vars, ids, infos, datas) = foldr f ([], [], [], []) varNames - let cfsFileName = targetDir </> "fsw" </> "src" </> "copilot_cfs.c"- cfsFileContents = unlines $ fileContents vars ids infos datas+ let (variablesS, msgSubscriptionS, msgCasesS, msgHandlerS) =+ appComponents vars ids infos datas+ subst = object $+ [ "variablesS" .= pack variablesS+ , "msgSubscriptionsS" .= pack msgSubscriptionS+ , "msgCasesS" .= pack msgCasesS+ , "msgHandlerS" .= pack msgHandlerS+ ] - writeFile cfsFileName cfsFileContents+ -- Expand template+ copyTemplate templateDir subst targetDir+ return Success -- | Predefined list of Icarous variables that are known to Ogma@@ -170,9 +182,10 @@ , msgDataVarType :: String } --- | Return the contents of the main CFS application.-fileContents :: [VarDecl] -> [MsgInfoId] -> [MsgInfo] -> [MsgData] -> [String]-fileContents variables msgIds msgNames msgDatas = cfsFileContents+-- | Return the components that are customized in a cFS application.+appComponents :: [VarDecl] -> [MsgInfoId] -> [MsgInfo] -> [MsgData]+ -> (String, String, String, String)+appComponents variables msgIds msgNames msgDatas = cfsFileContents where variablesS = unlines $ map toVarDecl variables toVarDecl varDecl = varDeclType varDecl ++ " " ++ varDeclName varDecl ++ ";"@@ -205,157 +218,11 @@ ] cfsFileContents =- [ "/********************************************************************"- , "** File: copilot_cfs.c"- , "**"- , "** Purpose:"- , "** This file contains the source code for the Copilot App."- , "**"- , "*********************************************************************/"- , ""- , "/*"- , "** Include Files:"- , "*/"- , ""- , "#include \"copilot_cfs.h\""- , "#include \"copilot_cfs_perfids.h\""- , "#include \"copilot_cfs_msgids.h\""- , "#include \"copilot_cfs_msg.h\""- , "#include \"copilot_cfs_events.h\""- , "#include \"copilot_cfs_version.h\""- , "#include \"Icarous_msgids.h\""- , "#include \"Icarous_msg.h\""- , ""- , variablesS- , "void split(void);"- , "void step(void);"- , ""- , "/*"- , "** global data"- , "*/"- , ""- , "copilot_hk_tlm_t COPILOT_HkTelemetryPkt;"- , "CFE_SB_PipeId_t COPILOT_CommandPipe;"- , "CFE_SB_MsgPtr_t COPILOTMsgPtr;"- , ""- , "static CFE_EVS_BinFilter_t COPILOT_EventFilters[] ="- , " { /* Event ID mask */"- , " {COPILOT_STARTUP_INF_EID, 0x0000},"- , " {COPILOT_COMMAND_ERR_EID, 0x0000},"- , " {COPILOT_COMMANDCPVIOL_INF_EID, 0x0000},"- , " };"- , ""- , "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */"- , "/* COPILOT_AppMain() -- App entry point and main process loop */"- , "/* */"- , "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */"- , "void COPILOT_AppMain( void )"- , "{"- , " int32 status;"- , " uint32 RunStatus = CFE_ES_APP_RUN;"- , ""- , " CFE_ES_PerfLogEntry(COPILOT_CFS_PERF_ID);"- , ""- , " COPILOT_AppInit();"- , ""- , " /*"- , " ** COPILOT Runloop"- , " */"- , " while (CFE_ES_RunLoop(&RunStatus) == TRUE)"- , " {"- , " CFE_ES_PerfLogExit(COPILOT_CFS_PERF_ID);"- , ""- , " // Pend on receipt of command packet"- , " // (timeout set to 500 millisecs)"- , " status = CFE_SB_RcvMsg (&COPILOTMsgPtr,"- , " COPILOT_CommandPipe,"- , " 500);"- , " "- , " CFE_ES_PerfLogEntry(COPILOT_CFS_PERF_ID);"- , ""- , " if (status == CFE_SUCCESS)"- , " {"- , " COPILOT_ProcessCommandPacket();"- , " }"- , ""- , " }"- , ""- , " CFE_ES_ExitApp(RunStatus);"- , ""- , "} /* End of COPILOT_AppMain() */"- , ""- , "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */"- , "/* */"- , "/* COPILOT_AppInit() -- initialization */"- , "/* */"- , "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */"- , "void COPILOT_AppInit(void)"- , "{"- , " // Register the app with Executive services"- , " CFE_ES_RegisterApp();"- , ""- , " // Register the events"- , " CFE_EVS_Register(COPILOT_EventFilters,"- , " sizeof(COPILOT_EventFilters) / "- ++ "sizeof(CFE_EVS_BinFilter_t),"- , " CFE_EVS_BINARY_FILTER);"- , ""- , " // Create the Software Bus command pipe and subscribe to "- , " // housekeeping messages"- , " CFE_SB_CreatePipe(&COPILOT_CommandPipe, COPILOT_PIPE_DEPTH,"- ++ "\"COPILOT_CMD_PIPE\");"+ ( variablesS , msgSubscriptionS- , ""- , " CFE_EVS_SendEvent (COPILOT_STARTUP_INF_EID,"- , " CFE_EVS_INFORMATION,"- , " \"COPILOT App Initialized. Ver %d.%d.%d.%d\","- , " COPILOT_CFS_MAJOR_VERSION,"- , " COPILOT_CFS_MINOR_VERSION, "- , " COPILOT_CFS_REVISION, "- , " COPILOT_CFS_MISSION_REV);"- , ""- , "} /* End of COPILOT_AppInit() */"- , ""- , "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */"- , "/* Name: COPILOT_ProcessCommandPacket */"- , "/* */"- , "/* Purpose: */"- , "/* This routine will process any packet that is received */"- , "/* on the COPILOT command pipe. */"- , "/* */"- , "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */"- , "void COPILOT_ProcessCommandPacket(void)"- , "{"- , " CFE_SB_MsgId_t MsgId;"- , ""- , " MsgId = CFE_SB_GetMsgId(COPILOTMsgPtr);"- , ""- , " switch (MsgId)"- , " {" , msgCasesS- , " default:"- , " COPILOT_HkTelemetryPkt.copilot_command_error_count++;"- , " CFE_EVS_SendEvent(COPILOT_COMMAND_ERR_EID,CFE_EVS_ERROR,"- , " "- ++ "\"COPILOT: invalid command packet,MID = 0x%x\","- , " MsgId);"- , " break;"- , " }"- , ""- , " return;"- , ""- , "} /* End COPILOT_ProcessCommandPacket */"- , "" , msgHandlerS- , ""- , "/**"- , " * Report copilot property violations."- , " */"- , "void split(void) {"- , " CFE_EVS_SendEvent(COPILOT_COMMANDCPVIOL_INF_EID, CFE_EVS_ERROR,"- , " \"COPILOT: violation\");"- , "}"- ]+ ) -- * Exception handlers
− src/Command/FRETComponentSpec2Copilot.hs
@@ -1,204 +0,0 @@-{-# LANGUAGE ExistentialQuantification #-}--- Copyright 2020 United States Government as represented by the Administrator--- of the National Aeronautics and Space Administration. All Rights Reserved.------ Disclaimers------ No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY--- OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT--- LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO--- SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A--- PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE--- SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF--- PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN--- ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR--- RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR--- ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER,--- GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING--- THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES--- IT "AS IS."------ Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST--- THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS--- ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN--- ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE,--- INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S--- USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE--- UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY--- PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY--- FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS--- AGREEMENT.------ | Transform a FRET component specification into a Copilot specification.------ This module makes use of--- "Language.Trans.FRETComponentSpec2Copilot", which does most of the work.-module Command.FRETComponentSpec2Copilot- ( fretComponentSpec2Copilot- , FRETComponentSpec2CopilotOptions(..)- , ErrorCode- )- where---- External imports-import Data.Aeson ( eitherDecode, decode )-import Data.ByteString.Lazy (fromStrict)-import Data.Foldable (for_)---- External imports: auxiliary-import Data.ByteString.Extra as B ( safeReadFile )---- Internal imports: auxiliary-import Command.Result ( Result (..) )-import Data.Location ( Location (..) )---- Internal imports: language ASTs, transformers-import Data.OgmaSpec (Spec)--import qualified Language.CoCoSpec.AbsCoCoSpec as CoCoSpec-import qualified Language.CoCoSpec.ParCoCoSpec as CoCoSpec ( myLexer,- pBoolSpec )--import Language.JSONSpec.Parser (JSONFormat (..), parseJSONSpec)--import qualified Language.SMV.AbsSMV as SMV-import qualified Language.SMV.ParSMV as SMV (myLexer, pBoolSpec)-import Language.SMV.Substitution (substituteBoolExpr)--import qualified Language.Trans.CoCoSpec2Copilot as CoCoSpec (boolSpec2Copilot)-import Language.Trans.SMV2Copilot as SMV (boolSpec2Copilot)-import Language.Trans.Spec2Copilot (spec2Copilot, specAnalyze)---- | Print the contents of a Copilot module that implements the Past-time TL--- formula in a FRET file.------ PRE: The file given is readable, contains a valid FRET file with a PT--- formula in the @ptExpanded@ key, the formula does not use any identifiers--- that exist in Copilot, or any of @prop@, @clock@, @ftp@. All identifiers--- used are valid C99 identifiers.-fretComponentSpec2Copilot :: FilePath -- ^ Path to a file containing a FRET- -- Component Specification- -> FRETComponentSpec2CopilotOptions- -- ^ Customization options- -- formula- -> IO (Result ErrorCode)-fretComponentSpec2Copilot fp options = do-- let functions = fretExprPair (fretCS2CopilotUseCoCoSpec options)-- copilot <- fretComponentSpec2Copilot' fp options functions-- let (mOutput, result) =- fretComponentSpec2CopilotResult options fp copilot-- for_ mOutput putStrLn- return result---- | Print the contents of a Copilot module that implements the Past-time TL--- formula in a FRET file, using a subexpression handler.------ PRE: The file given is readable, contains a valid FRET file with a PT--- formula in the @ptExpanded@ key, the formula does not use any identifiers--- that exist in Copilot, or any of @prop@, @clock@, @ftp@. All identifiers--- used are valid C99 identifiers.-fretComponentSpec2Copilot' :: FilePath- -> FRETComponentSpec2CopilotOptions- -> FRETExprPair- -> IO (Either String String)-fretComponentSpec2Copilot' fp options (FRETExprPair parse replace print) = do- let name = fretCS2CopilotFilename options- useCoCoSpec = fretCS2CopilotUseCoCoSpec options- typeMaps = fretTypeToCopilotTypeMapping options-- -- All of the following operations use Either to return error messages. The- -- use of the monadic bind to pass arguments from one function to the next- -- will cause the program to stop at the earliest error.- content <- B.safeReadFile fp- res <- case content of- Left s -> return $ Left s- Right b -> return $ parseJSONSpec parse (fretFormat useCoCoSpec) =<< eitherDecode b-- let copilot = spec2Copilot name typeMaps replace print =<< specAnalyze =<< res-- return copilot---- | Options used to customize the conversion of FRET Component Specifications--- to Copilot code.-data FRETComponentSpec2CopilotOptions = FRETComponentSpec2CopilotOptions- { fretCS2CopilotUseCoCoSpec :: Bool- , fretCS2CopilotIntType :: String- , fretCS2CopilotRealType :: String- , fretCS2CopilotFilename :: String- }---- * Error codes---- | Encoding of reasons why the command can fail.------ The error code used is 1 for user error.-type ErrorCode = Int---- | Error: the FRET Component Spec file cannot be read due to the file being--- unreadable or the format being incorrect.-ecFretCSError :: ErrorCode-ecFretCSError = 1---- * Result---- | Process the result of the transformation function.-fretComponentSpec2CopilotResult :: FRETComponentSpec2CopilotOptions- -> FilePath- -> Either String String- -> (Maybe String, Result ErrorCode)-fretComponentSpec2CopilotResult options fp result = case result of- Left msg -> (Nothing, Error ecFretCSError msg (LocationFile fp))- Right t -> (Just t, Success)---- * Parser---- | JSONPath selectors for a FRET file-fretFormat :: Bool -> JSONFormat-fretFormat useCoCoSpec = JSONFormat- { specInternalVars = Just "..Internal_variables[*]"- , specInternalVarId = ".name"- , specInternalVarExpr = ".assignmentCopilot"- , specInternalVarType = Just ".type"- , specExternalVars = Just "..Other_variables[*]"- , specExternalVarId = ".name"- , specExternalVarType = Just ".type"- , specRequirements = "..Requirements[*]"- , specRequirementId = ".name"- , specRequirementDesc = Just ".fretish"- , specRequirementExpr = if useCoCoSpec then ".CoCoSpecCode" else ".ptLTL"- }---- * Mapping of types from FRET to Copilot-fretTypeToCopilotTypeMapping :: FRETComponentSpec2CopilotOptions- -> [(String, String)]-fretTypeToCopilotTypeMapping options =- [ ("bool", "Bool")- , ("int", fretCS2CopilotIntType options)- , ("integer", fretCS2CopilotIntType options)- , ("real", fretCS2CopilotRealType options)- , ("string", "String")- ]---- * Handler for boolean expressions---- | Handler for boolean expressions that knows how to parse them, replace--- variables in them, and convert them to Copilot.-data FRETExprPair = forall a . FRETExprPair- { exprParse :: String -> Either String a- , exprReplace :: [(String, String)] -> a -> a- , exprPrint :: a -> String- }---- | Return a handler depending on whether it should be for CoCoSpec boolean--- expressions or for SMV boolean expressions.-fretExprPair :: Bool -> FRETExprPair-fretExprPair True = FRETExprPair (CoCoSpec.pBoolSpec . CoCoSpec.myLexer)- (\_ -> id)- (CoCoSpec.boolSpec2Copilot)-fretExprPair False = FRETExprPair (SMV.pBoolSpec . SMV.myLexer)- (substituteBoolExpr)- (SMV.boolSpec2Copilot)
− src/Command/FRETReqsDB2Copilot.hs
@@ -1,213 +0,0 @@-{-# LANGUAGE ExistentialQuantification #-}--- Copyright 2020 United States Government as represented by the Administrator--- of the National Aeronautics and Space Administration. All Rights Reserved.------ Disclaimers------ No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY--- OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT--- LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO--- SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A--- PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE--- SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF--- PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN--- ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR--- RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR--- ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER,--- GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING--- THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES--- IT "AS IS."------ Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST--- THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS--- ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN--- ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE,--- INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S--- USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE--- UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY--- PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY--- FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS--- AGREEMENT.------ | Transform a FRET requirements database containing a temporal logic--- specification into a Copilot specification.-module Command.FRETReqsDB2Copilot- ( fretReqsDB2Copilot- , FRETReqsDB2CopilotOptions(..)- , ErrorCode- )- where---- External imports-import Data.Aeson (eitherDecode)-import Data.Foldable (for_)-import Data.List (nub, (\\))---- External imports: auxiliary-import Data.ByteString.Extra as B ( safeReadFile )---- Internal imports: auxiliary-import Command.Result ( Result (..) )-import Data.Location ( Location (..) )---- Internal imports: Generic specification, parser.-import Data.OgmaSpec (ExternalVariableDef (..),- InternalVariableDef (..), Requirement (..),- Spec (..))-import Language.JSONSpec.Parser (JSONFormat (..), parseJSONSpec)---- Internal imports: language ASTs, transformers-import qualified Language.CoCoSpec.ParCoCoSpec as CoCoSpec (myLexer, pBoolSpec)-import qualified Language.SMV.ParSMV as SMV (myLexer, pBoolSpec)-import Language.SMV.Substitution (substituteBoolExpr)--import qualified Language.Trans.CoCoSpec2Copilot as CoCoSpec (boolSpec2Copilot,- boolSpecNames)-import Language.Trans.SMV2Copilot as SMV (boolSpec2Copilot,- boolSpecNames)-import Language.Trans.Spec2Copilot (spec2Copilot, specAnalyze)---- | Print the contents of a Copilot module that implements the Past-time TL--- formula in a FRET file.------ PRE: The file given is readable, contains a valid FRET file with a PT--- formula in the @ptExpanded@ key, the formula does not use any identifiers--- that exist in Copilot, or any of @prop@, @clock@, @ftp@. All identifiers--- used are valid C99 identifiers.-fretReqsDB2Copilot :: FilePath -- ^ Path to a file containing a FRET- -- Requirements Database- -> FRETReqsDB2CopilotOptions- -- ^ Customization options formula- -> IO (Result ErrorCode)-fretReqsDB2Copilot fp options = do-- let functions = fretExprPair (fretReqsDB2CopilotUseCoCoSpec options)-- copilot <- fretReqsDB2Copilot' fp options functions-- let (mOutput, result) =- fretReqsDB2CopilotResult options fp copilot-- for_ mOutput putStrLn- return result---- | Print the contents of a Copilot module that implements the Past-time TL--- formula in a FRET file, using a subexpression handler.------ PRE: The file given is readable, contains a valid FRET file with a PT--- formula in the @ptExpanded@ key, the formula does not use any identifiers--- that exist in Copilot, or any of @prop@, @clock@, @ftp@. All identifiers--- used are valid C99 identifiers.-fretReqsDB2Copilot' :: FilePath- -> FRETReqsDB2CopilotOptions- -> FRETExprPair- -> IO (Either String String)-fretReqsDB2Copilot' fp options (FRETExprPair parse replace showExpr ids) = do- let name = fretReqsDB2CopilotFilename options- useCoCoSpec = fretReqsDB2CopilotUseCoCoSpec options- typeMaps = [("", "_")]-- -- All of the following operations use Either to return error messages. The- -- use of the monadic bind to pass arguments from one function to the next- -- will cause the program to stop at the earliest error.- content <- B.safeReadFile fp- res <- case content of- Left s -> return $ Left s- Right b -> return $ parseJSONSpec parse (fretFormat useCoCoSpec) =<< eitherDecode b-- -- Complement the specification with any missing/implicit definitions- let res' = fmap (addMissingIdentifiers ids) res-- let copilot =- spec2Copilot name typeMaps replace showExpr =<< specAnalyze =<< res'-- return copilot---- | Options used to customize the conversion of FRET Requirements Database--- to Copilot code.-data FRETReqsDB2CopilotOptions = FRETReqsDB2CopilotOptions- { fretReqsDB2CopilotUseCoCoSpec :: Bool- , fretReqsDB2CopilotFilename :: String- }---- * Error codes---- | Encoding of reasons why the command can fail.------ The error code used is 1 for user error.-type ErrorCode = Int---- | Error: the FRET Requirements DB file cannot be read due to the file being--- unreadable or the format being incorrect.-ecFretReqsDBError :: ErrorCode-ecFretReqsDBError = 1---- * Result---- | Process the result of the transformation function.-fretReqsDB2CopilotResult :: FRETReqsDB2CopilotOptions- -> FilePath- -> Either String String- -> (Maybe String, Result ErrorCode)-fretReqsDB2CopilotResult _options fp result = case result of- Left msg -> (Nothing, Error ecFretReqsDBError msg (LocationFile fp))- Right t -> (Just t, Success)---- * Parser---- | JSONPath selectors for a FRET file-fretFormat :: Bool -> JSONFormat-fretFormat useCoCoSpec = JSONFormat- { specInternalVars = Nothing- , specInternalVarId = ""- , specInternalVarExpr = ""- , specInternalVarType = Nothing- , specExternalVars = Just ".semantics.variables..*.*"- , specExternalVarId = ""- , specExternalVarType = Nothing- , specRequirements = "$[:]"- , specRequirementId = ".reqid"- , specRequirementDesc = Just ".fulltext"- , specRequirementExpr = if useCoCoSpec then ".semantics.CoCoSpecCode" else ".semantics.ptExpanded"- }---- * Handler for boolean expressions---- | Handler for boolean expressions that knows how to parse them, replace--- variables in them, and convert them to Copilot.-data FRETExprPair = forall a . FRETExprPair- { exprParse :: String -> Either String a- , exprReplace :: [(String, String)] -> a -> a- , exprPrint :: a -> String- , exprIdents :: a -> [String]- }---- | Return a handler depending on whether it should be for CoCoSpec boolean--- expressions or for SMV boolean expressions.-fretExprPair :: Bool -> FRETExprPair-fretExprPair True = FRETExprPair (CoCoSpec.pBoolSpec . CoCoSpec.myLexer)- (\_ -> id)- (CoCoSpec.boolSpec2Copilot)- (CoCoSpec.boolSpecNames)-fretExprPair False = FRETExprPair (SMV.pBoolSpec . SMV.myLexer)- (substituteBoolExpr)- (SMV.boolSpec2Copilot)- (SMV.boolSpecNames)---- | Add to a spec external variables for all identifiers mentioned in--- expressions that are not defined anywhere.-addMissingIdentifiers :: (a -> [String]) -> Spec a -> Spec a-addMissingIdentifiers f s = s { externalVariables = vars' }- where- vars' = externalVariables s ++ newVars- newVars = map (\n -> ExternalVariableDef n "") newVarNames-- -- Names that are not defined anywhere- newVarNames = identifiers \\ existingNames-- -- Identifiers being mentioned in the requirements.- identifiers = nub $ concatMap (f . requirementExpr) (requirements s)-- -- Names that are defined in variables.- existingNames = map externalVariableName (externalVariables s)- ++ map internalVariableName (internalVariables s)
src/Command/ROSApp.hs view
@@ -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. --@@ -46,15 +47,16 @@ 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.List (find, intersperse) 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,@@ -73,6 +75,7 @@ -- | Generate a new ROS application connected to Copilot. rosApp :: FilePath -- ^ Target directory where the application -- 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.@@ -83,7 +86,7 @@ -- Copilot specification. The handlers are assumed -- to receive no arguments. -> IO (Result ErrorCode)-rosApp targetDir fretCSFile varNameFile varDBFile handlersFile =+rosApp targetDir mTemplateDir fretCSFile varNameFile varDBFile handlersFile = processResult $ do cs <- parseOptionalFRETCS fretCSFile vs <- parseOptionalVariablesFile varNameFile@@ -95,13 +98,15 @@ let varNames = fromMaybe (fretCSExtractExternalVariables cs) vs monitors = fromMaybe (fretCSExtractHandlers cs) rs - e <- liftIO $ rosApp' targetDir varNames varDB monitors+ e <- liftIO $ rosApp' targetDir mTemplateDir varNames varDB monitors liftEither e -- | Generate a new ROS application connected to Copilot, by copying the -- template and filling additional necessary files. rosApp' :: FilePath -- ^ Target directory where the -- application should be created.+ -> Maybe FilePath -- ^ Directory where the template+ -- is to be found. -> [String] -- ^ List of variable names -- (data sources). -> [(String, String, String, String)] -- ^ List of variables with their@@ -112,14 +117,14 @@ -- to the monitors (or -- requirements monitored). -> IO (Either ErrorTriplet ())-rosApp' targetDir varNames varDB monitors =+rosApp' targetDir mTemplateDir varNames varDB monitors = E.handle (return . Left . cannotCopyTemplate) $ do -- Obtain template dir- dataDir <- getDataDir- let templateDir = dataDir </> "templates" </> "ros"-- -- Expand template- copyDirectoryRecursive templateDir targetDir+ templateDir <- case mTemplateDir of+ Just x -> return x+ Nothing -> do+ dataDir <- getDataDir+ return $ dataDir </> "templates" </> "ros" let f n o@(oVars, oIds, oInfos, oDatas) = case variableMap varDB n of@@ -135,21 +140,29 @@ let (vars, ids, infos, datas) = foldr f ([], [], [], []) varNames - let rosFileName =- targetDir </> "copilot" </> "src" </> "copilot_monitor.cpp"- rosFileContents =- unlines $- rosMonitorContents varNames vars ids infos datas monitors+ let (variablesS, msgSubscriptionS, msgPublisherS,+ msgHandlerInClassS, msgCallbacks, msgSubscriptionDeclrs,+ msgPublisherDeclrs, msgHandlerGlobalS) =+ rosMonitorComponents varNames vars ids infos datas monitors - writeFile rosFileName rosFileContents+ (logMsgSubscriptionS, logMsgCallbacks, logMsgSubscriptionDeclrs) =+ rosLoggerComponents varNames vars ids infos datas monitors - let rosFileName =- targetDir </> "copilot" </> "src" </> "copilot_logger.cpp"- rosFileContents =- unlines $- rosLoggerContents varNames vars ids infos datas monitors+ subst = object $+ [ "variablesS" .= pack variablesS+ , "msgSubscriptionS" .= pack msgSubscriptionS+ , "msgPublisherS" .= pack msgPublisherS+ , "msgHandlerInClassS" .= pack msgHandlerInClassS+ , "msgCallbacks" .= pack msgCallbacks+ , "msgSubscriptionDeclrs" .= pack msgSubscriptionDeclrs+ , "msgPublisherDeclrs" .= pack msgPublisherDeclrs+ , "msgHandlerGlobalS" .= pack msgHandlerGlobalS+ , "logMsgSubscriptionS" .= pack logMsgSubscriptionS+ , "logMsgCallbacks" .= pack logMsgCallbacks+ , "logMsgSubscriptionDeclrs" .= pack logMsgSubscriptionDeclrs+ ] - writeFile rosFileName rosFileContents+ copyTemplate templateDir subst targetDir return $ Right () @@ -301,52 +314,24 @@ -- * ROS apps content -- | Return the contents of the main ROS application.-rosMonitorContents :: [String] -- Variables- -> [VarDecl]- -> [MsgInfoId]- -> [MsgInfo]- -> [MsgData]- -> [String] -- Monitors- -> [String]-rosMonitorContents varNames variables msgIds msgNames msgDatas monitors =- [ "#include <functional>"- , "#include <memory>"- , ""- , "#include \"rclcpp/rclcpp.hpp\""- , ""- , typeIncludes- , copilotIncludes- , "using std::placeholders::_1;"- , ""- , variablesS- , "class CopilotRV : public rclcpp::Node {"- , " public:"- , " CopilotRV() : Node(\"copilotrv\") {"+rosMonitorComponents+ :: [String] -- Variables+ -> [VarDecl]+ -> [MsgInfoId]+ -> [MsgInfo]+ -> [MsgData]+ -> [String] -- Monitors+ -> (String, String, String, String, String, String, String, String)+rosMonitorComponents varNames variables msgIds msgNames msgDatas monitors =+ ( variablesS , msgSubscriptionS , msgPublisherS- , " }"- , "" , msgHandlerInClassS- , " // Needed so we can report messages to the log."- , " static CopilotRV& getInstance() {"- , " static CopilotRV instance;"- , " return instance;"- , " }"- , ""- , " private:" , msgCallbacks , msgSubscriptionDeclrs , msgPublisherDeclrs- , "};"- , "" , msgHandlerGlobalS- , "int main(int argc, char* argv[]) {"- , " rclcpp::init(argc, argv);"- , " rclcpp::spin(std::make_shared<CopilotRV>());"- , " rclcpp::shutdown();"- , " return 0;"- , "}"- ]+ ) where @@ -369,27 +354,6 @@ publisher = monitor ++ "_publisher_" - typeIncludes = unlines- [ "#include \"std_msgs/msg/bool.hpp\""- , "#include \"std_msgs/msg/empty.hpp\""- , "#include \"std_msgs/msg/u_int8.hpp\""- , "#include \"std_msgs/msg/u_int16.hpp\""- , "#include \"std_msgs/msg/u_int32.hpp\""- , "#include \"std_msgs/msg/u_int64.hpp\""- , "#include \"std_msgs/msg/int8.hpp\""- , "#include \"std_msgs/msg/int16.hpp\""- , "#include \"std_msgs/msg/int32.hpp\""- , "#include \"std_msgs/msg/int64.hpp\""- , "#include \"std_msgs/msg/float32.hpp\""- , "#include \"std_msgs/msg/float64.hpp\""- , "#include <cstdint>"- ]-- copilotIncludes = unlines- [ "#include \"monitor.h\""- , "#include \"monitor.c\""- ]- variablesS = unlines $ map toVarDecl variables toVarDecl varDecl = varDeclType' varDecl ++ " " ++ varDeclName varDecl ++ ";"@@ -515,49 +479,17 @@ publisher = nm ++ "_publisher_" -- | Return the contents of the logger ROS application.-rosLoggerContents :: [String] -- Variables- -> [VarDecl]- -> [MsgInfoId]- -> [MsgInfo]- -> [MsgData]- -> [String] -- Monitors- -> [String]-rosLoggerContents varNames variables msgIds msgNames msgDatas monitors =- rosFileContents+rosLoggerComponents :: [String] -- Variables+ -> [VarDecl]+ -> [MsgInfoId]+ -> [MsgInfo]+ -> [MsgData]+ -> [String] -- Monitors+ -> (String, String, String)+rosLoggerComponents varNames variables msgIds msgNames msgDatas monitors =+ (msgSubscriptionS, msgCallbacks, msgSubscriptionDeclrs) where-- rosFileContents =- [ "#include <functional>"- , "#include <memory>"- , ""- , "#include \"rclcpp/rclcpp.hpp\""- , ""- , typeIncludes- , "using std::placeholders::_1;"- , ""- , "class CopilotLogger : public rclcpp::Node {"- , " public:"- , " CopilotLogger() : Node(\"copilotlogger\") {"- , msgSubscriptionS- , " }"- , ""- , " private:"- , msgCallbacks- , msgSubscriptionDeclrs- , "};"- , ""- , "int main(int argc, char* argv[]) {"- , " rclcpp::init(argc, argv);"- , " rclcpp::spin(std::make_shared<CopilotLogger>());"- , " rclcpp::shutdown();"- , " return 0;"- , "}"- ]-- typeIncludes = unlines- [ "#include \"std_msgs/msg/empty.hpp\""- ] msgSubscriptionS = unlines $ concat
+ src/Command/Standalone.hs view
@@ -0,0 +1,223 @@+{-# LANGUAGE ExistentialQuantification #-}+-- Copyright 2020 United States Government as represented by the Administrator+-- of the National Aeronautics and Space Administration. All Rights Reserved.+--+-- Disclaimers+--+-- No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY+-- OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT+-- LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO+-- SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A+-- PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE+-- SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF+-- PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN+-- ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR+-- RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR+-- ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER,+-- GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING+-- THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES+-- IT "AS IS."+--+-- Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST+-- THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS+-- ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN+-- ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE,+-- INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S+-- USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE+-- UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY+-- PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY+-- FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS+-- AGREEMENT.+--+-- | Transform a specification into a standalone Copilot specification.+module Command.Standalone+ ( standalone+ , StandaloneOptions(..)+ , ErrorCode+ )+ where++-- External imports+import Data.Aeson (decode, eitherDecode)+import Data.ByteString.Lazy (fromStrict, pack)+import Data.Foldable (for_)+import Data.List (nub, (\\))+import Data.Maybe (fromMaybe)+import System.FilePath ((</>))++-- External imports: auxiliary+import Data.ByteString.Extra as B ( safeReadFile )++-- Internal imports: auxiliary+import Command.Result (Result (..))+import Data.Location (Location (..))+import Paths_ogma_core (getDataDir)++-- Internal imports: language ASTs, transformers+import Data.OgmaSpec (ExternalVariableDef (..), InternalVariableDef (..),+ Requirement (..), Spec (..))+import Language.JSONSpec.Parser (JSONFormat (..), parseJSONSpec)++-- Internal imports: language ASTs, transformers+import qualified Language.CoCoSpec.AbsCoCoSpec as CoCoSpec+import qualified Language.CoCoSpec.ParCoCoSpec as CoCoSpec ( myLexer,+ pBoolSpec )++import qualified Language.SMV.AbsSMV as SMV+import qualified Language.SMV.ParSMV as SMV (myLexer, pBoolSpec)+import Language.SMV.Substitution (substituteBoolExpr)++import qualified Language.Trans.CoCoSpec2Copilot as CoCoSpec (boolSpec2Copilot,+ boolSpecNames)+import Language.Trans.SMV2Copilot as SMV (boolSpec2Copilot,+ boolSpecNames)+import Language.Trans.Spec2Copilot (spec2Copilot, specAnalyze)++-- | Print the contents of a Copilot module that implements the spec in an+-- input file.+--+-- PRE: The file given is readable, contains a valid file with recognizable+-- format, the formulas in the file do not use any identifiers that exist in+-- Copilot, or any of @prop@, @clock@, @ftp@, @notPreviousNot@. All identifiers+-- used are valid C99 identifiers.+standalone :: FilePath -- ^ Path to a file containing a specification+ -> StandaloneOptions -- ^ Customization options+ -> IO (Result ErrorCode)+standalone fp options = do++ let functions = exprPair (standalonePropFormat options)++ copilot <- standalone' fp options functions++ let (mOutput, result) = standaloneResult options fp copilot++ for_ mOutput putStrLn+ return result++-- | Print the contents of a Copilot module that implements the spec in an+-- input file, using a subexpression handler.+--+-- PRE: The file given is readable, contains a valid file with recognizable+-- format, the formulas in the file do not use any identifiers that exist in+-- Copilot, or any of @prop@, @clock@, @ftp@, @notPreviousNot@. All identifiers+-- used are valid C99 identifiers.+standalone' :: FilePath+ -> StandaloneOptions+ -> ExprPair+ -> IO (Either String String)+standalone' fp options (ExprPair parse replace print ids) = do+ let name = standaloneFilename options+ typeMaps = typeToCopilotTypeMapping options++ -- Obtain format file+ dataDir <- getDataDir+ let formatFile =+ dataDir </> "data" </> "formats" </>+ (standaloneFormat options ++ "_" ++ standalonePropFormat options)++ format <- read <$> readFile formatFile++ -- All of the following operations use Either to return error messages. The+ -- use of the monadic bind to pass arguments from one function to the next+ -- will cause the program to stop at the earliest error.+ content <- B.safeReadFile fp+ res <- case content of+ Left s -> return $ Left s+ Right b -> return $ parseJSONSpec parse format =<< eitherDecode b++ -- Complement the specification with any missing/implicit definitions+ let res' = fmap (addMissingIdentifiers ids) res++ let copilot = spec2Copilot name typeMaps replace print =<< specAnalyze =<< res'++ return copilot++-- | Options used to customize the conversion of specifications to Copilot+-- code.+data StandaloneOptions = StandaloneOptions+ { standaloneFormat :: String+ , standalonePropFormat :: String+ , standaloneTypeMapping :: [(String, String)]+ , standaloneFilename :: String+ }++-- * Error codes++-- | Encoding of reasons why the command can fail.+--+-- The error code used is 1 for user error.+type ErrorCode = Int++-- | Error: the input file cannot be read due to it being unreadable or the+-- format being incorrect.+ecStandaloneError :: ErrorCode+ecStandaloneError = 1++-- * Result++-- | Process the result of the transformation function.+standaloneResult :: StandaloneOptions+ -> FilePath+ -> Either String String+ -> (Maybe String, Result ErrorCode)+standaloneResult options fp result = case result of+ Left msg -> (Nothing, Error ecStandaloneError msg (LocationFile fp))+ Right t -> (Just t, Success)++-- * Mapping of types from input format to Copilot+typeToCopilotTypeMapping :: StandaloneOptions -> [(String, String)]+typeToCopilotTypeMapping options =+ [ ("bool", "Bool")+ , ("int", intType)+ , ("integer", intType)+ , ("real", realType)+ , ("string", "String")+ , ("", "_")+ ]+ where+ intType = fromMaybe "Int64" $ lookup "int" types+ realType = fromMaybe "Float" $ lookup "real" types++ types = standaloneTypeMapping options++-- * Handler for boolean expressions++-- | Handler for boolean expressions that knows how to parse them, replace+-- variables in them, and convert them to Copilot.+data ExprPair = forall a . ExprPair+ { exprParse :: String -> Either String a+ , exprReplace :: [(String, String)] -> a -> a+ , exprPrint :: a -> String+ , exprIdents :: a -> [String]+ }++-- | Return a handler depending on whether it should be for CoCoSpec boolean+-- expressions or for SMV boolean expressions. We default to SMV if not format+-- is given.+exprPair :: String -> ExprPair+exprPair "cocospec" = ExprPair (CoCoSpec.pBoolSpec . CoCoSpec.myLexer)+ (\_ -> id)+ (CoCoSpec.boolSpec2Copilot)+ (CoCoSpec.boolSpecNames)+exprPair _ = ExprPair (SMV.pBoolSpec . SMV.myLexer)+ (substituteBoolExpr)+ (SMV.boolSpec2Copilot)+ (SMV.boolSpecNames)++-- | Add to a spec external variables for all identifiers mentioned in+-- expressions that are not defined anywhere.+addMissingIdentifiers :: (a -> [String]) -> Spec a -> Spec a+addMissingIdentifiers f s = s { externalVariables = vars' }+ where+ vars' = externalVariables s ++ newVars+ newVars = map (\n -> ExternalVariableDef n "") newVarNames++ -- Names that are not defined anywhere+ newVarNames = identifiers \\ existingNames++ -- Identifiers being mentioned in the requirements.+ identifiers = nub $ concatMap (f . requirementExpr) (requirements s)++ -- Names that are defined in variables.+ existingNames = map externalVariableName (externalVariables s)+ ++ map internalVariableName (internalVariables s)
src/Language/Trans/SMV2Copilot.hs view
@@ -29,7 +29,7 @@ -- AGREEMENT. -- --- | Transform a FRET TL specification into a Copilot specification.+-- | Transform an SMV TL specification into a Copilot specification. -- -- Normally, this module would be implemented as a conversion between ASTs, -- but we want to add comments to the generated code, which are not@@ -40,7 +40,7 @@ Ident (..), MultOp (..), NumExpr (..), Number (..), Op1Name (..), OpOne (..), OpTwo (..), OrdOp (..)) --- | Return the Copilot representation of a FRET BoolSpec.+-- | Return the Copilot representation of an SMV BoolSpec. -- -- This function returns the temporal property only. The string does not -- contain any top-level names, any imports, or auxiliary definitions that may@@ -96,7 +96,7 @@ ++ " " ++ boolSpec2Copilot spec2 ++ ")" --- | Return the Copilot representation of a FRET boolean constant.+-- | Return the Copilot representation of an SMV boolean constant. const2Copilot :: BoolConst -> String const2Copilot BoolConstTrue = "true" const2Copilot BoolConstFalse = "false"@@ -119,17 +119,17 @@ ++ numExpr2Copilot y ++ ")" --- | Return the Copilot representation of a FRET additive operator.+-- | Return the Copilot representation of an SMV additive operator. additiveOp2Copilot :: AdditiveOp -> String additiveOp2Copilot OpPlus = "+" additiveOp2Copilot OpMinus = "-" --- | Return the Copilot representation of a FRET multiplicative operator.+-- | Return the Copilot representation of an SMV multiplicative operator. multOp2Copilot :: MultOp -> String multOp2Copilot OpTimes = "*" multOp2Copilot OpDiv = "/" --- | Return the Copilot representation of a FRET comparison operator.+-- | Return the Copilot representation of an SMV comparison operator. ordOp2Copilot :: OrdOp -> String ordOp2Copilot OrdOpLT = "<" ordOp2Copilot OrdOpLE = "<="@@ -138,13 +138,13 @@ ordOp2Copilot OrdOpGT = ">" ordOp2Copilot OrdOpGE = ">=" --- | Return the Copilot representation of a unary logical FRET operator.+-- | Return the Copilot representation of a unary logical SMV operator. opOne2Copilot :: OpOne -> String opOne2Copilot (Op1Alone x) = opOneAlone2Copilot x opOne2Copilot (Op1MTL x op v) = opOneMTL2Copilot x op v opOne2Copilot (Op1MTLRange op mn mx) = opOneMTLRange2Copilot op mn mx --- | Return the Copilot representation of a unary logical non-MTL FRET+-- | Return the Copilot representation of a unary logical non-MTL SMV -- operator. opOneAlone2Copilot :: Op1Name -> String opOneAlone2Copilot Op1Pre = "pre"@@ -156,7 +156,7 @@ opOneAlone2Copilot Op1Hist = "PTLTL.alwaysBeen" opOneAlone2Copilot Op1O = "PTLTL.eventuallyPrev" --- | Return the Copilot representation of a unary logical MTL FRET operator.+-- | Return the Copilot representation of a unary logical MTL SMV operator. opOneMTL2Copilot :: Op1Name -> OrdOp -> Number -> String opOneMTL2Copilot operator _comparison number = opOneMTL2Copilot' operator ++ " " ++ show (0 :: Int)@@ -164,7 +164,7 @@ ++ " " ++ "clock" ++ " " ++ show (1 :: Int) --- | Return the Copilot representation of a unary logical MTL FRET operator+-- | Return the Copilot representation of a unary logical MTL SMV operator -- that uses an explicit range. opOneMTLRange2Copilot :: Op1Name -> Number -> Number -> String opOneMTLRange2Copilot operator mn mx =@@ -173,7 +173,7 @@ ++ " " ++ "clock" ++ " " ++ show (1 :: Int) --- | Return the Copilot representation of a unary logical possibly MTL FRET+-- | Return the Copilot representation of a unary logical possibly MTL SMV -- operator. opOneMTL2Copilot' :: Op1Name -> String opOneMTL2Copilot' Op1Pre = "pre"@@ -185,11 +185,11 @@ opOneMTL2Copilot' Op1Hist = "MTL.alwaysBeen" opOneMTL2Copilot' Op1O = "MTL.eventuallyPrev" --- | Return the Copilot representation of a FRET number.+-- | Return the Copilot representation of an SMV number. number2Copilot :: Number -> String number2Copilot (NumberInt n) = show n --- | Return the Copilot representation of a binary logical non-MTL FRET+-- | Return the Copilot representation of a binary logical non-MTL SMV -- operator. opTwo2Copilot :: OpTwo -> String opTwo2Copilot Op2S = "`since`"@@ -197,7 +197,7 @@ opTwo2Copilot Op2V = "`releases`" opTwo2Copilot Op2U = "`until`" --- | Return the Copilot representation of a FRET identifier.+-- | Return the Copilot representation of an SMV identifier. ident2Copilot :: Ident -> String ident2Copilot (Ident i) = i
src/Language/Trans/Spec2Copilot.hs view
@@ -67,6 +67,7 @@ , ftp , pre , tpre+ , notPreviousNot , copilotSpec , main' ]@@ -194,12 +195,19 @@ , "tpre = ([True] ++)" ] + -- Auxiliary streams: notPreviousNot+ notPreviousNot :: [String]+ notPreviousNot = [ ""+ , "notPreviousNot :: Stream Bool -> Stream Bool"+ , "notPreviousNot = not . PTLTL.previous . not"+ ]+ -- Main specification copilotSpec :: [String] copilotSpec = [ ""- , "-- | Complete specification. Calls the C function void "- ++ " handler(); when"- , "-- the property is violated."+ , "-- | Complete specification. Calls C handler functions"+ ++ " when"+ , "-- properties are violated." , "spec :: Spec" , "spec = do" ]
templates/copilot-cfs/fsw/src/copilot_cfs.c view
@@ -19,7 +19,7 @@ #include "Icarous_msgids.h" #include "Icarous_msg.h" -position_t my_position;+{{variablesS}} void split(void); void step(void); @@ -98,7 +98,7 @@ ** messages */ CFE_SB_CreatePipe(&COPILOT_CommandPipe, COPILOT_PIPE_DEPTH,"COPILOT_CMD_PIPE");- CFE_SB_Subscribe(ICAROUS_POSITION_MID, COPILOT_CommandPipe);+{{msgSubscriptionsS}} CFE_EVS_SendEvent (COPILOT_STARTUP_INF_EID, CFE_EVS_INFORMATION, "COPILOT App Initialized. Version %d.%d.%d.%d",@@ -125,9 +125,7 @@ switch (MsgId) {- case ICAROUS_POSITION_MID:- COPILOT_ProcessIcarousPosition();- break;+{{ msgCasesS }} default: COPILOT_HkTelemetryPkt.copilot_command_error_count++; CFE_EVS_SendEvent(COPILOT_COMMAND_ERR_EID,CFE_EVS_ERROR,@@ -139,18 +137,7 @@ } /* End COPILOT_ProcessCommandPacket */ -/**- * Make ICAROUS data available to Copilot and run monitors.- */-void COPILOT_ProcessIcarousPosition(void)-{- position_t* msg;- msg = (position_t*) COPILOTMsgPtr;- my_position = *msg;-- // Run all copilot monitors.- step();-}+{{msgHandlerS}} /** * Report copilot property violations.
templates/ros/Dockerfile view
@@ -1,4 +1,4 @@-FROM osrf/space-ros:latest+FROM osrf/space-ros:humble-2024.10.0 ARG USER=spaceros-user ARG PACKAGE_PATH=/home/${USER}/monitors@@ -12,5 +12,5 @@ SHELL ["/bin/bash", "-c"] WORKDIR ${PACKAGE_PATH}-RUN source ${ROS_PATH}/install/setup.bash && \+RUN source /opt/spaceros/install/setup.bash && \ colcon build
− templates/ros/copilot/src/.keep
+ templates/ros/copilot/src/copilot_logger.cpp view
@@ -0,0 +1,27 @@+#include <functional>+#include <memory>++#include "rclcpp/rclcpp.hpp"++#include "std_msgs/msg/empty.hpp"+using std::placeholders::_1;++class CopilotLogger : public rclcpp::Node {+ public:+ CopilotLogger() : Node("copilotlogger") {+{{{logMsgSubscriptionS}}}+ }++ private:+{{{logMsgCallbacks}}}+{{{logMsgSubscriptionDeclrs}}}+};++int main(int argc, char* argv[]) {+ rclcpp::init(argc, argv);+ rclcpp::spin(std::make_shared<CopilotLogger>());+ rclcpp::shutdown();+ return 0;+}++
+ templates/ros/copilot/src/copilot_monitor.cpp view
@@ -0,0 +1,52 @@+#include <functional>+#include <memory>++#include "rclcpp/rclcpp.hpp"++#include "std_msgs/msg/bool.hpp"+#include "std_msgs/msg/empty.hpp"+#include "std_msgs/msg/u_int8.hpp"+#include "std_msgs/msg/u_int16.hpp"+#include "std_msgs/msg/u_int32.hpp"+#include "std_msgs/msg/u_int64.hpp"+#include "std_msgs/msg/int8.hpp"+#include "std_msgs/msg/int16.hpp"+#include "std_msgs/msg/int32.hpp"+#include "std_msgs/msg/int64.hpp"+#include "std_msgs/msg/float32.hpp"+#include "std_msgs/msg/float64.hpp"+#include <cstdint>+#include "monitor.h"+#include "monitor.c"++using std::placeholders::_1;++{{{variablesS}}}+class CopilotRV : public rclcpp::Node {+ public:+ CopilotRV() : Node("copilotrv") {+{{{msgSubscriptionS}}}+{{{msgPublisherS}}}+ }++{{{msgHandlerInClassS}}}+ // Needed so we can report messages to the log.+ static CopilotRV& getInstance() {+ static CopilotRV instance;+ return instance;+ }++ private:+{{{msgCallbacks}}}+{{{msgSubscriptionDeclrs}}}+{{{msgPublisherDeclrs}}}+};++{{{msgHandlerGlobalS}}}+int main(int argc, char* argv[]) {+ rclcpp::init(argc, argv);+ rclcpp::spin(std::make_shared<CopilotRV>());+ rclcpp::shutdown();+ return 0;+}+
tests/Main.hs view
@@ -7,14 +7,9 @@ import Test.HUnit ( assertBool ) -- Internal imports-import Command.CStructs2Copilot ( cstructs2Copilot )-import Command.FRETComponentSpec2Copilot ( FRETComponentSpec2CopilotOptions (..)- , fretComponentSpec2Copilot- )-import Command.FRETReqsDB2Copilot ( FRETReqsDB2CopilotOptions (..)- , fretReqsDB2Copilot- )-import Command.Result ( isSuccess )+import Command.CStructs2Copilot (cstructs2Copilot)+import Command.Result (isSuccess)+import Command.Standalone (StandaloneOptions (..), standalone) -- | Run all unit tests on ogma-core. main :: IO ()@@ -106,13 +101,13 @@ -> Bool -> IO () testFretComponentSpec2Copilot file success = do- let opts = FRETComponentSpec2CopilotOptions- { fretCS2CopilotUseCoCoSpec = False- , fretCS2CopilotIntType = "Int64"- , fretCS2CopilotRealType = "Float"- , fretCS2CopilotFilename = "fret"+ let opts = StandaloneOptions+ { standaloneFormat = "fcs"+ , standalonePropFormat = "smv"+ , standaloneTypeMapping = [("int", "Int64"), ("real", "Float")]+ , standaloneFilename = "fret" }- result <- fretComponentSpec2Copilot file opts+ result <- standalone file opts -- True if success is expected and detected, or niether expected nor -- detected.@@ -138,11 +133,13 @@ -> Bool -> IO () testFretReqsDBCoCoSpec2Copilot file success = do- let opts = FRETReqsDB2CopilotOptions- { fretReqsDB2CopilotUseCoCoSpec = True- , fretReqsDB2CopilotFilename = "fret"+ let opts = StandaloneOptions+ { standaloneFormat = "fdb"+ , standalonePropFormat = "cocospec"+ , standaloneTypeMapping = []+ , standaloneFilename = "fret" }- result <- fretReqsDB2Copilot file opts+ result <- standalone file opts -- True if success is expected and detected, or niether expected nor -- detected.