2024-10-08 17:40:06 -07:00

1054 lines
34 KiB
Scala

package fpp.compiler.codegen
import fpp.compiler.analysis._
import fpp.compiler.ast._
import fpp.compiler.codegen._
import fpp.compiler.util._
/** Writes out C++ for component definitions */
case class ComponentCppWriter (
s: CppWriterState,
aNode: Ast.Annotated[AstNode[Ast.DefComponent]]
) extends ComponentCppWriterUtils(s, aNode) {
private val symbol = componentSymbol
private val data = componentData
private val name = componentName
private val className = componentClassName
private val namespaceIdentList = componentNamespaceIdentList
private val fileName = ComputeCppFiles.FileNames.getComponent(name)
private val dpWriter = ComponentDataProducts(s, aNode)
private val portWriter = ComponentPorts(s, aNode)
private val cmdWriter = ComponentCommands(s, aNode)
private val internalPortWriter = ComponentInternalPort(s, aNode)
private val eventWriter = ComponentEvents(s, aNode)
private val tlmWriter = ComponentTelemetry(s, aNode)
private val paramWriter = ComponentParameters(s, aNode)
private val externalStateMachineWriter = ComponentExternalStateMachines(s, aNode)
private val stateMachineWriter = ComponentStateMachines(s, aNode)
private val kindStr = data.kind match {
case Ast.ComponentKind.Active => "Active"
case Ast.ComponentKind.Passive => "Passive"
case Ast.ComponentKind.Queued => "Queued"
}
private val baseClassName = s"${kindStr}ComponentBase"
private val exitConstantName = s"${name.toUpperCase}_COMPONENT_EXIT"
private def writeIncludeDirectives: List[String] = {
val Right(a) = UsedSymbols.defComponentAnnotatedNode(s.a, aNode)
s.writeIncludeDirectives(a.usedSymbolSet)
}
def write: CppDoc = {
val includeGuard = s.includeGuardFromQualifiedName(symbol, fileName)
CppWriter.createCppDoc(
s"$name component base class",
fileName,
includeGuard,
getMembers,
s.toolName
)
}
private def getMembers: List[CppDoc.Member] = {
val hppIncludes = getHppIncludes
val cppIncludes = getCppIncludes
val externalSmInterfaces = externalStateMachineWriter.getSmInterfaces
val cls = classMember(
Some(
addSeparatedString(
s"\\class $className\n\\brief Auto-generated base for $name component",
AnnotationCppWriter.asStringOpt(aNode)
)
),
className,
Some(s"public Fw::$baseClassName$externalSmInterfaces"),
getClassMembers
)
List(
List(hppIncludes, cppIncludes),
getStaticAssertion,
wrapInNamespaces(namespaceIdentList, List(cls))
).flatten
}
private def getHppIncludes: CppDoc.Member = {
// Conditional headers
val dpHeaders =
guardedList (hasDataProducts) (List("Fw/Dp/DpContainer.hpp"))
val mutexHeaders =
guardedList (hasGuardedInputPorts || hasGuardedCommands || hasParameters) (
List("Os/Mutex.hpp")
)
val cmdStrHeaders =
guardedList (hasCommands || hasParameters) (List("Fw/Cmd/CmdString.hpp"))
val tlmStrHeaders =
guardedList (hasChannels) (List("Fw/Tlm/TlmString.hpp"))
val prmStrHeaders =
guardedList (hasParameters) (List("Fw/Prm/PrmString.hpp"))
val logStrHeaders =
guardedList (hasEvents) (List("Fw/Log/LogString.hpp"))
val internalStrHeaders =
guardedList (hasInternalPorts) (List("Fw/Types/InternalInterfaceString.hpp"))
val standardHeaders = List.concat(
List(
"FpConfig.hpp",
"Fw/Port/InputSerializePort.hpp",
"Fw/Port/OutputSerializePort.hpp",
"Fw/Comp/ActiveComponentBase.hpp"
),
dpHeaders,
mutexHeaders,
cmdStrHeaders,
tlmStrHeaders,
prmStrHeaders,
logStrHeaders,
internalStrHeaders
).map(CppWriter.headerString)
val symbolHeaders = writeIncludeDirectives
val headers = standardHeaders ++ symbolHeaders
linesMember(addBlankPrefix(headers.sorted.flatMap({
case s: "#include \"Fw/Log/LogTextPortAc.hpp\"" =>
lines(
s"""|#if FW_ENABLE_TEXT_LOGGING == 1
|$s
|#endif
|""".stripMargin
)
case s => lines(s)
})))
}
private def getCppIncludes: CppDoc.Member = {
val userHeaders = List(
"Fw/Types/Assert.hpp",
"Fw/Types/ExternalString.hpp",
"Fw/Types/String.hpp",
s"${s.getRelativePath(fileName).toString}.hpp"
).sorted.map(CppWriter.headerString).flatMap({
case s: "#include \"Fw/Types/String.hpp\"" =>
lines(
s"""|#if FW_ENABLE_TEXT_LOGGING
|$s
|#endif
|""".stripMargin
)
case s => lines(s)
})
linesMember(Line.blank :: userHeaders, CppDoc.Lines.Cpp)
}
private def getStaticAssertion: List[CppDoc.Member] = {
if serialInputPorts.isEmpty && serialOutputPorts.isEmpty then Nil
else List(
linesMember(
Line.blank :: lines(
s"""|static_assert(
| FW_PORT_SERIALIZATION == 1,
| \"$name component requires serialization\"
|);
|"""
)
)
)
}
private def getClassMembers: List[CppDoc.Class.Member] = {
List.concat(
// Friend classes
getFriendClassMembers,
// Constants
getConstantMembers,
// Anonymous namespace members
getAnonymousNamespaceMembers,
// Types
dpWriter.getTypeMembers,
stateMachineWriter.getTypeMembers,
// Public function members
getPublicComponentFunctionMembers,
portWriter.getPublicFunctionMembers,
cmdWriter.getPublicFunctionMembers,
paramWriter.getPublicFunctionMembers,
// Protected function members
getProtectedComponentFunctionMembers,
portWriter.getProtectedFunctionMembers,
internalPortWriter.getFunctionMembers,
stateMachineWriter.getProtectedFunctionMembers,
cmdWriter.getProtectedFunctionMembers,
eventWriter.getFunctionMembers,
tlmWriter.getFunctionMembers,
paramWriter.getProtectedFunctionMembers,
dpWriter.getProtectedDpFunctionMembers,
dpWriter.getVirtualFunctionMembers,
getTimeFunctionMember,
getMutexOperationMembers,
// Protected/private function members
getDispatchFunctionMember,
// Private function members
portWriter.getPrivateFunctionMembers,
stateMachineWriter.getPrivateFunctionMembers,
paramWriter.getPrivateFunctionMembers,
dpWriter.getPrivateDpFunctionMembers,
// Member variables
portWriter.getVariableMembers,
eventWriter.getVariableMembers,
tlmWriter.getVariableMembers,
paramWriter.getVariableMembers,
stateMachineWriter.getVariableMembers,
getMsgSizeVariableMember,
getMutexVariableMembers,
)
}
private def getConstantMembers: List[CppDoc.Class.Member] = {
val constants = List(
portWriter.getConstantMembers,
cmdWriter.getConstantMembers,
eventWriter.getConstantMembers,
tlmWriter.getConstantMembers,
paramWriter.getConstantMembers,
dpWriter.getConstantMembers,
stateMachineWriter.getConstantMembers
).flatten
if constants.isEmpty then Nil
else List(
List(
linesClassMember(
List(
CppDocHppWriter.writeAccessTag("PROTECTED"),
CppDocWriter.writeBannerComment(
"Constants"
),
).flatten
)
),
constants
).flatten
}
private def getFriendClassMembers: List[CppDoc.Class.Member] = {
List(
linesClassMember(
List(
CppDocWriter.writeBannerComment(
"Friend classes"
),
lines(
s"""|
|//! Friend class for white-box testing
|friend class ${className}Friend;
|"""
)
).flatten
)
)
}
private def getAnonymousNamespaceMembers: List[CppDoc.Class.Member] =
data.kind match {
case Ast.ComponentKind.Passive => Nil
case _ => {
val buffUnion = getBuffUnion
List(
linesClassMember(
Line.blank :: wrapInAnonymousNamespace(
intersperseBlankLines(
List(
stateMachineWriter.getAnonymousNamespaceLines,
getMsgTypeEnum,
buffUnion,
getComponentIpcSerializableBufferClass(buffUnion)
)
)
),
CppDoc.Lines.Cpp
)
)
}
}
private def getMsgTypeEnum: List[Line] = {
wrapInScope(
"enum MsgTypeEnum {",
List.concat(
lines(s"$exitConstantName = Fw::ActiveComponentBase::ACTIVE_COMPONENT_EXIT"),
dataProductAsyncInputPorts.map(portCppConstantName),
typedAsyncInputPorts.map(portCppConstantName),
serialAsyncInputPorts.map(portCppConstantName),
asyncCmds.map((_, cmd) => commandCppConstantName(cmd)),
internalPorts.map(internalPortCppConstantName),
guardedList (hasExternalStateMachineInstances) (List(externalStateMachineCppConstantName)),
guardedList (hasInternalStateMachineInstances) (List(internalStateMachineMsgType))
).map(s => line(s"$s,")),
"};"
)
}
/** Generates a union type that lets the compiler calculate
* the max serialized size of any list of arguments that goes
* on the queue */
private def getBuffUnion: List[Line] = {
// Collect the serialized sizes of all the async port arguments
// For each one, add a byte array of that size as a member
val internalPortsWithFormalParams: List[PortInstance.Internal] =
internalPorts.filter(p => getPortParams(p).size > 0)
val asyncInputPortsWithFormalParams =
(dataProductAsyncInputPorts ++ typedAsyncInputPorts).
filter(p => getPortParams(p).size > 0)
val members = List.concat(
// Data product and typed async input ports
asyncInputPortsWithFormalParams.flatMap(p => {
val portName = p.getUnqualifiedName
val portTypeName = getQualifiedPortTypeName(p, p.getDirection.get)
lines(s"BYTE ${portName}PortSize[${portTypeName}::SERIALIZED_SIZE];")
}),
// Command input port
guardedList (cmdRecvPort.isDefined)
(lines(s"BYTE cmdPortSize[Fw::InputCmdPort::SERIALIZED_SIZE];")),
// Internal ports
// Sum the sizes of the port arguments
internalPortsWithFormalParams.flatMap(p =>
line(s"// Size of ${p.getUnqualifiedName} argument list") ::
wrapInScope(
s"BYTE ${p.getUnqualifiedName}IntIfSize[",
lines(
p.aNode._2.data.params.map(param =>
writeSerializedSizeExpr(
s,
s.a.typeMap(param._2.data.typeName.id),
writeInternalPortParamType(param._2.data)
)
).mkString(" +\n")
),
"];"
)
),
guardedList (hasExternalStateMachineInstances) (
lines(
s"""|// Size of buffer for external state machine signals
|// The external SmSignalBuffer stores the signal data
|BYTE externalSmBufferSize[
| 2 * sizeof(FwEnumStoreType) + Fw::SmSignalBuffer::SERIALIZED_SIZE
|];"""
)
),
guardedList (hasInternalStateMachineInstances) (
lines(
s"""|// Size of buffer for internal state machine signals
|// The internal SmSignalBuffer stores the state machine id, the
|// signal id, and the signal data
|BYTE internalSmBufferSize[SmSignalBuffer::SERIALIZED_SIZE];"""
)
)
)
wrapInScope(
"""|// Get the max size by constructing a union of the async input, command, and
|// internal port serialization sizes
|union BuffUnion {""",
members,
"};"
)
}
private def getComponentIpcSerializableBufferClass(buffUnion: List[Line]): List[Line] = {
lines(
s"""|// Define a message buffer class large enough to handle all the
|// asynchronous inputs to the component
|class ComponentIpcSerializableBuffer :
| public Fw::SerializeBufferBase
|{
|
| public:
|
| enum {
| // Max. message size = size of data + message id + port
| SERIALIZATION_SIZE =${if (buffUnion.nonEmpty) """
| sizeof(BuffUnion) +""" else "" }
| sizeof(FwEnumStoreType) +
| sizeof(FwIndexType)
| };
|
| Fw::Serializable::SizeType getBuffCapacity() const {
| return sizeof(m_buff);
| }
|
| U8* getBuffAddr() {
| return m_buff;
| }
|
| const U8* getBuffAddr() const {
| return m_buff;
| }
|
| private:
| // Should be the max of all the input ports serialized sizes...
| U8 m_buff[SERIALIZATION_SIZE];
|
|};
|"""
)
}
private def getPublicComponentFunctionMembers: List[CppDoc.Class.Member] = {
def writePortConnections(port: PortInstance) =
ComponentCppWriter.writePortConnections(
port,
portNumGetterName,
portVariableName,
inputPortCallbackName,
(p: PortInstance) => s"${p.getUnqualifiedName}_${p.getDirection.get.toString.capitalize}Port"
)
def writeStateMachineInit(smi: StateMachineInstance, name: String) =
smi.getSmKind match {
case StateMachine.Kind.External =>
line(s"this->m_stateMachine_$name.init(static_cast<FwEnumStoreType>(${writeSmIdName(name)}));")
case StateMachine.Kind.Internal =>
line(s"this->m_stateMachine_$name.init(${writeSmIdName(name)});")
}
val body = intersperseBlankLines(
List(
lines(
s"""|// Initialize base class
|Fw::$baseClassName::init(instance);
|"""
),
smInstancesByName.map((name, smi) => writeStateMachineInit(smi, name)),
intersperseBlankLines(specialInputPorts.map(writePortConnections)),
intersperseBlankLines(typedInputPorts.map(writePortConnections)),
intersperseBlankLines(serialInputPorts.map(writePortConnections)),
intersperseBlankLines(specialOutputPorts.map(writePortConnections)),
intersperseBlankLines(typedOutputPorts.map(writePortConnections)),
intersperseBlankLines(serialOutputPorts.map(writePortConnections)),
data.kind match {
case Ast.ComponentKind.Passive => Nil
case _ => List.concat(
if hasSerialAsyncInputPorts then lines(
"""|// Passed-in size added to port number and message type enumeration sizes.
|this->m_msgSize = FW_MAX(
| msgSize +
| static_cast<FwSizeType>(sizeof(FwIndexType)) +
| static_cast<FwSizeType>(sizeof(FwEnumStoreType)),
| static_cast<FwSizeType>(ComponentIpcSerializableBuffer::SERIALIZATION_SIZE)
|);
|
|Os::Queue::Status qStat = this->createQueue(queueDepth, this->m_msgSize);
|"""
)
else lines(
"""|Os::Queue::Status qStat = this->createQueue(
| queueDepth,
| static_cast<FwSizeType>(ComponentIpcSerializableBuffer::SERIALIZATION_SIZE)
|);
|"""
),
lines(
"""|FW_ASSERT(
| Os::Queue::Status::OP_OK == qStat,
| static_cast<FwAssertArgType>(qStat)
|);
|"""
)
)
}
)
)
addAccessTagAndComment(
"public",
"Component initialization",
List(
functionClassMember(
Some(s"Initialize $className object"),
"init",
initParams,
CppDoc.Type("void"),
body
)
)
)
}
private def getProtectedComponentFunctionMembers: List[CppDoc.Class.Member] = {
def writeChannelInit(channel: TlmChannel) = {
List(
lines(
s"""|// Write telemetry channel ${channel.getName}
|this->${channelUpdateFlagName(channel.getName)} = true;
|"""
),
channel.channelType match {
case t if s.isPrimitive(t, writeChannelType(t)) => lines(
s"this->${channelStorageName(channel.getName)} = 0;"
)
case _ => Nil
}
).flatten
}
addAccessTagAndComment(
"PROTECTED",
"Component construction and destruction",
List(
constructorClassMember(
Some(s"Construct $className object"),
List(
CppDoc.Function.Param(
CppDoc.Type("const char*"),
"compName",
Some("The component name"),
Some("\"\"")
)
),
s"Fw::${kindStr}ComponentBase(compName)" ::
smInstancesByName.map((name, smi) => {
val sm = s.a.stateMachineMap(smi.symbol)
val hasActionsOrGuards = sm.hasActions || sm.hasGuards
val args = (smi.getSmKind, hasActionsOrGuards) match {
case (StateMachine.Kind.External, _) => "this"
case (StateMachine.Kind.Internal, true) => "*this"
case (StateMachine.Kind.Internal, false) => ""
}
s"m_stateMachine_$name($args)",
}),
intersperseBlankLines(
List(
intersperseBlankLines(
updateOnChangeChannels.map((_, channel) =>
writeChannelInit(channel)
)
),
throttledEvents.map((_, event) => line(
s"this->${eventThrottleCounterName(event.getName)} = 0;"
)),
sortedParams.map((_, param) => line(
s"this->${paramValidityFlagName(param.getName)} = Fw::ParamValid::UNINIT;"
))
)
)
),
destructorClassMember(
Some(s"Destroy $className object"),
Nil,
CppDoc.Class.Destructor.Virtual
)
)
)
}
private def getMutexOperationMembers: List[CppDoc.Class.Member] = {
if !(hasGuardedInputPorts || hasGuardedCommands) then Nil
else addAccessTagAndComment(
"PROTECTED",
"""|Mutex operations for guarded ports
|
|You can override these operations to provide more sophisticated
|synchronization
|""",
List(
functionClassMember(
Some("Lock the guarded mutex"),
"lock",
Nil,
CppDoc.Type("void"),
lines(
"this->m_guardedPortMutex.lock();"
),
CppDoc.Function.Virtual
),
functionClassMember(
Some("Unlock the guarded mutex"),
"unLock",
Nil,
CppDoc.Type("void"),
lines(
"this->m_guardedPortMutex.unLock();"
),
CppDoc.Function.Virtual
)
)
)
}
private def getDispatchFunctionMember: List[CppDoc.Class.Member] = {
def writeAsyncPortDispatch(p: PortInstance) = {
val body = p.getType.get match {
case PortInstance.Type.DefPort(_) =>
List(
intersperseBlankLines(
portParamTypeMap(p.getUnqualifiedName).map((n, tn, t) => {
val varDecl = writeVarDecl(s, tn, n, t)
lines(
s"""|// Deserialize argument $n
|$varDecl
|deserStatus = msg.deserialize($n);
|FW_ASSERT(
| deserStatus == Fw::FW_SERIALIZE_OK,
| static_cast<FwAssertArgType>(deserStatus)
|);
|"""
)
})
),
line("// Call handler function") ::
writeFunctionCall(
s"this->${inputPortHandlerName(p.getUnqualifiedName)}",
List("portNum"),
getPortParams(p).map(_._1)
),
Line.blank :: lines("break;")
).flatten
case PortInstance.Type.Serial => lines(
s"""|// Deserialize serialized buffer into new buffer
|U8 handBuff[this->m_msgSize];
|Fw::ExternalSerializeBuffer serHandBuff(handBuff,this->m_msgSize);
|deserStatus = msg.deserialize(serHandBuff);
|FW_ASSERT(
| deserStatus == Fw::FW_SERIALIZE_OK,
| static_cast<FwAssertArgType>(deserStatus)
|);
|this->${inputPortHandlerName(p.getUnqualifiedName)}(portNum, serHandBuff);
|
|break;
|"""
)
}
line(s"// Handle async input port ${p.getUnqualifiedName}") ::
wrapInScope(
s"case ${portCppConstantName(p)}: {",
body,
"}"
)
}
def writeAsyncCommandDispatch(opcode: Command.Opcode, cmd: Command) = {
val cmdRespVarName = portVariableName(cmdRespPort.get)
val body = intersperseBlankLines(
List(
lines(
"""|// Deserialize opcode
|FwOpcodeType opCode = 0;
|deserStatus = msg.deserialize(opCode);
|FW_ASSERT (
| deserStatus == Fw::FW_SERIALIZE_OK,
| static_cast<FwAssertArgType>(deserStatus)
|);
|
|// Deserialize command sequence
|U32 cmdSeq = 0;
|deserStatus = msg.deserialize(cmdSeq);
|FW_ASSERT (
| deserStatus == Fw::FW_SERIALIZE_OK,
| static_cast<FwAssertArgType>(deserStatus)
|);
|
|// Deserialize command argument buffer
|Fw::CmdArgBuffer args;
|deserStatus = msg.deserialize(args);
|FW_ASSERT (
| deserStatus == Fw::FW_SERIALIZE_OK,
| static_cast<FwAssertArgType>(deserStatus)
|);
|
|// Reset buffer
|args.resetDeser();
|"""
),
intersperseBlankLines(
cmdParamTypeMap(opcode).map((n, tn, _) =>
lines(
s"""|// Deserialize argument $n
|$tn $n;
|deserStatus = args.deserialize($n);
|if (deserStatus != Fw::FW_SERIALIZE_OK) {
| if (this->$cmdRespVarName[0].isConnected()) {
| this->cmdResponse_out(
| opCode,
| cmdSeq,
| Fw::CmdResponse::FORMAT_ERROR
| );
| }
| // Don't crash the task if bad arguments were passed from the ground
| break;
|}
|"""
)
)
),
lines(
s"""|// Make sure there was no data left over.
|// That means the argument buffer size was incorrect.
|#if FW_CMD_CHECK_RESIDUAL
|if (args.getBuffLeft() != 0) {
| if (this->$cmdRespVarName[0].isConnected()) {
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::FORMAT_ERROR);
| }
| // Don't crash the task if bad arguments were passed from the ground
| break;
|}
|#endif
|"""
),
line("// Call handler function") ::
writeFunctionCall(
s"this->${commandHandlerName(cmd.getName)}",
List("opCode, cmdSeq"),
cmdParamTypeMap(opcode).map(_._1)
),
lines("break;")
)
)
line(s"// Handle command ${cmd.getName}") ::
wrapInScope(
s"case ${commandCppConstantName(cmd)}: {",
body,
"}"
)
}
def writeInternalPortDispatch(p: PortInstance.Internal) = {
val body = intersperseBlankLines(
List(
intersperseBlankLines(
portParamTypeMap(p.getUnqualifiedName).map((n, tn, _) =>
lines(
s"""|$tn $n;
|deserStatus = msg.deserialize($n);
|
|// Internal interface should always deserialize
|FW_ASSERT(
| Fw::FW_SERIALIZE_OK == deserStatus,
| static_cast<FwAssertArgType>(deserStatus)
|);
|"""
)
)
),
lines(
"""|// Make sure there was no data left over.
|// That means the buffer size was incorrect.
|FW_ASSERT(
| msg.getBuffLeft() == 0,
| static_cast<FwAssertArgType>(msg.getBuffLeft())
|);
|"""
),
line("// Call handler function") ::
writeFunctionCall(
s"this->${internalInterfaceHandlerName(p.getUnqualifiedName)}",
Nil,
getPortParams(p).map(_._1)
),
lines("break;")
)
)
line(s"// Handle internal interface ${p.getUnqualifiedName}") ::
wrapInScope(
s"case ${internalPortCppConstantName(p)}: {",
body,
"}"
)
}
if data.kind == Ast.ComponentKind.Passive then Nil
else {
val assertMsgStatus = lines(
"""|FW_ASSERT(
| msgStatus == Os::Queue::OP_OK,
| static_cast<FwAssertArgType>(msgStatus)
|);
|"""
)
addAccessTagAndComment(
data.kind match {
case Ast.ComponentKind.Active => "PRIVATE"
case Ast.ComponentKind.Queued => "PROTECTED"
case _ => ""
},
"Message dispatch functions",
List(
functionClassMember(
Some("Called in the message loop to dispatch a message from the queue"),
"doDispatch",
Nil,
CppDoc.Type(
"MsgDispatchStatus",
Some("Fw::QueuedComponentBase::MsgDispatchStatus")
),
List(
if hasSerialAsyncInputPorts then lines(
"""|U8 msgBuff[this->m_msgSize];
|Fw::ExternalSerializeBuffer msg(msgBuff,this->m_msgSize);
|"""
)
else lines("ComponentIpcSerializableBuffer msg;"),
lines(
s"""|FwQueuePriorityType priority = 0;
|
|Os::Queue::Status msgStatus = this->m_queue.receive(
| msg,
| Os::Queue::${if data.kind == Ast.ComponentKind.Queued then "NON" else ""}BLOCKING,
| priority
|);
|""".stripMargin
),
if data.kind == Ast.ComponentKind.Queued then wrapInIfElse(
"Os::Queue::Status::EMPTY == msgStatus",
lines("return Fw::QueuedComponentBase::MSG_DISPATCH_EMPTY;"),
assertMsgStatus
)
else assertMsgStatus,
lines(
"""|
|// Reset to beginning of buffer
|msg.resetDeser();
|
|FwEnumStoreType desMsg = 0;
|Fw::SerializeStatus deserStatus = msg.deserialize(desMsg);
|FW_ASSERT(
| deserStatus == Fw::FW_SERIALIZE_OK,
| static_cast<FwAssertArgType>(deserStatus)
|);
|
|MsgTypeEnum msgType = static_cast<MsgTypeEnum>(desMsg);
|"""
),
Line.blank :: wrapInIf(
s"msgType == $exitConstantName",
lines("return MSG_DISPATCH_EXIT;")
),
lines(
"""|
|FwIndexType portNum = 0;
|deserStatus = msg.deserialize(portNum);
|FW_ASSERT(
| deserStatus == Fw::FW_SERIALIZE_OK,
| static_cast<FwAssertArgType>(deserStatus)
|);
|"""
),
Line.blank :: wrapInSwitch(
"msgType",
intersperseBlankLines(
List(
intersperseBlankLines(dataProductAsyncInputPorts.map(writeAsyncPortDispatch)),
intersperseBlankLines(typedAsyncInputPorts.map(writeAsyncPortDispatch)),
intersperseBlankLines(serialAsyncInputPorts.map(writeAsyncPortDispatch)),
intersperseBlankLines(asyncCmds.map(writeAsyncCommandDispatch)),
intersperseBlankLines(internalPorts.map(writeInternalPortDispatch)),
stateMachineWriter.writeDispatchCases,
lines(
"""|default:
| return MSG_DISPATCH_ERROR;
|"""
)
)
)
),
Line.blank :: lines("return MSG_DISPATCH_OK;")
).flatten,
CppDoc.Function.Virtual
)
)
)
}
}
private def getTimeFunctionMember: List[CppDoc.Class.Member] =
if !hasTimeGetPort then Nil
else {
val name = portVariableName(timeGetPort.get)
addAccessTagAndComment(
"PROTECTED",
"Time",
List(
functionClassMember(
Some(
"""| Get the time
|
|\\return The current time
|"""
),
"getTime",
Nil,
CppDoc.Type("Fw::Time"),
wrapInIfElse(
s"this->$name[0].isConnected()",
lines(
s"""|Fw::Time _time;
|this->$name[0].invoke(_time);
|return _time;
|"""
),
lines(
"return Fw::Time(TB_NONE, 0, 0);"
)
)
)
)
)
}
private def getMsgSizeVariableMember: List[CppDoc.Class.Member] = {
if !hasSerialAsyncInputPorts then Nil
else List(
linesClassMember(
List(
CppDocHppWriter.writeAccessTag("PRIVATE"),
lines(
"""|
|//! Stores max message size
|FwSizeType m_msgSize;
|"""
)
).flatten
)
)
}
private def getMutexVariableMembers: List[CppDoc.Class.Member] = {
if !(hasGuardedInputPorts || hasGuardedCommands || hasParameters) then Nil
else List(
linesClassMember(
List(
CppDocHppWriter.writeAccessTag("PRIVATE"),
CppDocWriter.writeBannerComment(
"Mutexes"
),
if !(hasGuardedInputPorts || hasGuardedCommands) then Nil
else lines(
"""|
|//! Mutex for guarded ports
|Os::Mutex m_guardedPortMutex;
|"""
),
if !hasParameters then Nil
else lines(
"""|
|//! Mutex for locking parameters during sets and saves
|Os::Mutex m_paramLock;
|"""
)
).flatten
)
)
}
}
object ComponentCppWriter extends CppWriterUtils {
sealed trait ConnectionSense
object ConnectionSense {
case object Forward extends ConnectionSense
case object Reversed extends ConnectionSense
}
def reverseDirection(direction: PortInstance.Direction) = {
import PortInstance.Direction._
direction match {
case Input => Output
case Output => Input
}
}
def writePortConnections(
port: PortInstance,
numGetterName: PortInstance => String,
variableName: PortInstance => String,
callbackName: String => String,
printName: PortInstance => String,
connectionSense: ConnectionSense = ConnectionSense.Forward
): List[Line] = {
val d = {
val trueDirection = port.getDirection.get
connectionSense match {
case ConnectionSense.Forward => trueDirection
case ConnectionSense.Reversed => reverseDirection(trueDirection)
}
}
val body = line(s"// Connect ${d.toString} port ${port.getUnqualifiedName}") ::
wrapInForLoopStaggered(
"FwIndexType port = 0",
s"port < static_cast<FwIndexType>(this->${numGetterName(port)}())",
"port++",
List(
lines(
s"|this->${variableName(port)}[port].init();"
),
d match {
case PortInstance.Direction.Input => lines(
s"""|this->${variableName(port)}[port].addCallComp(
| this,
| ${callbackName(port.getUnqualifiedName)}
|);
|this->${variableName(port)}[port].setPortNum(port);
|"""
)
case PortInstance.Direction.Output => Nil
},
Line.blank :: lines(
s"""|#if FW_OBJECT_NAMES == 1
|Fw::ObjectName portName;
|portName.format(
| "%s_${printName(port)}[%" PRI_PlatformIntType "]",
| this->m_objName.toChar(),
| port
|);
|this->${variableName(port)}[port].setObjName(portName.toChar());
|#endif
|"""
)
).flatten
)
port match {
case PortInstance.Special(aNode, _, _, _, _) => aNode._2.data match {
case Ast.SpecPortInstance.Special(_, kind, _, _, _) => kind match {
case Ast.SpecPortInstance.TextEvent => List.concat(
lines("#if FW_ENABLE_TEXT_LOGGING == 1"),
body,
lines("#endif")
)
case _ => body
}
case _ => body
}
case _ => body
}
}
}