/** * @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(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 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") }