Generation working and Compilation of MARTe components

This commit is contained in:
ferrog
2025-05-13 16:03:11 +00:00
parent 3a5e378d99
commit 4faee3802a
1571 changed files with 611466 additions and 0 deletions

View File

@@ -0,0 +1,366 @@
/**
* @file JAEPICSCAInput.cpp
* @brief Source file for class JAEPICSCAInput
* @date 20/04/2017
* @author Andre Neto
*
* @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
* the Development of Fusion Energy ('Fusion for Energy').
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence")
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
*
* @warning Unless required by applicable law or agreed to in writing,
* software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence permissions and limitations under the Licence.
* @details This source file contains the definition of all the methods for
* the class JAEPICSCAInput (public, protected, and private). Be aware that some
* methods, such as those inline could be defined on the header file, instead.
*/
/*---------------------------------------------------------------------------*/
/* Standard header includes */
/*---------------------------------------------------------------------------*/
#include "JAEPICSCAInput.h"
/*---------------------------------------------------------------------------*/
/* Project header includes */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "MemoryMapInputBroker.h"
/*---------------------------------------------------------------------------*/
/* Static definitions */
/*---------------------------------------------------------------------------*/
namespace MARTe {
/**
* @brief Callback function for the ca_create_subscription. Single point of access which
* delegates the events to the corresponding JAEPICSPV instance.
*/
static FastPollingMutexSem eventCallbackFastMux;
/*lint -e{1746} function must match required prototype and thus cannot be changed to constant reference.*/
void JAEPICSCAInputEventCallback(struct event_handler_args const args) {
(void) eventCallbackFastMux.FastLock();
PVWrapper *pv = static_cast<PVWrapper *>(args.usr);
if (pv != NULL_PTR(PVWrapper *)) {
(void) MemoryOperationsHelper::Copy(pv->memory, args.dbr, pv->memorySize);
}
eventCallbackFastMux.FastUnLock();
}
}
/*---------------------------------------------------------------------------*/
/* Method definitions */
/*---------------------------------------------------------------------------*/
namespace MARTe {
JAEPICSCAInput::JAEPICSCAInput() :
DataSourceI(), EmbeddedServiceMethodBinderI(), executor(*this) {
pvs = NULL_PTR(PVWrapper *);
stackSize = THREADS_DEFAULT_STACKSIZE * 4u;
cpuMask = 0xffu;
eventCallbackFastMux.Create();
}
/*lint -e{1551} must stop the SingleThreadService in the destructor.*/
JAEPICSCAInput::~JAEPICSCAInput() {
if (!executor.Stop()) {
if (!executor.Stop()) {
REPORT_ERROR(ErrorManagement::FatalError, "Could not stop SingleThreadService.");
}
}
(void) eventCallbackFastMux.FastLock();
uint32 nOfSignals = GetNumberOfSignals();
if (pvs != NULL_PTR(PVWrapper *)) {
uint32 n;
for (n = 0u; (n < nOfSignals); n++) {
if (pvs[n].memory != NULL_PTR(void *)) {
GlobalObjectsDatabase::Instance()->GetStandardHeap()->Free(pvs[n].memory);
}
}
delete[] pvs;
}
eventCallbackFastMux.FastUnLock();
}
bool JAEPICSCAInput::Initialise(StructuredDataI & data) {
bool ok = DataSourceI::Initialise(data);
if (ok) {
if (!data.Read("CPUs", cpuMask)) {
REPORT_ERROR(ErrorManagement::Information, "No CPUs defined. Using default = %d", cpuMask);
}
if (!data.Read("StackSize", stackSize)) {
REPORT_ERROR(ErrorManagement::Information, "No StackSize defined. Using default = %d", stackSize);
}
executor.SetStackSize(stackSize);
executor.SetCPUMask(cpuMask);
}
if (ok) {
ok = data.MoveRelative("Signals");
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "Could not move to the Signals section");
}
if (ok) {
ok = data.Copy(originalSignalInformation);
}
if (ok) {
ok = originalSignalInformation.MoveToRoot();
}
//Do not allow to add signals in run-time
if (ok) {
ok = signalsDatabase.MoveRelative("Signals");
}
if (ok) {
ok = signalsDatabase.Write("Locked", 1u);
}
if (ok) {
ok = signalsDatabase.MoveToAncestor(1u);
}
}
if (ok) {
ok = data.MoveToAncestor(1u);
}
return ok;
}
bool JAEPICSCAInput::SetConfiguredDatabase(StructuredDataI & data) {
bool ok = DataSourceI::SetConfiguredDatabase(data);
//Check the signal index of the timing signal.
uint32 nOfSignals = GetNumberOfSignals();
if (ok) {
ok = (nOfSignals > 0u);
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "At least one signal shall be defined");
}
}
if (ok) {
//Do not allow samples
uint32 functionNumberOfSignals = 0u;
uint32 n;
if (GetFunctionNumberOfSignals(InputSignals, 0u, functionNumberOfSignals)) {
for (n = 0u; (n < functionNumberOfSignals) && (ok); n++) {
uint32 nSamples;
ok = GetFunctionSignalSamples(InputSignals, 0u, n, nSamples);
if (ok) {
ok = (nSamples == 1u);
}
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "The number of samples shall be exactly 1");
}
}
}
}
if (ok) {
pvs = new PVWrapper[nOfSignals];
uint32 n;
for (n = 0u; (n < nOfSignals); n++) {
pvs[n].memory = NULL_PTR(void *);
}
for (n = 0u; (n < nOfSignals) && (ok); n++) {
//Note that the RealTimeApplicationConfigurationBuilder is allowed to change the order of the signals w.r.t. to the originalSignalInformation
StreamString orderedSignalName;
ok = GetSignalName(n, orderedSignalName);
if (ok) {
//Have to mix and match between the original setting of the DataSource signal
//and the ones which are later added by the RealTimeApplicationConfigurationBuilder
ok = originalSignalInformation.MoveRelative(orderedSignalName.Buffer());
}
StreamString pvName;
if (ok) {
ok = originalSignalInformation.Read("PVName", pvName);
if (!ok) {
uint32 nn = n;
REPORT_ERROR(ErrorManagement::ParametersError, "No PVName specified for signal at index %d", nn);
}
}
TypeDescriptor td = GetSignalType(n);
if (ok) {
(void) StringHelper::CopyN(&pvs[n].pvName[0], pvName.Buffer(), PV_NAME_MAX_SIZE);
if (td == CharString) {
pvs[n].pvType = DBR_STRING;
}
else if (td == Character8Bit) {
pvs[n].pvType = DBR_STRING;
}
else if (td == SignedInteger8Bit) {
pvs[n].pvType = DBR_CHAR;
}
else if (td == UnsignedInteger8Bit) {
pvs[n].pvType = DBR_CHAR;
}
else if (td == SignedInteger16Bit) {
pvs[n].pvType = DBR_SHORT;
}
else if (td == UnsignedInteger16Bit) {
pvs[n].pvType = DBR_SHORT;
}
else if (td == SignedInteger32Bit) {
pvs[n].pvType = DBR_LONG;
}
else if (td == UnsignedInteger32Bit) {
pvs[n].pvType = DBR_LONG;
}
else if (td == Float32Bit) {
pvs[n].pvType = DBR_FLOAT;
}
else if (td == Float64Bit) {
pvs[n].pvType = DBR_DOUBLE;
}
else {
REPORT_ERROR(ErrorManagement::ParametersError, "Type %s is not supported", TypeDescriptor::GetTypeNameFromTypeDescriptor(td));
ok = false;
}
}
uint32 numberOfElements = 1u;
if (ok) {
ok = GetSignalNumberOfElements(n, numberOfElements);
}
if (ok) {
if (pvs[n].pvType == DBR_STRING) {
ok = (numberOfElements == 40u);
}
if (!ok) {
//Could support arrays of strings with multiples of char8[40]
REPORT_ERROR(ErrorManagement::ParametersError, "Strings shall be defined with 40 elements char8[40]. Arrays of strings are not currently supported");
}
}
if (ok) {
pvs[n].numberOfElements = numberOfElements;
}
if (ok) {
pvs[n].memorySize = td.numberOfBits;
pvs[n].memorySize /= 8u;
pvs[n].memorySize *= numberOfElements;
pvs[n].memory = GlobalObjectsDatabase::Instance()->GetStandardHeap()->Malloc(pvs[n].memorySize);
ok = originalSignalInformation.MoveToAncestor(1u);
}
}
}
if (ok) {
ok = (executor.Start() == ErrorManagement::NoError);
}
return ok;
}
bool JAEPICSCAInput::AllocateMemory() {
return true;
}
uint32 JAEPICSCAInput::GetNumberOfMemoryBuffers() {
return 1u;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: The signalAddress is independent of the bufferIdx.*/
bool JAEPICSCAInput::GetSignalMemoryBuffer(const uint32 signalIdx, const uint32 bufferIdx, void*& signalAddress) {
bool ok = (pvs != NULL_PTR(PVWrapper *));
if (ok) {
ok = (signalIdx < GetNumberOfSignals());
}
if (ok) {
//lint -e{613} pvs cannot as otherwise ok would be false
signalAddress = pvs[signalIdx].memory;
}
return ok;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: The brokerName only depends on the direction */
const char8* JAEPICSCAInput::GetBrokerName(StructuredDataI& data, const SignalDirection direction) {
const char8* brokerName = "";
if (direction == InputSignals) {
brokerName = "MemoryMapInputBroker";
}
return brokerName;
}
bool JAEPICSCAInput::GetInputBrokers(ReferenceContainer& inputBrokers, const char8* const functionName, void* const gamMemPtr) {
ReferenceT<MemoryMapInputBroker> broker("MemoryMapInputBroker");
bool ok = broker->Init(InputSignals, *this, functionName, gamMemPtr);
if (ok) {
ok = inputBrokers.Insert(broker);
}
return ok;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: OutputBrokers are not supported. Function returns false irrespectively of the parameters.*/
bool JAEPICSCAInput::GetOutputBrokers(ReferenceContainer& outputBrokers, const char8* const functionName, void* const gamMemPtr) {
return false;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: NOOP at StateChange, independently of the function parameters.*/
bool JAEPICSCAInput::PrepareNextState(const char8* const currentStateName, const char8* const nextStateName) {
return true;
}
ErrorManagement::ErrorType JAEPICSCAInput::Execute(ExecutionInfo& info) {
ErrorManagement::ErrorType err = ErrorManagement::NoError;
if (info.GetStage() == ExecutionInfo::StartupStage) {
(void) eventCallbackFastMux.FastLock();
/*lint -e{9130} -e{835} -e{845} -e{747} Several false positives. lint is getting confused here for some reason.*/
if (ca_context_create(ca_enable_preemptive_callback) != ECA_NORMAL) {
err = ErrorManagement::FatalError;
REPORT_ERROR(err, "ca_enable_preemptive_callback failed");
}
uint32 n;
uint32 nOfSignals = GetNumberOfSignals();
if (pvs != NULL_PTR(PVWrapper *)) {
for (n = 0u; (n < nOfSignals); n++) {
/*lint -e{9130} -e{835} -e{845} -e{747} Several false positives. lint is getting confused here for some reason.*/
if (ca_create_channel(&pvs[n].pvName[0], NULL_PTR(caCh *), NULL_PTR(void *), 20u, &pvs[n].pvChid) != ECA_NORMAL) {
err = ErrorManagement::FatalError;
REPORT_ERROR(err, "ca_create_channel failed for PV with name %s", pvs[n].pvName);
}
if (err.ErrorsCleared()) {
/*lint -e{9130} -e{835} -e{845} -e{747} Several false positives. lint is getting confused here for some reason.*/
if (ca_create_subscription(pvs[n].pvType, pvs[n].numberOfElements, pvs[n].pvChid, DBE_VALUE, &JAEPICSCAInputEventCallback, &pvs[n],
&pvs[n].pvEvid) != ECA_NORMAL) {
err = ErrorManagement::FatalError;
REPORT_ERROR(err, "ca_create_subscription failed for PV %s", pvs[n].pvName);
}
}
}
}
eventCallbackFastMux.FastUnLock();
}
else if (info.GetStage() != ExecutionInfo::BadTerminationStage) {
Sleep::Sec(1.0F);
}
else {
(void) eventCallbackFastMux.FastLock();
uint32 n;
uint32 nOfSignals = GetNumberOfSignals();
if (pvs != NULL_PTR(PVWrapper *)) {
for (n = 0u; (n < nOfSignals); n++) {
(void) ca_clear_subscription(pvs[n].pvEvid);
(void) ca_clear_event(pvs[n].pvEvid);
(void) ca_clear_channel(pvs[n].pvChid);
}
}
ca_detach_context();
ca_context_destroy();
eventCallbackFastMux.FastUnLock();
}
return err;
}
uint32 JAEPICSCAInput::GetStackSize() const {
return stackSize;
}
uint32 JAEPICSCAInput::GetCPUMask() const {
return cpuMask;
}
bool JAEPICSCAInput::Synchronise() {
return false;
}
CLASS_REGISTER(JAEPICSCAInput, "1.0")
}

View File

@@ -0,0 +1,260 @@
/**
* @file EPICSCAInput.h
* @brief Header file for class EPICSCAInput
* @date 20/04/2017
* @author Andre Neto
*
* @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
* the Development of Fusion Energy ('Fusion for Energy').
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence")
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
*
* @warning Unless required by applicable law or agreed to in writing,
* software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence permissions and limitations under the Licence.
* @details This header file contains the declaration of the class EPICSCAInput
* with all of its public, protected and private members. It may also include
* definitions for inline methods which need to be visible to the compiler.
*/
#ifndef JAEPICSCAINPUT_H_
#define JAEPICSCAINPUT_H_
/*---------------------------------------------------------------------------*/
/* Standard header includes */
/*---------------------------------------------------------------------------*/
#include <cadef.h>
/*---------------------------------------------------------------------------*/
/* Project header includes */
/*---------------------------------------------------------------------------*/
#include "DataSourceI.h"
#include "EmbeddedServiceMethodBinderI.h"
#include "EventSem.h"
#include "SingleThreadService.h"
/*---------------------------------------------------------------------------*/
/* Class declaration */
/*---------------------------------------------------------------------------*/
namespace MARTe {
/**
* Maximum size that a PV name may have
*/
/*lint -esym(551, MARTe::PV_NAME_MAX_SIZE) the symbol is used to define the size of PVWrapper below*/
const uint32 PV_NAME_MAX_SIZE = 64u;
/**
* Wraps a PV
*/
struct PVWrapper {
/**
* The channel identifier
*/
chid pvChid;
/**
* The event identifier
*/
evid pvEvid;
/**
* The PV type
*/
chtype pvType;
/**
* The memory of the signal associated to this channel
*/
void *memory;
void *previousValue;
/**
* The number of elements > 0
*/
uint32 numberOfElements;
/**
* The memory size
*/
uint32 memorySize;
/**
* The PV name
*/
char8 pvName[PV_NAME_MAX_SIZE];
};
/**
* @brief A DataSource which allows to retrieved data from any number of PVs using the EPICS channel access client protocol.
* Data is asynchronously retrieved using ca_create_subscriptions in the context of a different thread (w.r.t. to the real-time thread).
*
* The configuration syntax is (names are only given as an example):
*
* <pre>
* +EPICSCAInput_1 = {
* Class = JAEPICSCA::JAEPICSCAInput
* StackSize = 1048576 //Optional the EmbeddedThread stack size. Default value is THREADS_DEFAULT_STACKSIZE * 4u
* CPUs = 0xff //Optional the affinity of the EmbeddedThread (where the EPICS context is attached).
* Signals = {
* PV1 = { //At least one shall be defined
* PVName = My::PV1 //Compulsory. Name of the PV.
* Type = uint32 //Compulsory. Supported types are char8[40], string[40], uint8, int8, uint16, int16, int32, uint32, uint64, int64, float32 and float64
* NumberOfElements = 1 //Arrays also supported
* }
* ...
* }
* }
*
* </pre>
*/
class JAEPICSCAInput: public DataSourceI, public EmbeddedServiceMethodBinderI {
public:
CLASS_REGISTER_DECLARATION()
/**
* @brief Default constructor. NOOP.
*/
JAEPICSCAInput();
/**
* @brief Destructor.
* @details TODO.
*/
virtual ~JAEPICSCAInput();
/**
* @brief See DataSourceI::AllocateMemory. NOOP.
* @return true.
*/
virtual bool AllocateMemory();
/**
* @brief See DataSourceI::GetNumberOfMemoryBuffers.
* @return 1.
*/
virtual uint32 GetNumberOfMemoryBuffers();
/**
* @brief See DataSourceI::GetSignalMemoryBuffer.
* @pre
* SetConfiguredDatabase
*/
virtual bool GetSignalMemoryBuffer(const uint32 signalIdx,
const uint32 bufferIdx,
void *&signalAddress);
/**
* @brief See DataSourceI::GetNumberOfMemoryBuffers.
* @details Only InputSignals are supported.
* @return MemoryMapInputBroker.
*/
virtual const char8 *GetBrokerName(StructuredDataI &data,
const SignalDirection direction);
/**
* @brief See DataSourceI::GetInputBrokers.
* @details adds a memory MemoryMapInputBroker instance to the inputBrokers
* @return true.
*/
virtual bool GetInputBrokers(ReferenceContainer &inputBrokers,
const char8* const functionName,
void * const gamMemPtr);
/**
* @brief See DataSourceI::GetOutputBrokers.
* @return false.
*/
virtual bool GetOutputBrokers(ReferenceContainer &outputBrokers,
const char8* const functionName,
void * const gamMemPtr);
/**
* @brief See DataSourceI::PrepareNextState. NOOP.
* @return true.
*/
virtual bool PrepareNextState(const char8 * const currentStateName,
const char8 * const nextStateName);
/**
* @brief Loads and verifies the configuration parameters detailed in the class description.
* @return true if all the mandatory parameters are correctly specified and if the specified optional parameters have valid values.
*/
virtual bool Initialise(StructuredDataI & data);
/**
* @brief Final verification of all the parameters. Setup of the memory required to hold all the signals.
* @details This method verifies that all the parameters requested by the GAMs interacting with this DataSource
* are valid and consistent with the parameters set during the initialisation phase.
* In particular the following conditions shall be met:
* - All the signals have the PVName defined
* - All the signals have one of the following types: uint32, int32, float32 or float64.
* @return true if all the parameters are valid and the conditions above are met.
*/
virtual bool SetConfiguredDatabase(StructuredDataI & data);
/**
* @brief Gets the affinity of the thread which is going to be used to asynchronously read data from the ca_create_subscription.
* @return the the affinity of the thread which is going to be used to asynchronously read data from the ca_create_subscription.
*/
uint32 GetCPUMask() const;
/**
* @brief Gets the stack size of the thread which is going to be used to asynchronously read data from the ca_create_subscription.
* @return the stack size of the thread which is going to be used to asynchronously read data from the ca_create_subscription.
*/
uint32 GetStackSize() const;
/**
* @brief Provides the context to execute all the EPICS relevant calls.
* @details Executes in the context of a spawned thread the following EPICS calls:
* ca_context_create, ca_create_channel, ca_create_subscription, ca_clear_subscription,
* ca_clear_event, ca_clear_channel, ca_detach_context and ca_context_destroy
* @return ErrorManagement::NoError if all the EPICS calls return without any error.
*/
virtual ErrorManagement::ErrorType Execute(ExecutionInfo & info);
/**
* @brief See DataSourceI::Synchronise.
* @return false.
*/
virtual bool Synchronise();
/**
* @brief Registered as the ca_create_subscription callback function.
* It calls updates the memory of the corresponding PV variable.
*/
friend void JAEPICSCAInputEventCallback(struct event_handler_args args);
private:
/**
* List of PVs.
*/
PVWrapper *pvs;
/**
* The CPU mask for the executor
*/
uint32 cpuMask;
/**
* The stack size
*/
uint32 stackSize;
/**
* The EmbeddedThread where the ca_pend_event is executed.
*/
SingleThreadService executor;
/**
* Stores the configuration information received at Initialise.
*/
ConfigurationDatabase originalSignalInformation;
};
}
/*---------------------------------------------------------------------------*/
/* Inline method definitions */
/*---------------------------------------------------------------------------*/
#endif /* EPICSCADATASOURCE_H_ */

View File

@@ -0,0 +1,375 @@
/**
* @file EPICSCAOutput.cpp
* @brief Source file for class EPICSCAOutput
* @date 20/04/2017
* @author Andre Neto
*
* @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
* the Development of Fusion Energy ('Fusion for Energy').
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence")
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
*
* @warning Unless required by applicable law or agreed to in writing,
* software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence permissions and limitations under the Licence.
* @details This source file contains the definition of all the methods for
* the class EPICSCAOutput (public, protected, and private). Be aware that some
* methods, such as those inline could be defined on the header file, instead.
*/
/*---------------------------------------------------------------------------*/
/* Standard header includes */
/*---------------------------------------------------------------------------*/
#include "JAEPICSCAOutput.h"
/*---------------------------------------------------------------------------*/
/* Project header includes */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "MemoryMapAsyncOutputBroker.h"
/*---------------------------------------------------------------------------*/
/* Static definitions */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* Method definitions */
/*---------------------------------------------------------------------------*/
namespace MARTe {
JAEPICSCAOutput::JAEPICSCAOutput() :
DataSourceI() {
pvs = NULL_PTR(PVWrapper *);
stackSize = THREADS_DEFAULT_STACKSIZE * 4u;
cpuMask = 0xffu;
numberOfBuffers = 0u;
ignoreBufferOverrun = 1u;
threadContextSet = false;
}
/*lint -e{1551} must free the memory allocated to the different PVs.*/
JAEPICSCAOutput::~JAEPICSCAOutput() {
uint32 nOfSignals = GetNumberOfSignals();
if (pvs != NULL_PTR(PVWrapper *)) {
uint32 n;
for (n = 0u; (n < nOfSignals); n++) {
if (pvs[n].pvChid != NULL_PTR(chid)) {
(void) ca_clear_channel(pvs[n].pvChid);
}
if (pvs[n].memory != NULL_PTR(void *)) {
GlobalObjectsDatabase::Instance()->GetStandardHeap()->Free(pvs[n].memory);
GlobalObjectsDatabase::Instance()->GetStandardHeap()->Free(pvs[n].previousValue);
}
}
delete[] pvs;
}
}
bool JAEPICSCAOutput::Initialise(StructuredDataI & data) {
bool ok = DataSourceI::Initialise(data);
if (ok) {
ok = data.Read("NumberOfBuffers", numberOfBuffers);
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "NumberOfBuffers shall be specified");
}
}
if (ok) {
if (!data.Read("CPUs", cpuMask)) {
REPORT_ERROR(ErrorManagement::Information, "No CPUs defined. Using default = %d", cpuMask);
}
if (!data.Read("StackSize", stackSize)) {
REPORT_ERROR(ErrorManagement::Information, "No StackSize defined. Using default = %d", stackSize);
}
if (!data.Read("IgnoreBufferOverrun", ignoreBufferOverrun)) {
REPORT_ERROR(ErrorManagement::Information, "No IgnoreBufferOverrun defined. Using default = %d", ignoreBufferOverrun);
}
}
if (ok) {
ok = data.MoveRelative("Signals");
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "Could not move to the Signals section");
}
if (ok) {
ok = data.Copy(originalSignalInformation);
}
if (ok) {
ok = originalSignalInformation.MoveToRoot();
}
//Do not allow to add signals in run-time
if (ok) {
ok = signalsDatabase.MoveRelative("Signals");
}
if (ok) {
ok = signalsDatabase.Write("Locked", 1u);
}
if (ok) {
ok = signalsDatabase.MoveToAncestor(1u);
}
}
if (ok) {
ok = data.MoveToAncestor(1u);
}
return ok;
}
bool JAEPICSCAOutput::SetConfiguredDatabase(StructuredDataI & data) {
bool ok = DataSourceI::SetConfiguredDatabase(data);
//Check the signal index of the timing signal.
uint32 nOfSignals = GetNumberOfSignals();
if (ok) {
ok = (nOfSignals > 0u);
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "At least one signal shall be defined");
}
}
if (ok) {
//Do not allow samples
uint32 functionNumberOfSignals = 0u;
uint32 n;
if (GetFunctionNumberOfSignals(OutputSignals, 0u, functionNumberOfSignals)) {
for (n = 0u; (n < functionNumberOfSignals) && (ok); n++) {
uint32 nSamples;
ok = GetFunctionSignalSamples(OutputSignals, 0u, n, nSamples);
if (ok) {
ok = (nSamples == 1u);
}
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "The number of samples shall be exactly 1");
}
}
}
}
//Only one and one GAM allowed to interact with this DataSourceI
if (ok) {
ok = (GetNumberOfFunctions() == 1u);
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "Exactly one Function allowed to interact with this DataSourceI");
}
}
if (ok) {
pvs = new PVWrapper[nOfSignals];
uint32 n;
for (n = 0u; (n < nOfSignals); n++) {
pvs[n].memory = NULL_PTR(void *); //value to write PV
pvs[n].previousValue = NULL_PTR(void *); //written value
pvs[n].pvChid = NULL_PTR(chid);
}
for (n = 0u; (n < nOfSignals) && (ok); n++) {
//Note that the RealTimeApplicationConfigurationBuilder is allowed to change the order of the signals w.r.t. to the originalSignalInformation
StreamString orderedSignalName;
ok = GetSignalName(n, orderedSignalName);
if (ok) {
//Have to mix and match between the original setting of the DataSource signal
//and the ones which are later added by the RealTimeApplicationConfigurationBuilder
ok = originalSignalInformation.MoveRelative(orderedSignalName.Buffer());
}
StreamString pvName;
if (ok) {
ok = originalSignalInformation.Read("PVName", pvName);
if (!ok) {
uint32 nn = n;
REPORT_ERROR(ErrorManagement::ParametersError, "No PVName specified for signal at index %d", nn);
}
}
TypeDescriptor td = GetSignalType(n);
if (ok) {
(void) StringHelper::CopyN(&pvs[n].pvName[0], pvName.Buffer(), PV_NAME_MAX_SIZE);
if (td == CharString) {
pvs[n].pvType = DBR_STRING;
}
else if (td == Character8Bit) {
pvs[n].pvType = DBR_STRING;
}
else if (td == SignedInteger8Bit) {
pvs[n].pvType = DBR_CHAR;
}
else if (td == UnsignedInteger8Bit) {
pvs[n].pvType = DBR_CHAR;
}
else if (td == SignedInteger16Bit) {
pvs[n].pvType = DBR_SHORT;
}
else if (td == UnsignedInteger16Bit) {
pvs[n].pvType = DBR_SHORT;
}
else if (td == SignedInteger32Bit) {
pvs[n].pvType = DBR_LONG;
}
else if (td == UnsignedInteger32Bit) {
pvs[n].pvType = DBR_LONG;
}
else if (td == Float32Bit) {
pvs[n].pvType = DBR_FLOAT;
}
else if (td == Float64Bit) {
pvs[n].pvType = DBR_DOUBLE;
}
else {
REPORT_ERROR(ErrorManagement::ParametersError, "Type %s is not supported", TypeDescriptor::GetTypeNameFromTypeDescriptor(td));
ok = false;
}
}
uint32 numberOfElements = 1u;
if (ok) {
ok = GetSignalNumberOfElements(n, numberOfElements);
}
if (ok) {
if (pvs[n].pvType == DBR_STRING) {
ok = (numberOfElements == 40u);
}
if (!ok) {
//Could support arrays of strings with multiples of char8[40]
REPORT_ERROR(ErrorManagement::ParametersError,
"Strings shall be defined with 40 elements char8[40]. Arrays of strings are not currently supported");
}
}
if (ok) {
pvs[n].numberOfElements = numberOfElements;
}
if (ok) {
pvs[n].memorySize = td.numberOfBits;
pvs[n].memorySize /= 8u;
pvs[n].memorySize *= numberOfElements;
pvs[n].memory = GlobalObjectsDatabase::Instance()->GetStandardHeap()->Malloc(pvs[n].memorySize);
pvs[n].previousValue = GlobalObjectsDatabase::Instance()->GetStandardHeap()->Malloc(pvs[n].memorySize);
ok = originalSignalInformation.MoveToAncestor(1u);
}
}
}
return ok;
}
bool JAEPICSCAOutput::AllocateMemory() {
return true;
}
uint32 JAEPICSCAOutput::GetNumberOfMemoryBuffers() {
return 1u;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: The signalAddress is independent of the bufferIdx.*/
bool JAEPICSCAOutput::GetSignalMemoryBuffer(const uint32 signalIdx, const uint32 bufferIdx, void*& signalAddress) {
bool ok = (pvs != NULL_PTR(PVWrapper *));
if (ok) {
ok = (signalIdx < GetNumberOfSignals());
}
if (ok) {
//lint -e{613} pvs cannot as otherwise ok would be false
signalAddress = pvs[signalIdx].memory;
}
return ok;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: The brokerName only depends on the direction */
const char8* JAEPICSCAOutput::GetBrokerName(StructuredDataI& data, const SignalDirection direction) {
const char8* brokerName = "";
if (direction == OutputSignals) {
brokerName = "MemoryMapAsyncOutputBroker";
}
return brokerName;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: InputBrokers are not supported. Function returns false irrespectively of the parameters.*/
bool JAEPICSCAOutput::GetInputBrokers(ReferenceContainer& inputBrokers, const char8* const functionName, void* const gamMemPtr) {
return false;
}
bool JAEPICSCAOutput::GetOutputBrokers(ReferenceContainer& outputBrokers, const char8* const functionName, void* const gamMemPtr) {
ReferenceT<MemoryMapAsyncOutputBroker> broker("MemoryMapAsyncOutputBroker");
bool ok = broker->InitWithBufferParameters(OutputSignals, *this, functionName, gamMemPtr, numberOfBuffers, cpuMask, stackSize);
if (ok) {
ok = outputBrokers.Insert(broker);
broker->SetIgnoreBufferOverrun(ignoreBufferOverrun == 1u);
}
return ok;
}
/*lint -e{715} [MISRA C++ Rule 0-1-11], [MISRA C++ Rule 0-1-12]. Justification: NOOP at StateChange, independently of the function parameters.*/
bool JAEPICSCAOutput::PrepareNextState(const char8* const currentStateName, const char8* const nextStateName) {
return true;
}
uint32 JAEPICSCAOutput::GetStackSize() const {
return stackSize;
}
uint32 JAEPICSCAOutput::GetCPUMask() const {
return cpuMask;
}
uint32 JAEPICSCAOutput::GetNumberOfBuffers() const {
return numberOfBuffers;
}
bool JAEPICSCAOutput::Synchronise() {
bool ok = true;
uint32 n;
uint32 nOfSignals = GetNumberOfSignals();
if (!threadContextSet) {
ok = (ca_context_create(ca_enable_preemptive_callback) == ECA_NORMAL);
if (!ok) {
REPORT_ERROR(ErrorManagement::FatalError, "ca_enable_preemptive_callback failed");
}
threadContextSet = ok;
if (pvs != NULL_PTR(PVWrapper *)) {
for (n = 0u; (n < nOfSignals); n++) {
ok = (ca_create_channel(&pvs[n].pvName[0], NULL_PTR(caCh *), NULL_PTR(void *), 20u, &pvs[n].pvChid) == ECA_NORMAL);
if (!ok) {
REPORT_ERROR(ErrorManagement::FatalError, "ca_create_channel failed for PV with name %s", pvs[n].pvName);
}
}
}
}
//Allow to write event at the first time!
if (threadContextSet) {
if (pvs != NULL_PTR(PVWrapper *)) {
for (n = 0u; (n < nOfSignals); n++) {
bool isNewValue = true;
if (pvs[n].pvType == DBR_STRING) {
if(strcmp((char*)pvs[n].memory,(char*)pvs[n].previousValue)==0){
isNewValue = false;
continue;
}
if(isNewValue){
ok = (ca_put(pvs[n].pvType, pvs[n].pvChid, pvs[n].memory) == ECA_NORMAL);
memcpy(pvs[n].previousValue,pvs[n].memory, pvs[n].numberOfElements);
}
}
else {
if(memcmp(pvs[n].memory, pvs[n].previousValue, pvs[n].numberOfElements)==0){
isNewValue = false;
continue;
}
if(isNewValue){
ok = (ca_array_put(pvs[n].pvType, pvs[n].numberOfElements, pvs[n].pvChid, pvs[n].memory) == ECA_NORMAL);
memcpy(pvs[n].previousValue, pvs[n].memory, pvs[n].numberOfElements);
}
}
if (!ok) {
REPORT_ERROR(ErrorManagement::FatalError, "ca_put failed for PV: %s", pvs[n].pvName);
}
(void) ca_pend_io(0.1);
}
}
}
return ok;
}
bool JAEPICSCAOutput::IsIgnoringBufferOverrun() const {
return (ignoreBufferOverrun == 1u);
}
CLASS_REGISTER(JAEPICSCAOutput, "1.0")
}

View File

@@ -0,0 +1,233 @@
/**
* @file EPICSCAOutput.h
* @brief Header file for class EPICSCAOutput
* @date 20/04/2017
* @author Andre Neto
*
* @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
* the Development of Fusion Energy ('Fusion for Energy').
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence")
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
*
* @warning Unless required by applicable law or agreed to in writing,
* software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence permissions and limitations under the Licence.
* @details This header file contains the declaration of the class EPICSCAOutput
* with all of its public, protected and private members. It may also include
* definitions for inline methods which need to be visible to the compiler.
*/
#ifndef JAEPICSCAOutput_H_
#define JAEPICSCAOutput_H_
/*---------------------------------------------------------------------------*/
/* Standard header includes */
/*---------------------------------------------------------------------------*/
#include <cadef.h>
/*---------------------------------------------------------------------------*/
/* Project header includes */
/*---------------------------------------------------------------------------*/
#include "DataSourceI.h"
#include "JAEPICSCAInput.h"
#include "EmbeddedServiceMethodBinderI.h"
#include "EventSem.h"
#include "SingleThreadService.h"
/*---------------------------------------------------------------------------*/
/* Class declaration */
/*---------------------------------------------------------------------------*/
namespace MARTe {
//Maximum size that a PV name may have
/**
* @brief A DataSource which allows to output data into any number of PVs using the EPICS channel access client protocol.
* Data is asynchronously ca_put in the context of a different thread (w.r.t. to the real-time thread).
*
* The configuration syntax is (names are only given as an example):
*
* <pre>
* +EPICSCAOutput_1 = {
* Class = EPICSCA::EPICSCAOutput
* StackSize = 1048576 //Optional the EmbeddedThread stack size. Default value is THREADS_DEFAULT_STACKSIZE * 4u
* CPUs = 0xff //Optional the affinity of the EmbeddedThread (where the EPICS context is attached).
* IgnoreBufferOverrun = 1 //Optional. If true no error will be triggered when the thread that writes into EPICS does not consume the data fast enough.
* NumberOfBuffers = 10 //Compulsory. Number of buffers in a circular buffer that asynchronously writes the PV values. Each buffer is capable of holding a copy of all the DataSourceI signals.
* Signals = {
* PV1 = { //At least one shall be defined
* PVName = My::PV1 //Compulsory. Name of the PV.
* Type = uint32 //Compulsory. Supported types are char8[40], string[40], uint8, int8, uint16, int16, int32, uint32, float32 and float64
* }
* ...
* }
* }
*
* </pre>
*/
class JAEPICSCAOutput: public DataSourceI {
public:
CLASS_REGISTER_DECLARATION()
/**
* @brief Default constructor. NOOP.
*/
JAEPICSCAOutput();
/**
* @brief Destructor.
* @details TODO.
*/
virtual ~JAEPICSCAOutput();
/**
* @brief See DataSourceI::AllocateMemory. NOOP.
* @return true.
*/
virtual bool AllocateMemory();
/**
* @brief See DataSourceI::GetNumberOfMemoryBuffers.
* @return 1.
*/
virtual uint32 GetNumberOfMemoryBuffers();
/**
* @brief See DataSourceI::GetSignalMemoryBuffer.
* @pre
* SetConfiguredDatabase
*/
virtual bool GetSignalMemoryBuffer(const uint32 signalIdx,
const uint32 bufferIdx,
void *&signalAddress);
/**
* @brief See DataSourceI::GetNumberOfMemoryBuffers.
* @details Only OutputSignals are supported.
* @return MemoryMapAsyncOutputBroker.
*/
virtual const char8 *GetBrokerName(StructuredDataI &data,
const SignalDirection direction);
/**
* @brief See DataSourceI::GetInputBrokers.
* @return false.
*/
virtual bool GetInputBrokers(ReferenceContainer &inputBrokers,
const char8* const functionName,
void * const gamMemPtr);
/**
* @brief See DataSourceI::GetOutputBrokers.
* @details adds a memory MemoryMapOutputBroker instance to the outputBrokers
* @return true.
*/
virtual bool GetOutputBrokers(ReferenceContainer &outputBrokers,
const char8* const functionName,
void * const gamMemPtr);
/**
* @brief See DataSourceI::PrepareNextState. NOOP.
* @return true.
*/
virtual bool PrepareNextState(const char8 * const currentStateName,
const char8 * const nextStateName);
/**
* @brief Loads and verifies the configuration parameters detailed in the class description.
* @return true if all the mandatory parameters are correctly specified and if the specified optional parameters have valid values.
*/
virtual bool Initialise(StructuredDataI & data);
/**
* @brief Final verification of all the parameters. Setup of the memory required to hold all the signals.
* @details This method verifies that all the parameters requested by the GAMs interacting with this DataSource
* are valid and consistent with the parameters set during the initialisation phase.
* In particular the following conditions shall be met:
* - All the signals have the PVName defined
* - All the signals have one of the following types: uint32, int32, float32 or float64.
* @return true if all the parameters are valid and the conditions above are met.
*/
virtual bool SetConfiguredDatabase(StructuredDataI & data);
/**
* @brief Gets the affinity of the thread which is going to be used to asynchronously write data with ca_put.
* @return the affinity of the thread which is going to be used to asynchronously write data with ca_put.
*/
uint32 GetCPUMask() const;
/**
* @brief Gets the stack size of the thread which is going to be used to asynchronously write data with ca_put.
* @return the stack size of the thread which is going to be used to asynchronously write data with ca_put.
*/
uint32 GetStackSize() const;
/**
* @brief Gets the number of buffers in the circular buffer that asynchronously writes the PV values.
* @return the number of buffers in the circular buffer that asynchronously writes the PV values.
*/
uint32 GetNumberOfBuffers() const;
/**
* @brief Provides the context to execute all the EPICS ca_put calls.
* @details Executes in the context of the MemoryMapAsyncOutputBroker thread the following EPICS calls:
* ca_context_create, ca_create_channel, ca_create_subscription, ca_clear_subscription,
* ca_clear_event, ca_clear_channel, ca_detach_context and ca_context_destroy
* @return true if all the EPICS calls return without any error.
*/
virtual bool Synchronise();
/**
* @brief Gets if buffer overruns is being ignored (i.e. the consumer thread which writes into EPICS is not consuming the data fast enough).
* @return if true no error is to be triggered when there is a buffer overrun.
*/
bool IsIgnoringBufferOverrun() const;
private:
/**
* List of PVs.
*/
PVWrapper *pvs;
/**
* The CPU mask for the executor
*/
uint32 cpuMask;
/**
* The stack size
*/
uint32 stackSize;
/**
* Stores the configuration information received at Initialise.
*/
ConfigurationDatabase originalSignalInformation;
/**
* The number of buffers for the circular buffer that flushes data into EPICS
*/
uint32 numberOfBuffers;
/**
* True once the epics thread context is set
*/
bool threadContextSet;
/**
* If true no error will be triggered when the data cannot be consumed by the thread doing the caputs.
*/
uint32 ignoreBufferOverrun;
};
}
/*---------------------------------------------------------------------------*/
/* Inline method definitions */
/*---------------------------------------------------------------------------*/
#endif /* EPICSCADATASOURCE_H_ */

View File

@@ -0,0 +1,29 @@
#############################################################
#
# Copyright 2015 F4E | European Joint Undertaking for ITER
# and the Development of Fusion Energy ('Fusion for Energy')
#
# Licensed under the EUPL, Version 1.1 or - as soon they
# will be approved by the European Commission - subsequent
# versions of the EUPL (the "Licence");
# You may not use this work except in compliance with the
# Licence.
# You may obtain a copy of the Licence at:
#
# http://ec.europa.eu/idabc/eupl
#
# Unless required by applicable law or agreed to in
# writing, software distributed under the Licence is
# distributed on an "AS IS" basis,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied.
# See the Licence for the specific language governing
# permissions and limitations under the Licence.
#
# $Id: Makefile.gcc 3 2012-01-15 16:26:07Z aneto $
#
#############################################################
include Makefile.inc
LIBRARIES += -L$(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)/ -lca

View File

@@ -0,0 +1,58 @@
#############################################################
#
# Copyright 2015 F4E | European Joint Undertaking for ITER
# and the Development of Fusion Energy ('Fusion for Energy')
#
# Licensed under the EUPL, Version 1.1 or - as soon they
# will be approved by the European Commission - subsequent
# versions of the EUPL (the "Licence");
# You may not use this work except in compliance with the
# Licence.
# You may obtain a copy of the Licence at:
#
# http://ec.europa.eu/idabc/eupl
#
# Unless required by applicable law or agreed to in
# writing, software distributed under the Licence is
# distributed on an "AS IS" basis,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied.
# See the Licence for the specific language governing
# permissions and limitations under the Licence.
#
# $Id: Makefile.inc 3 2012-01-15 16:26:07Z aneto $
#
#############################################################
OBJSX=JAEPICSCAOutput.x JAEPICSCAInput.x
PACKAGE=DataSources
ROOT_DIR=../../
MAKEDEFAULTDIR=$(MARTe2_DIR)/MakeDefaults
include $(MAKEDEFAULTDIR)/MakeStdLibDefs.$(TARGET)
INCLUDES += -I.
INCLUDES += -I$(EPICS_BASE)/include/
INCLUDES += -I$(EPICS_BASE)/include/os/Linux/
INCLUDES += -I$(EPICS_BASE)/include/compiler/gcc/
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L0Types
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L1Portability
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L2Objects
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L3Streams
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L4Messages
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L4Configuration
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L5GAMs
INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L1Portability
INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L3Services
INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L4Messages
INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L5GAMs
all: $(OBJS) $(SUBPROJ) \
$(BUILD_DIR)/JAEPICSCA$(LIBEXT) \
$(BUILD_DIR)/JAEPICSCA$(DLLEXT)
echo $(OBJS)
include depends.$(TARGET)
include $(MAKEDEFAULTDIR)/MakeStdLibRules.$(TARGET)