From 7aae74008aa3bb1c8b30111fc82c61a3d1622952 Mon Sep 17 00:00:00 2001 From: Geoff Phillips Date: Wed, 10 May 2023 10:09:35 +0200 Subject: [PATCH] Release v1.0.5 --- .clang-format | 1 + .git-blame-ignore-revs | 2 + .github/template/fwe-build/action.yml | 11 +- CHANGELOG.md | 19 + CMakeLists.txt | 2 +- README.md | 13 +- THIRD-PARTY-LICENSES | 6 +- configuration/static-config.json | 1 - docs/dev-guide/edge-agent-dev-guide.md | 110 ++- docs/metrics.md | 200 ++++ .../cloudToEdge/collection_schemes.proto | 4 +- .../cloudToEdge/decoder_manifest.proto | 4 +- .../staticConfiguration.json | 11 +- .../schemas/edgeToCloud/vehicle_data.proto | 4 +- .../custom/example/iwavegps/README.md | 4 +- .../example/iwavegps/include/IWaveGpsSource.h | 25 +- .../example/iwavegps/src/IWaveGpsSource.cpp | 99 +- .../iwavegps/test/IWaveGpsSourceTest.cpp | 10 +- .../custom/generic/include/CustomDataSource.h | 4 +- .../custom/generic/src/CustomDataSource.cpp | 32 +- .../datacollection/CMakeLists.txt | 2 +- .../include/CollectionSchemeIngestion.h | 2 +- .../include/DataCollectionSender.h | 5 +- .../include/ICollectionScheme.h | 7 +- .../src/CollectionSchemeIngestion.cpp | 50 +- .../src/DataCollectionProtoWriter.cpp | 2 +- .../src/DataCollectionSender.cpp | 17 +- .../test/DataCollectionProtoWriterTest.cpp | 6 +- .../test/DataCollectionSenderTest.cpp | 4 +- .../datadecoding/CMakeLists.txt | 2 +- .../datadecoding/include/CANDecoder.h | 22 +- .../datadecoding/include/IDecoderDictionary.h | 7 +- .../datadecoding/include/IDecoderManifest.h | 5 - .../datadecoding/src/CANDecoder.cpp | 19 +- .../src/DecoderManifestIngestion.cpp | 31 +- .../datadecoding/src/OBDDataDecoder.cpp | 6 +- .../datadecoding/test/CANDecoderTest.cpp | 135 ++- .../datainspection/CMakeLists.txt | 8 +- .../datainspection/include/CANDataConsumer.h | 83 +- .../datainspection/include}/CANDataSource.h | 80 +- .../include/CollectionInspectionEngine.h | 3 +- .../CollectionInspectionWorkerThread.h | 6 +- .../include/GeohashFunctionNode.h | 15 +- .../include/IActiveConditionProcessor.h | 4 +- .../include/IVehicleDataConsumer.h | 158 ---- .../datainspection/include/OBDOverCANModule.h | 22 +- .../include/VehicleDataSourceBinder.h | 142 --- .../src/CollectionInspectionEngine.cpp | 14 +- .../src/CollectionInspectionWorkerThread.cpp | 26 +- .../src/diag/OBDOverCANModule.cpp | 120 ++- .../src/location/GeohashFunctionNode.cpp | 6 +- .../src/vehicledatasource/CANDataConsumer.cpp | 496 +++------- .../src/vehicledatasource}/CANDataSource.cpp | 227 ++--- .../VehicleDataSourceBinder.cpp | 503 ---------- .../datainspection/test/CANDataSourceTest.cpp | 331 +++++++ .../test/CollectionInspectionEngineTest.cpp | 16 +- .../test/OBDOverCANModuleTest.cpp | 3 +- .../test/VehicleDataSourceBinderTest.cpp | 872 ------------------ .../include/CollectionSchemeManager.h | 33 +- .../datamanager/include/Schema.h | 10 +- .../datamanager/src/CheckinAndPersistency.cpp | 6 +- .../src/CollectionSchemeManager.cpp | 117 +-- .../src/DecoderDictionaryExtractor.cpp | 28 +- .../test/DecoderDictionaryExtractorTest.cpp | 39 +- .../datamanager/test/SchemaTest.cpp | 12 +- .../include/CollectionSchemeManagerTest.h | 39 - .../types/include/CANDataTypes.h | 21 +- .../include/CollectionInspectionAPITypes.h | 18 +- .../types/include/OBDDataTypes.h | 6 +- .../types/include/SignalTypes.h | 3 + .../include/IoTFleetWiseEngine.h | 7 +- .../src/IoTFleetWiseEngine.cpp | 186 ++-- .../src/IoTFleetWiseVersion.cpp.in | 17 +- src/executionmanagement/src/main.cpp | 27 +- .../test/IoTFleetWiseConfigTest.cpp | 1 - .../test/em-example-config.json | 1 - .../implementation/aws/CMakeLists.txt | 4 +- .../aws/bootstrap/src/AwsBootstrap.cpp | 2 +- .../aws/bootstrap/src/AwsSDKMemoryManager.cpp | 14 +- .../aws/iotcpp/include/PayloadManager.h | 2 +- .../aws/iotcpp/include/RetryThread.h | 6 +- .../aws/iotcpp/src/AwsIotChannel.cpp | 11 +- .../iotcpp/src/AwsIotConnectivityModule.cpp | 13 +- .../aws/iotcpp/src/PayloadManager.cpp | 4 +- .../aws/iotcpp/src/RemoteProfiler.cpp | 36 +- .../aws/iotcpp/src/RetryThread.cpp | 5 +- .../logmanagement/include/ConsoleLogger.h | 2 +- .../linux/logmanagement/include/TraceModule.h | 36 +- .../linux/logmanagement/src/ConsoleLogger.cpp | 6 +- .../linux/logmanagement/src/TraceModule.cpp | 295 +++--- .../src/CacheAndPersist.cpp | 10 +- .../resourcemanagement/src/CPUUsageInfo.cpp | 10 +- .../src/MemoryUsageInfo.cpp | 2 +- .../threadingmanagement/include/Listener.h | 11 - .../threadingmanagement/include/Signal.h | 13 +- .../threadingmanagement/include/Thread.h | 2 +- .../linux/threadingmanagement/src/Thread.cpp | 2 +- .../linux/timemanagement/include/Timer.h | 3 +- src/testingsupport/include/WaitUntil.h | 20 +- src/testingsupport/test/WaitUntilTest.cpp | 8 +- src/vehiclenetwork/CMakeLists.txt | 10 +- .../businterfaces/AbstractVehicleDataSource.h | 155 ---- .../businterfaces/VehicleDataSourceListener.h | 41 - .../include/datatypes/VehicleDataMessage.h | 115 --- .../datatypes/VehicleDataSourceConfig.h | 38 - .../datatypes/VehicleDataSourceTypes.h | 49 +- .../include/dds/CameraDataSubscriber.h | 2 +- .../include/dds/IDDSPublisher.h | 23 +- .../include/dds/IDDSSubscriber.h | 23 +- .../src/CameraDataPublisher.cpp | 1 - .../src/CameraDataSubscriber.cpp | 1 - src/vehiclenetwork/test/CANDataSourceTest.cpp | 402 -------- .../test/VehicleDataMessageTest.cpp | 36 - tools/cfn-templates/fwdemo.yml | 248 ++--- tools/cfn-templates/fwdev.yml | 142 +-- tools/cfn-templates/fwremoteprofiler.yml | 112 +++ tools/install-deps-versions.sh | 4 +- 117 files changed, 2004 insertions(+), 4505 deletions(-) create mode 100644 .git-blame-ignore-revs create mode 100644 docs/metrics.md rename src/{vehiclenetwork/include/businterfaces => datamanagement/datainspection/include}/CANDataSource.h (54%) delete mode 100644 src/datamanagement/datainspection/include/IVehicleDataConsumer.h delete mode 100644 src/datamanagement/datainspection/include/VehicleDataSourceBinder.h rename src/{vehiclenetwork/src => datamanagement/datainspection/src/vehicledatasource}/CANDataSource.cpp (61%) delete mode 100644 src/datamanagement/datainspection/src/vehicledatasource/VehicleDataSourceBinder.cpp create mode 100644 src/datamanagement/datainspection/test/CANDataSourceTest.cpp delete mode 100644 src/datamanagement/datainspection/test/VehicleDataSourceBinderTest.cpp delete mode 100644 src/vehiclenetwork/include/businterfaces/AbstractVehicleDataSource.h delete mode 100644 src/vehiclenetwork/include/businterfaces/VehicleDataSourceListener.h delete mode 100644 src/vehiclenetwork/include/datatypes/VehicleDataMessage.h delete mode 100644 src/vehiclenetwork/include/datatypes/VehicleDataSourceConfig.h delete mode 100644 src/vehiclenetwork/test/CANDataSourceTest.cpp delete mode 100644 src/vehiclenetwork/test/VehicleDataMessageTest.cpp create mode 100644 tools/cfn-templates/fwremoteprofiler.yml diff --git a/.clang-format b/.clang-format index 4808e0c7..15747fa8 100644 --- a/.clang-format +++ b/.clang-format @@ -2,6 +2,7 @@ BasedOnStyle: Microsoft IndentWidth: 4 AccessModifierOffset: -4 +AllowShortLambdasOnASingleLine: Empty AlwaysBreakAfterReturnType: AllDefinitions AlwaysBreakTemplateDeclarations: Yes BreakConstructorInitializers: BeforeComma diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..0a903627 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Format most files with black and prettier +5048591323cd5aac93af69d0d2b4e359be33b9a3 diff --git a/.github/template/fwe-build/action.yml b/.github/template/fwe-build/action.yml index f197b048..b642ec35 100644 --- a/.github/template/fwe-build/action.yml +++ b/.github/template/fwe-build/action.yml @@ -38,7 +38,11 @@ runs: - name: build shell: bash - run: ./tools/build-fwe-${{ inputs.build-arch }}.sh + run: | + ./tools/build-fwe-${{ inputs.build-arch }}.sh + ./tools/build-dist.sh build/src/executionmanagement/aws-iot-fleetwise-edge + # If the output file changes, make sure to update the upload-asset job below + mv build/aws-iot-fleetwise-edge.tar.gz aws-iot-fleetwise-edge-${{ inputs.upload-arch }}.tar.gz - name: unit-test shell: bash @@ -61,7 +65,4 @@ runs: shell: bash run: | RELEASE_VERSION="${GITHUB_REF/refs\/tags\//}" - ASSET_FILENAME="aws-iot-fleetwise-edge-${{ inputs.upload-arch }}.tar.gz" - ./tools/build-dist.sh build/src/executionmanagement/aws-iot-fleetwise-edge - mv build/aws-iot-fleetwise-edge.tar.gz ${ASSET_FILENAME} - gh release upload ${RELEASE_VERSION} ${ASSET_FILENAME} + gh release upload ${RELEASE_VERSION} aws-iot-fleetwise-edge-${{ inputs.upload-arch }}.tar.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ba36fe6..d21beb49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Change Log +## v1.0.5 (2023-05-11) + +Bugfixes: + +- RemoteProfiler not always uploading logs + +Improvements: + +- Refactor Producer/Consumer architecture, removing the buffer and thread between the + `CANDataSource` and the `CANDataConsumer`. The static config option `socketCANBufferSize` was + therefore removed. +- Add documentation on [how to use edge specific metrics](docs/metrics.md). +- Change from `arn` to `sync_id` for all decoder manifest Protobuf fields, the `sync_id` being the + ARN followed by the timestamp of the last update. The change is backwards compatible with older + versions of the edge agent. +- Improve MISRA C++ 2008, and AUTOSAR C++ compliance. +- Updated CloudFormation templates to use + [IMDSv2](https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/). + ## v1.0.4 (2023-03-02) Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index a7384b0c..b30f85d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10.2) -project(iotfleetwise VERSION 1.0.4) +project(iotfleetwise VERSION 1.0.5) # AWS IoT FleetWise Edge uses C++14 for compatibility reasons with # Automotive middlewares ( Adaptive AUTOSAR, ROS2) diff --git a/README.md b/README.md index ae516a86..6029c4b8 100644 --- a/README.md +++ b/README.md @@ -109,16 +109,16 @@ See [SECURITY](./SECURITY.md) for more information Edge Agent Reference Implementation for AWS IoT FleetWise depends on the following open source libraries. Refer to the corresponding links for more information. -- [AWS SDK for C++: v1.9.253](https://github.com/aws/aws-sdk-cpp) +- [AWS SDK for C++: v1.11.46](https://github.com/aws/aws-sdk-cpp) - [Curl: v7.58.0](https://github.com/curl/curl) - [OpenSSL: v1.1.1](https://github.com/openssl/openssl) - [zlib: v1.2.11](https://github.com/madler/zlib) - [GoogleTest: v1.10.0](https://github.com/google/googletest) - [Google Benchmark: v1.6.1](https://github.com/google/benchmark) - [Protobuf: v3.21.7](https://github.com/protocolbuffers/protobuf) -- [Boost: v1.65.1](https://github.com/boostorg/boost) -- [JsonCpp: v1.7.4](https://github.com/open-source-parsers/jsoncpp) -- [Snappy: v1.1.7](https://github.com/google/snappy) +- [Boost: v1.71.1](https://github.com/boostorg/boost) +- [JsonCpp: v1.9.5](https://github.com/open-source-parsers/jsoncpp) +- [Snappy: v1.1.8](https://github.com/google/snappy) Optional: The following dependencies are only required when the experimental option `FWE_FEATURE_CAMERA` is enabled. @@ -136,6 +136,11 @@ See [LICENSE](./LICENSE) for more information. [Contact AWS Support](https://aws.amazon.com/contact-us/) if you have any technical questions about Edge Agent Reference Implementation for AWS IoT FleetWise. +## Metrics + +See [Metrics](./docs/metrics.md) for details, which Edge specific metrics exist and how they can be +accessed. + ## Resources The following documents provide more information about AWS IoT FleetWise Edge. diff --git a/THIRD-PARTY-LICENSES b/THIRD-PARTY-LICENSES index 13b3ef94..60b0d6ed 100644 --- a/THIRD-PARTY-LICENSES +++ b/THIRD-PARTY-LICENSES @@ -1,6 +1,6 @@ AWS IoT FleetWise Edge includes the following third-party software/licensing: -** AWS SDK for C++: v1.9.253 - https://github.com/aws/aws-sdk-cpp +** AWS SDK for C++: v1.11.46 - https://github.com/aws/aws-sdk-cpp ** Fast-DDS: v2.3.3 - https://github.com/eProsima/Fast-DDS ** Fast-CDR: v1.0.21 - https://github.com/eProsima/Fast-CDR ** Foonathan Memory Vendor: v1.1.0 - https://github.com/eProsima/foonathan_memory_vendor @@ -246,7 +246,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------- -** Boost: v1.65.1 - https://github.com/boostorg/boost +** Boost: v1.71.1 - https://github.com/boostorg/boost Boost Software License - Version 1.0 - August 17th, 2003 @@ -274,7 +274,7 @@ DEALINGS IN THE SOFTWARE. ---------------- -** JsonCpp: v1.7.4 - https://github.com/open-source-parsers/jsoncpp +** JsonCpp: v1.9.5 - https://github.com/open-source-parsers/jsoncpp The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following diff --git a/configuration/static-config.json b/configuration/static-config.json index 4bd94d4b..835dcc55 100644 --- a/configuration/static-config.json +++ b/configuration/static-config.json @@ -25,7 +25,6 @@ "staticConfig": { "bufferSizes": { "dtcBufferSize": 100, - "socketCANBufferSize": 10000, "decodedSignalsBufferSize": 10000, "rawCANFrameBufferSize": 10000 }, diff --git a/docs/dev-guide/edge-agent-dev-guide.md b/docs/dev-guide/edge-agent-dev-guide.md index 8a9e2396..8e4b02be 100644 --- a/docs/dev-guide/edge-agent-dev-guide.md +++ b/docs/dev-guide/edge-agent-dev-guide.md @@ -540,23 +540,15 @@ figure above. **Vehicle Data Acquisition** This layer binds the AWS IoT FleetWise Edge Agent software with the vehicle network, leveraging the -onboard device drivers and middle-wares the OEM uses in the target hardware. This layer listens -and/or requests data from the vehicle network and forwards it to the Normalization layer for further -decoding. It has a dependency on the host environment including the operating system, the peripheral -drivers and the vehicle architecture overall. It communicates with the Vehicle Data Normalization -layer via a message queue that implements a circular buffer, a FIFO, with a fixed size. Each network -interface e.g. a CAN Bus Interface gets allocated a message queue and can be activated/deactivated -separately. - -**Vehicle Data Normalization** - -This layer operates on the raw data received from the vehicle data acquisition layer, by applying -various signal decoding rules that are vehicle protocol specific e.g. CAN Signal database files. The -output of this layer is a set of transformed key/value pairs of signals and their corresponding -values. This layer has a dependency on the decoding rules the OEM has defined for the signals they -want to inspect, which are specified in the Cloud Control plane. This layer consumes the message -queues provided by the data acquisition layer, and stores the decoded values into a signal history -buffer. This signal history buffer has a maximum fixed size to not exhaust the system resources. +onboard device drivers and middle-wares the OEM uses in the target hardware. It has a dependency on +the host environment including the operating system, the peripheral drivers and the vehicle +architecture overall. This layer listens and/or requests data from the vehicle network and +normalizes it, before applying various signal decoding rules that are vehicle protocol specific e.g. +CAN Signal database files. The output of this layer is a set of transformed key/value pairs of +signals and their corresponding values. This layer has a dependency on the decoding rules the OEM +has defined for the signals they want to inspect, which are specified in the Cloud Control plane. +This layer stores the decoded values into a signal history buffer. This signal history buffer has a +maximum fixed size to not exhaust the system resources. **Vehicle Data Inspection** @@ -1590,45 +1582,50 @@ described below in the configuration section. Each log entry includes the follow ## Configuration -| Category | Attributes | Description | DataType | -| ------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -| canInterface | interfaceName | Interface name for CAN network | string | -| | protocolName | Protocol used- CAN or CAN-FD | string | -| | protocolVersion | Protocol version used- 2.0A, 2.0B. | string | -| | interfaceId | Every CAN signal decoder is associated with a CAN network interface using a unique Id | string | -| | type | Specifies if the interface carries CAN or OBD signals over this channel, this will be CAN for a CAN network interface | string | -| | timestampType | Defines which timestamp type should be used: Software, Hardware or Polling. Default is Software. | string | -| obdInterface | interfaceName | CAN Interface connected to OBD bus | string | -| | obdStandard | OBD Standard (eg. J1979 or Enhanced (for advanced standards)) | string | -| | pidRequestIntervalSeconds | Interval used to schedule PID requests (in seconds) | integer | -| | dtcRequestIntervalSeconds | Interval used to schedule DTC requests (in seconds) | integer | -| | interfaceId | Every OBD signal decoder is associated with a OBD network interface using a unique Id | string | -| | type | Specifies if the interface carries CAN or OBD signals over this channel, this will be OBD for a OBD network interface | string | -| bufferSizes | dtcBufferSize | Max size of the buffer shared between data collection module (Collection Engine) and Vehicle Data Consumer. This is a single producer single consumer buffer. | integer | -| | socketCANBufferSize | Max size of the circular buffer associated with a network channel (CAN Bus) for data consumption from that channel. This is a single producer-single consumer buffer. | integer | -| | decodedSignalsBufferSize | Max size of the buffer shared between data collection module (Collection Engine) and Vehicle Data Consumer for OBD and CAN signals. This buffer receives the raw packets from the Vehicle Data e.g. CAN bus and stores the decoded/filtered data according to the signal decoding information provided in decoder manifest. This is a multiple producer single consumer buffer. | integer | -| | rawCANFrameBufferSize | Max size of the buffer shared between Vehicle Data Consumer and data collection module (Collection Engine). This buffer stores raw CAN frames coming in from the CAN Bus. This is a lock-free multi-producer single consumer buffer. | integer | -| threadIdleTimes | inspectionThreadIdleTimeMs | Sleep time for inspection engine thread if no new data is available (in milliseconds) | integer | -| | socketCANThreadIdleTimeMs | Sleep time for CAN interface if no new data is available (in milliseconds) | integer | -| | canDecoderThreadIdleTimeMs | Sleep time for CAN decoder thread if no new data is available (in milliseconds) | integer | -| persistency | persistencyPath | Local storage path to persist Collection Scheme, decoder manifest and data snapshot | string | -| | persistencyPartitionMaxSize | Maximum size allocated for persistency (Bytes) | integer | -| | persistencyUploadRetryIntervalMs | Interval to wait before retrying to upload persisted signal data (in milliseconds). After successfully uploading, the persisted signal data will be cleared. Only signal data that could not be uploaded will be persisted. (in milliseconds) | integer | -| internalParameters | readyToPublishDataBufferSize | Size of the buffer used for storing ready to publish, filtered data | integer | -| | systemWideLogLevel | Sets logging level severity: `Trace`, `Info`, `Warning`, `Error` | string | -| | logColor | Whether logs should be colored: `Auto`, `Yes`, `No`. Default to `Auto`, meaning the agent will try to detect whether colored output is supported (for example when connected to a tty) | string | -| | dataReductionProbabilityDisabled | Disables probability-based DDC (only for debug purpose) | boolean | -| | metricsCyclicPrintIntervalMs | Sets the interval in milliseconds how often the application metrics should be printed to stdout. Default 0 means never | string | -| publishToCloudParameters | maxPublishMessageCount | Maximum messages that can be published to the cloud in one payload | integer | -| | collectionSchemeManagementCheckinIntervalMs | Time interval between collection schemes checkins(in milliseconds) | integer | -| mqttConnection | endpointUrl | AWS account’s IoT device endpoint | string | -| | clientId | The ID that uniquely identifies this device in the AWS Region | string | -| | collectionSchemeListTopic | Topic for subscribing to Collection Scheme | string | -| | decoderManifestTopic | Topic for subscribing to Decoder Manifest | string | -| | canDataTopic | Topic for sending collected data to cloud | string | -| | checkinTopic | Topic for sending checkins to the cloud | string | -| | certificateFilename | The path to the device’s certificate file | string | -| | privateKeyFilename | The path to the device’s private key file. | string | +| Category | Attributes | Description | DataType | +| --------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | +| canInterface | interfaceName | Interface name for CAN network | string | +| | protocolName | Protocol used- CAN or CAN-FD | string | +| | protocolVersion | Protocol version used- 2.0A, 2.0B. | string | +| | interfaceId | Every CAN signal decoder is associated with a CAN network interface using a unique Id | string | +| | type | Specifies if the interface carries CAN or OBD signals over this channel, this will be CAN for a CAN network interface | string | +| | timestampType | Defines which timestamp type should be used: Software, Hardware or Polling. Default is Software. | string | +| obdInterface | interfaceName | CAN Interface connected to OBD bus | string | +| | obdStandard | OBD Standard (eg. J1979 or Enhanced (for advanced standards)) | string | +| | pidRequestIntervalSeconds | Interval used to schedule PID requests (in seconds) | integer | +| | dtcRequestIntervalSeconds | Interval used to schedule DTC requests (in seconds) | integer | +| | interfaceId | Every OBD signal decoder is associated with a OBD network interface using a unique Id | string | +| | type | Specifies if the interface carries CAN or OBD signals over this channel, this will be OBD for a OBD network interface | string | +| bufferSizes | dtcBufferSize | Max size of the buffer shared between data collection module (Collection Engine) and Vehicle Data Consumer. This is a single producer single consumer buffer. | integer | +| | decodedSignalsBufferSize | Max size of the buffer shared between data collection module (Collection Engine) and Vehicle Data Consumer for OBD and CAN signals. This buffer receives the raw packets from the Vehicle Data e.g. CAN bus and stores the decoded/filtered data according to the signal decoding information provided in decoder manifest. This is a multiple producer single consumer buffer. | integer | +| | rawCANFrameBufferSize | Max size of the buffer shared between Vehicle Data Consumer and data collection module (Collection Engine). This buffer stores raw CAN frames coming in from the CAN Bus. This is a lock-free multi-producer single consumer buffer. | integer | +| threadIdleTimes | inspectionThreadIdleTimeMs | Sleep time for inspection engine thread if no new data is available (in milliseconds) | integer | +| | socketCANThreadIdleTimeMs | Sleep time for CAN interface if no new data is available (in milliseconds) | integer | +| | canDecoderThreadIdleTimeMs | Sleep time for CAN decoder thread if no new data is available (in milliseconds) | integer | +| persistency | persistencyPath | Local storage path to persist Collection Scheme, decoder manifest and data snapshot | string | +| | persistencyPartitionMaxSize | Maximum size allocated for persistency (Bytes) | integer | +| | persistencyUploadRetryIntervalMs | Interval to wait before retrying to upload persisted signal data (in milliseconds). After successfully uploading, the persisted signal data will be cleared. Only signal data that could not be uploaded will be persisted. (in milliseconds) | integer | +| internalParameters | readyToPublishDataBufferSize | Size of the buffer used for storing ready to publish, filtered data | integer | +| | systemWideLogLevel | Sets logging level severity: `Trace`, `Info`, `Warning`, `Error` | string | +| | logColor | Whether logs should be colored: `Auto`, `Yes`, `No`. Default to `Auto`, meaning the agent will try to detect whether colored output is supported (for example when connected to a tty) | string | +| | dataReductionProbabilityDisabled | Disables probability-based DDC (only for debug purpose) | boolean | +| | metricsCyclicPrintIntervalMs | Sets the interval in milliseconds how often the application metrics should be printed to stdout. Default 0 means never | string | +| publishToCloudParameters | maxPublishMessageCount | Maximum messages that can be published to the cloud in one payload | integer | +| | collectionSchemeManagementCheckinIntervalMs | Time interval between collection schemes checkins(in milliseconds) | integer | +| mqttConnection | endpointUrl | AWS account’s IoT device endpoint | string | +| | clientId | The ID that uniquely identifies this device in the AWS Region | string | +| | collectionSchemeListTopic | Topic for subscribing to Collection Scheme | string | +| | decoderManifestTopic | Topic for subscribing to Decoder Manifest | string | +| | canDataTopic | Topic for sending collected data to cloud | string | +| | checkinTopic | Topic for sending checkins to the cloud | string | +| | certificateFilename | The path to the device’s certificate file | string | +| | privateKeyFilename | The path to the device’s private key file. | string | +| | metricsUploadTopic | Topic used to upload application metrics in plain json. Only used if `remoteProfilerDefaultValues` section is configured | string | +| | loggingUploadTopic | Topic used to upload log messages in plain json. Only used if `remoteProfilerDefaultValues` section is configured | string | +| remoteProfilerDefaultValues | loggingUploadLevelThreshold | Only log messages with this or higher severity will be uploaded | integer | +| | metricsUploadIntervalMs | The interval in milliseconds to wait for uploading new values of all metrics | integer | +| | loggingUploadMaxWaitBeforeUploadMs | The maximum time in milliseconds to cache log messages before uploading them | string | +| | profilerPrefix | The prefix used to categorize the metrics and logs. Can be set unique per vehicle such as `clientId` or the same for all vehicles if metrics should be aggregated | string | ## Security @@ -1837,7 +1834,6 @@ The following documents or websites provide more information about AWS IoT Fleet "staticConfig": { "bufferSizes": { "dtcBufferSize": 100, - "socketCANBufferSize": 10000, "decodedSignalsBufferSize": 10000, "rawCANFrameBufferSize": 10000 }, diff --git a/docs/metrics.md b/docs/metrics.md new file mode 100644 index 00000000..49f403e7 --- /dev/null +++ b/docs/metrics.md @@ -0,0 +1,200 @@ +# Application level metrics + +The AWS IoT Fleetwise Edge agent includes a +[TraceModule](../src/platform/linux/logmanagement/src/TraceModule.cpp). The TraceModule provides a +set of metrics that are used as an entry point to efficiently diagnose issues, saving you time since +you no longer need to review the entire log of all edge agent instances running. + +- **`RFrames0` - `RFrames19`** are monotonic counters of the number of raw can frames read on each + bus. If these counters remain null or remain fixed for a longer runtime, the system might either + have no CAN bus traffic or there is no CAN bound data collection campaign ( e.g. OBD2 only + campaign ). +- **`ConInt`** and **`ConRes`** enable you to monitor the the number of MQTT connection + interruptions and connection resumptions. If and how long it takes to detect a connection loss + depends on the kernel configuration parameters `/proc/sys/net/ipv4/tcp/keepalive*` and the compile + time constants of AWS IoT Fleetwise Edge: `MQTT_CONNECT_KEEP_ALIVE_SECONDS` and + `MQTT_PING_TIMOUT_MS`. If the values of the metric `ConInt` are not null, the internet coverage in + the tested environment might be unreliable, or `MQTT_PING_TIMOUT_MS`, which defaults to 3 seconds, + needs to be increased because there's high latency to the IoT Core endpoint. Changing the AWS + Region can help to decrease latency. +- **`CeTrgCnt`** is a monotonic counter that monitors the number of triggers (inspection rules) + detected since the AWS IoT Fleetwise Edge process started. Triggers are detected if one or more + data collection campaign conditions are true. If this counter is larger than zero, but no data + appears in the cloud, either no actual data was collected ( such as a time-based data collection + campaign with no bus activity), or the data has been ingested to the cloud but there was an error + processing it. To debug this, + [enable cloud logs in AWS IoT Fleetwise settings](https://docs.aws.amazon.com/iot-fleetwise/latest/developerguide/logging-cw.html). +- **`QUEUE_CONSUMER_TO_INSPECTION_SIGNALS`** monitors the current count of signals in queue to the + signal history buffer. If this value is close to the value defined in the static config + `decodedSignalsBufferSize`, increase the static config, decrease `inspectionThreadIdleTimeMs`, + reduce the bus load or reduce the amount of decoded signals in the decoder manifest in the cloud. +- **`ConRej`** monitors the number of MQTT connection rejects. If this is not zero check the + certificates and make sure you use a unique client id for each vehicle. +- **`ConFail`** monitors the number of MQTT connection failures. This can have multiple root causes. + If this is not zero please check the logs and search for `Connection failed with error` +- **`FWE_STARTUP`** and **`FWE_SHUTDOWN`** provide the amount of time it takes to start and stop the + AWS IoT Edge Fleetwise process. If any value is more than 5 seconds, review the logs and make sure + all required resources such as internet and buses are available before starting the process. +- **`ObdE0`** to **`ObdE3`** monitors errors related to the OBD session. If you see non-zero values, + make sure you're connected to a compatible OBD vehicle which is powered on. Otherwise turn off the + OBD signals collection in the cloud. +- **`PmE3`** provides hints on whether the data persistency framework (a mechanism used to store and + forward vehicle data when no connectivity is available) has an error. If this error counter is not + zero, make sure that the directory defined in `persistencyPath` is writeable and that there is + space available in the filesystem +- **`SysKerTimeDiff`** shows the difference between the CAN frame RX timestamp from the kernel and + the system time. If this is significantly higher than `socketCANThreadIdleTimeMs`, which is 50 + milliseconds in the default configuration, the timestamps from the kernel are out of sync. Make + sure an updated SocketCAN driver for your CAN device is used. Alternatively, consider switching + `timestampType` in the static config to `Polling`. This will affect timestamp precision. Consider + reducing the polling time `socketCANThreadIdleTimeMs` to mitigate. +- `CeSCnt` is a monotonic counter that counts the signals decoded and processed since startup. This + can be used for performance evaluations. +- `CpuPercentageSum` and `CpuThread_*` tracks the CPU usage for the complete process and per thread. + In multi-core systems this can be above 100%. AWS IoT Fleetwise Edge uses the linux `/proc/` + directory to calculate this information. +- `MemoryMaxResidentRam` gives the maximum bytes of resident RAM used by the process. If this is + above 50 MB high consider switching from cmake Debug to Release build. Also the queue sizes in the + static config can be reduced. +- `CampaignFailures` monitors errors related to the campaign activation. If you see non-zero values, + please check the logs. Make sure not to deploy more campaigns in parallel than defined in + `MAX_NUMBER_OF_ACTIVE_CONDITION`, which defaults to 256. Also check that the `maxSampleCount` of + all collected signals fits into the memory used for the signals history buffer defined in + `MAX_SAMPLE_MEMORY`, which defaults to 20MB. +- `CampaignRxToDataTx` provides the amount of time it takes from changing the set of active + campaigns to the first signal data being published. If at least one time based collection scheme + is active this should be at most the time period of that collection scheme. + +# How to collect metrics from a FWE + +There are multiple ways to collect metrics depending on how AWS IoT Fleetwise Edge (FWE) is +integrated. We describe two methods: using the RemoteProfiler and collecting processed logs and +extract metrics (like through the AWS Systems Manager). + +Each method incurs charges for different AWS services like +[AWS IoT Core](https://aws.amazon.com/iot-core/pricing/), +[Amazon CloudWatch](https://aws.amazon.com/cloudwatch/pricing/), +[AWS System Manager](https://aws.amazon.com/systems-manager/pricing/) and more. For example, using +the [RemoteProfiler](#method-1-use-the-remoteprofiler-module) method, AWS Iot Fleetwise Edge uploads +at your configured interval, which is currently ~300 metrics. Per 10 metrics data points uploaded, +at least one message will be published to AWS IoT Core and one AWS IoT Rules Engine Action will be +executed. If `profilerPrefix` is different for every vehicle, ~300 new Amazon CloudWatch metrics +will be used per vehicle. + +## Method 1: Use the RemoteProfiler module + +The RemoteProfiler module is provided as part of the AWS IoT FleetWise Edge C++ code base. If +activated, it will regularly ingest the metrics and logs to AWS IoT Core topics, which have +underlying AWS IoT Core Rules and actions to route the data to Amazon CloudWatch. The same existing +MQTT connection used to ingest the data collection campaign is reused for this purpose. In order to +activate the RemoteProfile, add the following parameters to your config file: + +```json +{ + ... + "staticConfig": { + ... + "mqttConnection": { + ... + "metricsUploadTopic": "aws-iot-fleetwise-metrics-upload", + "loggingUploadTopic": "aws-iot-fleetwise-logging-upload" + }, + "remoteProfilerDefaultValues": { + "loggingUploadLevelThreshold": "Warning", + "metricsUploadIntervalMs": 60000, + "loggingUploadMaxWaitBeforeUploadMs": 60000, + "profilerPrefix": "TestVehicle1" + }, + } +} +``` + +In the above example configuration, a plain text json file with metrics will be uploaded to the AWS +IoT Core topic: `aws-iot-fleetwise-metrics-upload` and log messages of level Warning and Error to +the topic `aws-iot-fleetwise-logging-upload`. If `profilerPrefix` is unique for every vehicle, such +as if it's the same as `clientId`, there will be separate Amazon CloudWatch metrics for each +vehicle. If all vehicles have the same `profilerPrefix`, Amazon CloudWatch metrics are aggregated. + +Two AWS IoT Core rule actions are needed for these topics to forward the data to Amazon CloudWatch +metrics and logs. They can be created by using the following AWS CloudFormation stack template: +[fwremoteprofiler.yml](../tools/cfn-templates/fwremoteprofiler.yml) + +Click here to +[**Launch CloudFormation Template**](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/quickcreate?templateUrl=https%3A%2F%2Faws-iot-fleetwise.s3.us-west-2.amazonaws.com%2Flatest%2Fcfn-templates%2Ffwremoteprofiler.yml&stackName=fwremoteprofiler). + +After the first vehicle uploads metrics, they can be found under the namespace +**AWSIotFleetWiseEdge**. The format is +`{profilerPrefix}_(variableMaxSinceStartup|variableMaxSinceLast|)_{name}` for variables and +`{profilerPrefix}_(sectionAvgSinceStartup|sectionCountSinceStartup|sectionMaxSinceLast|sectionMaxSinceStartup)_{name}` +for measuring the time in seconds needed for certain code sections. After running a vehicle with the +above config the metrics `TestVehicle1_variableMaxSinceStartup_RFrames0` and ~ 300 more will appear +in Amazon CloudWatch. Every minute new values will appear as `metricsUploadIntervalMs` is set +to 60000. + +For the direct upload of every log message above the specified threshold +(`loggingUploadLevelThreshold`), log messages are cached at edge for a maximum of 60 seconds +(`loggingUploadMaxWaitBeforeUploadMs`) before being uploaded over MQTT. + +The RemoteProfile module will not cache any metrics or logs during the loss of connectivity. The +local system log file can be used in that case, see the following section. + +## Method 2: Retrieving metrics from logs e.g. over SSH + +This method uses remote access, such as over SSH leveraging AWS Systems Manager or AWS IoT secure +tunneling to access the logs/metrics. In our examples, we use journald to manage the FWE logs. This +has the benefits of log rotation which might be necessary as FWE logs on TRACE level under high load +can produce multiple gigabytes of logs per day. These logs can be collected fully or aggregated like +over ssh from single vehicles in case of need for debugging or cyclically from the whole fleet. To +manage easy remote connections to multiple vehicles AWS Systems Manager or AWS IoT secure tunneling +could be used. For aggregation, custom scripts can be used to filter certain log levels. The log +levels in AWS IoT Fleetwise Edge logs go from `[ERROR]` to `[TRACE]`. To make the metrics easier to +parse, you can set the parameter `.staticConfig.internalParameters.metricsCyclicPrintIntervalMs` in +the static config an interval like 60000. This will cause the metrics to print in an easy parsable +format to the log every minute. The following regex expression can be used by any log/metrics +aggregator/uploader that supports Python. For lines that start with +`TraceModule-ConsoleLogging-TraceAtomicVariable` or `TraceModule-ConsoleLogging-Variable`: + +```python +regex_variable = re.compile( + r".*\'(?P.*?)\'" + r" \[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" +) +``` + +For lines starting with `TraceModule-ConsoleLogging-Section` + +```python +regex_section = re.compile( + r".*\'(?P.*?)\'" + r" \[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" + r" .*\[(?P.*?)\]" +) +``` + +After the metrics are parsed from the local log files, a local health monitoring program can decide +if and how to upload them to the cloud. + +# Adding new metrics + +Adding new metrics requires changing the C++ code and recompiling AWS IoT Fleetwise Edge. Add the +Metrics to the `TraceVariable` enum in +[TraceModule.h](../src/platform/linux/logmanagement/include/TraceModule.h) and assign a short name +in the function `getVariableName` of +[TraceModule.cpp](../src/platform/linux/logmanagement/src/TraceModule.cpp). Then you can set the +metrics anywhere by using: + +```cpp + TraceModule::get().setVariable( TraceVariable::MAX_SYSTEMTIME_KERNELTIME_DIFF, observedNewValue); +``` + +The metric will be automatically included in both methods described above. There are no changes +needed in the cloud, and the new metric will just show up in the same namespace. diff --git a/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto b/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto index 4eb9bf1c..2ed6c1c4 100644 --- a/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto +++ b/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto @@ -35,9 +35,9 @@ message CollectionScheme { string campaign_arn = 1; /* - * Amazon Resource Name of the required decoder manifest for this collectionScheme + * Synchronization ID of the required decoder manifest for this collectionScheme */ - string decoder_manifest_arn = 2; + string decoder_manifest_sync_id = 2; /* * When collectionScheme should start in milliseconds since the Unix epoch diff --git a/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto b/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto index 97118090..3cc69fc1 100644 --- a/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto +++ b/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto @@ -9,9 +9,9 @@ package Aws.IoTFleetWise.Schemas.DecoderManifestMsg; message DecoderManifest { /* - * Amazon Resource Name and version of the decoder manifest + * Synchronization ID of the decoder manifest */ - string arn = 1; + string sync_id = 1; /* * List of signals that are sent and received on the CAN BUS in the vehicle diff --git a/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json b/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json index 2bb48715..4bff6c17 100644 --- a/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json +++ b/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json @@ -119,10 +119,6 @@ "type": "integer", "description": "Max size of the buffer shared between data collection module (Collection Engine) and Network Channel Consumer. This is a single producer single consumer buffer." }, - "socketCANBufferSize": { - "type": "integer", - "description": "Max size of the circular buffer associated with a network channel (CAN Bus) for data consumption from that channel. This is a single producer-single consumer buffer." - }, "decodedSignalsBufferSize": { "type": "integer", "description": "Max size of the buffer shared between data collection module (Collection Engine) and Network Channel Consumer for OBD and CAN signals.This is a multiple producer single consumer buffer." @@ -132,12 +128,7 @@ "description": "Max size of the buffer shared between Network Channel Consumer and data collection module ( Collection Engine). This buffer stores raw CAN frames coming in from the CAN Bus. This is a lock-free multi-producer single consumer buffer. " } }, - "required": [ - "dtcBufferSize", - "socketCANBufferSize", - "decodedSignalsBufferSize", - "rawCANFrameBufferSize" - ] + "required": ["dtcBufferSize", "decodedSignalsBufferSize", "rawCANFrameBufferSize"] }, "threadIdleTimes": { "type": "object", diff --git a/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto b/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto index 89d5a126..d209341b 100644 --- a/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto +++ b/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto @@ -18,9 +18,9 @@ message VehicleData { string campaign_arn = 1; /* - * Amazon Resource Name of the decoder manifest used to decode the signals in this message + * Synchronization ID of the decoder manifest used to decode the signals in this message */ - string decoder_arn = 2; + string decoder_sync_id = 2; /* * A unique ID that FWE generates each time a collectionScheme condition is triggered. diff --git a/src/datamanagement/custom/example/iwavegps/README.md b/src/datamanagement/custom/example/iwavegps/README.md index 3d20f841..a364b180 100644 --- a/src/datamanagement/custom/example/iwavegps/README.md +++ b/src/datamanagement/custom/example/iwavegps/README.md @@ -24,8 +24,8 @@ change the parameters without recompiling AWS IoT FleetWise Edge: "nmeaFilePath": "/dev/ttyUSB1", "canChannel" : "IWAVE-GPS-CAN", "canFrameId" : "1", - "longitudeStartBit" : "32", - "latitudeStartBit" : "0" + "longitudeStartBit" : "0", + "latitudeStartBit" : "32" } ``` diff --git a/src/datamanagement/custom/example/iwavegps/include/IWaveGpsSource.h b/src/datamanagement/custom/example/iwavegps/include/IWaveGpsSource.h index 4029349e..15b11c8d 100644 --- a/src/datamanagement/custom/example/iwavegps/include/IWaveGpsSource.h +++ b/src/datamanagement/custom/example/iwavegps/include/IWaveGpsSource.h @@ -6,7 +6,6 @@ #include "CollectionInspectionAPITypes.h" #include "CustomDataSource.h" #include "Timer.h" -#include "businterfaces/AbstractVehicleDataSource.h" namespace Aws { @@ -22,7 +21,7 @@ using namespace Aws::IoTFleetWise::DataInspection; * To implement a custom data source create a new class and inherit from CustomDataSource * then call setFilter() then start() and provide an implementation for pollData */ -class IWaveGpsSource : public CustomDataSource, public AbstractVehicleDataSource +class IWaveGpsSource : public CustomDataSource { public: /** @@ -46,12 +45,8 @@ class IWaveGpsSource : public CustomDataSource, public AbstractVehicleDataSource uint16_t latitudeStartBit, uint16_t longitudeStartBit ); - bool init( const std::vector &sourceConfigs ) override; - bool connect() override; - bool disconnect() override; - bool isAlive() override; - void suspendDataAcquisition() override; - void resumeDataAcquisition() override; + bool connect(); + bool disconnect(); static constexpr const char *PATH_TO_NMEA = "nmeaFilePath"; static constexpr const char *CAN_CHANNEL_NUMBER = "canChannel"; @@ -66,9 +61,6 @@ class IWaveGpsSource : public CustomDataSource, public AbstractVehicleDataSource private: static bool validLatitude( double latitude ); static bool validLongitude( double longitude ); - static bool extractIntegerFromConfig( const std::vector &sourceConfigs, - const std::string key, - uint32_t &extractedValue ); /** * The NMEA protocol provides the position in $GPGGA in the following format @@ -83,6 +75,9 @@ class IWaveGpsSource : public CustomDataSource, public AbstractVehicleDataSource static int extractLongAndLatitudeFromLine( const char *start, int limit, double &longitude, double &latitude, bool &north, bool &east ); + static const uint32_t CYCLIC_LOG_PERIOD_MS = 10000; + static const uint32_t MAX_BYTES_READ_PER_POLL = 2048; + uint16_t mLatitudeStartBit = 0; uint16_t mLongitudeStartBit = 0; @@ -93,11 +88,9 @@ class IWaveGpsSource : public CustomDataSource, public AbstractVehicleDataSource uint32_t mGpggaLineCounter = 0; uint32_t mValidCoordinateCounter = 0; std::string mPathToNmeaSource; - CANChannelNumericID mCanChannel; - CANRawFrameID mCanRawFrameId; - - static const uint32_t CYCLIC_LOG_PERIOD_MS = 10000; - static const uint32_t MAX_BYTES_READ_PER_POLL = 2048; + CANChannelNumericID mCanChannel{ INVALID_CAN_SOURCE_NUMERIC_ID }; + CANRawFrameID mCanRawFrameId{}; + char mBuffer[MAX_BYTES_READ_PER_POLL]{}; }; } // namespace DataManagement } // namespace IoTFleetWise diff --git a/src/datamanagement/custom/example/iwavegps/src/IWaveGpsSource.cpp b/src/datamanagement/custom/example/iwavegps/src/IWaveGpsSource.cpp index 68ff612d..88986ef4 100644 --- a/src/datamanagement/custom/example/iwavegps/src/IWaveGpsSource.cpp +++ b/src/datamanagement/custom/example/iwavegps/src/IWaveGpsSource.cpp @@ -22,10 +22,8 @@ constexpr const char *IWaveGpsSource::CAN_RAW_FRAME_ID; // NOLINT constexpr const char *IWaveGpsSource::LATITUDE_START_BIT; // NOLINT constexpr const char *IWaveGpsSource::LONGITUDE_START_BIT; // NOLINT IWaveGpsSource::IWaveGpsSource( SignalBufferPtr signalBufferPtr ) + : mSignalBufferPtr{ std::move( signalBufferPtr ) } { - mSignalBufferPtr = signalBufferPtr; - mCanChannel = INVALID_CAN_SOURCE_NUMERIC_ID; - mCanRawFrameId = 0; } bool @@ -57,10 +55,8 @@ IWaveGpsSource::getThreadName() void IWaveGpsSource::pollData() { - char buffer[MAX_BYTES_READ_PER_POLL]{}; - // Read from NMEA formatted file - auto bytes = read( mFileHandle, buffer, MAX_BYTES_READ_PER_POLL - 1 ); + auto bytes = read( mFileHandle, mBuffer, MAX_BYTES_READ_PER_POLL - 1 ); if ( bytes < 0 ) { FWE_LOG_ERROR( "Error reading from file" ); @@ -74,7 +70,7 @@ IWaveGpsSource::pollData() int i = 0; while ( i < bytes - 7 ) { - if ( strncmp( "$GPGGA,", &buffer[i], 7 ) == 0 ) + if ( strncmp( "$GPGGA,", &mBuffer[i], 7 ) == 0 ) { mGpggaLineCounter++; double longitudeRaw = HUGE_VAL; @@ -82,7 +78,7 @@ IWaveGpsSource::pollData() bool north = true; bool east = true; int processedBytes = extractLongAndLatitudeFromLine( - &buffer[i + 7], static_cast( bytes ) - ( i + 7 ), longitudeRaw, latitudeRaw, north, east ); + &mBuffer[i + 7], static_cast( bytes ) - ( i + 7 ), longitudeRaw, latitudeRaw, north, east ); i += processedBytes; double longitude = convertDmmToDdCoordinates( longitudeRaw, east ); double latitude = convertDmmToDdCoordinates( latitudeRaw, north ); @@ -197,71 +193,6 @@ IWaveGpsSource::extractLongAndLatitudeFromLine( return i; } -// compatibility with AbstractVehicleDataSource -bool -IWaveGpsSource::init( const std::vector &sourceConfigs ) -{ - std::string pathToNmeaSource; - CANChannelNumericID canChannel = 0; - CANRawFrameID canRawFrameId = 0; - uint32_t latitudeStartBit = 0; - uint32_t longitudeStartBit = 0; - - if ( ( sourceConfigs.size() > 1 ) || sourceConfigs.empty() ) - { - FWE_LOG_ERROR( "Only one source config is supported" ); - return false; - } - auto settingsIterator = sourceConfigs[0].transportProperties.find( std::string( PATH_TO_NMEA ) ); - if ( settingsIterator == sourceConfigs[0].transportProperties.end() ) - { - FWE_LOG_ERROR( "Could not find nmeaFilePath in the config" ); - return false; - } - else - { - pathToNmeaSource = settingsIterator->second; - } - - if ( extractIntegerFromConfig( sourceConfigs, CAN_CHANNEL_NUMBER, canChannel ) && - extractIntegerFromConfig( sourceConfigs, CAN_RAW_FRAME_ID, canRawFrameId ) && - extractIntegerFromConfig( sourceConfigs, LONGITUDE_START_BIT, longitudeStartBit ) && - extractIntegerFromConfig( sourceConfigs, LATITUDE_START_BIT, latitudeStartBit ) ) - { - return init( pathToNmeaSource, - canChannel, - canRawFrameId, - static_cast( latitudeStartBit ), - static_cast( longitudeStartBit ) ); - } - return false; -} - -bool -IWaveGpsSource::extractIntegerFromConfig( const std::vector &sourceConfigs, - const std::string key, - uint32_t &extractedValue ) -{ - auto settingsIterator = sourceConfigs[0].transportProperties.find( std::string( key ) ); - if ( settingsIterator == sourceConfigs[0].transportProperties.end() ) - { - FWE_LOG_ERROR( "Could not find " + key + " in the config" ); - return false; - } - else - { - try - { - extractedValue = static_cast( std::stoul( settingsIterator->second ) ); - } - catch ( const std::exception &e ) - { - FWE_LOG_ERROR( "Could not cast the " + key + ", invalid input: " + std::string( e.what() ) ); - return false; - } - } - return true; -} bool IWaveGpsSource::connect() { @@ -277,22 +208,12 @@ IWaveGpsSource::connect() bool IWaveGpsSource::disconnect() { - return ( close( mFileHandle ) == 0 ); -} -bool -IWaveGpsSource::isAlive() -{ - return isRunning(); -} -void -IWaveGpsSource::suspendDataAcquisition() -{ - setFilter( INVALID_CAN_SOURCE_NUMERIC_ID, 0 ); -} -void -IWaveGpsSource::resumeDataAcquisition() -{ - setFilter( mCanChannel, mCanRawFrameId ); + if ( close( mFileHandle ) != 0 ) + { + return false; + } + mFileHandle = -1; + return true; } } // namespace DataManagement diff --git a/src/datamanagement/custom/example/iwavegps/test/IWaveGpsSourceTest.cpp b/src/datamanagement/custom/example/iwavegps/test/IWaveGpsSourceTest.cpp index 1d89a561..a7322233 100644 --- a/src/datamanagement/custom/example/iwavegps/test/IWaveGpsSourceTest.cpp +++ b/src/datamanagement/custom/example/iwavegps/test/IWaveGpsSourceTest.cpp @@ -93,15 +93,7 @@ TEST_F( IWaveGpsSourceTest, testWestNegativeLongitude ) *nmeaFile << "$GPGGA,133120.00,5234.56789,N,01234.56789,W,1,08,0.6,123.4,M,56.7,M,,*89\n\n"; nmeaFile->close(); IWaveGpsSource gpsSource( signalBufferPtr ); - std::vector configs; - configs.emplace_back(); - configs.back().transportProperties.emplace( IWaveGpsSource::PATH_TO_NMEA, filePath ); - configs.back().transportProperties.emplace( IWaveGpsSource::CAN_CHANNEL_NUMBER, "1" ); - configs.back().transportProperties.emplace( IWaveGpsSource::CAN_RAW_FRAME_ID, "1" ); - configs.back().transportProperties.emplace( IWaveGpsSource::LATITUDE_START_BIT, "0" ); - configs.back().transportProperties.emplace( IWaveGpsSource::LONGITUDE_START_BIT, "32" ); - - gpsSource.init( configs ); + gpsSource.init( filePath, 1, 1, 0, 32 ); gpsSource.connect(); gpsSource.start(); gpsSource.onChangeOfActiveDictionary( mDictionary, VehicleDataSourceProtocol::RAW_SOCKET ); diff --git a/src/datamanagement/custom/generic/include/CustomDataSource.h b/src/datamanagement/custom/generic/include/CustomDataSource.h index 1e7bdea7..bb3191b0 100644 --- a/src/datamanagement/custom/generic/include/CustomDataSource.h +++ b/src/datamanagement/custom/generic/include/CustomDataSource.h @@ -78,7 +78,7 @@ class CustomDataSource : public IActiveDecoderDictionaryListener } private: - void matchDictionaryToFilter( const CANDecoderDictionary &dictionary, + void matchDictionaryToFilter( std::shared_ptr &dictionary, CANChannelNumericID canChannel, CANRawFrameID canRawFrameId ); // Main work function that runs in new thread @@ -100,7 +100,7 @@ class CustomDataSource : public IActiveDecoderDictionaryListener CANMessageFormat mUsedMessageFormat; mutable std::mutex mExtractionOngoing; - std::shared_ptr lastReceivedDictionary; + std::shared_ptr mLastReceivedDictionary; static const uint32_t DEFAULT_POLL_INTERVAL_MS = 50; // Default poll every 50ms data }; diff --git a/src/datamanagement/custom/generic/src/CustomDataSource.cpp b/src/datamanagement/custom/generic/src/CustomDataSource.cpp index 5c3af903..29fb9030 100644 --- a/src/datamanagement/custom/generic/src/CustomDataSource.cpp +++ b/src/datamanagement/custom/generic/src/CustomDataSource.cpp @@ -127,26 +127,29 @@ CustomDataSource::setFilter( CANChannelNumericID canChannel, CANRawFrameID canRa std::lock_guard lock( mExtractionOngoing ); mCanChannel = canChannel; mCanRawFrameId = canRawFrameId; - canDecoderDictionary = lastReceivedDictionary; - } - if ( canDecoderDictionary != nullptr ) - { - matchDictionaryToFilter( *canDecoderDictionary, canChannel, canRawFrameId ); + canDecoderDictionary = mLastReceivedDictionary; } + matchDictionaryToFilter( canDecoderDictionary, canChannel, canRawFrameId ); } void -CustomDataSource::matchDictionaryToFilter( const CANDecoderDictionary &dictionary, +CustomDataSource::matchDictionaryToFilter( std::shared_ptr &dictionary, CANChannelNumericID canChannel, CANRawFrameID canRawFrameId ) { - if ( mCanChannel == INVALID_CAN_SOURCE_NUMERIC_ID ) + if ( canChannel == INVALID_CAN_SOURCE_NUMERIC_ID ) + { + FWE_LOG_TRACE( "CAN channel invalid, so requesting sleep" ); + mShouldSleep = true; + return; + } + if ( dictionary == nullptr ) { - FWE_LOG_TRACE( "No Valid CAN so requesting sleep" ); - mShouldSleep = true; // Nothing found + FWE_LOG_TRACE( "No decoder dictionary, so requesting sleep" ); + mShouldSleep = true; return; } - for ( auto &channel : dictionary.canMessageDecoderMethod ) + for ( auto &channel : dictionary->canMessageDecoderMethod ) { if ( channel.first == canChannel ) { @@ -185,15 +188,13 @@ CustomDataSource::onChangeOfActiveDictionary( ConstDecoderDictionaryConstPtr &di auto canDecoderDictionary = std::dynamic_pointer_cast( dictionary ); { std::lock_guard lock( mExtractionOngoing ); - lastReceivedDictionary = canDecoderDictionary; + mLastReceivedDictionary = canDecoderDictionary; canChannel = mCanChannel; canRawFrameId = mCanRawFrameId; } - if ( canDecoderDictionary != nullptr ) - { - matchDictionaryToFilter( *canDecoderDictionary, canChannel, canRawFrameId ); - } + matchDictionaryToFilter( canDecoderDictionary, canChannel, canRawFrameId ); } + CustomDataSource::~CustomDataSource() { if ( isRunning() ) @@ -201,6 +202,7 @@ CustomDataSource::~CustomDataSource() stop(); } } + } // namespace DataManagement } // namespace IoTFleetWise } // namespace Aws diff --git a/src/datamanagement/datacollection/CMakeLists.txt b/src/datamanagement/datacollection/CMakeLists.txt index 16ab368c..ecb15d02 100644 --- a/src/datamanagement/datacollection/CMakeLists.txt +++ b/src/datamanagement/datacollection/CMakeLists.txt @@ -22,7 +22,7 @@ find_library(JSONCPP_LIBRARY NAMES jsoncpp) target_include_directories(${libraryTargetName} PUBLIC include ${JSONCPP_INCLUDE_DIR}) -find_package(Boost 1.65.1 REQUIRED COMPONENTS filesystem) +find_package(Boost 1.71.0 REQUIRED COMPONENTS filesystem) target_link_libraries( ${libraryTargetName} diff --git a/src/datamanagement/datacollection/include/CollectionSchemeIngestion.h b/src/datamanagement/datacollection/include/CollectionSchemeIngestion.h index 08142b5d..9d6528cd 100644 --- a/src/datamanagement/datacollection/include/CollectionSchemeIngestion.h +++ b/src/datamanagement/datacollection/include/CollectionSchemeIngestion.h @@ -113,7 +113,7 @@ class CollectionSchemeIngestion : public ICollectionScheme */ ExpressionNode *serializeNode( const CollectionSchemesMsg::ConditionNode &node, std::size_t &nextIndex, - const int depth ); + int remainingDepth ); /** * @brief Helper function that returns all nodes in the AST by doing a recursive traversal diff --git a/src/datamanagement/datacollection/include/DataCollectionSender.h b/src/datamanagement/datacollection/include/DataCollectionSender.h index b02c8493..a6bd8913 100644 --- a/src/datamanagement/datacollection/include/DataCollectionSender.h +++ b/src/datamanagement/datacollection/include/DataCollectionSender.h @@ -75,7 +75,8 @@ class DataCollectionSender ConnectivityError transmit( const std::string &payload ); private: - uint32_t mCollectionEventID; // A unique ID that FWE generates each time a collectionScheme condition is triggered. + // A unique ID that FWE generates each time a collectionScheme condition is triggered + uint32_t mCollectionEventID{}; std::shared_ptr mSender; SendDestination mSendDestination{ SendDestination::MQTT }; unsigned mTransmitThreshold; // max number of messages that can be sent to cloud at one time @@ -87,7 +88,7 @@ class DataCollectionSender /** * @brief Set up collectionSchemeParams struct */ - void setCollectionSchemeParameters( const TriggeredCollectionSchemeDataPtr &triggeredCollectionSchemeDataPtr ); + void setCollectionSchemeParameters( const TriggeredCollectionSchemeData &triggeredCollectionSchemeDataPtr ); /** * @brief Serialize and send the protobuf data to the cloud diff --git a/src/datamanagement/datacollection/include/ICollectionScheme.h b/src/datamanagement/datacollection/include/ICollectionScheme.h index bb768ec7..639f0942 100644 --- a/src/datamanagement/datacollection/include/ICollectionScheme.h +++ b/src/datamanagement/datacollection/include/ICollectionScheme.h @@ -30,11 +30,11 @@ enum class ImageCollectionType struct ImageCollectionInfo { ImageCollectionInfo() = default; - ImageCollectionInfo( ImageDeviceID id, uint32_t format, ImageCollectionType cType, uint32_t beforeDurationMs ) + ImageCollectionInfo( ImageDeviceID id, uint32_t format, ImageCollectionType cType, uint32_t beforeDurationMsIn ) : deviceID( id ) , imageFormat( format ) , collectionType( cType ) - , beforeDurationMs( beforeDurationMs ) + , beforeDurationMs( beforeDurationMsIn ) { } ImageDeviceID deviceID{ 0 }; // Unique Identifier of the image sensor in the system @@ -150,7 +150,8 @@ struct GeohashFunction DECIMAL_DEGREE = 0, MICROARCSECOND, MILLIARCSECOND, - ARCSECOND + ARCSECOND, + MAX }; SignalID latitudeSignalID{ 0 }; SignalID longitudeSignalID{ 0 }; diff --git a/src/datamanagement/datacollection/src/CollectionSchemeIngestion.cpp b/src/datamanagement/datacollection/src/CollectionSchemeIngestion.cpp index 245468ee..c3bb5364 100644 --- a/src/datamanagement/datacollection/src/CollectionSchemeIngestion.cpp +++ b/src/datamanagement/datacollection/src/CollectionSchemeIngestion.cpp @@ -39,7 +39,7 @@ CollectionSchemeIngestion::build() { // Check if Collection collectionScheme has an ID and a Decoder Manifest ID if ( mProtoCollectionSchemeMessagePtr->campaign_arn().empty() || - mProtoCollectionSchemeMessagePtr->decoder_manifest_arn().empty() ) + mProtoCollectionSchemeMessagePtr->decoder_manifest_sync_id().empty() ) { FWE_LOG_ERROR( "CollectionScheme does not have ID or DM ID" ); return false; @@ -346,28 +346,36 @@ CollectionSchemeIngestion::serializeNode( const CollectionSchemesMsg::ConditionN else if ( node.node_function().functionType_case() == CollectionSchemesMsg::ConditionNode_NodeFunction::kGeohashFunction ) { - currentNode->nodeType = ExpressionNodeType::GEOHASHFUNCTION; // geohash - currentNode->function.geohashFunction.latitudeSignalID = - node.node_function().geohash_function().latitude_signal_id(); - currentNode->function.geohashFunction.longitudeSignalID = - node.node_function().geohash_function().longitude_signal_id(); - currentNode->function.geohashFunction.precision = - static_cast( node.node_function().geohash_function().geohash_precision() ); - currentNode->function.geohashFunction.gpsUnitType = - static_cast( node.node_function().geohash_function().gps_unit() ); - FWE_LOG_TRACE( - - "Creating Geohash FUNCTION node: Lat SignalID: " + - std::to_string( currentNode->function.geohashFunction.latitudeSignalID ) + - "; Lon SignalID: " + std::to_string( currentNode->function.geohashFunction.longitudeSignalID ) + - "; precision: " + std::to_string( currentNode->function.geohashFunction.precision ) + - "; GPS Unit Type: " + - std::to_string( static_cast( currentNode->function.geohashFunction.gpsUnitType ) ) ); - return currentNode; + if ( ( node.node_function().geohash_function().geohash_precision() > UINT8_MAX ) || + ( node.node_function().geohash_function().gps_unit() >= + toUType( GeohashFunction::GPSUnitType::MAX ) ) ) + { + FWE_LOG_WARN( "Invalid Geohash function arguments" ); + } + else + { + currentNode->nodeType = ExpressionNodeType::GEOHASHFUNCTION; // geohash + currentNode->function.geohashFunction.latitudeSignalID = + node.node_function().geohash_function().latitude_signal_id(); + currentNode->function.geohashFunction.longitudeSignalID = + node.node_function().geohash_function().longitude_signal_id(); + currentNode->function.geohashFunction.precision = + static_cast( node.node_function().geohash_function().geohash_precision() ); + // coverity[autosar_cpp14_a7_2_1_violation] Range is checked by the if-statement above + currentNode->function.geohashFunction.gpsUnitType = + static_cast( node.node_function().geohash_function().gps_unit() ); + FWE_LOG_TRACE( + "Creating Geohash FUNCTION node: Lat SignalID: " + + std::to_string( currentNode->function.geohashFunction.latitudeSignalID ) + + "; Lon SignalID: " + std::to_string( currentNode->function.geohashFunction.longitudeSignalID ) + + "; precision: " + std::to_string( currentNode->function.geohashFunction.precision ) + + "; GPS Unit Type: " + + std::to_string( static_cast( currentNode->function.geohashFunction.gpsUnitType ) ) ); + return currentNode; + } } else { - // unsupported function type FWE_LOG_WARN( "Unsupported Function Node Type" ); } } @@ -429,7 +437,7 @@ CollectionSchemeIngestion::getDecoderManifestID() const return INVALID_DECODER_MANIFEST_ID; } - return mProtoCollectionSchemeMessagePtr->decoder_manifest_arn(); + return mProtoCollectionSchemeMessagePtr->decoder_manifest_sync_id(); } uint64_t diff --git a/src/datamanagement/datacollection/src/DataCollectionProtoWriter.cpp b/src/datamanagement/datacollection/src/DataCollectionProtoWriter.cpp index 69822712..31f25101 100644 --- a/src/datamanagement/datacollection/src/DataCollectionProtoWriter.cpp +++ b/src/datamanagement/datacollection/src/DataCollectionProtoWriter.cpp @@ -30,7 +30,7 @@ DataCollectionProtoWriter::setupVehicleData( const TriggeredCollectionSchemeData mVehicleData.Clear(); mVehicleData.set_campaign_arn( triggeredCollectionSchemeData->metaData.collectionSchemeID ); - mVehicleData.set_decoder_arn( triggeredCollectionSchemeData->metaData.decoderID ); + mVehicleData.set_decoder_sync_id( triggeredCollectionSchemeData->metaData.decoderID ); mVehicleData.set_collection_event_id( collectionEventID ); mTriggerTime = triggeredCollectionSchemeData->triggerTime; mVehicleData.set_collection_event_time_ms_epoch( mTriggerTime ); diff --git a/src/datamanagement/datacollection/src/DataCollectionSender.cpp b/src/datamanagement/datacollection/src/DataCollectionSender.cpp index 9adde299..eeeadb14 100644 --- a/src/datamanagement/datacollection/src/DataCollectionSender.cpp +++ b/src/datamanagement/datacollection/src/DataCollectionSender.cpp @@ -4,6 +4,7 @@ // Includes #include "DataCollectionSender.h" #include "LoggingModule.h" +#include "TraceModule.h" #include #include #include @@ -19,10 +20,9 @@ DataCollectionSender::DataCollectionSender( std::shared_ptr sender, unsigned maxMessageCount, CANInterfaceIDTranslator &canIDTranslator ) : mSender( std::move( sender ) ) + , mTransmitThreshold{ ( maxMessageCount > 0U ) ? maxMessageCount : UINT_MAX } , mProtoWriter( canIDTranslator ) { - mTransmitThreshold = ( maxMessageCount > 0U ) ? maxMessageCount : UINT_MAX; - mCollectionEventID = 0U; } void @@ -37,7 +37,7 @@ DataCollectionSender::send( const TriggeredCollectionSchemeDataPtr triggeredColl // Assign a unique event id to the edge to cloud payload mCollectionEventID = triggeredCollectionSchemeDataPtr->eventID; - setCollectionSchemeParameters( triggeredCollectionSchemeDataPtr ); + setCollectionSchemeParameters( *triggeredCollectionSchemeDataPtr ); if ( mSendDestination == SendDestination::MQTT ) { @@ -140,7 +140,7 @@ DataCollectionSender::transmit() if ( mCollectionSchemeParams.compression ) { FWE_LOG_TRACE( "Compress the payload before transmitting since compression flag is true" ); - if ( snappy::Compress( mProtoOutput.data(), mProtoOutput.size(), &payloadData ) == 0u ) + if ( snappy::Compress( mProtoOutput.data(), mProtoOutput.size(), &payloadData ) == 0U ) { FWE_LOG_TRACE( "Error in compressing the payload" ); return ConnectivityError::WrongInputData; @@ -160,6 +160,7 @@ DataCollectionSender::transmit() } else { + TraceModule::get().sectionEnd( TraceSection::COLLECTION_SCHEME_CHANGE_TO_FIRST_DATA ); FWE_LOG_INFO( "A Payload of size: " + std::to_string( payloadData.length() ) + " bytes has been unloaded to AWS IoT Core" ); } @@ -211,11 +212,11 @@ DataCollectionSender::serializeAndTransmit() void DataCollectionSender::setCollectionSchemeParameters( - const TriggeredCollectionSchemeDataPtr &triggeredCollectionSchemeDataPtr ) + const TriggeredCollectionSchemeData &triggeredCollectionSchemeDataPtr ) { - mCollectionSchemeParams.persist = triggeredCollectionSchemeDataPtr->metaData.persist; - mCollectionSchemeParams.compression = triggeredCollectionSchemeDataPtr->metaData.compress; - mCollectionSchemeParams.priority = triggeredCollectionSchemeDataPtr->metaData.priority; + mCollectionSchemeParams.persist = triggeredCollectionSchemeDataPtr.metaData.persist; + mCollectionSchemeParams.compression = triggeredCollectionSchemeDataPtr.metaData.compress; + mCollectionSchemeParams.priority = triggeredCollectionSchemeDataPtr.metaData.priority; } } // namespace DataManagement diff --git a/src/datamanagement/datacollection/test/DataCollectionProtoWriterTest.cpp b/src/datamanagement/datacollection/test/DataCollectionProtoWriterTest.cpp index 1491b3a2..8dacfe1c 100644 --- a/src/datamanagement/datacollection/test/DataCollectionProtoWriterTest.cpp +++ b/src/datamanagement/datacollection/test/DataCollectionProtoWriterTest.cpp @@ -70,7 +70,7 @@ TEST_F( DataCollectionProtoWriterTest, TestVehicleData ) /* Read and compare to written fields */ ASSERT_EQ( "123", vehicleDataTest.campaign_arn() ); - ASSERT_EQ( "456", vehicleDataTest.decoder_arn() ); + ASSERT_EQ( "456", vehicleDataTest.decoder_sync_id() ); ASSERT_EQ( collectionEventID, vehicleDataTest.collection_event_id() ); ASSERT_EQ( testTriggerTime, vehicleDataTest.collection_event_time_ms_epoch() ); } @@ -122,7 +122,7 @@ TEST_F( DataCollectionProtoWriterTest, TestDTCData ) /* Read and compare to written fields */ ASSERT_EQ( "123", vehicleDataTest.campaign_arn() ); - ASSERT_EQ( "456", vehicleDataTest.decoder_arn() ); + ASSERT_EQ( "456", vehicleDataTest.decoder_sync_id() ); ASSERT_EQ( collectionEventID, vehicleDataTest.collection_event_id() ); ASSERT_EQ( testTriggerTime, vehicleDataTest.collection_event_time_ms_epoch() ); @@ -170,7 +170,7 @@ TEST_F( DataCollectionProtoWriterTest, TestGeohash ) /* Read and compare to written fields */ ASSERT_EQ( "123", vehicleDataTest.campaign_arn() ); - ASSERT_EQ( "456", vehicleDataTest.decoder_arn() ); + ASSERT_EQ( "456", vehicleDataTest.decoder_sync_id() ); ASSERT_EQ( collectionEventID, vehicleDataTest.collection_event_id() ); ASSERT_EQ( testTriggerTime, vehicleDataTest.collection_event_time_ms_epoch() ); diff --git a/src/datamanagement/datacollection/test/DataCollectionSenderTest.cpp b/src/datamanagement/datacollection/test/DataCollectionSenderTest.cpp index 8a0619ff..f2518f66 100644 --- a/src/datamanagement/datacollection/test/DataCollectionSenderTest.cpp +++ b/src/datamanagement/datacollection/test/DataCollectionSenderTest.cpp @@ -60,7 +60,7 @@ class DataCollectionSenderTest : public ::testing::Test /* Read and compare to written fields */ ASSERT_EQ( "123", vehicleDataTest.campaign_arn() ); - ASSERT_EQ( "456", vehicleDataTest.decoder_arn() ); + ASSERT_EQ( "456", vehicleDataTest.decoder_sync_id() ); ASSERT_EQ( 800, vehicleDataTest.collection_event_time_ms_epoch() ); ASSERT_EQ( vehicleDataTest.captured_signals_size(), 3 ); ASSERT_EQ( vehicleDataTest.can_frames_size(), 3 ); @@ -86,7 +86,7 @@ class DataCollectionSenderTest : public ::testing::Test /* Read and compare to written fields */ ASSERT_EQ( "123", vehicleDataTest.campaign_arn() ); - ASSERT_EQ( "456", vehicleDataTest.decoder_arn() ); + ASSERT_EQ( "456", vehicleDataTest.decoder_sync_id() ); ASSERT_EQ( 800, vehicleDataTest.collection_event_time_ms_epoch() ); // Number of messages should always be less than or equal to the transmit threshold specified in config auto dtcData = vehicleDataTest.mutable_dtc_data(); diff --git a/src/datamanagement/datadecoding/CMakeLists.txt b/src/datamanagement/datadecoding/CMakeLists.txt index 32abb62b..c58e8fcc 100644 --- a/src/datamanagement/datadecoding/CMakeLists.txt +++ b/src/datamanagement/datadecoding/CMakeLists.txt @@ -17,7 +17,7 @@ target_include_directories(${libraryTargetName} PUBLIC include ) -find_package(Boost 1.65.1 REQUIRED COMPONENTS filesystem) +find_package(Boost 1.71.0 REQUIRED COMPONENTS filesystem) target_link_libraries( ${libraryTargetName} diff --git a/src/datamanagement/datadecoding/include/CANDecoder.h b/src/datamanagement/datadecoding/include/CANDecoder.h index 16065b78..d8bd2b95 100644 --- a/src/datamanagement/datadecoding/include/CANDecoder.h +++ b/src/datamanagement/datadecoding/include/CANDecoder.h @@ -5,10 +5,9 @@ // Includes #include "CANDataTypes.h" -#include "ClockHandler.h" -#include "IDecoderManifest.h" -#include "Timer.h" -#include +#include "MessageTypes.h" +#include "SignalTypes.h" +#include #include #include namespace Aws @@ -33,15 +32,15 @@ class CANDecoder * @param frameSize size in bytes of the frame. * @param format of the frame according to the DBC file. * @param signalIDsToCollect the id for the signals to be collected - * @param decodedMessage result of the decoding. + * @param decodedSignals result of the decoding. * @return True if the decoding is successful, False means that the decoding was * partially not successful */ - bool decodeCANMessage( const uint8_t *frameData, - size_t frameSize, - const CANMessageFormat &format, - const std::unordered_set &signalIDsToCollect, - CANDecodedMessage &decodedMessage ); + static bool decodeCANMessage( const uint8_t *frameData, + size_t frameSize, + const CANMessageFormat &format, + const std::unordered_set &signalIDsToCollect, + std::vector &decodedSignals ); /** * @brief extracts a signal raw value from a frame. @@ -50,9 +49,6 @@ class CANDecoder * @return 8 bytes representation of the physical value of the signal. */ static int64_t extractSignalFromFrame( const uint8_t *frameData, const CANSignalFormat &signalDescription ); - -private: - std::shared_ptr mClock = ClockHandler::getClock(); }; } // namespace DataManagement diff --git a/src/datamanagement/datadecoding/include/IDecoderDictionary.h b/src/datamanagement/datadecoding/include/IDecoderDictionary.h index 18a2330a..d5e5c647 100644 --- a/src/datamanagement/datadecoding/include/IDecoderDictionary.h +++ b/src/datamanagement/datadecoding/include/IDecoderDictionary.h @@ -73,9 +73,10 @@ struct CANDecoderDictionary : DecoderDictionary using CANMsgDecoderMethodType = std::unordered_map>; CANDecoderDictionary() = default; - CANDecoderDictionary( CANMsgDecoderMethodType canMsgDecoderMethod, std::unordered_set signalIDsToCollect ) - : DecoderDictionary( std::move( signalIDsToCollect ) ) - , canMessageDecoderMethod( std::move( canMsgDecoderMethod ) ) + CANDecoderDictionary( CANMsgDecoderMethodType canMsgDecoderMethodIn, + std::unordered_set signalIDsToCollectIn ) + : DecoderDictionary( std::move( signalIDsToCollectIn ) ) + , canMessageDecoderMethod( std::move( canMsgDecoderMethodIn ) ) { } CANMsgDecoderMethodType canMessageDecoderMethod; diff --git a/src/datamanagement/datadecoding/include/IDecoderManifest.h b/src/datamanagement/datadecoding/include/IDecoderManifest.h index 4e921ee5..0ef50117 100644 --- a/src/datamanagement/datadecoding/include/IDecoderManifest.h +++ b/src/datamanagement/datadecoding/include/IDecoderManifest.h @@ -18,11 +18,6 @@ namespace IoTFleetWise { namespace DataManagement { -/** - * @brief number of bits in one byte - */ -constexpr uint8_t BYTE_SIZE = 8; - /** * @brief An invalid CAN Message Format, set as a CANMessageFormat object initialized to all zeros */ diff --git a/src/datamanagement/datadecoding/src/CANDecoder.cpp b/src/datamanagement/datadecoding/src/CANDecoder.cpp index f7e2fc5f..f17a048e 100644 --- a/src/datamanagement/datadecoding/src/CANDecoder.cpp +++ b/src/datamanagement/datadecoding/src/CANDecoder.cpp @@ -6,7 +6,7 @@ #include "LoggingModule.h" #include #include -#define MASK64( nbits ) ( ( 0xFFFFFFFFFFFFFFFFULL ) >> ( 64 - ( nbits ) ) ) +#define MASK64( nbits ) ( static_cast( 0xFFFFFFFFFFFFFFFFULL ) >> ( 64 - ( nbits ) ) ) namespace Aws { @@ -20,7 +20,7 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, size_t frameSize, const CANMessageFormat &format, const std::unordered_set &signalIDsToCollect, - CANDecodedMessage &decodedMessage ) + std::vector &decodedSignals ) { uint8_t errorCounter = 0; uint32_t frameSizeInBits = static_cast( frameSize * 8 ); @@ -57,7 +57,7 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, } auto physicalRawValue = static_cast( multiplexorValue ); auto physicalValue = CANPhysicalValueType( physicalRawValue, CANsignalType ); - decodedMessage.mFrameInfo.mSignals.emplace_back( + decodedSignals.emplace_back( CANDecodedSignal( it->mSignalID, rawValue, physicalValue, CANsignalType ) ); break; } @@ -65,7 +65,7 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, auto physicalRawValue = static_cast( multiplexorValue ); auto physicalValue = CANPhysicalValueType( physicalRawValue, CANsignalType ); - decodedMessage.mFrameInfo.mSignals.emplace_back( + decodedSignals.emplace_back( CANDecodedSignal( it->mSignalID, rawValue, physicalValue, CANsignalType ) ); break; } @@ -73,7 +73,7 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, auto physicalRawValue = static_cast( multiplexorValue ); auto physicalValue = CANPhysicalValueType( physicalRawValue, CANsignalType ); - decodedMessage.mFrameInfo.mSignals.emplace_back( + decodedSignals.emplace_back( CANDecodedSignal( it->mSignalID, rawValue, physicalValue, CANsignalType ) ); break; } @@ -124,7 +124,7 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, static_cast( rawValue ) * static_cast( format.mSignals[i].mFactor ) + static_cast( format.mSignals[i].mOffset ); auto physicalValue = CANPhysicalValueType( physicalRawValue, CANsignalType ); - decodedMessage.mFrameInfo.mSignals.emplace_back( + decodedSignals.emplace_back( CANDecodedSignal( format.mSignals[i].mSignalID, rawValue, physicalValue, CANsignalType ) ); break; } @@ -133,7 +133,7 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, static_cast( rawValue ) * static_cast( format.mSignals[i].mFactor ) + static_cast( format.mSignals[i].mOffset ); auto physicalValue = CANPhysicalValueType( physicalRawValue, CANsignalType ); - decodedMessage.mFrameInfo.mSignals.emplace_back( + decodedSignals.emplace_back( CANDecodedSignal( format.mSignals[i].mSignalID, rawValue, physicalValue, CANsignalType ) ); break; } @@ -141,15 +141,13 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, auto physicalRawValue = static_cast( rawValue ) * format.mSignals[i].mFactor + format.mSignals[i].mOffset; auto physicalValue = CANPhysicalValueType( physicalRawValue, CANsignalType ); - decodedMessage.mFrameInfo.mSignals.emplace_back( + decodedSignals.emplace_back( CANDecodedSignal( format.mSignals[i].mSignalID, rawValue, physicalValue, CANsignalType ) ); } } } } - // Message decoding time - decodedMessage.mDecodingTime = mClock->systemTimeSinceEpochMs(); // Should not harm, callers will ignore the return code. return errorCounter == 0; } @@ -157,7 +155,6 @@ CANDecoder::decodeCANMessage( const uint8_t *frameData, int64_t CANDecoder::extractSignalFromFrame( const uint8_t *frameData, const CANSignalFormat &signalDescription ) { - const uint8_t BYTE_SIZE = 8; uint16_t startBit = static_cast( signalDescription.mFirstBitPosition ); uint8_t startByte = static_cast( startBit / BYTE_SIZE ); uint8_t startBitInByte = startBit % BYTE_SIZE; diff --git a/src/datamanagement/datadecoding/src/DecoderManifestIngestion.cpp b/src/datamanagement/datadecoding/src/DecoderManifestIngestion.cpp index 8791bfb9..25a05271 100644 --- a/src/datamanagement/datadecoding/src/DecoderManifestIngestion.cpp +++ b/src/datamanagement/datadecoding/src/DecoderManifestIngestion.cpp @@ -30,7 +30,7 @@ DecoderManifestIngestion::getID() const return std::string(); } - return mProtoDecoderManifest.arn(); + return mProtoDecoderManifest.sync_id(); } bool @@ -183,7 +183,7 @@ DecoderManifestIngestion::build() return false; } - FWE_LOG_INFO( "Building Decoder Manifest with ID: " + mProtoDecoderManifest.arn() ); + FWE_LOG_INFO( "Building Decoder Manifest with Sync ID: " + mProtoDecoderManifest.sync_id() ); // Iterate over CAN Signals and build the mCANSignalFormatDictionary for ( int i = 0; i < mProtoDecoderManifest.can_signals_size(); i++ ) @@ -266,17 +266,24 @@ DecoderManifestIngestion::build() { // Get a reference to the OBD PID signal in the protobuf const DecoderManifestMsg::OBDPIDSignal &pidSignal = mProtoDecoderManifest.obd_pid_signals( i ); + if ( ( pidSignal.service_mode() >= toUType( SID::MAX ) ) || ( pidSignal.pid() > UINT8_MAX ) || + ( pidSignal.bit_right_shift() > UINT8_MAX ) || ( pidSignal.bit_mask_length() > UINT8_MAX ) ) + { + FWE_LOG_WARN( "Invalid OBD PID signal" ); + continue; + } mSignalToVehicleDataSourceProtocol[pidSignal.signal_id()] = VehicleDataSourceProtocol::OBD; - PIDSignalDecoderFormat obdPIDSignalDecoderFormat = - PIDSignalDecoderFormat( pidSignal.pid_response_length(), - static_cast( pidSignal.service_mode() ), - static_cast( pidSignal.pid() ), - pidSignal.scaling(), - pidSignal.offset(), - pidSignal.start_byte(), - pidSignal.byte_length(), - static_cast( pidSignal.bit_right_shift() ), - static_cast( pidSignal.bit_mask_length() ) ); + PIDSignalDecoderFormat obdPIDSignalDecoderFormat = PIDSignalDecoderFormat( + pidSignal.pid_response_length(), + // coverity[autosar_cpp14_a7_2_1_violation] The if-statement above checks the correct range + static_cast( pidSignal.service_mode() ), + static_cast( pidSignal.pid() ), + pidSignal.scaling(), + pidSignal.offset(), + pidSignal.start_byte(), + pidSignal.byte_length(), + static_cast( pidSignal.bit_right_shift() ), + static_cast( pidSignal.bit_mask_length() ) ); mSignalToPIDDictionary[pidSignal.signal_id()] = obdPIDSignalDecoderFormat; // TODO :: Update the datatype from the DM after the schema update for the datatype support mSignalIDToTypeMap[pidSignal.signal_id()] = SignalType::DOUBLE; // using double as default diff --git a/src/datamanagement/datadecoding/src/OBDDataDecoder.cpp b/src/datamanagement/datadecoding/src/OBDDataDecoder.cpp index c09035aa..b12a869e 100644 --- a/src/datamanagement/datadecoding/src/OBDDataDecoder.cpp +++ b/src/datamanagement/datadecoding/src/OBDDataDecoder.cpp @@ -11,7 +11,7 @@ #include #include constexpr int POSITIVE_ECU_RESPONSE_BASE = 0x40; -#define IS_BIT_SET( var, pos ) ( ( ( var ) & ( 1 << ( pos ) ) ) != 0 ) +#define IS_BIT_SET( var, pos ) ( ( ( var ) & static_cast( 1U << ( pos ) ) ) != 0U ) namespace Aws { @@ -349,7 +349,7 @@ OBDDataDecoder::calculateValueFromFormula( PID pid, // we firstly right shift by 4, then apply bit mask 0b1111 rawData = inputData[byteIdx]; rawData >>= formula.mFirstBitPosition % BYTE_SIZE; - rawData &= ( 0xFF >> ( BYTE_SIZE - formula.mSizeInBits ) ); + rawData &= static_cast( 0xFFULL ) >> ( BYTE_SIZE - formula.mSizeInBits ); } else { @@ -359,7 +359,7 @@ OBDDataDecoder::calculateValueFromFormula( PID pid, while ( numOfBytes != 0 ) { --numOfBytes; - rawData = ( rawData << BYTE_SIZE ) | inputData[byteIdx]; + rawData = ( rawData << BYTE_SIZE ) | static_cast( inputData[byteIdx] ); byteIdx++; } } diff --git a/src/datamanagement/datadecoding/test/CANDecoderTest.cpp b/src/datamanagement/datadecoding/test/CANDecoderTest.cpp index f19ef8ed..275862c1 100644 --- a/src/datamanagement/datadecoding/test/CANDecoderTest.cpp +++ b/src/datamanagement/datadecoding/test/CANDecoderTest.cpp @@ -45,12 +45,12 @@ TEST( CANDecoderTest, CANDecoderTestSimpleMessage ) msgFormat.mSignals.emplace_back( sigFormat2 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1, 7 }; - ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 2 ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[0].mRawValue, 13 ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[1].mRawValue, 4084 ); + ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 2 ); + ASSERT_EQ( decodedSignals[0].mRawValue, 13 ); + ASSERT_EQ( decodedSignals[1].mRawValue, 4084 ); } TEST( CANDecoderTest, CANDecoderTestSimpleMessage2 ) @@ -91,12 +91,12 @@ TEST( CANDecoderTest, CANDecoderTestSimpleMessage2 ) msgFormat.mSignals.emplace_back( sigFormat2 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1, 7 }; - ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 2 ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[0].mRawValue, 153638137 ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[1].mRawValue, -299667802 ); + ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 2 ); + ASSERT_EQ( decodedSignals[0].mRawValue, 153638137 ); + ASSERT_EQ( decodedSignals[1].mRawValue, -299667802 ); } // Precision Test @@ -141,18 +141,16 @@ TEST( CANDecoderTest, CANDecoderPrecisionTest ) msgFormat.mSignals.emplace_back( sigFormat2 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1, 7 }; ASSERT_TRUE( - decoder.decodeCANMessage( frameData.data(), msgSizeBytes, msgFormat, signalIDsToCollect, decodedMsg ) ); + decoder.decodeCANMessage( frameData.data(), msgSizeBytes, msgFormat, signalIDsToCollect, decodedSignals ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 2 ); - - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[0].mSignalType, SignalType::UINT64 ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[1].mSignalType, SignalType::INT64 ); - - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[0].mPhysicalValue.signalValue.uint64Val, maxUnSignedVal ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[1].mPhysicalValue.signalValue.int64Val, maxSignedVal ); + ASSERT_EQ( decodedSignals.size(), 2 ); + ASSERT_EQ( decodedSignals[0].mSignalType, SignalType::UINT64 ); + ASSERT_EQ( decodedSignals[1].mSignalType, SignalType::INT64 ); + ASSERT_EQ( decodedSignals[0].mPhysicalValue.signalValue.uint64Val, maxUnSignedVal ); + ASSERT_EQ( decodedSignals[1].mPhysicalValue.signalValue.int64Val, maxSignedVal ); } TEST( CANDecoderTest, CANDecoderPrecisionSignedTest ) @@ -185,16 +183,14 @@ TEST( CANDecoderTest, CANDecoderPrecisionSignedTest ) msgFormat.mSignals.emplace_back( sigFormat1 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1 }; ASSERT_TRUE( - decoder.decodeCANMessage( frameData.data(), msgSizeBytes, msgFormat, signalIDsToCollect, decodedMsg ) ); - - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 1 ); + decoder.decodeCANMessage( frameData.data(), msgSizeBytes, msgFormat, signalIDsToCollect, decodedSignals ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[0].mSignalType, SignalType::INT64 ); - - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals[0].mPhysicalValue.signalValue.int64Val, minSignedVal ); + ASSERT_EQ( decodedSignals.size(), 1 ); + ASSERT_EQ( decodedSignals[0].mSignalType, SignalType::INT64 ); + ASSERT_EQ( decodedSignals[0].mPhysicalValue.signalValue.int64Val, minSignedVal ); } TEST( CANDecoderTest, CANDecoderTestSimpleMessage3 ) @@ -243,13 +239,13 @@ TEST( CANDecoderTest, CANDecoderTestSimpleMessage3 ) msgFormat.mSignals.emplace_back( sigFormat3 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1, 2, 3 }; - ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 3 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[0].mRawValue, 0x2301 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[1].mRawValue, 0x4567 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[2].mRawValue, 0x89AB ); + ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 3 ); + EXPECT_EQ( decodedSignals[0].mRawValue, 0x2301 ); + EXPECT_EQ( decodedSignals[1].mRawValue, 0x4567 ); + EXPECT_EQ( decodedSignals[2].mRawValue, 0x89AB ); } TEST( CANDecoderTest, CANDecoderTestSimpleCanFdMessage ) { @@ -308,14 +304,14 @@ TEST( CANDecoderTest, CANDecoderTestSimpleCanFdMessage ) msgFormat.mSignals.emplace_back( sigFormat4 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1, 2, 3, 4 }; - ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 64, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 4 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[0].mRawValue, 0x2301 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[1].mRawValue, 0x70123456 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[2].mRawValue, 0x456701 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[3].mRawValue, 0x7012 ); + ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 64, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 4 ); + EXPECT_EQ( decodedSignals[0].mRawValue, 0x2301 ); + EXPECT_EQ( decodedSignals[1].mRawValue, 0x70123456 ); + EXPECT_EQ( decodedSignals[2].mRawValue, 0x456701 ); + EXPECT_EQ( decodedSignals[3].mRawValue, 0x7012 ); } TEST( CANDecoderTest, CANDecoderTestOnlyDecodeSomeSignals ) @@ -364,13 +360,13 @@ TEST( CANDecoderTest, CANDecoderTestOnlyDecodeSomeSignals ) msgFormat.mSignals.emplace_back( sigFormat3 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; // Only collect Signal ID 1 and 3. Do not collect and decode Signal ID 2 std::unordered_set signalIDsToCollect = { 1, 3 }; - ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 2 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[0].mRawValue, 0x2301 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[1].mRawValue, 0x89AB ); + ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 2 ); + EXPECT_EQ( decodedSignals[0].mRawValue, 0x2301 ); + EXPECT_EQ( decodedSignals[1].mRawValue, 0x89AB ); } TEST( CANDecoderTest, CANDecoderTestMultiplexedMessage1 ) @@ -441,15 +437,14 @@ TEST( CANDecoderTest, CANDecoderTestMultiplexedMessage1 ) msgFormat.mIsMultiplexed = true; CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 50, 51, 52, 53 }; - ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 4 ); - - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[0].mRawValue, 0x05 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[1].mRawValue, 0x4B ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[2].mRawValue, 0x20B ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[3].mRawValue, 0xD3 ); + ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 4 ); + EXPECT_EQ( decodedSignals[0].mRawValue, 0x05 ); + EXPECT_EQ( decodedSignals[1].mRawValue, 0x4B ); + EXPECT_EQ( decodedSignals[2].mRawValue, 0x20B ); + EXPECT_EQ( decodedSignals[3].mRawValue, 0xD3 ); } TEST( CANDecoderTest, CANDecoderTestMultiplexedMessage2 ) @@ -534,15 +529,14 @@ TEST( CANDecoderTest, CANDecoderTestMultiplexedMessage2 ) msgFormat.mIsMultiplexed = true; CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 54, 55, 56, 57, 58 }; - ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 4 ); - - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[0].mRawValue, 0x06 ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[1].mRawValue, 0x1B ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[2].mRawValue, 0x3EB ); - EXPECT_EQ( decodedMsg.mFrameInfo.mSignals[3].mRawValue, 0xE3B ); + ASSERT_TRUE( decoder.decodeCANMessage( frameData.data(), 8, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 4 ); + EXPECT_EQ( decodedSignals[0].mRawValue, 0x06 ); + EXPECT_EQ( decodedSignals[1].mRawValue, 0x1B ); + EXPECT_EQ( decodedSignals[2].mRawValue, 0x3EB ); + EXPECT_EQ( decodedSignals[3].mRawValue, 0xE3B ); } TEST( CANDecoderTest, CANDecoderTestInvalidSignalLayout ) @@ -578,11 +572,12 @@ TEST( CANDecoderTest, CANDecoderTestInvalidSignalLayout ) msgFormat.mSignals.emplace_back( sigFormat2 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; // Only collect Signal ID 1 and 2. std::unordered_set signalIDsToCollect = { 1, 2 }; - ASSERT_FALSE( decoder.decodeCANMessage( frameData.data(), frameSize, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 1 ); // we are expecting the signal 1 only. + ASSERT_FALSE( + decoder.decodeCANMessage( frameData.data(), frameSize, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 1 ); // we are expecting the signal 1 only. } TEST( CANDecoderTest, CANDecoderTestSkippingOutOfBoundSignals1 ) @@ -619,10 +614,11 @@ TEST( CANDecoderTest, CANDecoderTestSkippingOutOfBoundSignals1 ) msgFormat.mSignals.emplace_back( sigFormat2 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1, 2 }; - ASSERT_FALSE( decoder.decodeCANMessage( frameData.data(), frameSize, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 0 ); + ASSERT_FALSE( + decoder.decodeCANMessage( frameData.data(), frameSize, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 0 ); } TEST( CANDecoderTest, CANDecoderTestSkippingOutOfBoundSignals2 ) @@ -657,8 +653,9 @@ TEST( CANDecoderTest, CANDecoderTestSkippingOutOfBoundSignals2 ) msgFormat.mSignals.emplace_back( sigFormat2 ); CANDecoder decoder; - CANDecodedMessage decodedMsg; + std::vector decodedSignals; std::unordered_set signalIDsToCollect = { 1, 2 }; - ASSERT_FALSE( decoder.decodeCANMessage( frameData.data(), frameSize, msgFormat, signalIDsToCollect, decodedMsg ) ); - ASSERT_EQ( decodedMsg.mFrameInfo.mSignals.size(), 1 ); + ASSERT_FALSE( + decoder.decodeCANMessage( frameData.data(), frameSize, msgFormat, signalIDsToCollect, decodedSignals ) ); + ASSERT_EQ( decodedSignals.size(), 1 ); } diff --git a/src/datamanagement/datainspection/CMakeLists.txt b/src/datamanagement/datainspection/CMakeLists.txt index 8bb7ebf8..ac2aa55b 100644 --- a/src/datamanagement/datainspection/CMakeLists.txt +++ b/src/datamanagement/datainspection/CMakeLists.txt @@ -11,7 +11,7 @@ set(SRCS src/diag/OBDOverCANModule.cpp src/diag/OBDOverCANECU.cpp src/location/GeohashFunctionNode.cpp - src/vehicledatasource/VehicleDataSourceBinder.cpp + src/vehicledatasource/CANDataSource.cpp src/vehicledatasource/CANDataConsumer.cpp ) @@ -54,12 +54,10 @@ install( include/IActiveConditionProcessor.h include/IDataReadyToPublishListener.h include/InspectionEventListener.h - include/IVehicleDataConsumer.h + include/CANDataSource.h include/CANDataConsumer.h include/OBDOverCANModule.h include/OBDOverCANECU.h - include/CANDataConsumer.h - include/VehicleDataSourceBinder.h DESTINATION include ) @@ -72,7 +70,7 @@ set( test/OBDOverCANModuleTest.cpp test/CollectionInspectionEngineTest.cpp test/CollectionInspectionWorkerThreadTest.cpp - test/VehicleDataSourceBinderTest.cpp + test/CANDataSourceTest.cpp ) if(FWE_FEATURE_CAMERA) diff --git a/src/datamanagement/datainspection/include/CANDataConsumer.h b/src/datamanagement/datainspection/include/CANDataConsumer.h index 4a75fccb..01bb47fe 100644 --- a/src/datamanagement/datainspection/include/CANDataConsumer.h +++ b/src/datamanagement/datainspection/include/CANDataConsumer.h @@ -5,14 +5,14 @@ // Includes -#include "CANDecoder.h" -#include "ClockHandler.h" +#include "CANDataTypes.h" +#include "CollectionInspectionAPITypes.h" #include "IDecoderDictionary.h" -#include "IVehicleDataConsumer.h" -#include "Signal.h" -#include "Thread.h" -#include "Timer.h" -#include +#include "SignalTypes.h" +#include "TimeTypes.h" +#include +#include +#include namespace Aws { @@ -25,60 +25,22 @@ using namespace Aws::IoTFleetWise::Platform::Linux; * @brief CAN Network Data Consumer impl, outputs data to an in-memory buffer. * Operates in Polling mode. */ -class CANDataConsumer : public IVehicleDataConsumer +class CANDataConsumer { public: - CANDataConsumer() = default; - ~CANDataConsumer() override; + CANDataConsumer( CANChannelNumericID channelId, SignalBufferPtr signalBufferPtr, CANBufferPtr canBufferPtr ); + ~CANDataConsumer() = default; CANDataConsumer( const CANDataConsumer & ) = delete; CANDataConsumer &operator=( const CANDataConsumer & ) = delete; CANDataConsumer( CANDataConsumer && ) = delete; CANDataConsumer &operator=( CANDataConsumer && ) = delete; - bool init( VehicleDataSourceID canChannelID, SignalBufferPtr signalBufferPtr, uint32_t idleTimeMs ) override; - - bool connect() override; - - bool disconnect() override; - - bool isAlive() final; - - void resumeDataConsumption( ConstDecoderDictionaryConstPtr &dictionary ) override; - - void suspendDataConsumption() override; - - /** - * @brief This setter is specific to CAN Bus data consumers and captures the raw - * CAN Frames if wanted. - * @param canBufferPtr- RAW CAN Buffer - */ - inline void - setCANBufferPtr( CANBufferPtr canBufferPtr ) - { - mCANBufferPtr = canBufferPtr; - } - - /** - * @brief Handle of the CAN Raw Output Buffer. This buffer shared between Collection Engine - * and Vehicle Data CAN Consumer. - * @return shared object pointer to the CAN Frame buffer. - */ - inline CANBufferPtr - getCANBufferPtr() const - { - return mCANBufferPtr; - } + void processMessage( std::shared_ptr &dictionary, + const struct canfd_frame &message, + Timestamp timestamp ); private: - // Start the thread - bool start(); - // Stop the thread - bool stop(); - // Intercepts stop signals. - bool shouldStop() const; - // Intercepts sleep signals. - bool shouldSleep() const; /** * @brief Finds whether there exists a decoder method for a message id or not * If found, returns the method by reference @@ -87,24 +49,11 @@ class CANDataConsumer : public IVehicleDataConsumer */ bool findDecoderMethod( uint32_t &messageId, const CANDecoderDictionary::CANMsgDecoderMethodType &decoderMethod, - CANDecodedMessage &decodedMessage, - CANMessageDecoderMethod ¤tMessageDecoderMethod ); - // Main work function. consumes messages from the channel Circular buffer. - // Decodes the messages and puts the results in the output buffer. - static void doWork( void *data ); + CANMessageDecoderMethod ¤tMessageDecoderMethod ) const; - Thread mThread; - std::atomic mShouldStop{ false }; - std::atomic mShouldSleep{ false }; - mutable std::mutex mThreadMutex; - std::shared_ptr mClock = ClockHandler::getClock(); - std::mutex mDecoderDictMutex; - std::unique_ptr mCANDecoder; - Platform::Linux::Signal mWait; - uint32_t mIdleTime{ DEFAULT_THREAD_IDLE_TIME_MS }; - static constexpr uint32_t DEFAULT_THREAD_IDLE_TIME_MS = 1000; - // Raw CAN Frame Buffer shared pointer CANBufferPtr mCANBufferPtr; + SignalBufferPtr mSignalBufferPtr; + CANChannelNumericID mChannelId{ INVALID_CAN_SOURCE_NUMERIC_ID }; }; } // namespace DataInspection } // namespace IoTFleetWise diff --git a/src/vehiclenetwork/include/businterfaces/CANDataSource.h b/src/datamanagement/datainspection/include/CANDataSource.h similarity index 54% rename from src/vehiclenetwork/include/businterfaces/CANDataSource.h rename to src/datamanagement/datainspection/include/CANDataSource.h index b2312a58..63e4c235 100644 --- a/src/vehiclenetwork/include/businterfaces/CANDataSource.h +++ b/src/datamanagement/datainspection/include/CANDataSource.h @@ -5,13 +5,22 @@ #if defined( IOTFLEETWISE_LINUX ) // Includes -#include "AbstractVehicleDataSource.h" +#include "CANDataConsumer.h" #include "ClockHandler.h" +#include "CollectionInspectionAPITypes.h" +#include "IActiveDecoderDictionaryListener.h" +#include "IDecoderDictionary.h" #include "Signal.h" +#include "SignalTypes.h" #include "Thread.h" +#include "TimeTypes.h" #include "Timer.h" -#include -#include +#include +#include +#include +#include +#include +#include using namespace Aws::IoTFleetWise::Platform::Linux; @@ -19,33 +28,33 @@ namespace Aws { namespace IoTFleetWise { -namespace VehicleNetwork +namespace DataInspection { // This timestamp is used when uploading data to the cloud -enum class CAN_TIMESTAMP_TYPE +enum class CanTimestampType { KERNEL_SOFTWARE_TIMESTAMP, // default and the best option in most scenarios - KERNEL_HARDWARE_TIMESTAMP, // is not necassary a unix epoch timestamp which will lead to problems and records + KERNEL_HARDWARE_TIMESTAMP, // is not necessarily a unix epoch timestamp which will lead to problems and records // potentially rejected by cloud POLLING_TIME, // fallback if selected value is 0. can lead to multiple can frames having the same timestamp and so // being dropped by cloud }; inline bool -stringToCanTimestampType( std::string const ×tampType, CAN_TIMESTAMP_TYPE &outTimestampType ) +stringToCanTimestampType( std::string const ×tampType, CanTimestampType &outTimestampType ) { if ( timestampType == "Software" ) { - outTimestampType = CAN_TIMESTAMP_TYPE::KERNEL_SOFTWARE_TIMESTAMP; + outTimestampType = CanTimestampType::KERNEL_SOFTWARE_TIMESTAMP; } else if ( timestampType == "Hardware" ) { - outTimestampType = CAN_TIMESTAMP_TYPE::KERNEL_HARDWARE_TIMESTAMP; + outTimestampType = CanTimestampType::KERNEL_HARDWARE_TIMESTAMP; } else if ( timestampType == "Polling" ) { - outTimestampType = CAN_TIMESTAMP_TYPE::POLLING_TIME; + outTimestampType = CanTimestampType::POLLING_TIME; } else { @@ -53,26 +62,36 @@ stringToCanTimestampType( std::string const ×tampType, CAN_TIMESTAMP_TYPE & } return true; } + /** * @brief Linux CAN Bus implementation. Uses Raw Sockets to listen to CAN * data on 1 single CAN IF. */ // coverity[cert_dcl60_cpp_violation] false positive - class only defined once // coverity[autosar_cpp14_m3_2_2_violation] false positive - class only defined once -class CANDataSource : public AbstractVehicleDataSource +class CANDataSource : public IActiveDecoderDictionaryListener { public: static constexpr int PARALLEL_RECEIVED_FRAMES_FROM_KERNEL = 10; static constexpr int DEFAULT_THREAD_IDLE_TIME_MS = 1000; /** - * @brief Data Source Constructor. + * @brief Construct CAN data source + * @param channelId CAN channel identifier * @param timestampTypeToUse which timestamp type should be used to tag the can frames, this timestamp will be * visible in the cloud + * @param interfaceName SocketCAN interface name, e.g. vcan0 + * @param forceCanFD True to force CAN-FD mode, which will cause #connect to return an error if not available. + * False to use CAN-FD if available. + * @param threadIdleTimeMs Poll period of SocketCAN interface. + * @param consumer CAN data consumer */ - CANDataSource( CAN_TIMESTAMP_TYPE timestampTypeToUse ); - CANDataSource(); - + CANDataSource( CANChannelNumericID channelId, + CanTimestampType timestampTypeToUse, + std::string interfaceName, + bool forceCanFD, + uint32_t threadIdleTimeMs, + CANDataConsumer &consumer ); ~CANDataSource() override; CANDataSource( const CANDataSource & ) = delete; @@ -80,16 +99,15 @@ class CANDataSource : public AbstractVehicleDataSource CANDataSource( CANDataSource && ) = delete; CANDataSource &operator=( CANDataSource && ) = delete; - bool init( const std::vector &sourceConfigs ) override; - bool connect() override; - - bool disconnect() override; + bool init(); + bool connect(); - bool isAlive() final; + bool disconnect(); - void resumeDataAcquisition() override; + bool isAlive(); - void suspendDataAcquisition() override; + void onChangeOfActiveDictionary( ConstDecoderDictionaryConstPtr &dictionary, + VehicleDataSourceProtocol networkProtocol ) override; private: // Start the bus thread @@ -98,8 +116,6 @@ class CANDataSource : public AbstractVehicleDataSource bool stop(); // atomic state of the bus. If true, we should stop bool shouldStop() const; - // Intercepts sleep signals. - bool shouldSleep() const; // Main work function. Listens on the socket for CAN Messages // and push data to the circular buffer. static void doWork( void *data ); @@ -108,20 +124,24 @@ class CANDataSource : public AbstractVehicleDataSource Thread mThread; std::atomic mShouldStop{ false }; - std::atomic mShouldSleep{ false }; mutable std::mutex mThreadMutex; Timer mTimer; std::shared_ptr mClock = ClockHandler::getClock(); int mSocket{ -1 }; Platform::Linux::Signal mWait; uint32_t mIdleTimeMs{ DEFAULT_THREAD_IDLE_TIME_MS }; - uint64_t receivedMessages{ 0 }; - uint64_t discardedMessages{ 0 }; - CAN_TIMESTAMP_TYPE mTimestampTypeToUse{ CAN_TIMESTAMP_TYPE::KERNEL_SOFTWARE_TIMESTAMP }; - std::atomic mResumeTime{ 0 }; + uint64_t mReceivedMessages{ 0 }; + CanTimestampType mTimestampTypeToUse{ CanTimestampType::KERNEL_SOFTWARE_TIMESTAMP }; bool mForceCanFD{ false }; + std::mutex mDecoderDictMutex; + std::shared_ptr mDecoderDictionary; + CANChannelNumericID mChannelId{ INVALID_CAN_SOURCE_NUMERIC_ID }; + std::string mIfName; + CANDataConsumer &mConsumer; }; -} // namespace VehicleNetwork + +} // namespace DataInspection } // namespace IoTFleetWise } // namespace Aws + #endif // IOTFLEETWISE_LINUX diff --git a/src/datamanagement/datainspection/include/CollectionInspectionEngine.h b/src/datamanagement/datainspection/include/CollectionInspectionEngine.h index 13573990..00940c81 100644 --- a/src/datamanagement/datainspection/include/CollectionInspectionEngine.h +++ b/src/datamanagement/datainspection/include/CollectionInspectionEngine.h @@ -45,7 +45,7 @@ class CollectionInspectionEngine : public IActiveConditionProcessor, public Thre */ CollectionInspectionEngine( bool sendDataOnlyOncePerCondition = true ); - void onChangeInspectionMatrix( const std::shared_ptr &activeInspectionMatrix ) override; + void onChangeInspectionMatrix( const std::shared_ptr &inspectionMatrix ) override; /** * @brief Go through all conditions with changed condition signals and evaluate condition @@ -269,7 +269,6 @@ class CollectionInspectionEngine : public IActiveConditionProcessor, public Thre template struct SignalHistoryBuffer { - SignalHistoryBuffer() = default; SignalHistoryBuffer( uint32_t sizeIn, uint32_t sampleInterval ) : mMinimumSampleIntervalMs( sampleInterval ) , mSize( sizeIn ) diff --git a/src/datamanagement/datainspection/include/CollectionInspectionWorkerThread.h b/src/datamanagement/datainspection/include/CollectionInspectionWorkerThread.h index 29c7954a..03c3face 100644 --- a/src/datamanagement/datainspection/include/CollectionInspectionWorkerThread.h +++ b/src/datamanagement/datainspection/include/CollectionInspectionWorkerThread.h @@ -32,7 +32,7 @@ class CollectionInspectionWorkerThread : public IActiveConditionProcessor, CollectionInspectionWorkerThread &operator=( CollectionInspectionWorkerThread && ) = delete; // Inherited from IActiveConditionProcessor - void onChangeInspectionMatrix( const std::shared_ptr &activeConditions ) override; + void onChangeInspectionMatrix( const std::shared_ptr &inspectionMatrix ) override; /** * @brief As soon as new data is available in any input queue call this to wakeup the thread @@ -46,7 +46,7 @@ class CollectionInspectionWorkerThread : public IActiveConditionProcessor, * @param inputActiveDTCBuffer OBDModule DTC Circular buffer * @param outputCollectedData this thread will put data that should be sent to cloud into this queue * @param idleTimeMs if no new data is available sleep for this amount of milliseconds - * @param dataReductionProbability set to true to disable data reduction using probability + * @param dataReductionProbabilityDisabled set to true to disable data reduction using probability * * @return true if initialization was successful * */ @@ -55,7 +55,7 @@ class CollectionInspectionWorkerThread : public IActiveConditionProcessor, const std::shared_ptr &inputActiveDTCBuffer, const std::shared_ptr &outputCollectedData, uint32_t idleTimeMs, - bool dataReductionProbability = false ); + bool dataReductionProbabilityDisabled = false ); /** * @brief stops the internal thread if started and wait until it finishes diff --git a/src/datamanagement/datainspection/include/GeohashFunctionNode.h b/src/datamanagement/datainspection/include/GeohashFunctionNode.h index 76bbacf5..2a347bb7 100644 --- a/src/datamanagement/datainspection/include/GeohashFunctionNode.h +++ b/src/datamanagement/datainspection/include/GeohashFunctionNode.h @@ -41,14 +41,17 @@ class GeohashFunctionNode * specified as 5, the comparison would return EQUAL. If the precision is specified as 6, the comparison * would return NOT EQUAL. * - * @param lat: latitude from GPS - * @param lon: longitude from GPS - * @param precision: In Geohash, precision is the length of hash character. - * @param gpsUnitType: The GPS signal latitude / longitude unit type. The following unit type is supported: + * @param latitude latitude from GPS + * @param longitude longitude from GPS + * @param precision In Geohash, precision is the length of hash character. + * @param gpsUnitType The GPS signal latitude / longitude unit type. The following unit type is supported: * 1) DECIMAL DEGREE 2) MICROARCSECOND 3) MILLIARCSECOND 4) ARCSECOND * @return True if geohash has changed at given precision. False if geohash has not changed. */ - bool evaluateGeohash( double lat, double lon, uint8_t precision, GeohashFunction::GPSUnitType gpsUnitType ); + bool evaluateGeohash( double latitude, + double longitude, + uint8_t precision, + GeohashFunction::GPSUnitType gpsUnitType ); /** * @brief Consume the latest evaluated geohash info. This function will set the mIsGeohashNew @@ -69,7 +72,7 @@ class GeohashFunctionNode * * @return converted GPS signal in Decimal Degree */ - static double convertToDecimalDegree( double val, GeohashFunction::GPSUnitType gpsUnitType ); + static double convertToDecimalDegree( double value, GeohashFunction::GPSUnitType gpsUnitType ); /** * @brief This is the Geohash in String format holding the latest calculated geohash diff --git a/src/datamanagement/datainspection/include/IActiveConditionProcessor.h b/src/datamanagement/datainspection/include/IActiveConditionProcessor.h index 1b6a5975..2c47e654 100644 --- a/src/datamanagement/datainspection/include/IActiveConditionProcessor.h +++ b/src/datamanagement/datainspection/include/IActiveConditionProcessor.h @@ -29,10 +29,10 @@ class IActiveConditionProcessor * This function should be called as rarely as possible. * All condition should fulfill the restriction like max signal id or equation depth. * After this call all cached signal values that were not published are deleted - * @param activeConditions all currently active Conditions + * @param inspectionMatrix all currently active Conditions * @return true if valid conditions were handed over * */ - virtual void onChangeInspectionMatrix( const std::shared_ptr &activeConditions ) = 0; + virtual void onChangeInspectionMatrix( const std::shared_ptr &inspectionMatrix ) = 0; virtual ~IActiveConditionProcessor() = default; }; diff --git a/src/datamanagement/datainspection/include/IVehicleDataConsumer.h b/src/datamanagement/datainspection/include/IVehicleDataConsumer.h deleted file mode 100644 index 93fe66f7..00000000 --- a/src/datamanagement/datainspection/include/IVehicleDataConsumer.h +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -// Includes - -#include "CollectionInspectionAPITypes.h" -#include "IDecoderDictionary.h" -#include "businterfaces/AbstractVehicleDataSource.h" -#include - -namespace Aws -{ -namespace IoTFleetWise -{ -namespace DataInspection -{ -using namespace Aws::IoTFleetWise::VehicleNetwork; -using namespace Aws::IoTFleetWise::DataManagement; -// Identifier of the Consumer -using VehicleDataConsumerID = uint32_t; -constexpr VehicleDataConsumerID INVALID_DATA_CONSUMER_ID = 0; - -/** - * @brief Abstract Vehicle Data Consumer Interface. A Vehicle Data Consumer is - * bound to only one Vehicle Data Source. It receives the raw or synthetic data from - * the Vehicle Data Source and decodes/filters the data according to the decoder dictionary. - * The result will be put into three output buffers for inspection engine to process in a fanout - * fashion. - * The life cycle of this Consumer is fully managed by the VehicleDataSourceBinder worker thread. - * Only instantiation is allowed on startup. - */ -class IVehicleDataConsumer -{ -public: - IVehicleDataConsumer() = default; - virtual ~IVehicleDataConsumer() = default; - - /** - * @brief Initializes the Vehicle Data Consumer. - * @param dataSourceID Vehicle Data Source ID - * @param signalBufferPtr Signal Buffer shared pointer. - * @param idleTimeMs if no new data is available sleep for this amount of milliseconds - * @return True if the Consumer has been setup correctly. - */ - virtual bool init( VehicleDataSourceID dataSourceID, SignalBufferPtr signalBufferPtr, uint32_t idleTimeMs ) = 0; - - /** - * @brief Creates and starts the worker thread of the consumer to start - * consuming the data from the input buffer. - * @return True if the thread is active and consumption has started. - */ - virtual bool connect() = 0; - - /** - * @brief Stops the thread and disconnect from the Vehicle Data Source input buffer. - * @return True if the thread is stopped and connection has been closed. - */ - virtual bool disconnect() = 0; - - /** - * @brief Checks that the worker thread is healthy and consuming data. - * @return True if the consumption is healthy. - */ - virtual bool isAlive() = 0; - - /** - * @brief Ask the consumer to resume the decoding of incoming data - * with the provided dictionary. If the dictionary is null or corrupt, - * the consumer should self interrupt and continue to sleep. - * @param dictionary const pointer to the decoder manifest. - */ - virtual void resumeDataConsumption( ConstDecoderDictionaryConstPtr &dictionary ) = 0; - - /** - * @brief Ask the consumer to stop the decoding of incoming data. - * This is typically the case when the source of the data is either interrupted or disconnected. - */ - virtual void suspendDataConsumption() = 0; - - /** - * @brief Handle of the Signal Output Buffer. This buffer shared between Collection Engine - * and Vehicle Data Consumer. This buffer holds the transformed data from its raw form into - * a synthetic representation i.e. after applying the decoding rules. - * @return shared object pointer to the Signal buffer. - */ - inline SignalBufferPtr - getSignalBufferPtr() const - { - return mSignalBufferPtr; - } - - /** - * @brief Set the consumption buffer of the consumer. - * @param producerBufferPtr Pointer to the Producer - */ - inline void - setInputBuffer( VehicleMessageCircularBufferPtr producerBufferPtr ) - { - mInputBufferPtr = std::move( producerBufferPtr ); - } - - /** - * @return the unique ID of the consumer. - */ - inline VehicleDataConsumerID - getConsumerID() const - { - return mID; - } - - /** - * @brief Set the consumption buffer of the consumer. - */ - inline void - setChannelMetadata( std::tuple metadata ) - { - std::tie( mType, mDataSourceProtocol, mIfName ) = metadata; - } - - /** - * @return the Network Protocol Type supported by the consumer - */ - inline VehicleDataSourceProtocol - getVehicleDataSourceProtocol() const - { - return mDataSourceProtocol; - } - -protected: - /** - * @brief Thread safe Consumer ID generator - * @return returns a unique identifier of a consumer ID. - */ - static VehicleDataConsumerID - generateConsumerID() - { - static std::atomic consumerID( INVALID_DATA_CONSUMER_ID ); - return ++consumerID; - } - - VehicleDataSourceID mDataSourceID; - // Input Buffer on which the consumer will run the decoding rules. - VehicleMessageCircularBufferPtr mInputBufferPtr; - // shared pointer to decoder dictionary - std::shared_ptr mDecoderDictionaryConstPtr; - // Signal Buffer shared pointer - SignalBufferPtr mSignalBufferPtr; - VehicleDataConsumerID mID; - VehicleDataSourceType mType; - VehicleDataSourceIfName mIfName; - VehicleDataSourceProtocol mDataSourceProtocol; -}; -using VehicleDataConsumerPtr = std::shared_ptr; -} // namespace DataInspection -} // namespace IoTFleetWise -} // namespace Aws diff --git a/src/datamanagement/datainspection/include/OBDOverCANModule.h b/src/datamanagement/datainspection/include/OBDOverCANModule.h index ce62e958..52645d56 100644 --- a/src/datamanagement/datainspection/include/OBDOverCANModule.h +++ b/src/datamanagement/datainspection/include/OBDOverCANModule.h @@ -26,6 +26,18 @@ using namespace Aws::IoTFleetWise::Platform::Linux; using namespace Aws::IoTFleetWise::VehicleNetwork; using namespace Aws::IoTFleetWise::DataManagement; +// ECU IDs +enum class ECUID +{ + INVALID_ECU_ID = 0, + BROADCAST_ID = 0x7DF, + BROADCAST_EXTENDED_ID = 0x18DB33F1, + LOWEST_ECU_EXTENDED_RX_ID = 0x18DAF100, + LOWEST_ECU_RX_ID = 0x7E8, + HIGHEST_ECU_EXTENDED_RX_ID = 0x18DAF1FF, + HIGHEST_ECU_RX_ID = 0x7EF, +}; + /** * @brief This class is responsible for coordinating OBD requests on all ECUs */ @@ -86,7 +98,7 @@ class OBDOverCANModule : public IActiveDecoderDictionaryListener, public IActive // From IActiveConditionProcessor // We need this to know whether DTCs should be requested or not - void onChangeInspectionMatrix( const std::shared_ptr &activeConditions ) override; + void onChangeInspectionMatrix( const std::shared_ptr &inspectionMatrix ) override; /** * @brief Handle of the Signal Output Buffer. This buffer shared between Collection Engine @@ -126,11 +138,11 @@ class OBDOverCANModule : public IActiveDecoderDictionaryListener, public IActive /** * @brief Initialize ECUs by detected CAN IDs * @param isExtendedID When true, 29-bit messages were detected, otherwise 11-bit - * @param canIDResponse Detected CAN ID via broadcast request + * @param canIDResponses Detected CAN ID via broadcast request * @param broadcastSocket Broadcast socket for sending request. Can be -1 to send using physical socket. * @return True if all OBDOverCANECU modules are initialized successfully for ECUs */ - bool initECUs( bool isExtendedID, std::vector &canIDResponse, int broadcastSocket ); + bool initECUs( bool isExtendedID, std::vector &canIDResponses, int broadcastSocket ); // Start the thread bool start(); @@ -152,10 +164,10 @@ class OBDOverCANModule : public IActiveDecoderDictionaryListener, public IActive void assignPIDsToECUs(); /** * @brief Open an ISO-TP broadcast socket to send requests for all ECUs - * @param isExtendedId Whether the broadcast ID is in extended format (29-bit) or standard format (11 bit) + * @param isExtendedID Whether the broadcast ID is in extended format (29-bit) or standard format (11 bit) * @return Broadcast socket, or -1 on error */ - int openISOTPBroadcastSocket( bool isExtendedId ); + int openISOTPBroadcastSocket( bool isExtendedID ); /** * @brief Flush ecus sockets to ignore received data from broadcast request. If mBroadcastRequests is false, * no flushing will be performed. diff --git a/src/datamanagement/datainspection/include/VehicleDataSourceBinder.h b/src/datamanagement/datainspection/include/VehicleDataSourceBinder.h deleted file mode 100644 index 5b822e6d..00000000 --- a/src/datamanagement/datainspection/include/VehicleDataSourceBinder.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -// Includes -#include "ClockHandler.h" -#include "IActiveDecoderDictionaryListener.h" -#include "IVehicleDataConsumer.h" -#include "Signal.h" -#include "Thread.h" -#include "Timer.h" -#include "businterfaces/AbstractVehicleDataSource.h" -#include -#include -#include - -namespace Aws -{ -namespace IoTFleetWise -{ -namespace DataInspection -{ -using namespace Aws::IoTFleetWise::VehicleNetwork; -using namespace Aws::IoTFleetWise::Platform::Linux; - -/** - * @brief The binder is the entity responsible for : - * 1- creating all consumers - * 2- handing the circular buffers of the data sources to the consumers in a thread safe way. - * 3- Manages the life cycle of the consumer thread - * 4- receives interrupts when the data sources are disconnected and reflects that to the consumer state. - */ -class VehicleDataSourceBinder : public VehicleDataSourceListener, public IActiveDecoderDictionaryListener -{ -public: - VehicleDataSourceBinder() = default; - ~VehicleDataSourceBinder() override; - - VehicleDataSourceBinder( const VehicleDataSourceBinder & ) = delete; - VehicleDataSourceBinder &operator=( const VehicleDataSourceBinder & ) = delete; - VehicleDataSourceBinder( VehicleDataSourceBinder && ) = delete; - VehicleDataSourceBinder &operator=( VehicleDataSourceBinder && ) = delete; - - /** - * @brief Adds a Vehicle Data Source. - * @param source Pointer to the Vehicle Data Source. - * @return True if the data source has been added. - */ - bool addVehicleDataSource( VehicleDataSourcePtr source ); - /** - * @brief removes a Vehicle Data Source - * @param id Vehicle Data Source ID. - * @return True if the data source has been removed. - */ - bool removeVehicleDataSource( const VehicleDataSourceID &id ); - /** - * @brief Binds a Vehicle Data Source to a consumer - * @param id Vehicle Data Source ID. - * @param consumer Pointer to the consumer - * @return True if the circular buffer of the data source is handed over to the consumer. - * Starts the consumer worker. - */ - bool bindConsumerToVehicleDataSource( VehicleDataConsumerPtr consumer, const VehicleDataSourceID &id ); - /** - * @brief unBinds a Vehicle Data Source from the consumer. - * @param id Vehicle Data Source ID. - * @return True of the consumer worker is stopped. - */ - bool unBindConsumerFromVehicleDataSource( const VehicleDataSourceID &id ); - - /** - * @brief Reports runtime state of the binder. - * @return True if alive. False otherwise. - */ - bool isAlive(); - - /** - * @brief Connect the Binder by starting its worker thread. - * @return True if successful. False otherwise. - */ - bool connect(); - - /** - * @brief disconnects all Vehicle Data Sources and corresponding consumers. Then stop the worker - * thread - * @return True if successful. False otherwise. - */ - bool disconnect(); - - /** - * @brief From IActiveDecoderDictionaryListener. Effectively listens to the CollectionScheme Management - * module and propagate that gracefully to the data sources and consumers. - */ - void onChangeOfActiveDictionary( ConstDecoderDictionaryConstPtr &dictionary, - VehicleDataSourceProtocol networkProtocol ) override; - - using SourcesToConsumers = std::map; - using IdsToDataSources = std::map; - using IdsToStates = std::map; - -private: - // atomic state of the thread. If true, we should stop - bool shouldStop() const; - - // Main work function - static void doWork( void *data ); - - /** - * @brief starts the binder worker. Worker is ready to bind data sources to consumers. - * @return True of the binder worker is running. - */ - bool start(); - - /** - * @brief stops the binder worker. - * @return True of the binder worker is stopped. - */ - bool stop(); - - // Listener Callbacks - void onVehicleDataSourceConnected( const VehicleDataSourceID &id ) override; - void onVehicleDataSourceDisconnected( const VehicleDataSourceID &id ) override; - bool reConnectConsumer( const VehicleDataSourceID &id ); - bool disconnectConsumer( const VehicleDataSourceID &id ); - - Thread mThread; - std::atomic mShouldStop{ false }; - mutable std::mutex mThreadMutex; - mutable std::mutex mVehicleDataSourceUpdatesMutex; - mutable std::mutex mVehicleDataSourcesMutex; - mutable std::mutex mConsumersMutex; - IdsToStates mDataSourceStates; - Platform::Linux::Signal mWait; - Timer mTimer; - std::shared_ptr mClock = ClockHandler::getClock(); - SourcesToConsumers mDataSourcesToConsumers; - IdsToDataSources mIdsToDataSources; -}; -} // namespace DataInspection -} // namespace IoTFleetWise -} // namespace Aws diff --git a/src/datamanagement/datainspection/src/CollectionInspectionEngine.cpp b/src/datamanagement/datainspection/src/CollectionInspectionEngine.cpp index 18f19378..af65e6c7 100644 --- a/src/datamanagement/datainspection/src/CollectionInspectionEngine.cpp +++ b/src/datamanagement/datainspection/src/CollectionInspectionEngine.cpp @@ -73,13 +73,12 @@ CollectionInspectionEngine::addSignalToBuffer( const InspectionMatrixSignalColle } void -CollectionInspectionEngine::onChangeInspectionMatrix( - const std::shared_ptr &activeInspectionMatrix ) +CollectionInspectionEngine::onChangeInspectionMatrix( const std::shared_ptr &inspectionMatrix ) { // Clears everything in this class including all data in the signal history buffer clear(); - mActiveInspectionMatrix = activeInspectionMatrix; // Pointers and references into this memory are maintained so hold - // a shared_ptr to it so it does not get deleted + mActiveInspectionMatrix = inspectionMatrix; // Pointers and references into this memory are maintained so hold + // a shared_ptr to it so it does not get deleted mConditionsNotTriggeredWaitingPublished.set(); for ( auto &p : mActiveInspectionMatrix->conditions ) { @@ -90,6 +89,7 @@ CollectionInspectionEngine::onChangeInspectionMatrix( FWE_LOG_WARN( "Too many conditions are active. Up to " + std::to_string( MAX_NUMBER_OF_ACTIVE_CONDITION - 1 ) + " conditions can be active at a time. Additional conditions will be skipped" ); + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::COLLECTION_SCHEME_ERROR ); break; } mConditions.emplace_back( p ); @@ -98,6 +98,7 @@ CollectionInspectionEngine::onChangeInspectionMatrix( TraceModule::get().incrementVariable( TraceVariable::CE_SIGNAL_ID_OUTBOUND ); FWE_LOG_ERROR( "There can be only " + std::to_string( MAX_DIFFERENT_SIGNAL_IDS ) + " different signal IDs" ); + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::COLLECTION_SCHEME_ERROR ); return; } for ( auto &s : p.signals ) @@ -105,12 +106,14 @@ CollectionInspectionEngine::onChangeInspectionMatrix( if ( s.signalID == INVALID_SIGNAL_ID ) { FWE_LOG_ERROR( "A SignalID with value" + std::to_string( INVALID_SIGNAL_ID ) + " is not allowed" ); + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::COLLECTION_SCHEME_ERROR ); return; } if ( s.sampleBufferSize == 0 ) { TraceModule::get().incrementVariable( TraceVariable::CE_SAMPLE_SIZE_ZERO ); FWE_LOG_ERROR( "A Sample buffer size of 0 is not allowed" ); + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::COLLECTION_SCHEME_ERROR ); return; } auto signalIDIn = s.signalID; @@ -287,6 +290,7 @@ CollectionInspectionEngine::allocateBufferVector( SignalID signalIDIn, uint32_t "configured of " + std::to_string( MAX_SAMPLE_MEMORY ) + "Bytes" ); signal.mSize = 0; + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::COLLECTION_SCHEME_ERROR ); return false; } usedBytes += static_cast( requiredBytes ); @@ -1249,7 +1253,7 @@ EventID CollectionInspectionEngine::generateEventID( InspectionTimestamp timestamp ) { // Generate an eventId as a combination of an event counter and a timestamp - uint32_t eventId = static_cast( ( generateEventCounter() & 0xFF ) | ( timestamp << 8 ) ); + uint32_t eventId = static_cast( generateEventCounter() ) | static_cast( timestamp << 8 ); // As Kotlin reads eventId as int32, set most significant bit to 0 so event IDs stay positive eventId = eventId & 0x7FFFFFFF; return eventId; diff --git a/src/datamanagement/datainspection/src/CollectionInspectionWorkerThread.cpp b/src/datamanagement/datainspection/src/CollectionInspectionWorkerThread.cpp index a148df0e..29ef5a73 100644 --- a/src/datamanagement/datainspection/src/CollectionInspectionWorkerThread.cpp +++ b/src/datamanagement/datainspection/src/CollectionInspectionWorkerThread.cpp @@ -13,17 +13,17 @@ namespace DataInspection { bool -CollectionInspectionWorkerThread::init( const std::shared_ptr &inputSignalBufferIn, - const std::shared_ptr &inputCANBufferIn, +CollectionInspectionWorkerThread::init( const std::shared_ptr &inputSignalBuffer, + const std::shared_ptr &inputCANBuffer, const std::shared_ptr &inputActiveDTCBuffer, - const std::shared_ptr &outputCollectedDataIn, + const std::shared_ptr &outputCollectedData, uint32_t idleTimeMs, bool dataReductionProbabilityDisabled ) { - fInputSignalBuffer = inputSignalBufferIn; - fInputCANBuffer = inputCANBufferIn; + fInputSignalBuffer = inputSignalBuffer; + fInputCANBuffer = inputCANBuffer; fInputActiveDTCBuffer = inputActiveDTCBuffer; - fOutputCollectedData = outputCollectedDataIn; + fOutputCollectedData = outputCollectedData; if ( idleTimeMs != 0 ) { fIdleTimeMs = idleTimeMs; @@ -85,11 +85,11 @@ CollectionInspectionWorkerThread::shouldStop() const void CollectionInspectionWorkerThread::onChangeInspectionMatrix( - const std::shared_ptr &activeConditions ) + const std::shared_ptr &inspectionMatrix ) { { std::lock_guard lock( fInspectionMatrixMutex ); - fUpdatedInspectionMatrix = activeConditions; + fUpdatedInspectionMatrix = inspectionMatrix; fUpdatedInspectionMatrixAvailable = true; FWE_LOG_TRACE( "New inspection matrix handed over" ); // Wake up the thread. @@ -115,7 +115,7 @@ CollectionInspectionWorkerThread::doWork( void *data ) uint32_t statisticInputMessagesProcessed = 0; uint32_t statisticDataSentOut = 0; uint32_t activations = 0; - do + while ( true ) { activations++; if ( consumer->fUpdatedInspectionMatrixAvailable ) @@ -298,7 +298,7 @@ CollectionInspectionWorkerThread::doWork( void *data ) ( lastTraceOutput + LoggingModule::LOG_AGGREGATION_TIME_MS ) ) { FWE_LOG_TRACE( "Activations: " + std::to_string( activations ) + - ". Waiting for some data to come. Idling for :" + std::to_string( timeToWait ) + + ". Waiting for some data to come. Idling for: " + std::to_string( timeToWait ) + " ms or until notify. Since last idling processed " + std::to_string( statisticInputMessagesProcessed ) + " incoming data packages and sent out " + std::to_string( statisticDataSentOut ) + @@ -316,7 +316,11 @@ CollectionInspectionWorkerThread::doWork( void *data ) // No inspection Matrix available. Wait for it from the CollectionScheme manager consumer->fWait.wait( Platform::Linux::Signal::WaitWithPredicate ); } - } while ( !consumer->shouldStop() ); + if ( consumer->shouldStop() ) + { + break; + } + } } TimePoint diff --git a/src/datamanagement/datainspection/src/diag/OBDOverCANModule.cpp b/src/datamanagement/datainspection/src/diag/OBDOverCANModule.cpp index 6daf6047..a10a1201 100644 --- a/src/datamanagement/datainspection/src/diag/OBDOverCANModule.cpp +++ b/src/datamanagement/datainspection/src/diag/OBDOverCANModule.cpp @@ -384,7 +384,7 @@ OBDOverCANModule::autoDetectECUs( bool isExtendedID, std::vector &canI { if ( broadcastTimer.getElapsedMs().count() > MAX_WAITING_MS ) { - FWE_LOG_TRACE( "Time elapsed:" + std::to_string( broadcastTimer.getElapsedMs().count() ) + + FWE_LOG_TRACE( "Time elapsed: " + std::to_string( broadcastTimer.getElapsedMs().count() ) + ", time to stop ECUs' detection" ); break; } @@ -421,7 +421,7 @@ OBDOverCANModule::autoDetectECUs( bool isExtendedID, std::vector &canI canIDResponses.push_back( frameCANId ); } } - FWE_LOG_TRACE( "Detected number of ECUs:" + std::to_string( canIDResponses.size() ) ); + FWE_LOG_TRACE( "Detected number of ECUs: " + std::to_string( canIDResponses.size() ) ); for ( std::size_t i = 0; i < canIDResponses.size(); ++i ) { std::stringstream stream_rx; @@ -565,12 +565,12 @@ OBDOverCANModule::isAlive() } void -OBDOverCANModule::onChangeInspectionMatrix( const std::shared_ptr &activeConditions ) +OBDOverCANModule::onChangeInspectionMatrix( const std::shared_ptr &inspectionMatrix ) { // We check here that at least one condition needs DTCs. If yes, we activate that. - if ( activeConditions ) + if ( inspectionMatrix ) { - for ( auto const &condition : activeConditions->conditions ) + for ( auto const &condition : inspectionMatrix->conditions ) { if ( condition.includeActiveDtcs ) { @@ -590,67 +590,65 @@ void OBDOverCANModule::onChangeOfActiveDictionary( ConstDecoderDictionaryConstPtr &dictionary, VehicleDataSourceProtocol networkProtocol ) { - if ( networkProtocol == VehicleDataSourceProtocol::OBD ) - { - std::lock_guard lock( mDecoderDictMutex ); - mDecoderDictionaryPtr = std::make_shared(); - // Here we up cast the decoder dictionary to CAN Decoder Dictionary to extract can decoder method - auto canDecoderDictionaryPtr = std::dynamic_pointer_cast( dictionary ); - // As OBD only has one port, we expect the decoder dictionary only has one channel - if ( mOBDDataDecoder != nullptr && canDecoderDictionaryPtr != nullptr ) + if ( networkProtocol != VehicleDataSourceProtocol::OBD ) + { + return; + } + std::lock_guard lock( mDecoderDictMutex ); + mDecoderDictionaryPtr = std::make_shared(); + // Here we up cast the decoder dictionary to CAN Decoder Dictionary to extract can decoder method + auto canDecoderDictionaryPtr = std::dynamic_pointer_cast( dictionary ); + if ( canDecoderDictionaryPtr == nullptr ) + { + FWE_LOG_TRACE( "Received empty Decoder Manifest" ); + return; + } + // As OBD only has one port, we expect the decoder dictionary only has one channel + if ( canDecoderDictionaryPtr->canMessageDecoderMethod.size() != 1 ) + { + FWE_LOG_WARN( "Received Invalid Decoder Manifest, ignoring it" ); + return; + } + // Iterate through the received generic decoder dictionary to construct the OBD specific dictionary + std::vector pidsRequestedByDecoderDict{}; + for ( const auto &canMessageDecoderMethod : canDecoderDictionaryPtr->canMessageDecoderMethod.cbegin()->second ) + { + // The key is PID; The Value is decoder format + mDecoderDictionaryPtr->emplace( canMessageDecoderMethod.first, canMessageDecoderMethod.second.format ); + // Check if this PID's decoder method contains signals to be collected by + // Decoder Dictionary. If so, add the PID to pidsRequestedByDecoderDict + // Note in worst case scenario when no OBD signals are to be collected, this will + // iterate through the entire OBD signal lists which only contains a few hundreds signals. + for ( const auto &signal : canMessageDecoderMethod.second.format.mSignals ) { - // Iterate through the received generic decoder dictionary to construct the OBD specific dictionary - std::vector pidsRequestedByDecoderDict{}; - if ( canDecoderDictionaryPtr->canMessageDecoderMethod.size() == 1 ) + // if the signal is to be collected according to decoder dictionary, push + // the corresponding PID to the pidsRequestedByDecoderDict + if ( canDecoderDictionaryPtr->signalIDsToCollect.find( signal.mSignalID ) != + canDecoderDictionaryPtr->signalIDsToCollect.end() ) { - for ( const auto &canMessageDecoderMethod : - canDecoderDictionaryPtr->canMessageDecoderMethod.cbegin()->second ) - { - // The key is PID; The Value is decoder format - mDecoderDictionaryPtr->emplace( canMessageDecoderMethod.first, - canMessageDecoderMethod.second.format ); - // Check if this PID's decoder method contains signals to be collected by - // Decoder Dictionary. If so, add the PID to pidsRequestedByDecoderDict - // Note in worst case scenario when no OBD signals are to be collected, this will - // iterate through the entire OBD signal lists which only contains a few hundreds signals. - for ( const auto &signal : canMessageDecoderMethod.second.format.mSignals ) - { - // if the signal is to be collected according to decoder dictionary, push - // the corresponding PID to the pidsRequestedByDecoderDict - if ( canDecoderDictionaryPtr->signalIDsToCollect.find( signal.mSignalID ) != - canDecoderDictionaryPtr->signalIDsToCollect.end() ) - { - pidsRequestedByDecoderDict.emplace_back( - static_cast( canMessageDecoderMethod.first ) ); - // We know this PID needs to be requested, break to move on next PID - break; - } - } - } + pidsRequestedByDecoderDict.emplace_back( static_cast( canMessageDecoderMethod.first ) ); + // We know this PID needs to be requested, break to move on next PID + break; } - std::sort( pidsRequestedByDecoderDict.begin(), pidsRequestedByDecoderDict.end() ); - FWE_LOG_TRACE( "Decoder Dictionary requests PIDs: " + getStringFromBytes( pidsRequestedByDecoderDict ) ); - // For now we only support OBD Service Mode 1 PID - mPIDsRequestedByDecoderDict[SID::CURRENT_STATS] = pidsRequestedByDecoderDict; - - // If the program already know the supported PIDs from ECU, below two update will update - // For each ecu update the PIDs requested by the Dict - assignPIDsToECUs(); - - // Pass on the decoder manifest to the OBD Decoder and wake up the thread. - // Before that we should interrupt the thread so that no further decoding - // is done using the previous decoder, then assign the new decoder manifest, - // the wake up the thread. - mDecoderManifestAvailable.store( true, std::memory_order_relaxed ); - // Wake up the worker thread. - mDataAvailableWait.notify(); - FWE_LOG_INFO( "Decoder Manifest Updated" ); - } - else - { - FWE_LOG_WARN( "Received Invalid Decoder Manifest, ignoring it" ); } } + std::sort( pidsRequestedByDecoderDict.begin(), pidsRequestedByDecoderDict.end() ); + FWE_LOG_TRACE( "Decoder Dictionary requests PIDs: " + getStringFromBytes( pidsRequestedByDecoderDict ) ); + // For now we only support OBD Service Mode 1 PID + mPIDsRequestedByDecoderDict[SID::CURRENT_STATS] = pidsRequestedByDecoderDict; + + // If the program already know the supported PIDs from ECU, below two update will update + // For each ecu update the PIDs requested by the Dict + assignPIDsToECUs(); + + // Pass on the decoder manifest to the OBD Decoder and wake up the thread. + // Before that we should interrupt the thread so that no further decoding + // is done using the previous decoder, then assign the new decoder manifest, + // the wake up the thread. + mDecoderManifestAvailable.store( true, std::memory_order_relaxed ); + // Wake up the worker thread. + mDataAvailableWait.notify(); + FWE_LOG_INFO( "Decoder Manifest Updated" ); } } // namespace DataInspection diff --git a/src/datamanagement/datainspection/src/location/GeohashFunctionNode.cpp b/src/datamanagement/datainspection/src/location/GeohashFunctionNode.cpp index 85423151..8c4fd71e 100644 --- a/src/datamanagement/datainspection/src/location/GeohashFunctionNode.cpp +++ b/src/datamanagement/datainspection/src/location/GeohashFunctionNode.cpp @@ -94,13 +94,13 @@ GeohashFunctionNode::convertToDecimalDegree( double value, GeohashFunction::GPSU convertedValue = value; break; case GeohashFunction::GPSUnitType::MICROARCSECOND: - convertedValue = value / 3600000000.0f; + convertedValue = value / 3600000000.0F; break; case GeohashFunction::GPSUnitType::MILLIARCSECOND: - convertedValue = value / 3600000.0f; + convertedValue = value / 3600000.0F; break; case GeohashFunction::GPSUnitType::ARCSECOND: - convertedValue = value / 3600.0f; + convertedValue = value / 3600.0F; break; default: break; diff --git a/src/datamanagement/datainspection/src/vehicledatasource/CANDataConsumer.cpp b/src/datamanagement/datainspection/src/vehicledatasource/CANDataConsumer.cpp index 4ae1d1e6..44dbde5c 100644 --- a/src/datamanagement/datainspection/src/vehicledatasource/CANDataConsumer.cpp +++ b/src/datamanagement/datainspection/src/vehicledatasource/CANDataConsumer.cpp @@ -3,11 +3,9 @@ // Includes #include "CANDataConsumer.h" +#include "CANDecoder.h" #include "LoggingModule.h" #include "TraceModule.h" -#include -#include -#include namespace Aws { @@ -16,147 +14,22 @@ namespace IoTFleetWise namespace DataInspection { -CANDataConsumer::~CANDataConsumer() +CANDataConsumer::CANDataConsumer( CANChannelNumericID channelId, + SignalBufferPtr signalBufferPtr, + CANBufferPtr canBufferPtr ) + : mCANBufferPtr{ std::move( canBufferPtr ) } + , mSignalBufferPtr{ std::move( signalBufferPtr ) } + , mChannelId{ channelId } { - // To make sure the thread stops during teardown of tests. - if ( isAlive() ) - { - stop(); - } -} - -bool -CANDataConsumer::init( VehicleDataSourceID canChannelID, SignalBufferPtr signalBufferPtr, uint32_t idleTimeMs ) -{ - mID = generateConsumerID(); - mCANDecoder = std::make_unique(); - if ( signalBufferPtr.get() == nullptr ) - { - FWE_LOG_TRACE( "Init Failed due to bufferPtr as nullptr" ); - return false; - } - else - { - FWE_LOG_TRACE( "Init Network channel consumer with id: " + std::to_string( canChannelID ) ); - mDataSourceID = canChannelID; - mSignalBufferPtr = signalBufferPtr; - } - if ( idleTimeMs != 0 ) - { - mIdleTime = idleTimeMs; - } - return true; -} - -void -CANDataConsumer::suspendDataConsumption() -{ - // Go back to sleep - FWE_LOG_TRACE( "Going to sleep until a the resume signal. Consumer : " + std::to_string( mDataSourceID ) ); - mShouldSleep.store( true, std::memory_order_relaxed ); -} - -void -CANDataConsumer::resumeDataConsumption( ConstDecoderDictionaryConstPtr &dictionary ) -{ - if ( dictionary.get() != nullptr ) - { - { - // As this function can be asynchronously invoked by other module in the middle of - // CAN message Decoding, a mutex has to be used to avoid decoding race condition in which - // a single CAN message could got decoded by two different formula. This mutex ensure - // the shared pointer assignment is atomic - std::lock_guard lock( mDecoderDictMutex ); - // Convert the Generic Decoder Dictionary to CAN Decoder Dictionary - // TODO : This downcast is done three times in this entity. One time when the generic decoder is - // provider and another one on every work iteration. As we plan to consolidate all decoding - // rules for different data source types in one single decoder instance, this down cast - // shall be removed. - mDecoderDictionaryConstPtr = std::dynamic_pointer_cast( dictionary ); - } - if ( mDecoderDictionaryConstPtr != nullptr ) - { - // Decoder dictionary - auto decoderDictPtr = std::dynamic_pointer_cast( mDecoderDictionaryConstPtr ); - const auto &decoderMethod = decoderDictPtr->canMessageDecoderMethod; - std::string canIds; - // check if this CAN message ID on this CAN Channel has the decoder method and if yes log the number - if ( decoderMethod.find( mDataSourceID ) != decoderMethod.cend() ) - { - for ( auto &decode : decoderMethod.at( mDataSourceID ) ) - { - canIds += std::to_string( decode.first ) + ", "; - } - } - FWE_LOG_TRACE( "Changing Decoder Dictionary on Consumer :" + std::to_string( mID ) + - " with decoding rules for CAN-IDs: " + canIds ); - // Make sure the thread does not sleep anymore - mShouldSleep.store( false ); - // Wake up the worker thread. - mWait.notify(); - } - else - { - FWE_LOG_ERROR( "Received invalid decoder dictionary :" + std::to_string( mID ) ); - } - } -} - -bool -CANDataConsumer::start() -{ - // Prevent concurrent stop/init - std::lock_guard lock( mThreadMutex ); - // On multi core systems the shared variable mShouldStop must be updated for - // all cores before starting the thread otherwise thread will directly end - mShouldStop.store( false ); - // Make sure the thread goes into sleep immediately to wait for - // the manifest to be available - mShouldSleep.store( true ); - if ( !mThread.create( doWork, this ) ) - { - FWE_LOG_TRACE( "Thread failed to start" ); - } - else - { - FWE_LOG_TRACE( "Thread started" ); - mThread.setThreadName( "fwDIConsumer" + std::to_string( mID ) ); - } - - return mThread.isActive() && mThread.isValid(); -} - -bool -CANDataConsumer::stop() -{ - std::lock_guard lock( mThreadMutex ); - mShouldStop.store( true, std::memory_order_relaxed ); - mWait.notify(); - mThread.release(); - mShouldStop.store( false, std::memory_order_relaxed ); - FWE_LOG_TRACE( "Thread stopped" ); - return !mThread.isActive(); -} - -bool -CANDataConsumer::shouldStop() const -{ - return mShouldStop.load( std::memory_order_relaxed ); -} - -bool -CANDataConsumer::shouldSleep() const -{ - return mShouldSleep.load( std::memory_order_relaxed ); + FWE_LOG_TRACE( "Init Network channel consumer with id: " + std::to_string( channelId ) ); } bool CANDataConsumer::findDecoderMethod( uint32_t &messageId, const CANDecoderDictionary::CANMsgDecoderMethodType &decoderMethod, - CANDecodedMessage &decodedMessage, - CANMessageDecoderMethod ¤tMessageDecoderMethod ) + CANMessageDecoderMethod ¤tMessageDecoderMethod ) const { - auto outerMapIt = decoderMethod.find( mDataSourceID ); + auto outerMapIt = decoderMethod.find( mChannelId ); if ( outerMapIt != decoderMethod.cend() ) { auto it = outerMapIt->second.find( messageId ); @@ -172,7 +45,6 @@ CANDataConsumer::findDecoderMethod( uint32_t &messageId, { messageId = messageId & CAN_EFF_MASK; currentMessageDecoderMethod = it->second; - decodedMessage.mFrameInfo.mFrameID = messageId; return true; } } @@ -180,279 +52,137 @@ CANDataConsumer::findDecoderMethod( uint32_t &messageId, } void -CANDataConsumer::doWork( void *data ) +CANDataConsumer::processMessage( std::shared_ptr &dictionary, + const struct canfd_frame &message, + Timestamp timestamp ) { - - CANDataConsumer *consumer = static_cast( data ); - - uint32_t activations = 0; - Timer logTimer; - - // Only used for TRACE log level logging - std::array, 8> lastFrameIds{}; // .first=can id, .second=counter - uint8_t lastFrameIdPos = 0; - uint32_t processedFramesCounter = 0; - + // Skip if the dictionary was invalidated during message processing: + if ( dictionary == nullptr ) + { + return; + } TraceSection traceSection = - ( ( consumer->mDataSourceID < static_cast( toUType( TraceSection::CAN_DECODER_CYCLE_19 ) - - toUType( TraceSection::CAN_DECODER_CYCLE_0 ) ) ) - ? static_cast( consumer->mDataSourceID + toUType( TraceSection::CAN_DECODER_CYCLE_0 ) ) + ( ( mChannelId < static_cast( toUType( TraceSection::CAN_DECODER_CYCLE_19 ) - + toUType( TraceSection::CAN_DECODER_CYCLE_0 ) ) ) + ? static_cast( mChannelId + toUType( TraceSection::CAN_DECODER_CYCLE_0 ) ) : TraceSection::CAN_DECODER_CYCLE_19 ); - do + TraceModule::get().sectionBegin( traceSection ); + // get decoderMethod from the decoder dictionary + const auto &decoderMethod = dictionary->canMessageDecoderMethod; + // a set of signalID specifying which signal to collect + const auto &signalIDsToCollect = dictionary->signalIDsToCollect; + // check if this CAN message ID on this CAN Channel has the decoder method + uint32_t messageId = message.can_id; + CANMessageDecoderMethod currentMessageDecoderMethod; + // The value of messageId may be changed by the findDecoderMethod function. This is a + // workaround as the cloud as of now does not send extended id messages. + // If the decoder method for this message is not found in + // decoderMethod dictionary, we check for the same id without the MSB set. + // The message id which has a decoderMethod gets passed into messageId + if ( findDecoderMethod( messageId, decoderMethod, currentMessageDecoderMethod ) ) { - activations++; - if ( consumer->shouldSleep() ) - { - // We either just started or there was a decoder manifest update that we can't use. - // We should sleep - FWE_LOG_TRACE( "No valid decoding dictionary available, Consumer going to sleep" ); - // Wait here for the decoder Manifest to come. - consumer->mWait.wait( Platform::Linux::Signal::WaitWithPredicate ); - // At this point, we should be able to see events coming as the channel is also - // woken up. - TraceModule::get().sectionBegin( traceSection ); - } - // Below section utilize decoder dictionary to perform CAN message decoding and collection. - // Use a Mutex to prevent updating decoder dictionary in the middle of CAN Frame processing. - std::shared_ptr decoderDictPtr; - { - std::lock_guard lock( consumer->mDecoderDictMutex ); - decoderDictPtr = - std::dynamic_pointer_cast( consumer->mDecoderDictionaryConstPtr ); - } + // format to be used for decoding + const auto &format = currentMessageDecoderMethod.format; + const auto &collectType = currentMessageDecoderMethod.collectType; - // Pop any message from the Input Buffer - VehicleDataMessage message; - if ( consumer->mInputBufferPtr->pop( message ) ) + // Check if we want to collect RAW CAN Frame; If so we also need to ensure Buffer is valid + if ( ( mCANBufferPtr.get() != nullptr ) && ( ( collectType == CANMessageCollectType::RAW ) || + ( collectType == CANMessageCollectType::RAW_AND_DECODE ) ) ) { - TraceVariable traceQueue = static_cast( - consumer->mDataSourceID + toUType( TraceVariable::QUEUE_SOCKET_TO_CONSUMER_0 ) ); - TraceModule::get().setVariable( ( traceQueue < TraceVariable::QUEUE_SOCKET_TO_CONSUMER_19 ) - ? traceQueue - : TraceVariable::QUEUE_SOCKET_TO_CONSUMER_19, - consumer->mInputBufferPtr->read_available() + 1 ); - CANDecodedMessage decodedMessage; - decodedMessage.mChannelProtocol = consumer->mDataSourceProtocol; - decodedMessage.mChannelType = consumer->mType; - decodedMessage.mChannelIfName = consumer->mIfName; - decodedMessage.mReceptionTime = message.getReceptionTimestamp(); - decodedMessage.mFrameInfo.mFrameID = static_cast( message.getMessageID() ); - - const auto messageRawData = message.getRawData().data(); - if ( messageRawData != nullptr ) + // prepare the raw CAN Frame + struct CollectedCanRawFrame canRawFrame; + canRawFrame.frameID = messageId; + canRawFrame.channelId = mChannelId; + canRawFrame.receiveTime = timestamp; + // CollectedCanRawFrame receive up to 64 CAN Raw Bytes + canRawFrame.size = std::min( static_cast( message.len ), MAX_CAN_FRAME_BYTE_SIZE ); + std::copy( message.data, message.data + canRawFrame.size, canRawFrame.data.begin() ); + // Push raw CAN Frame to the Buffer for next stage to consume + // Note buffer is lock_free buffer and multiple Vehicle Data Source Instance could push + // data to it. + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_CAN ); + if ( !mCANBufferPtr->push( canRawFrame ) ) { - decodedMessage.mFrameInfo.mFrameRawData.assign( messageRawData, - message.getRawData().size() + messageRawData ); + TraceModule::get().decrementAtomicVariable( TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_CAN ); + FWE_LOG_WARN( "RAW CAN Frame Buffer Full" ); } - // get decoderMethod from the decoder dictionary - const auto &decoderMethod = decoderDictPtr->canMessageDecoderMethod; - // a set of signalID specifying which signal to collect - const auto &signalIDsToCollect = decoderDictPtr->signalIDsToCollect; - - // check if this CAN message ID on this CAN Channel has the decoder method - uint32_t messageId = static_cast( message.getMessageID() ); - CANMessageDecoderMethod currentMessageDecoderMethod; - - // The value of messageId may be changed by the findDecoderMethod function. This is a - // workaround as the cloud as of now does not send extended id messages. - // If the decoder method for this message is not found in - // decoderMethod dictionary, we check for the same id without the MSB set. - // The message id which has a decoderMethod gets passed into messageId - - bool hasDecoderMethod = - consumer->findDecoderMethod( messageId, decoderMethod, decodedMessage, currentMessageDecoderMethod ); - - if ( hasDecoderMethod ) + else { - // format to be used for decoding - const auto &format = currentMessageDecoderMethod.format; - const auto &collectType = currentMessageDecoderMethod.collectType; - - // Only used for TRACE log level logging - if ( ( collectType == CANMessageCollectType::RAW ) || - ( collectType == CANMessageCollectType::RAW_AND_DECODE ) || - ( collectType == CANMessageCollectType::DECODE ) ) + // Enable below logging for debugging + // FWE_LOG_TRACE( "CANDataConsumer::doWork", + // "Collect RAW CAN Frame ID: " + std::to_string( messageId ) ); + } + } + // check if we want to decode can frame into signals and collect signals + if ( ( mSignalBufferPtr.get() != nullptr ) && ( ( collectType == CANMessageCollectType::DECODE ) || + ( collectType == CANMessageCollectType::RAW_AND_DECODE ) ) ) + { + if ( format.isValid() ) + { + std::vector decodedSignals; + if ( CANDecoder::decodeCANMessage( + message.data, message.len, format, signalIDsToCollect, decodedSignals ) ) { - bool found = false; - for ( auto &p : lastFrameIds ) + for ( auto const &signal : decodedSignals ) { - if ( p.first == messageId ) + // Create Collected Signal Object + struct CollectedSignal collectedSignal; + const auto signalType = signal.mSignalType; + switch ( signalType ) { - found = true; - p.second++; + case SignalType::UINT64: + collectedSignal = CollectedSignal{ signal.mSignalID, + timestamp, + signal.mPhysicalValue.signalValue.uint64Val, + signal.mSignalType }; + break; + case SignalType::INT64: + collectedSignal = CollectedSignal{ signal.mSignalID, + timestamp, + signal.mPhysicalValue.signalValue.int64Val, + signal.mSignalType }; + break; + default: + collectedSignal = CollectedSignal{ signal.mSignalID, + timestamp, + signal.mPhysicalValue.signalValue.doubleVal, + signal.mSignalType }; break; } - } - if ( !found ) - { - lastFrameIdPos++; - if ( lastFrameIdPos >= lastFrameIds.size() ) - { - lastFrameIdPos = 0; - } - lastFrameIds[lastFrameIdPos] = std::pair( messageId, 1 ); - } - processedFramesCounter++; - } - // Check if we want to collect RAW CAN Frame; If so we also need to ensure Buffer is valid - if ( ( consumer->mCANBufferPtr.get() != nullptr ) && - ( ( collectType == CANMessageCollectType::RAW ) || - ( collectType == CANMessageCollectType::RAW_AND_DECODE ) ) ) - { - // prepare the raw CAN Frame - struct CollectedCanRawFrame canRawFrame; - canRawFrame.frameID = messageId; - canRawFrame.channelId = consumer->mDataSourceID; - canRawFrame.receiveTime = message.getReceptionTimestamp(); - // CollectedCanRawFrame receive up to 64 CAN Raw Bytes - canRawFrame.size = - std::min( static_cast( message.getRawData().size() ), MAX_CAN_FRAME_BYTE_SIZE ); - std::copy( message.getRawData().begin(), - message.getRawData().begin() + canRawFrame.size, - canRawFrame.data.begin() ); - // Push raw CAN Frame to the Buffer for next stage to consume - // Note buffer is lock_free buffer and multiple Vehicle Data Source Instance could push - // data to it. - TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_CAN ); - if ( !consumer->mCANBufferPtr->push( canRawFrame ) ) - { - TraceModule::get().decrementAtomicVariable( - TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_CAN ); - FWE_LOG_WARN( "RAW CAN Frame Buffer Full" ); - } - else - { - // Enable below logging for debugging - // FWE_LOG_TRACE( "CANDataConsumer::doWork", - // "Collect RAW CAN Frame ID: " + - // std::to_string( static_cast( messageId ) ) ); - } - } - // check if we want to decode can frame into signals and collect signals - if ( ( consumer->mSignalBufferPtr.get() != nullptr ) && - ( ( collectType == CANMessageCollectType::DECODE ) || - ( collectType == CANMessageCollectType::RAW_AND_DECODE ) ) ) - { - if ( format.isValid() ) - { - if ( consumer->mCANDecoder->decodeCANMessage( message.getRawData().data(), - message.getRawData().size(), - format, - signalIDsToCollect, - decodedMessage ) ) + // Push collected signal to the Signal Buffer + TraceModule::get().incrementAtomicVariable( + TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_SIGNALS ); + if ( !mSignalBufferPtr->push( collectedSignal ) ) { - for ( auto const &signal : decodedMessage.mFrameInfo.mSignals ) - { - // Create Collected Signal Object - struct CollectedSignal collectedSignal; - const auto signalType = signal.mSignalType; - switch ( signalType ) - { - case SignalType::UINT64: - collectedSignal = CollectedSignal{ signal.mSignalID, - decodedMessage.mReceptionTime, - signal.mPhysicalValue.signalValue.uint64Val, - signal.mSignalType }; - break; - case SignalType::INT64: - collectedSignal = CollectedSignal{ signal.mSignalID, - decodedMessage.mReceptionTime, - signal.mPhysicalValue.signalValue.int64Val, - signal.mSignalType }; - break; - default: - collectedSignal = CollectedSignal{ signal.mSignalID, - decodedMessage.mReceptionTime, - signal.mPhysicalValue.signalValue.doubleVal, - signal.mSignalType }; - break; - } - - // Push collected signal to the Signal Buffer - TraceModule::get().incrementAtomicVariable( - TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_SIGNALS ); - if ( !consumer->mSignalBufferPtr->push( collectedSignal ) ) - { - TraceModule::get().decrementAtomicVariable( - TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_SIGNALS ); - FWE_LOG_WARN( "Signal Buffer Full" ); - } - else - { - // Enable below logging for debugging - // FWE_LOG_TRACE( "CANDataConsumer::doWork", - // "Acquire Signal ID: " + std::to_string( signal.mSignalID ) ); - } - } + TraceModule::get().decrementAtomicVariable( + TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_SIGNALS ); + FWE_LOG_WARN( "Signal Buffer Full" ); } else { - // The decoding was not fully successful - FWE_LOG_WARN( "CAN Frame " + std::to_string( messageId ) + " decoding failed" ); + // Enable below logging for debugging + // FWE_LOG_TRACE( "CANDataConsumer::doWork", + // "Acquire Signal ID: " + std::to_string( signal.mSignalID ) ); } } - else - { - // The CAN Message format is not valid, report as warning - FWE_LOG_WARN( - - "CANMessageFormat Invalid for format message id: " + std::to_string( format.mMessageID ) + - " can message id: " + std::to_string( messageId ) + - " on CAN Channel Id: " + std::to_string( consumer->mDataSourceID ) ); - } } - } - } - else - { - if ( logTimer.getElapsedMs().count() > static_cast( LoggingModule::LOG_AGGREGATION_TIME_MS ) ) - { - // Nothing is in the ring buffer to consume. Go to idle mode for some time. - std::stringstream logMessage; - logMessage << "Channel Id: " << consumer->mDataSourceID - << ". Activations since last print: " << std::to_string( activations ) - << ". Number of frames over all processed " << processedFramesCounter - << ".Last CAN IDs processed:"; - for ( auto id : lastFrameIds ) + else { - logMessage << id.first << " (x " << id.second << "), "; + // The decoding was not fully successful + FWE_LOG_WARN( "CAN Frame " + std::to_string( messageId ) + " decoding failed! " ); } - logMessage << ". Waiting for some data to come. Idling for :" + std::to_string( consumer->mIdleTime ) + - " ms"; - FWE_LOG_TRACE( logMessage.str() ); - activations = 0; - logTimer.reset(); + } + else + { + // The CAN Message format is not valid, report as warning + FWE_LOG_WARN( "CANMessageFormat Invalid for format message id: " + std::to_string( format.mMessageID ) + + " can message id: " + std::to_string( messageId ) + + " on CAN Channel Id: " + std::to_string( mChannelId ) ); } TraceModule::get().sectionEnd( traceSection ); - consumer->mWait.wait( consumer->mIdleTime ); - TraceModule::get().sectionBegin( traceSection ); } - } while ( !consumer->shouldStop() ); -} - -bool -CANDataConsumer::connect() -{ - if ( ( mInputBufferPtr.get() != nullptr ) && ( mSignalBufferPtr.get() != nullptr ) && - ( mCANBufferPtr.get() != nullptr ) && start() ) - { - return true; } - - return false; -} - -bool -CANDataConsumer::disconnect() -{ - return stop(); -} - -bool -CANDataConsumer::isAlive() -{ - return mThread.isValid() && mThread.isActive(); } } // namespace DataInspection diff --git a/src/vehiclenetwork/src/CANDataSource.cpp b/src/datamanagement/datainspection/src/vehicledatasource/CANDataSource.cpp similarity index 61% rename from src/vehiclenetwork/src/CANDataSource.cpp rename to src/datamanagement/datainspection/src/vehicledatasource/CANDataSource.cpp index 4ffd86fd..81af3ca5 100644 --- a/src/vehiclenetwork/src/CANDataSource.cpp +++ b/src/datamanagement/datainspection/src/vehicledatasource/CANDataSource.cpp @@ -3,14 +3,11 @@ #if defined( IOTFLEETWISE_LINUX ) // Includes -#include "businterfaces/CANDataSource.h" -#include "ClockHandler.h" +#include "CANDataSource.h" #include "EnumUtility.h" #include "LoggingModule.h" #include "TraceModule.h" -#include #include -#include #include #include #include @@ -22,25 +19,23 @@ namespace Aws { namespace IoTFleetWise { -namespace VehicleNetwork +namespace DataInspection { using namespace Aws::IoTFleetWise::Platform::Utility; -static const std::string INTERFACE_NAME_KEY = "interfaceName"; -static const std::string THREAD_IDLE_TIME_KEY = "threadIdleTimeMs"; -static const std::string PROTOCOL_NAME_KEY = "protocolName"; -CANDataSource::CANDataSource( CAN_TIMESTAMP_TYPE timestampTypeToUse ) - : mTimestampTypeToUse{ timestampTypeToUse } -{ - mType = VehicleDataSourceType::CAN_SOURCE; - mNetworkProtocol = VehicleDataSourceProtocol::RAW_SOCKET; - mID = generateSourceID(); -} -CANDataSource::CANDataSource() +CANDataSource::CANDataSource( CANChannelNumericID channelId, + CanTimestampType timestampTypeToUse, + std::string interfaceName, + bool forceCanFD, + uint32_t threadIdleTimeMs, + CANDataConsumer &consumer ) + : mIdleTimeMs{ threadIdleTimeMs } + , mTimestampTypeToUse{ timestampTypeToUse } + , mForceCanFD{ forceCanFD } + , mChannelId{ channelId } + , mIfName{ std::move( interfaceName ) } + , mConsumer{ consumer } { - mType = VehicleDataSourceType::CAN_SOURCE; - mNetworkProtocol = VehicleDataSourceProtocol::RAW_SOCKET; - mID = generateSourceID(); } CANDataSource::~CANDataSource() @@ -48,65 +43,22 @@ CANDataSource::~CANDataSource() // To make sure the thread stops during teardown of tests. if ( isAlive() ) { - stop(); + disconnect(); } } bool -CANDataSource::init( const std::vector &sourceConfigs ) +CANDataSource::init() { - // Only one source config is supported on the CAN stack i.e. we manage one socket with - // one single thread. - if ( ( sourceConfigs.size() > 1 ) || sourceConfigs.empty() ) - { - FWE_LOG_ERROR( "Only one source config is supported" ); - return false; - } - auto settingsIterator = sourceConfigs[0].transportProperties.find( std::string( INTERFACE_NAME_KEY ) ); - if ( settingsIterator == sourceConfigs[0].transportProperties.end() ) - { - FWE_LOG_ERROR( "Could not find interfaceName in the config" ); - return false; - } - else - { - mIfName = settingsIterator->second; - } - - mCircularBuffPtr = - std::make_shared( sourceConfigs[0].maxNumberOfVehicleDataMessages ); - settingsIterator = sourceConfigs[0].transportProperties.find( std::string( THREAD_IDLE_TIME_KEY ) ); - if ( settingsIterator == sourceConfigs[0].transportProperties.end() ) - { - FWE_LOG_ERROR( "Could not find threadIdleTimeMs in the config" ); - return false; - } - else - { - try - { - mIdleTimeMs = static_cast( std::stoul( settingsIterator->second ) ); - } - catch ( const std::exception &e ) - { - FWE_LOG_ERROR( "Could not cast the threadIdleTimeMs, invalid input: " + std::string( e.what() ) ); - return false; - } - } - - settingsIterator = sourceConfigs[0].transportProperties.find( std::string( PROTOCOL_NAME_KEY ) ); - if ( settingsIterator == sourceConfigs[0].transportProperties.end() ) + if ( mChannelId == INVALID_CAN_SOURCE_NUMERIC_ID ) { - FWE_LOG_ERROR( "Could not find protocolName in the config" ); + FWE_LOG_ERROR( "Invalid can channel id" ); return false; } - else - { - mForceCanFD = settingsIterator->second == "CAN-FD"; - } mTimer.reset(); - return true; + + return connect(); } bool @@ -117,9 +69,6 @@ CANDataSource::start() // On multi core systems the shared variable mShouldStop must be updated for // all cores before starting the thread otherwise thread will directly end mShouldStop.store( false ); - // Make sure the thread goes into sleep immediately to wait for - // the manifest to be available - mShouldSleep.store( true ); if ( !mThread.create( doWork, this ) ) { FWE_LOG_TRACE( "CAN Data Source Thread failed to start" ); @@ -127,31 +76,12 @@ CANDataSource::start() else { FWE_LOG_TRACE( "CAN Data Source Thread started" ); - mThread.setThreadName( "fwVNLinuxCAN" + std::to_string( mID ) ); + // Thread name has channelID+1 to match legacy naming of threads in dashboards + mThread.setThreadName( "fwVNLinuxCAN" + std::to_string( mChannelId + 1 ) ); } return mThread.isActive() && mThread.isValid(); } -void -CANDataSource::suspendDataAcquisition() -{ - // Go back to sleep - FWE_LOG_TRACE( "Going to sleep until a the resume signal. CAN Data Source: " + std::to_string( mID ) ); - mShouldSleep.store( true, std::memory_order_relaxed ); -} - -void -CANDataSource::resumeDataAcquisition() -{ - - FWE_LOG_TRACE( "Resuming Network data acquisition on Data Source: " + std::to_string( mID ) ); - // Make sure the thread does not sleep anymore - mResumeTime = mClock->systemTimeSinceEpochMs(); - mShouldSleep.store( false ); - // Wake up the worker thread. - mWait.notify(); -} - bool CANDataSource::stop() { @@ -170,12 +100,6 @@ CANDataSource::shouldStop() const return mShouldStop.load( std::memory_order_relaxed ); } -bool -CANDataSource::shouldSleep() const -{ - return mShouldSleep.load( std::memory_order_relaxed ); -} - Timestamp CANDataSource::extractTimestamp( struct msghdr *msgHeader ) { @@ -185,7 +109,7 @@ CANDataSource::extractTimestamp( struct msghdr *msgHeader ) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) struct cmsghdr *currentHeader = CMSG_FIRSTHDR( msgHeader ); Timestamp timestamp = 0; - if ( mTimestampTypeToUse != CAN_TIMESTAMP_TYPE::POLLING_TIME ) + if ( mTimestampTypeToUse != CanTimestampType::POLLING_TIME ) { while ( currentHeader != nullptr ) { @@ -196,13 +120,13 @@ CANDataSource::extractTimestamp( struct msghdr *msgHeader ) scm_timestamping *timestampArray = (scm_timestamping *)( CMSG_DATA( currentHeader ) ); // From https://www.kernel.org/doc/Documentation/networking/timestamping.txt // Most timestamps are passed in ts[0]. Hardware timestamps are passed in ts[2]. - if ( mTimestampTypeToUse == CAN_TIMESTAMP_TYPE::KERNEL_HARDWARE_TIMESTAMP ) + if ( mTimestampTypeToUse == CanTimestampType::KERNEL_HARDWARE_TIMESTAMP ) { timestamp = static_cast( ( static_cast( timestampArray->ts[2].tv_sec ) * 1000 ) + ( static_cast( timestampArray->ts[2].tv_nsec ) / 1000000 ) ); } - else if ( mTimestampTypeToUse == CAN_TIMESTAMP_TYPE::KERNEL_SOFTWARE_TIMESTAMP ) // default + else if ( mTimestampTypeToUse == CanTimestampType::KERNEL_SOFTWARE_TIMESTAMP ) // default { timestamp = static_cast( ( static_cast( timestampArray->ts[0].tv_sec ) * 1000 ) + @@ -235,10 +159,10 @@ CANDataSource::doWork( void *data ) false; /**< This variable is true after the thread is woken up for example because a valid decoder manifest was received until the thread sleeps for the next time when it is false again*/ Timer logTimer; - do + while ( true ) { activations++; - if ( dataSource->shouldSleep() ) + if ( dataSource->mDecoderDictionary == nullptr ) { // We either just started or there was a decoder manifest update that we can't use // We should sleep @@ -268,67 +192,49 @@ CANDataSource::doWork( void *data ) msg[i].msg_hdr.msg_controllen = sizeof( cmsgReturnBuffer[i] ); } // In one syscall receive up to PARALLEL_RECEIVED_FRAMES_FROM_KERNEL frames in parallel - nmsgs = recvmmsg( dataSource->mSocket, msg, PARALLEL_RECEIVED_FRAMES_FROM_KERNEL, 0, nullptr ); + nmsgs = recvmmsg( dataSource->mSocket, &msg[0], PARALLEL_RECEIVED_FRAMES_FROM_KERNEL, 0, nullptr ); for ( int i = 0; i < nmsgs; i++ ) { - VehicleDataMessage message; - const std::vector syntheticData{}; - std::vector rawData = {}; - Timestamp timestamp = dataSource->extractTimestamp( &msg[i].msg_hdr ); - if ( timestamp < lastFrameTime ) - { - TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::NOT_TIME_MONOTONIC_FRAMES ); - } // After waking up the Socket Can old messages in the kernel queue need to be ignored if ( !wokeUpFromSleep ) { + Timestamp timestamp = dataSource->extractTimestamp( &msg[i].msg_hdr ); + if ( timestamp < lastFrameTime ) + { + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::NOT_TIME_MONOTONIC_FRAMES ); + } lastFrameTime = timestamp; - dataSource->receivedMessages++; - TraceVariable traceFrames = - static_cast( dataSource->mID + toUType( TraceVariable::READ_SOCKET_FRAMES_0 ) ); + dataSource->mReceivedMessages++; + TraceVariable traceFrames = static_cast( + dataSource->mChannelId + toUType( TraceVariable::READ_SOCKET_FRAMES_0 ) ); TraceModule::get().setVariable( ( traceFrames < TraceVariable::READ_SOCKET_FRAMES_19 ) ? traceFrames : TraceVariable::READ_SOCKET_FRAMES_19, - dataSource->receivedMessages ); - rawData.reserve( frame[i].len ); - for ( size_t j = 0; j < frame[i].len; ++j ) - { - rawData.emplace_back( frame[i].data[j] ); - } - message.setup( frame[i].can_id, rawData, syntheticData, timestamp ); - if ( message.isValid() ) - { - if ( !dataSource->mCircularBuffPtr->push( message ) ) - { - dataSource->discardedMessages++; - TraceModule::get().setVariable( TraceVariable::DISCARDED_FRAMES, - dataSource->discardedMessages ); - FWE_LOG_WARN( "Circular Buffer is full" ); - } - } - else - { - FWE_LOG_WARN( "Message is not valid" ); - } + dataSource->mReceivedMessages ); + std::lock_guard lock( dataSource->mDecoderDictMutex ); + dataSource->mConsumer.processMessage( dataSource->mDecoderDictionary, frame[i], timestamp ); } } - if ( nmsgs <= 0 ) + if ( nmsgs < PARALLEL_RECEIVED_FRAMES_FROM_KERNEL ) { if ( logTimer.getElapsedMs().count() > static_cast( LoggingModule::LOG_AGGREGATION_TIME_MS ) ) { // Nothing is in the ring buffer to consume. Go to idle mode for some time. FWE_LOG_TRACE( - "Activations: " + std::to_string( activations ) + - ". Waiting for some data to come. Idling for :" + std::to_string( dataSource->mIdleTimeMs ) + - " ms, processed " + std::to_string( dataSource->receivedMessages ) + " frames" ); + ". Waiting for some data to come. Idling for: " + std::to_string( dataSource->mIdleTimeMs ) + + " ms, processed " + std::to_string( dataSource->mReceivedMessages ) + " frames" ); activations = 0; logTimer.reset(); } dataSource->mWait.wait( static_cast( dataSource->mIdleTimeMs ) ); wokeUpFromSleep = false; } - } while ( !dataSource->shouldStop() ); + if ( dataSource->shouldStop() ) + { + break; + } + } } bool @@ -373,8 +279,8 @@ CANDataSource::connect() close( mSocket ); return false; } - if ( ( mTimestampTypeToUse == CAN_TIMESTAMP_TYPE::KERNEL_SOFTWARE_TIMESTAMP ) || - ( mTimestampTypeToUse == CAN_TIMESTAMP_TYPE::KERNEL_HARDWARE_TIMESTAMP ) ) + if ( ( mTimestampTypeToUse == CanTimestampType::KERNEL_SOFTWARE_TIMESTAMP ) || + ( mTimestampTypeToUse == CanTimestampType::KERNEL_HARDWARE_TIMESTAMP ) ) { const int timestampFlags = ( SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RAW_HARDWARE ); @@ -386,7 +292,6 @@ CANDataSource::connect() } } - memset( &interfaceAddress, 0, sizeof( interfaceAddress ) ); interfaceAddress.can_family = AF_CAN; interfaceAddress.can_ifindex = interfaceRequest.ifr_ifindex; @@ -397,8 +302,6 @@ CANDataSource::connect() close( mSocket ); return false; } - // Notify on connection success - notifyListeners( &VehicleDataSourceListener::onVehicleDataSourceConnected, mID ); // Start the main thread. return start(); } @@ -406,13 +309,7 @@ CANDataSource::connect() bool CANDataSource::disconnect() { - if ( ( !stop() ) && ( close( mSocket ) < 0 ) ) - { - return false; - } - // Notify on connection closure - notifyListeners( &VehicleDataSourceListener::onVehicleDataSourceDisconnected, mID ); - return true; + return stop() && ( close( mSocket ) == 0 ); } bool @@ -429,7 +326,29 @@ CANDataSource::isAlive() return true; } -} // namespace VehicleNetwork +void +CANDataSource::onChangeOfActiveDictionary( ConstDecoderDictionaryConstPtr &dictionary, + VehicleDataSourceProtocol networkProtocol ) +{ + if ( networkProtocol != VehicleDataSourceProtocol::RAW_SOCKET ) + { + return; + } + std::lock_guard lock( mDecoderDictMutex ); + mDecoderDictionary = std::dynamic_pointer_cast( dictionary ); + if ( dictionary == nullptr ) + { + FWE_LOG_TRACE( "Going to sleep until a the resume signal. CAN Data Source: " + std::to_string( mChannelId ) ); + } + else + { + FWE_LOG_TRACE( "Resuming Network data acquisition on Data Source: " + std::to_string( mChannelId ) ); + // Wake up the worker thread. + mWait.notify(); + } +} + +} // namespace DataInspection } // namespace IoTFleetWise } // namespace Aws #endif // IOTFLEETWISE_LINUX diff --git a/src/datamanagement/datainspection/src/vehicledatasource/VehicleDataSourceBinder.cpp b/src/datamanagement/datainspection/src/vehicledatasource/VehicleDataSourceBinder.cpp deleted file mode 100644 index 880338df..00000000 --- a/src/datamanagement/datainspection/src/vehicledatasource/VehicleDataSourceBinder.cpp +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Includes -#include "VehicleDataSourceBinder.h" -#include "LoggingModule.h" - -namespace Aws -{ -namespace IoTFleetWise -{ -namespace DataInspection -{ - -VehicleDataSourceBinder::~VehicleDataSourceBinder() -{ - mDataSourcesToConsumers.clear(); - mIdsToDataSources.clear(); - mDataSourceStates.clear(); - // To make sure the thread stops during teardown of tests. - if ( isAlive() ) - { - stop(); - } -} - -bool -VehicleDataSourceBinder::addVehicleDataSource( VehicleDataSourcePtr source ) -{ - // Check if the data source is valid - if ( ( source.get() == nullptr ) || ( source->getVehicleDataSourceID() == INVALID_DATA_SOURCE_ID ) ) - { - FWE_LOG_ERROR( "Invalid vehicle data source" ); - return false; - } - // Insert the Source if it's not already inserted - { - std::lock_guard lock( mVehicleDataSourcesMutex ); - auto sourceIterator = mIdsToDataSources.emplace( source->getVehicleDataSourceID(), source ); - if ( !sourceIterator.second ) - { - FWE_LOG_ERROR( "Could not add the vehicle data source to the binder instance" ); - return false; - } - else - { - FWE_LOG_TRACE( "SourceID: " + std::to_string( source->getVehicleDataSourceID() ) + " added" ); - } - } - // Register Self for Connect and Disconnect Callbacks and connect the vehicle data source - if ( source->connect() && source->subscribeListener( this ) ) - { - return true; - } - else - { - FWE_LOG_ERROR( "Could not connect the vehicle data source with ID: " + - std::to_string( source->getVehicleDataSourceID() ) ); - return false; - } -} - -bool -VehicleDataSourceBinder::removeVehicleDataSource( const VehicleDataSourceID &id ) -{ - // Check if the data source is valid - if ( id == INVALID_DATA_SOURCE_ID ) - { - FWE_LOG_ERROR( "Invalid consumer instance or data source" ); - return false; - } - auto backupSource = VehicleDataSourcePtr(); - // Lookup the Channel in the internal list - { - std::lock_guard lock( mVehicleDataSourcesMutex ); - auto sourceIterator = mIdsToDataSources.find( id ); - // Something went wrong... removing a data source that does not exist - if ( sourceIterator == mIdsToDataSources.end() ) - { - FWE_LOG_ERROR( "Attempting to remove a vehicle data source that was not added" ); - return false; - } - backupSource = sourceIterator->second; - mIdsToDataSources.erase( sourceIterator ); - } - // Deregister this thread from any of the callbacks of this data source and disconnect the data source - if ( backupSource != nullptr ) - { - if ( backupSource->unSubscribeListener( this ) && backupSource->disconnect() ) - { - return true; - } - else - { - FWE_LOG_ERROR( "Could not disconnect the vehicle data source with ID: " + - std::to_string( backupSource->getVehicleDataSourceID() ) ); - return false; - } - } - else - { - return false; - } -} - -bool -VehicleDataSourceBinder::bindConsumerToVehicleDataSource( VehicleDataConsumerPtr consumer, - const VehicleDataSourceID &id ) -{ - // Check if the channelID and the consumer are valid - if ( ( consumer.get() == nullptr ) || ( id == INVALID_DATA_SOURCE_ID ) ) - { - FWE_LOG_ERROR( "Invalid consumer instance or data source" ); - return false; - } - // First lookup the data source and check if it's registered - auto dataSourceIterator = mIdsToDataSources.find( id ); - if ( dataSourceIterator == mIdsToDataSources.end() ) - { - FWE_LOG_ERROR( "Source not found" ); - return false; - } - // Insert the consumer/ID pair - { - std::lock_guard lock( mConsumersMutex ); - auto consumerIterator = mDataSourcesToConsumers.emplace( id, consumer ); - - if ( !consumerIterator.second ) - { - return false; - } - } - // Propagate the data source metadata - consumer->setChannelMetadata( std::make_tuple( dataSourceIterator->second->getVehicleDataSourceType(), - dataSourceIterator->second->getVehicleDataSourceProtocol(), - dataSourceIterator->second->getVehicleDataSourceIfName() ) ); - // Assign the circular buffer of the data source to the consumer - consumer->setInputBuffer( dataSourceIterator->second->getBuffer() ); - // Start the consumer worker - return consumer->connect(); -} - -bool -VehicleDataSourceBinder::unBindConsumerFromVehicleDataSource( const VehicleDataSourceID &id ) -{ - // Check if the data source ID is valid - if ( id == INVALID_DATA_SOURCE_ID ) - { - FWE_LOG_ERROR( "Invalid consumer instance or data source" ); - return false; - } - - auto backupConsumer = VehicleDataConsumerPtr(); - // Lookup the consumer registered to this data source - { - std::lock_guard lock( mConsumersMutex ); - auto consumerIterator = mDataSourcesToConsumers.find( id ); - // Something went wrong... No consumer is registered for this data source - if ( consumerIterator == mDataSourcesToConsumers.end() ) - { - FWE_LOG_ERROR( "Consumer not found" ); - return false; - } - backupConsumer = consumerIterator->second; - mDataSourcesToConsumers.erase( consumerIterator ); - } - // Disconnect the consumer. - if ( backupConsumer != nullptr ) - { - return backupConsumer->disconnect(); - } - else - { - return false; - } -} - -bool -VehicleDataSourceBinder::disconnectConsumer( const VehicleDataSourceID &id ) -{ - auto backupConsumer = VehicleDataConsumerPtr(); - // Lookup the consumer registered to this data source - // release the Mutex after this context to allow addition of consumers. - { - std::lock_guard lock( mConsumersMutex ); - auto consumerIterator = mDataSourcesToConsumers.find( id ); - // Something went wrong... No consumer is registered for this data source - if ( consumerIterator == mDataSourcesToConsumers.end() ) - { - FWE_LOG_ERROR( "Consumer not found" ); - return false; - } - backupConsumer = consumerIterator->second; - } - // Disconnect the consumer. - if ( backupConsumer != nullptr ) - { - return backupConsumer->disconnect(); - } - else - { - return false; - } -} - -bool -VehicleDataSourceBinder::reConnectConsumer( const VehicleDataSourceID &id ) -{ - auto backupConsumer = VehicleDataConsumerPtr(); - // Lookup the consumer registered to this data source - // release the Mutex after this context to allow addition of consumers. - { - std::lock_guard lock( mConsumersMutex ); - auto consumerIterator = mDataSourcesToConsumers.find( id ); - // Something went wrong... No consumer is registered for this data source - if ( consumerIterator == mDataSourcesToConsumers.end() ) - { - FWE_LOG_ERROR( "Consumer not found" ); - return false; - } - backupConsumer = consumerIterator->second; - } - // reConnect the consumer. Make sure that the consumer is not alive already - if ( ( backupConsumer != nullptr ) && ( !backupConsumer->isAlive() ) ) - { - return backupConsumer->connect(); - } - else - { - return false; - } -} - -bool -VehicleDataSourceBinder::start() -{ - // Prevent concurrent stop/init - std::lock_guard lock( mThreadMutex ); - // On multi core systems the shared variable mShouldStop must be updated for - // all cores before starting the thread otherwise thread will directly end - mShouldStop.store( false ); - if ( !mThread.create( doWork, this ) ) - { - FWE_LOG_TRACE( "Binder Thread failed to start" ); - } - else - { - FWE_LOG_TRACE( "Binder Thread started" ); - mThread.setThreadName( "fwDIBinder" ); - } - - return mThread.isActive() && mThread.isValid(); -} - -bool -VehicleDataSourceBinder::stop() -{ - std::lock_guard lock( mThreadMutex ); - mShouldStop.store( true, std::memory_order_relaxed ); - mWait.notify(); - mThread.release(); - mShouldStop.store( false, std::memory_order_relaxed ); - return !mThread.isActive(); -} - -bool -VehicleDataSourceBinder::shouldStop() const -{ - return mShouldStop.load( std::memory_order_relaxed ); -} - -bool -VehicleDataSourceBinder::isAlive() -{ - return mThread.isValid() && mThread.isActive(); -} - -void -VehicleDataSourceBinder::doWork( void *data ) -{ - - VehicleDataSourceBinder *binder = static_cast( data ); - while ( !binder->shouldStop() ) - { - // The idea of this thread is to actively monitor all the channels and - // connect / disconnect them according to their states. - // We can think of running this in the main Engine thread in polling mode - // instead of Interrupt mode if the engine knows exactly when Channels get disconnected. - - binder->mTimer.reset(); - uint32_t elapsedTimeUs = 0; - binder->mWait.wait( Platform::Linux::Signal::WaitWithPredicate ); - elapsedTimeUs += static_cast( binder->mTimer.getElapsedMs().count() ); - FWE_LOG_TRACE( "Time Elapsed waiting for the interrupt : " + std::to_string( elapsedTimeUs ) ); - - // Some Channels have been either connected or disconnected. - // Copy the updates and release the lock so that other channels can - // signal their updates. - IdsToStates sourceStatesCopy; - { - std::lock_guard lock( binder->mVehicleDataSourceUpdatesMutex ); - if ( !binder->mDataSourceStates.empty() ) - { - sourceStatesCopy.swap( binder->mDataSourceStates ); - } - } - for ( auto &sourceID : sourceStatesCopy ) - { - if ( sourceID.second == VehicleDataSourceState::CONNECTED ) - { - // This is only successful if the source has been disconnected and reconnected. - // On bootstrap, this will be simply ignored, as the data source is started - // by the binder. - if ( binder->reConnectConsumer( sourceID.first ) ) - { - FWE_LOG_TRACE( "Reconnected Source ID: " + std::to_string( sourceID.first ) ); - } - } - else if ( sourceID.second == VehicleDataSourceState::DISCONNECTED ) - { - // Data source is disconnected, we need to make sure the consumer is also disconnected - if ( binder->disconnectConsumer( sourceID.first ) ) - { - FWE_LOG_TRACE( "Disconnected Source ID: " + std::to_string( sourceID.first ) ); - } - } - } - } -} - -// This callback/Interrupt arrives from a different thread context. We just copy the -// interrupt and wake up this worker thread. -void -VehicleDataSourceBinder::onVehicleDataSourceConnected( const VehicleDataSourceID &id ) -{ - // Check if the data source ID is valid, should not happen - if ( id == INVALID_DATA_SOURCE_ID ) - { - FWE_LOG_ERROR( "Invalid consumer instance or data source" ); - return; - } - // This event happens if the data source has been disconnected before and got - // externally re-connected. We should signal to this worker thread to rebind the - // data source to underlying consumer - { - std::lock_guard lock( mVehicleDataSourceUpdatesMutex ); - mDataSourceStates.emplace( id, VehicleDataSourceState::CONNECTED ); - } - // Send an Interrupt - mWait.notify(); -} - -// This callback/Interrupt arrives from a different thread context. We just copy the -// interrupt and wake up this worker thread. -void -VehicleDataSourceBinder::onVehicleDataSourceDisconnected( const VehicleDataSourceID &id ) -{ - // Check if the data source ID is valid, should not happen - if ( id == INVALID_DATA_SOURCE_ID ) - { - FWE_LOG_ERROR( "Invalid consumer instance or data source" ); - return; - } - // This event happens if the data source has been disconnected. - // We should signal to this worker thread to unbind the underlying consumer - { - std::lock_guard lock( mVehicleDataSourceUpdatesMutex ); - mDataSourceStates.emplace( id, VehicleDataSourceState::DISCONNECTED ); - } - // Send an Interrupt - mWait.notify(); -} - -bool -VehicleDataSourceBinder::connect() -{ - // Limiting the connect call to only starting the thread. - // We could move the connect of all channels in this function, however it will limit - // the runtime addition of channels, which might be a wanted feature in the future. - // At this point, only the binder thread will start. - return start(); -} - -bool -VehicleDataSourceBinder::disconnect() -{ - // In the disconnect, we need to make sure we disconnect all channels and consumers - // in a safe way. - // We start first disconnecting the consumers as they hold references to channels buffers. - { - // This mutex guarantees that when the consumers are disconnected, - // this thread does not accidentally tries to reconnect them. - std::lock_guard lock( mConsumersMutex ); - for ( auto const &consumer : mDataSourcesToConsumers ) - { - if ( !consumer.second->disconnect() ) - { - FWE_LOG_ERROR( "Failed to disconnect Consumer" ); - return false; - } - else - { - FWE_LOG_TRACE( "Consumer disconnected" ); - } - } - } - - // In the disconnect, we need to make sure we disconnect all channels and consumers - // in a safe way. - { - // This mutex guarantees that when the channels are disconnected, - // this thread does not accidentally tries to reconnect them. - std::lock_guard lock( mVehicleDataSourcesMutex ); - for ( auto const &source : mIdsToDataSources ) - { - // if ( !source.second->unSubscribeListener( this ) || !source.second->disconnect() ) - if ( !source.second->disconnect() ) - { - FWE_LOG_ERROR( "Failed to disconnect Data source ID: " + std::to_string( source.first ) ); - return false; - } - else - { - FWE_LOG_TRACE( "Data source ID: " + std::to_string( source.first ) + " disconnected" ); - } - } - } - // Finally, stop the thread - return stop(); -} - -void -VehicleDataSourceBinder::onChangeOfActiveDictionary( ConstDecoderDictionaryConstPtr &dictionary, - VehicleDataSourceProtocol networkProtocol ) -{ - // This callbacks arrives from the CollectionScheme Management thread and does the following : - // 1- If there were no active dictionary available at all in the system, or we have a corrupt one, - // the channels and consumers must go to sleep. - // 2- If we receive a new manifest, we should wake up the data source and the consumer for the given - // Vehicle Data Consumer type - FWE_LOG_TRACE( "Decoder Manifest received" ); - // Start with the consumers, make sure that wake up first so that they pick up - // the data for decoding immediately - - // The critical section is kept on both channels and consumers - // Just in case new channels and consumers are added while we are applying this - // transformation. - std::lock_guard lockChannel( mVehicleDataSourcesMutex ); - std::lock_guard lockConsumer( mConsumersMutex ); - if ( dictionary.get() != nullptr ) - { - - std::for_each( mDataSourcesToConsumers.begin(), - mDataSourcesToConsumers.end(), - [&]( const std::pair &consumer ) { - if ( networkProtocol == consumer.second->getVehicleDataSourceProtocol() ) - { - consumer.second->resumeDataConsumption( dictionary ); - FWE_LOG_TRACE( "Resuming Consumption on Consumer: " + - std::to_string( consumer.second->getConsumerID() ) ); - } - } ); - - std::for_each( mIdsToDataSources.begin(), - mIdsToDataSources.end(), - [&]( const std::pair &source ) { - if ( networkProtocol == source.second->getVehicleDataSourceProtocol() ) - { - source.second->resumeDataAcquisition(); - FWE_LOG_TRACE( "Resuming Consumption on Data source: " + - std::to_string( source.second->getVehicleDataSourceID() ) ); - } - } ); - } - else - { - - std::for_each( mDataSourcesToConsumers.begin(), - mDataSourcesToConsumers.end(), - [&]( const std::pair &consumer ) { - if ( networkProtocol == consumer.second->getVehicleDataSourceProtocol() ) - { - consumer.second->suspendDataConsumption(); - FWE_LOG_TRACE( "Interrupting Consumption on Consumer :" + - std::to_string( consumer.second->getConsumerID() ) ); - } - } ); - - std::for_each( mIdsToDataSources.begin(), - mIdsToDataSources.end(), - [&]( const std::pair &source ) { - if ( networkProtocol == source.second->getVehicleDataSourceProtocol() ) - { - source.second->suspendDataAcquisition(); - FWE_LOG_TRACE( "Interrupting Consumption on Data source: " + - std::to_string( source.second->getVehicleDataSourceID() ) ); - } - } ); - } -} -} // namespace DataInspection -} // namespace IoTFleetWise -} // namespace Aws diff --git a/src/datamanagement/datainspection/test/CANDataSourceTest.cpp b/src/datamanagement/datainspection/test/CANDataSourceTest.cpp new file mode 100644 index 00000000..2e8a4f41 --- /dev/null +++ b/src/datamanagement/datainspection/test/CANDataSourceTest.cpp @@ -0,0 +1,331 @@ + +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "CANDataSource.h" +#include "WaitUntil.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Aws::IoTFleetWise::TestingSupport; +using namespace Aws::IoTFleetWise::DataInspection; + +static void +cleanUp( int mSocketFD ) +{ + close( mSocketFD ); +} + +static int +setup( bool fd = false ) +{ + // Setup a socket + std::string socketCANIFName( "vcan0" ); + struct sockaddr_can interfaceAddress; + struct ifreq interfaceRequest; + + int type = SOCK_RAW | SOCK_NONBLOCK; + int mSocketFD = socket( PF_CAN, type, CAN_RAW ); + if ( mSocketFD < 0 ) + { + return -1; + } + if ( fd ) + { + int canfd_on = 1; + if ( setsockopt( mSocketFD, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof( canfd_on ) ) != 0 ) + { + return -1; + } + } + + if ( socketCANIFName.size() >= sizeof( interfaceRequest.ifr_name ) ) + { + cleanUp( mSocketFD ); + return -1; + } + (void)strncpy( interfaceRequest.ifr_name, socketCANIFName.c_str(), sizeof( interfaceRequest.ifr_name ) - 1U ); + + if ( ioctl( mSocketFD, SIOCGIFINDEX, &interfaceRequest ) ) + { + cleanUp( mSocketFD ); + return -1; + } + + memset( &interfaceAddress, 0, sizeof( interfaceAddress ) ); + interfaceAddress.can_family = AF_CAN; + interfaceAddress.can_ifindex = interfaceRequest.ifr_ifindex; + + if ( bind( mSocketFD, (struct sockaddr *)&interfaceAddress, sizeof( interfaceAddress ) ) < 0 ) + { + cleanUp( mSocketFD ); + return -1; + } + + return mSocketFD; +} + +static bool +sendTestMessage( int mSocketFD, uint32_t messageId = 0x123 ) +{ + struct can_frame frame = {}; + frame.can_id = messageId; + frame.can_dlc = 8; + for ( uint8_t i = 0; i < 8; ++i ) + { + frame.data[i] = i; + } + ssize_t bytesWritten = write( mSocketFD, &frame, sizeof( struct can_frame ) ); + EXPECT_EQ( bytesWritten, sizeof( struct can_frame ) ); + return true; +} + +static bool +sendTestFDMessage( int mSocketFD ) +{ + struct canfd_frame frame = {}; + frame.can_id = 0x123; + frame.len = 64; + for ( uint8_t i = 0; i < 64; ++i ) + { + frame.data[i] = i; + } + ssize_t bytesWritten = write( mSocketFD, &frame, sizeof( struct canfd_frame ) ); + EXPECT_EQ( bytesWritten, sizeof( struct canfd_frame ) ); + return true; +} + +static bool +sendTestMessageExtendedID( int mSocketFD ) +{ + struct can_frame frame = {}; + frame.can_id = 0x123 | CAN_EFF_FLAG; + + frame.can_dlc = 8; + for ( uint8_t i = 0; i < 8; ++i ) + { + frame.data[i] = i; + } + ssize_t bytesWritten = write( mSocketFD, &frame, sizeof( struct can_frame ) ); + EXPECT_EQ( bytesWritten, sizeof( struct can_frame ) ); + return true; +} + +class CANDataSourceTest : public ::testing::Test +{ +protected: + void + SetUp() override + { + mSocketFD = setup(); + if ( mSocketFD == -1 ) + { + GTEST_SKIP() << "Skipping test fixture due to unavailability of socket"; + } + std::unordered_map frameMap; + CANMessageDecoderMethod decoderMethod; + decoderMethod.collectType = CANMessageCollectType::RAW_AND_DECODE; + + decoderMethod.format.mMessageID = 0x123; + decoderMethod.format.mSizeInBytes = 8; + + CANSignalFormat sigFormat1; + sigFormat1.mSignalID = 1; + sigFormat1.mIsBigEndian = true; + sigFormat1.mIsSigned = true; + sigFormat1.mFirstBitPosition = 24; + sigFormat1.mSizeInBits = 30; + sigFormat1.mOffset = 0.0; + sigFormat1.mFactor = 1.0; + + CANSignalFormat sigFormat2; + sigFormat2.mSignalID = 7; + sigFormat2.mIsBigEndian = true; + sigFormat2.mIsSigned = true; + sigFormat2.mFirstBitPosition = 56; + sigFormat2.mSizeInBits = 31; + sigFormat2.mOffset = 0.0; + sigFormat2.mFactor = 1.0; + + decoderMethod.format.mSignals.push_back( sigFormat1 ); + decoderMethod.format.mSignals.push_back( sigFormat2 ); + frameMap[0x123] = decoderMethod; + mDictionary = std::make_shared(); + mDictionary->canMessageDecoderMethod[0] = frameMap; + mDictionary->signalIDsToCollect.emplace( 1 ); + mDictionary->signalIDsToCollect.emplace( 7 ); + } + + void + TearDown() override + { + cleanUp( mSocketFD ); + } + + int mSocketFD; + std::shared_ptr mDictionary; +}; + +TEST_F( CANDataSourceTest, invalidInit ) +{ + auto signalBufferPtr = std::make_shared( 10 ); + auto canRawBufferPtr = std::make_shared( 10 ); + + CANDataConsumer consumer{ INVALID_CAN_SOURCE_NUMERIC_ID, signalBufferPtr, canRawBufferPtr }; + CANDataSource dataSource{ + INVALID_CAN_SOURCE_NUMERIC_ID, CanTimestampType::KERNEL_HARDWARE_TIMESTAMP, "vcan0", false, 100, consumer }; + ASSERT_FALSE( dataSource.init() ); +} + +TEST_F( CANDataSourceTest, testNoDecoderDictionary ) +{ + ASSERT_TRUE( mSocketFD != -1 ); + auto signalBufferPtr = std::make_shared( 10 ); + auto canRawBufferPtr = std::make_shared( 10 ); + + CANDataConsumer consumer{ 0, signalBufferPtr, canRawBufferPtr }; + CANDataSource dataSource{ 0, CanTimestampType::KERNEL_HARDWARE_TIMESTAMP, "vcan0", false, 100, consumer }; + ASSERT_TRUE( dataSource.init() ); + ASSERT_TRUE( dataSource.isAlive() ); + CollectedSignal signal; + DELAY_ASSERT_FALSE( sendTestMessage( mSocketFD ) && signalBufferPtr->pop( signal ) ); + CollectedCanRawFrame frame; + ASSERT_FALSE( canRawBufferPtr->pop( frame ) ); + // No disconnect to test destructor disconnect +} + +TEST_F( CANDataSourceTest, testValidDecoderDictionary ) +{ + ASSERT_TRUE( mSocketFD != -1 ); + auto signalBufferPtr = std::make_shared( 10 ); + auto canRawBufferPtr = std::make_shared( 10 ); + + CANDataConsumer consumer{ 0, signalBufferPtr, canRawBufferPtr }; + CANDataSource dataSource{ 0, CanTimestampType::KERNEL_HARDWARE_TIMESTAMP, "vcan0", false, 100, consumer }; + ASSERT_TRUE( dataSource.init() ); + ASSERT_TRUE( dataSource.isAlive() ); + dataSource.onChangeOfActiveDictionary( mDictionary, VehicleDataSourceProtocol::RAW_SOCKET ); + CollectedSignal signal; + WAIT_ASSERT_TRUE( sendTestMessage( mSocketFD ) && signalBufferPtr->pop( signal ) ); + ASSERT_EQ( signal.value.type, SignalType::DOUBLE ); + ASSERT_EQ( signal.signalID, 1 ); + ASSERT_DOUBLE_EQ( signal.value.value.doubleVal, 0x10203 ); + ASSERT_TRUE( signalBufferPtr->pop( signal ) ); + ASSERT_EQ( signal.signalID, 7 ); + ASSERT_EQ( signal.value.type, SignalType::DOUBLE ); + ASSERT_DOUBLE_EQ( signal.value.value.doubleVal, 0x4050607 ); + CollectedCanRawFrame frame; + ASSERT_TRUE( canRawBufferPtr->pop( frame ) ); + ASSERT_EQ( frame.channelId, 0 ); + ASSERT_EQ( frame.frameID, 0x123 ); + ASSERT_EQ( frame.size, 8 ); + for ( auto i = 0; i < 8; i++ ) + { + ASSERT_EQ( frame.data[i], i ); + } + + // Test message a different message ID is not received + DELAY_ASSERT_FALSE( sendTestMessage( mSocketFD, 0x456 ) && + ( signalBufferPtr->pop( signal ) || canRawBufferPtr->pop( frame ) ) ); + + // Test invalidation of decoder dictionary + dataSource.onChangeOfActiveDictionary( nullptr, VehicleDataSourceProtocol::RAW_SOCKET ); + DELAY_ASSERT_FALSE( sendTestMessage( mSocketFD ) && + ( signalBufferPtr->pop( signal ) || canRawBufferPtr->pop( frame ) ) ); + // Check it ignores dictionaries for other protocols + dataSource.onChangeOfActiveDictionary( mDictionary, VehicleDataSourceProtocol::OBD ); + DELAY_ASSERT_FALSE( sendTestMessage( mSocketFD ) && + ( signalBufferPtr->pop( signal ) || canRawBufferPtr->pop( frame ) ) ); + ASSERT_TRUE( dataSource.disconnect() ); +} + +TEST_F( CANDataSourceTest, testCanFDSocketMode ) +{ + cleanUp( mSocketFD ); + mSocketFD = setup( true ); + ASSERT_TRUE( mSocketFD != -1 ); + auto signalBufferPtr = std::make_shared( 10 ); + auto canRawBufferPtr = std::make_shared( 10 ); + + CANDataConsumer consumer{ 0, signalBufferPtr, canRawBufferPtr }; + CANDataSource dataSource{ 0, CanTimestampType::KERNEL_SOFTWARE_TIMESTAMP, "vcan0", false, 100, consumer }; + ASSERT_TRUE( dataSource.init() ); + ASSERT_TRUE( dataSource.isAlive() ); + dataSource.onChangeOfActiveDictionary( mDictionary, VehicleDataSourceProtocol::RAW_SOCKET ); + CollectedSignal signal; + WAIT_ASSERT_TRUE( sendTestFDMessage( mSocketFD ) && signalBufferPtr->pop( signal ) ); + ASSERT_EQ( signal.value.type, SignalType::DOUBLE ); + ASSERT_EQ( signal.signalID, 1 ); + ASSERT_DOUBLE_EQ( signal.value.value.doubleVal, 0x10203 ); + ASSERT_TRUE( signalBufferPtr->pop( signal ) ); + ASSERT_EQ( signal.signalID, 7 ); + ASSERT_EQ( signal.value.type, SignalType::DOUBLE ); + ASSERT_DOUBLE_EQ( signal.value.value.doubleVal, 0x4050607 ); + DELAY_ASSERT_FALSE( signalBufferPtr->pop( signal ) ); + CollectedCanRawFrame frame; + ASSERT_TRUE( canRawBufferPtr->pop( frame ) ); + ASSERT_EQ( frame.channelId, 0 ); + ASSERT_EQ( frame.frameID, 0x123 ); + ASSERT_EQ( frame.size, 64 ); + for ( auto i = 0; i < 64; i++ ) + { + ASSERT_EQ( frame.data[i], i ); + } + ASSERT_FALSE( signalBufferPtr->pop( signal ) ); + ASSERT_TRUE( dataSource.disconnect() ); +} + +TEST_F( CANDataSourceTest, testExtractExtendedID ) +{ + ASSERT_TRUE( mSocketFD != -1 ); + auto signalBufferPtr = std::make_shared( 10 ); + auto canRawBufferPtr = std::make_shared( 10 ); + + CANDataConsumer consumer{ 0, signalBufferPtr, canRawBufferPtr }; + CANDataSource dataSource{ 0, CanTimestampType::KERNEL_HARDWARE_TIMESTAMP, "vcan0", false, 100, consumer }; + ASSERT_TRUE( dataSource.init() ); + ASSERT_TRUE( dataSource.isAlive() ); + dataSource.onChangeOfActiveDictionary( mDictionary, VehicleDataSourceProtocol::RAW_SOCKET ); + CollectedSignal signal; + WAIT_ASSERT_TRUE( sendTestMessageExtendedID( mSocketFD ) && signalBufferPtr->pop( signal ) ); + ASSERT_EQ( signal.value.type, SignalType::DOUBLE ); + ASSERT_EQ( signal.signalID, 1 ); + ASSERT_DOUBLE_EQ( signal.value.value.doubleVal, 0x10203 ); + ASSERT_TRUE( signalBufferPtr->pop( signal ) ); + ASSERT_EQ( signal.signalID, 7 ); + ASSERT_EQ( signal.value.type, SignalType::DOUBLE ); + ASSERT_DOUBLE_EQ( signal.value.value.doubleVal, 0x4050607 ); + DELAY_ASSERT_FALSE( signalBufferPtr->pop( signal ) ); + CollectedCanRawFrame frame; + ASSERT_TRUE( canRawBufferPtr->pop( frame ) ); + ASSERT_EQ( frame.channelId, 0 ); + ASSERT_EQ( frame.frameID, 0x123 ); + ASSERT_EQ( frame.size, 8 ); + for ( auto i = 0; i < 8; i++ ) + { + ASSERT_EQ( frame.data[i], i ); + } + ASSERT_FALSE( signalBufferPtr->pop( signal ) ); + ASSERT_TRUE( dataSource.disconnect() ); +} + +TEST_F( CANDataSourceTest, testStringToCanTimestampType ) +{ + CanTimestampType timestampType; + ASSERT_TRUE( stringToCanTimestampType( "Software", timestampType ) ); + ASSERT_EQ( timestampType, CanTimestampType::KERNEL_SOFTWARE_TIMESTAMP ); + ASSERT_TRUE( stringToCanTimestampType( "Hardware", timestampType ) ); + ASSERT_EQ( timestampType, CanTimestampType::KERNEL_HARDWARE_TIMESTAMP ); + ASSERT_TRUE( stringToCanTimestampType( "Polling", timestampType ) ); + ASSERT_EQ( timestampType, CanTimestampType::POLLING_TIME ); + ASSERT_FALSE( stringToCanTimestampType( "abc", timestampType ) ); +} diff --git a/src/datamanagement/datainspection/test/CollectionInspectionEngineTest.cpp b/src/datamanagement/datainspection/test/CollectionInspectionEngineTest.cpp index 9136ee07..bf99a69a 100644 --- a/src/datamanagement/datainspection/test/CollectionInspectionEngineTest.cpp +++ b/src/datamanagement/datainspection/test/CollectionInspectionEngineTest.cpp @@ -856,12 +856,16 @@ TEST_F( CollectionInspectionEngineDoubleTest, MultipleSubsamplingOfSameSignalUse // condition[1] has higher subsampling so less signals so it should the first in sorted list EXPECT_GE( std::count_if( collectedDataList[0]->signals.begin(), collectedDataList[0]->signals.end(), - []( CollectedSignal s ) { return s.signalID == 2222; } ), + []( CollectedSignal s ) { + return s.signalID == 2222; + } ), 1 ); EXPECT_GE( std::count_if( collectedDataList[1]->signals.begin(), collectedDataList[1]->signals.end(), - []( CollectedSignal s ) { return s.signalID == 1111; } ), + []( CollectedSignal s ) { + return s.signalID == 1111; + } ), 1 ); } @@ -932,12 +936,16 @@ TEST_F( CollectionInspectionEngineDoubleTest, MultipleFixedWindowsOfSameSignalUs EXPECT_GE( std::count_if( collectedDataList[0]->signals.begin(), collectedDataList[0]->signals.end(), - []( CollectedSignal s ) { return s.signalID == 2222; } ), + []( CollectedSignal s ) { + return s.signalID == 2222; + } ), 1 ); EXPECT_GE( std::count_if( collectedDataList[1]->signals.begin(), collectedDataList[1]->signals.end(), - []( CollectedSignal s ) { return s.signalID == 1111; } ), + []( CollectedSignal s ) { + return s.signalID == 1111; + } ), 1 ); } diff --git a/src/datamanagement/datainspection/test/OBDOverCANModuleTest.cpp b/src/datamanagement/datainspection/test/OBDOverCANModuleTest.cpp index 6b0f76d0..2144df33 100644 --- a/src/datamanagement/datainspection/test/OBDOverCANModuleTest.cpp +++ b/src/datamanagement/datainspection/test/OBDOverCANModuleTest.cpp @@ -560,9 +560,8 @@ TEST_F( OBDOverCANModuleTest, DecoderDictionaryUpdatePIDsToCollectTest ) ASSERT_TRUE( obdModule.getSignalBufferPtr()->empty() ); // Update Decoder Dictionary to collect no signals. - decoderDictPtr->signalIDsToCollect.clear(); // publish the new decoder dictionary to OBD module - obdModule.onChangeOfActiveDictionary( decoderDictPtr, VehicleDataSourceProtocol::OBD ); + obdModule.onChangeOfActiveDictionary( nullptr, VehicleDataSourceProtocol::OBD ); std::this_thread::sleep_for( std::chrono::seconds( obdPIDRequestInterval ) ); // wait for one cycle and clear out the signal buffer while ( !obdModule.getSignalBufferPtr()->empty() ) diff --git a/src/datamanagement/datainspection/test/VehicleDataSourceBinderTest.cpp b/src/datamanagement/datainspection/test/VehicleDataSourceBinderTest.cpp deleted file mode 100644 index d79b8920..00000000 --- a/src/datamanagement/datainspection/test/VehicleDataSourceBinderTest.cpp +++ /dev/null @@ -1,872 +0,0 @@ - -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#include "VehicleDataSourceBinder.h" -#include "CANDataConsumer.h" -#include "IActiveDecoderDictionaryListener.h" -#include "Signal.h" -#include "Thread.h" -#include "WaitUntil.h" -#include "businterfaces/CANDataSource.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Aws::IoTFleetWise::DataInspection; -using namespace Aws::IoTFleetWise::VehicleNetwork; -using namespace Aws::IoTFleetWise::DataManagement; -using namespace Aws::IoTFleetWise::Platform::Linux; -using namespace Aws::IoTFleetWise::TestingSupport; - -static int -setup() -{ - // Setup a socket - std::string socketCANIFName( "vcan0" ); - struct sockaddr_can interfaceAddress; - struct ifreq interfaceRequest; - - if ( socketCANIFName.size() >= sizeof( interfaceRequest.ifr_name ) ) - { - return -1; - } - - int type = SOCK_RAW | SOCK_NONBLOCK; - int socketFD = socket( PF_CAN, type, CAN_RAW ); - if ( socketFD < 0 ) - { - return -1; - } - - // Set the IF Name, address - (void)strncpy( interfaceRequest.ifr_name, socketCANIFName.c_str(), sizeof( interfaceRequest.ifr_name ) - 1U ); - - if ( ioctl( socketFD, SIOCGIFINDEX, &interfaceRequest ) ) - { - close( socketFD ); - return -1; - } - - memset( &interfaceAddress, 0, sizeof( interfaceAddress ) ); - interfaceAddress.can_family = AF_CAN; - interfaceAddress.can_ifindex = interfaceRequest.ifr_ifindex; - - if ( bind( socketFD, (struct sockaddr *)&interfaceAddress, sizeof( interfaceAddress ) ) < 0 ) - { - close( socketFD ); - return -1; - } - - return socketFD; -} - -static void -cleanUp( int socketFD ) -{ - close( socketFD ); -} -// 0x0408004019011008 -static void -sendTestMessage( int socketFD, struct can_frame &frame ) -{ - frame.can_id = 0x224; - frame.can_dlc = 8; - std::string payload = "0408004019011008"; - int j = 0; - for ( unsigned int i = 0; i < payload.length(); i += 2 ) - { - std::string byteString = payload.substr( i, 2 ); - uint8_t byte = (uint8_t)strtol( byteString.c_str(), NULL, 16 ); - frame.data[j] = byte; - j++; - } - - ASSERT_TRUE( write( socketFD, &frame, sizeof( struct can_frame ) ) > 0 ); -} - -static void -sendTestExtendedIdMessage( int socketFD, struct can_frame &frame ) -{ - frame.can_id = 0x224 | 0x80000000; - frame.can_dlc = 8; - std::string payload = "0408004019011008"; - int j = 0; - for ( unsigned int i = 0; i < payload.length(); i += 2 ) - { - std::string byteString = payload.substr( i, 2 ); - uint8_t byte = (uint8_t)strtol( byteString.c_str(), NULL, 16 ); - frame.data[j] = byte; - j++; - } - - ASSERT_TRUE( write( socketFD, &frame, sizeof( struct can_frame ) ) > 0 ); -} - -static CANDecoderDictionary -generateDecoderDictionary1() -{ - // Below section construct decoder dictionary - std::vector signals = std::vector( 1 ); - struct CANMessageFormat canMessageFormat; - - canMessageFormat.mMessageID = 0x224; - canMessageFormat.mSizeInBytes = 8; - canMessageFormat.mIsMultiplexed = false; - canMessageFormat.mSignals = signals; - - struct CANMessageDecoderMethod decodeMethod = { - CANMessageCollectType::RAW, - canMessageFormat, - }; - std::unordered_map canIdToDecode{ { 0x224, decodeMethod } }; - CANDecoderDictionary dict = { { { 0, canIdToDecode }, { 1, canIdToDecode } }, { 0x123 } }; - return dict; -} - -static CANDecoderDictionary -generateDecoderDictionary2() -{ - // Below section construct decoder dictionary - struct CANMessageFormat canMessageFormat; - - std::vector signals = std::vector(); - CANSignalFormat sigFormat1; - sigFormat1.mSignalID = 0x123; - sigFormat1.mIsBigEndian = false; - sigFormat1.mIsSigned = false; - sigFormat1.mFirstBitPosition = 0; - sigFormat1.mSizeInBits = 16; - sigFormat1.mOffset = 0.0; - sigFormat1.mFactor = 1.0; - signals.emplace_back( sigFormat1 ); - - canMessageFormat.mMessageID = 0x224; - canMessageFormat.mSizeInBytes = 8; - canMessageFormat.mIsMultiplexed = false; - canMessageFormat.mSignals = signals; - - struct CANMessageDecoderMethod decodeMethod = { - CANMessageCollectType::DECODE, - canMessageFormat, - }; - std::unordered_map canIdToDecode{ { 0x224, decodeMethod } }; - CANDecoderDictionary dict = { { { 0, canIdToDecode }, { 1, canIdToDecode } }, { 0x123 } }; - return dict; -} - -static CANDecoderDictionary -generateDecoderDictionary3() -{ - // Below section construct decoder dictionary - struct CANMessageFormat canMessageFormat; - - std::vector signals = std::vector(); - CANSignalFormat sigFormat1; - sigFormat1.mSignalID = 0x123; - sigFormat1.mIsBigEndian = false; - sigFormat1.mIsSigned = false; - sigFormat1.mFirstBitPosition = 0; - sigFormat1.mSizeInBits = 16; - sigFormat1.mOffset = 0.0; - sigFormat1.mFactor = 1.0; - signals.emplace_back( sigFormat1 ); - - canMessageFormat.mMessageID = 0x224; - canMessageFormat.mSizeInBytes = 8; - canMessageFormat.mIsMultiplexed = false; - canMessageFormat.mSignals = signals; - - struct CANMessageDecoderMethod decodeMethod = { - CANMessageCollectType::DECODE, - canMessageFormat, - }; - std::unordered_map canIdToDecode{ { 0x224, decodeMethod } }; - CANDecoderDictionary dict = { { { 0, canIdToDecode }, { 1, canIdToDecode } }, { 0x111 } }; - return dict; -} - -static CANDecoderDictionary -generateDecoderDictionary4() -{ - // Below section construct decoder dictionary - struct CANMessageFormat canMessageFormat0; - std::vector signals0 = std::vector(); - CANSignalFormat sigFormat1; - sigFormat1.mSignalID = 0x123; - sigFormat1.mIsBigEndian = false; - sigFormat1.mIsSigned = false; - sigFormat1.mFirstBitPosition = 0; - sigFormat1.mSizeInBits = 16; - sigFormat1.mOffset = 0.0; - sigFormat1.mFactor = 1.0; - signals0.emplace_back( sigFormat1 ); - - CANSignalFormat sigFormat2; - sigFormat2.mSignalID = 0x111; - sigFormat2.mIsBigEndian = true; - sigFormat2.mIsSigned = false; - sigFormat2.mFirstBitPosition = 24; - sigFormat2.mSizeInBits = 16; - sigFormat2.mOffset = 0.0; - sigFormat2.mFactor = 1.0; - signals0.emplace_back( sigFormat2 ); - - canMessageFormat0.mMessageID = 0x224; - canMessageFormat0.mSizeInBytes = 8; - canMessageFormat0.mIsMultiplexed = false; - canMessageFormat0.mSignals = signals0; - - struct CANMessageDecoderMethod decodeMethod0 = { - CANMessageCollectType::DECODE, - canMessageFormat0, - }; - - struct CANMessageFormat canMessageFormat1; - std::vector signals1 = std::vector(); - CANSignalFormat sigFormat3; - sigFormat3.mSignalID = 0x528; - sigFormat3.mIsBigEndian = false; - sigFormat3.mIsSigned = false; - sigFormat3.mFirstBitPosition = 0; - sigFormat3.mSizeInBits = 16; - sigFormat3.mOffset = 0.0; - sigFormat3.mFactor = 1.0; - signals1.emplace_back( sigFormat3 ); - - CANSignalFormat sigFormat4; - sigFormat4.mSignalID = 0x411; - sigFormat4.mIsBigEndian = true; - sigFormat4.mIsSigned = false; - sigFormat4.mFirstBitPosition = 24; - sigFormat4.mSizeInBits = 16; - sigFormat4.mOffset = 0.0; - sigFormat4.mFactor = 1.0; - signals1.emplace_back( sigFormat4 ); - - canMessageFormat1.mMessageID = 0x224; - canMessageFormat1.mSizeInBytes = 8; - canMessageFormat1.mIsMultiplexed = false; - canMessageFormat1.mSignals = signals1; - - struct CANMessageDecoderMethod decodeMethod1 = { - CANMessageCollectType::DECODE, - canMessageFormat1, - }; - - std::unordered_map canIdToDecodeBus0{ { 0x224, decodeMethod0 } }; - std::unordered_map canIdToDecodeBus1{ { 0x224, decodeMethod1 } }; - CANDecoderDictionary dict = { { { 0, canIdToDecodeBus0 }, { 1, canIdToDecodeBus1 } }, { 0x111, 0x528 } }; - return dict; -} - -static CANDecoderDictionary -generateDecoderDictionary5() -{ - // Below section construct decoder dictionary - struct CANMessageFormat canMessageFormat0; - std::vector signals0 = std::vector(); - CANSignalFormat sigFormat1; - sigFormat1.mSignalID = 0x123; - sigFormat1.mIsBigEndian = false; - sigFormat1.mIsSigned = false; - sigFormat1.mFirstBitPosition = 0; - sigFormat1.mSizeInBits = 16; - sigFormat1.mOffset = 0.0; - sigFormat1.mFactor = 1.0; - signals0.emplace_back( sigFormat1 ); - - CANSignalFormat sigFormat2; - sigFormat2.mSignalID = 0x111; - sigFormat2.mIsBigEndian = true; - sigFormat2.mIsSigned = false; - sigFormat2.mFirstBitPosition = 24; - sigFormat2.mSizeInBits = 16; - sigFormat2.mOffset = 0.0; - sigFormat2.mFactor = 1.0; - signals0.emplace_back( sigFormat2 ); - - canMessageFormat0.mMessageID = 0x224; - canMessageFormat0.mSizeInBytes = 8; - canMessageFormat0.mIsMultiplexed = false; - canMessageFormat0.mSignals = signals0; - - struct CANMessageDecoderMethod decodeMethod0 = { - CANMessageCollectType::DECODE, - canMessageFormat0, - }; - - struct CANMessageFormat canMessageFormat1; - std::vector signals1 = std::vector(); - CANSignalFormat sigFormat3; - sigFormat3.mSignalID = 0x528; - sigFormat3.mIsBigEndian = false; - sigFormat3.mIsSigned = false; - sigFormat3.mFirstBitPosition = 0; - sigFormat3.mSizeInBits = 16; - sigFormat3.mOffset = 0.0; - sigFormat3.mFactor = 1.0; - signals1.emplace_back( sigFormat3 ); - - CANSignalFormat sigFormat4; - sigFormat4.mSignalID = 0x411; - sigFormat4.mIsBigEndian = true; - sigFormat4.mIsSigned = false; - sigFormat4.mFirstBitPosition = 24; - sigFormat4.mSizeInBits = 16; - sigFormat4.mOffset = 0.0; - sigFormat4.mFactor = 1.0; - signals1.emplace_back( sigFormat4 ); - - canMessageFormat1.mMessageID = 0x224; - canMessageFormat1.mSizeInBytes = 8; - canMessageFormat1.mIsMultiplexed = false; - canMessageFormat1.mSignals = signals1; - - struct CANMessageDecoderMethod decodeMethod1 = { - CANMessageCollectType::DECODE, - canMessageFormat1, - }; - - std::unordered_map canIdToDecodeBus0{ { 0x224, decodeMethod0 } }; - std::unordered_map canIdToDecodeBus1{ { 0x224, decodeMethod1 } }; - std::unordered_set signalIDsToCollect = { 0x111, 0x123, 0x528, 0x411 }; - std::unordered_map> method = { - { 0, canIdToDecodeBus0 }, { 1, canIdToDecodeBus1 } }; - CANDecoderDictionary dict = { method, signalIDsToCollect }; - return dict; -} - -static CANDecoderDictionary -generateDecoderDictionary6() -{ - // Define a message id depending on whether we want MSB set or not - // Below section construct decoder dictionary - struct CANMessageFormat canMessageFormat0; - std::vector signals0 = std::vector(); - CANSignalFormat sigFormat1; - sigFormat1.mSignalID = 0x123; - sigFormat1.mIsBigEndian = false; - sigFormat1.mIsSigned = false; - sigFormat1.mFirstBitPosition = 0; - sigFormat1.mSizeInBits = 16; - sigFormat1.mOffset = 0.0; - sigFormat1.mFactor = 1.0; - signals0.emplace_back( sigFormat1 ); - - CANSignalFormat sigFormat2; - sigFormat2.mSignalID = 0x111; - sigFormat2.mIsBigEndian = true; - sigFormat2.mIsSigned = false; - sigFormat2.mFirstBitPosition = 24; - sigFormat2.mSizeInBits = 16; - sigFormat2.mOffset = 0.0; - sigFormat2.mFactor = 1.0; - signals0.emplace_back( sigFormat2 ); - - canMessageFormat0.mMessageID = 0x224; - canMessageFormat0.mSizeInBytes = 8; - canMessageFormat0.mIsMultiplexed = false; - canMessageFormat0.mSignals = signals0; - - struct CANMessageDecoderMethod decodeMethod0 = { - CANMessageCollectType::RAW_AND_DECODE, - canMessageFormat0, - }; - - struct CANMessageFormat canMessageFormat1; - std::vector signals1 = std::vector(); - CANSignalFormat sigFormat3; - sigFormat3.mSignalID = 0x528; - sigFormat3.mIsBigEndian = false; - sigFormat3.mIsSigned = false; - sigFormat3.mFirstBitPosition = 0; - sigFormat3.mSizeInBits = 16; - sigFormat3.mOffset = 0.0; - sigFormat3.mFactor = 1.0; - signals1.emplace_back( sigFormat3 ); - - CANSignalFormat sigFormat4; - sigFormat4.mSignalID = 0x411; - sigFormat4.mIsBigEndian = true; - sigFormat4.mIsSigned = false; - sigFormat4.mFirstBitPosition = 24; - sigFormat4.mSizeInBits = 16; - sigFormat4.mOffset = 0.0; - sigFormat4.mFactor = 1.0; - signals1.emplace_back( sigFormat4 ); - - canMessageFormat1.mMessageID = 0x224; - canMessageFormat1.mSizeInBytes = 8; - canMessageFormat1.mIsMultiplexed = false; - canMessageFormat1.mSignals = signals1; - - struct CANMessageDecoderMethod decodeMethod1 = { - CANMessageCollectType::RAW_AND_DECODE, - canMessageFormat1, - }; - - std::unordered_map canIdToDecodeBus0{ { 0x224, decodeMethod0 } }; - std::unordered_map canIdToDecodeBus1{ { 0x224, decodeMethod1 } }; - std::unordered_set signalIDsToCollect = { 0x111, 0x123, 0x528, 0x411 }; - std::unordered_map> method = { - { 0, canIdToDecodeBus0 }, { 1, canIdToDecodeBus1 } }; - CANDecoderDictionary dict = { method, signalIDsToCollect }; - return dict; -} - -/** - * @brief This Binder instance creates multiple channels and consumers - */ -class VehicleDataSourceBinderTest : public ::testing::Test -{ -public: - int socketFD; - std::shared_ptr canSourcePtr; - std::shared_ptr canSourcePtr2; - std::shared_ptr canConsumerPtr; - std::shared_ptr canConsumerPtr2; - std::shared_ptr canDictionarySharedPtr; - VehicleDataSourceID sourceID; - VehicleDataSourceID sourceID2; - VehicleDataSourceBinder binder; - -protected: - void - SetUp() override - { - // CAN Source and Consumer setup - socketFD = setup(); - if ( socketFD == -1 ) - { - GTEST_SKIP() << "Skipping test fixture due to unavailability of socket"; - } - VehicleDataSourceConfig canSourceConfig; - canSourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - canSourceConfig.transportProperties.emplace( "protocolName", "CAN" ); - canSourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - canSourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector canSourceConfigs = { canSourceConfig }; - canSourcePtr = std::make_shared(); - canSourcePtr2 = std::make_shared(); - ASSERT_TRUE( canSourcePtr->init( canSourceConfigs ) ); - ASSERT_TRUE( canSourcePtr2->init( canSourceConfigs ) ); - sourceID = canSourcePtr->getVehicleDataSourceID(); - sourceID2 = canSourcePtr2->getVehicleDataSourceID(); - - // three buffers - auto canSignalBufferPtr = std::make_shared( 256 ); - auto canRawBufferPtr = std::make_shared( 256 ); - - canConsumerPtr = std::make_shared(); - canConsumerPtr2 = std::make_shared(); - ASSERT_TRUE( canConsumerPtr->init( 0, canSignalBufferPtr, 100 ) ); - ASSERT_TRUE( canConsumerPtr2->init( 1, canSignalBufferPtr, 100 ) ); - canConsumerPtr->setCANBufferPtr( canRawBufferPtr ); - canConsumerPtr2->setCANBufferPtr( canRawBufferPtr ); - ASSERT_TRUE( binder.connect() ); - ASSERT_TRUE( binder.addVehicleDataSource( canSourcePtr ) ); - ASSERT_TRUE( binder.addVehicleDataSource( canSourcePtr2 ) ); - ASSERT_TRUE( binder.bindConsumerToVehicleDataSource( canConsumerPtr, sourceID ) ); - ASSERT_TRUE( binder.bindConsumerToVehicleDataSource( canConsumerPtr2, sourceID2 ) ); - - canDictionarySharedPtr = std::make_shared(); - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - } - - void - TearDown() override - { - ASSERT_TRUE( binder.disconnect() ); - cleanUp( socketFD ); - } - std::shared_ptr mClock = ClockHandler::getClock(); -}; - -/** @brief In this test, raw CAN Frame to be collected based on decoder dictionary - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestCollectCANFrame ) -{ - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary1() ); - - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify raw CAN Frames are collected correctly - CollectedCanRawFrame rawCANMsg; - struct can_frame frame = {}; - auto sendAndPop = [&] { - sendTestMessage( socketFD, frame ); - return canConsumerPtr->getCANBufferPtr()->pop( rawCANMsg ); - }; - WAIT_ASSERT_TRUE( sendAndPop() ); - ASSERT_EQ( 8, rawCANMsg.size ); - for ( int i = 0; i < rawCANMsg.size; ++i ) - { - ASSERT_EQ( frame.data[i], rawCANMsg.data[i] ); - } - auto sendAndPop2 = [&] { - sendTestMessage( socketFD, frame ); - return canConsumerPtr2->getCANBufferPtr()->pop( rawCANMsg ); - }; - WAIT_ASSERT_TRUE( sendAndPop2() ); - ASSERT_EQ( 8, rawCANMsg.size ); - for ( int i = 0; i < rawCANMsg.size; ++i ) - { - ASSERT_EQ( frame.data[i], rawCANMsg.data[i] ); - } -} - -/** @brief In this test, first signal in the CAN Frame will be collected based on decoder dictionary - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestCollectSelectedSignalBuffer ) -{ - - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary2() ); - - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify signals are decoded and collected correctly - CollectedSignal signal; - auto sendAndPop = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - return canConsumerPtr->getSignalBufferPtr()->pop( signal ); - }; - WAIT_ASSERT_TRUE( sendAndPop() ); - ASSERT_EQ( 0x123, signal.signalID ); - ASSERT_DOUBLE_EQ( 0x0804, signal.value.value.doubleVal ); - auto sendAndPop2 = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - return canConsumerPtr2->getSignalBufferPtr()->pop( signal ); - }; - WAIT_ASSERT_TRUE( sendAndPop2() ); - ASSERT_EQ( 0x123, signal.signalID ); - ASSERT_DOUBLE_EQ( 0x0804, signal.value.value.doubleVal ); -} - -/** @brief In this test, no signals in the CAN Frame will be collected based on decoder dictionary - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestNotCollectSignalBuffer ) -{ - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary3() ); - - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify NO signals are decoded and collected based on decoder dictionary - CollectedSignal signal; - auto sendAndPop = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - return canConsumerPtr->getSignalBufferPtr()->pop( signal ); - }; - DELAY_ASSERT_FALSE( sendAndPop() ); - auto sendAndPop2 = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - return canConsumerPtr2->getSignalBufferPtr()->pop( signal ); - }; - DELAY_ASSERT_FALSE( sendAndPop2() ); -} - -/** @brief In this test, only one signal from the CAN Frame to - * be collected based on decoder dictionary - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestCollectSignalBuffer2 ) -{ - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary4() ); - - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify signals are decoded and collected correctly - std::unordered_map collectedSignals; - auto sendPopAndGetCollectedSize = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - CollectedSignal signal; - if ( canConsumerPtr->getSignalBufferPtr()->pop( signal ) ) - { - collectedSignals[signal.signalID] = signal.value.value.doubleVal; - } - return collectedSignals.size(); - }; - WAIT_ASSERT_EQ( 2U, sendPopAndGetCollectedSize() ); - ASSERT_EQ( 1, collectedSignals.count( 0x111 ) ); - ASSERT_EQ( 1, collectedSignals.count( 0x528 ) ); - if ( collectedSignals.count( 0x111 ) > 0 ) - { - ASSERT_EQ( 0x0040, collectedSignals[0x111] ); - } - if ( collectedSignals.count( 0x528 ) > 0 ) - { - ASSERT_EQ( 0x0804, collectedSignals[0x528] ); - } -} - -/** @brief In this test, two signals from the CAN Frame on Both two Channels to be collected - * based on decoder dictionary - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestCollectSignalBuffer3 ) -{ - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary5() ); - - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify signals are decoded and collected correctly - std::unordered_map collectedSignals; - auto sendPopAndGetCollectedSize = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - CollectedSignal signal; - if ( canConsumerPtr->getSignalBufferPtr()->pop( signal ) ) - { - collectedSignals[signal.signalID] = signal.value.value.doubleVal; - } - return collectedSignals.size(); - }; - WAIT_ASSERT_EQ( 4U, sendPopAndGetCollectedSize() ); - ASSERT_EQ( 1, collectedSignals.count( 0x111 ) ); - ASSERT_EQ( 0x0040, collectedSignals[0x111] ); - ASSERT_EQ( 1, collectedSignals.count( 0x528 ) ); - ASSERT_EQ( 0x0804, collectedSignals[0x528] ); - ASSERT_EQ( 1, collectedSignals.count( 0x411 ) ); - ASSERT_EQ( 0x0040, collectedSignals[0x411] ); - ASSERT_EQ( 1, collectedSignals.count( 0x123 ) ); - ASSERT_EQ( 0x0804, collectedSignals[0x123] ); -} - -/** @brief In this test, two RAW CAN Frames and two signals from the CAN Frame on Both two Channels - * to be collected based on decoder dictionary - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestCollectSignalBufferAndRawFrame ) -{ - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary6() ); - - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify signals are decoded and collected correctly - std::unordered_map collectedSignals; - struct can_frame frame = {}; - auto sendPopAndGetCollectedSize = [&] { - sendTestMessage( socketFD, frame ); - CollectedSignal signal; - if ( canConsumerPtr->getSignalBufferPtr()->pop( signal ) ) - { - collectedSignals[signal.signalID] = signal.value.value.doubleVal; - } - return collectedSignals.size(); - }; - WAIT_ASSERT_EQ( 4U, sendPopAndGetCollectedSize() ); - ASSERT_EQ( 1, collectedSignals.count( 0x111 ) ); - ASSERT_EQ( 0x0040, collectedSignals[0x111] ); - ASSERT_EQ( 1, collectedSignals.count( 0x528 ) ); - ASSERT_EQ( 0x0804, collectedSignals[0x528] ); - ASSERT_EQ( 1, collectedSignals.count( 0x411 ) ); - ASSERT_EQ( 0x0040, collectedSignals[0x411] ); - ASSERT_EQ( 1, collectedSignals.count( 0x123 ) ); - ASSERT_EQ( 0x0804, collectedSignals[0x123] ); - - // Verify CAN RAW Frame is collected as well - CollectedCanRawFrame rawCANMsg; - ASSERT_TRUE( canConsumerPtr->getCANBufferPtr()->pop( rawCANMsg ) ); - ASSERT_EQ( 8, rawCANMsg.size ); - for ( int i = 0; i < rawCANMsg.size; ++i ) - { - ASSERT_EQ( frame.data[i], rawCANMsg.data[i] ); - } - ASSERT_TRUE( canConsumerPtr2->getCANBufferPtr()->pop( rawCANMsg ) ); - ASSERT_EQ( 8, rawCANMsg.size ); - for ( int i = 0; i < rawCANMsg.size; ++i ) - { - ASSERT_EQ( frame.data[i], rawCANMsg.data[i] ); - } -} - -/** @brief In this test, two RAW CAN Frames and two signals from the CAN Frame on Both two Channels - * to be collected based on decoder dictionary, with a minor difference that the messages are - * extended id - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestCollectSignalBufferAndRawFrameExtendedId ) -{ - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary6() ); - - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify signals are decoded and collected correctly - std::unordered_map collectedSignals; - struct can_frame frame = {}; - auto sendPopAndGetCollectedSize = [&] { - sendTestExtendedIdMessage( socketFD, frame ); - CollectedSignal signal; - if ( canConsumerPtr->getSignalBufferPtr()->pop( signal ) ) - { - collectedSignals[signal.signalID] = signal.value.value.doubleVal; - } - return collectedSignals.size(); - }; - WAIT_ASSERT_EQ( 4U, sendPopAndGetCollectedSize() ); - ASSERT_EQ( 1, collectedSignals.count( 0x111 ) ); - ASSERT_EQ( 0x0040, collectedSignals[0x111] ); - ASSERT_EQ( 1, collectedSignals.count( 0x528 ) ); - ASSERT_EQ( 0x0804, collectedSignals[0x528] ); - ASSERT_EQ( 1, collectedSignals.count( 0x411 ) ); - ASSERT_EQ( 0x0040, collectedSignals[0x411] ); - ASSERT_EQ( 1, collectedSignals.count( 0x123 ) ); - ASSERT_EQ( 0x0804, collectedSignals[0x123] ); - - // Verify CAN RAW Frame is collected as well - CollectedCanRawFrame rawCANMsg; - ASSERT_TRUE( canConsumerPtr->getCANBufferPtr()->pop( rawCANMsg ) ); - ASSERT_EQ( 8, rawCANMsg.size ); - for ( int i = 0; i < rawCANMsg.size; ++i ) - { - ASSERT_EQ( frame.data[i], rawCANMsg.data[i] ); - } - ASSERT_TRUE( canConsumerPtr2->getCANBufferPtr()->pop( rawCANMsg ) ); - ASSERT_EQ( 8, rawCANMsg.size ); - for ( int i = 0; i < rawCANMsg.size; ++i ) - { - ASSERT_EQ( frame.data[i], rawCANMsg.data[i] ); - } -} - -/** @brief In this test, decoder dictionary update invoked in the middle of message decoding - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestChangeDecoderDictionaryDuringDecoding ) -{ - canDictionarySharedPtr = std::make_shared( generateDecoderDictionary4() ); - struct can_frame frame = {}; - - auto f = []( int socketFD, struct can_frame frame, int msgCnt ) { - for ( int i = 0; i < msgCnt; ++i ) - { - sendTestMessage( socketFD, frame ); - } - }; - // At this point, the decoder dictionary is still empty with no decoding rule - // Generate a long duration of CAN decoding scenario by sending 256 messages - std::thread canMessageThread( f, socketFD, frame, 256 ); - - // Sleep for one second for canMessageThread to send out some messages - std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); - // update dictionary while thread is still busy decoding. This test check whether Vehicle Data - // Consumer can gracefully handle such multi thread scenario. - binder.onChangeOfActiveDictionary( canDictionarySharedPtr, VehicleDataSourceProtocol::RAW_SOCKET ); - - canMessageThread.join(); - // Verify signals are decoded and collected correctly - std::unordered_map collectedSignals; - auto sendPopAndGetCollectedSize = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - CollectedSignal signal; - if ( canConsumerPtr->getSignalBufferPtr()->pop( signal ) ) - { - collectedSignals[signal.signalID] = signal.value.value.doubleVal; - } - return collectedSignals.size(); - }; - WAIT_ASSERT_EQ( 2U, sendPopAndGetCollectedSize() ); - ASSERT_EQ( 1, collectedSignals.count( 0x111 ) ); - ASSERT_EQ( 1, collectedSignals.count( 0x528 ) ); - if ( collectedSignals.count( 0x111 ) > 0 ) - { - ASSERT_EQ( 0x0040, collectedSignals[0x111] ); - } - if ( collectedSignals.count( 0x528 ) > 0 ) - { - ASSERT_EQ( 0x0804, collectedSignals[0x528] ); - } -} - -/** @brief In this test, consumer received the data but cannot perform decoding due to missing - * decoder dictionary. - * We expect consumer to discard all the input data if decoder dictionary is not valid - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderTestDiscardInputBufferWhenDecoderDictionaryIsInValid ) -{ - // set decoder dictionary as invalid - binder.onChangeOfActiveDictionary( nullptr, VehicleDataSourceProtocol::RAW_SOCKET ); - - // Verify no signals were collected due to missing decoder dictionary - auto sendAndCheckConsumerEmpty = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - return canConsumerPtr->getSignalBufferPtr()->empty(); - }; - DELAY_ASSERT_TRUE( sendAndCheckConsumerEmpty() ); - // Now we provide Vehicle Data Binder a valid decoder dictionary - binder.onChangeOfActiveDictionary( std::make_shared( generateDecoderDictionary4() ), - VehicleDataSourceProtocol::RAW_SOCKET ); - WAIT_ASSERT_FALSE( sendAndCheckConsumerEmpty() ); -} - -/** @brief In this test, we validate the startup and the shutdown APIs. - */ -TEST_F( VehicleDataSourceBinderTest, VehicleDataSourceBinderStartupAndShutdownCycle ) -{ - std::shared_ptr canSource; - std::shared_ptr canConsumer; - std::shared_ptr dictionary; - VehicleDataSourceID sourceID; - VehicleDataSourceBinder networkBinder; - ASSERT_TRUE( socketFD != -1 ); - canSource = std::make_shared(); - VehicleDataSourceConfig canSourceConfig; - canSourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - canSourceConfig.transportProperties.emplace( "protocolName", "CAN" ); - canSourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - canSourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector canSourceConfigs = { canSourceConfig }; - ASSERT_TRUE( canSource->init( canSourceConfigs ) ); - sourceID = canSource->getVehicleDataSourceID(); - - // Buffers - auto signalBufferPtr = std::make_shared( 256 ); - auto canRawBufferPtr = std::make_shared( 256 ); - - canConsumer = std::make_shared(); - - ASSERT_TRUE( canConsumer->init( 0, signalBufferPtr, 100 ) ); - canConsumer->setCANBufferPtr( canRawBufferPtr ); - ASSERT_TRUE( networkBinder.connect() ); - ASSERT_TRUE( networkBinder.addVehicleDataSource( canSource ) ); - ASSERT_TRUE( networkBinder.bindConsumerToVehicleDataSource( canConsumer, sourceID ) ); - // Initially Channels and Consumers should be running but in a sleep mode - // We send few messages on the bus and check that they are neither consumed - // by the Channel nor by the consumer - auto sendAndCheckSourceEmpty = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - return canSource->getBuffer()->empty(); - }; - DELAY_ASSERT_TRUE( sendAndCheckSourceEmpty() ); - auto sendAndCheckConsumerEmpty = [&] { - struct can_frame frame = {}; - sendTestMessage( socketFD, frame ); - return canConsumer->getCANBufferPtr()->empty(); - }; - DELAY_ASSERT_TRUE( sendAndCheckConsumerEmpty() ); - // Pass a null decoder manifest, and check that both the channel and the consumer - // are still sleeping. - binder.onChangeOfActiveDictionary( nullptr, VehicleDataSourceProtocol::RAW_SOCKET ); - - DELAY_ASSERT_TRUE( sendAndCheckSourceEmpty() ); - DELAY_ASSERT_TRUE( sendAndCheckConsumerEmpty() ); - // Pass a correct decoder Manifest and Make sure that channel and consumer - // consumed the data. - dictionary = std::make_shared( generateDecoderDictionary1() ); - networkBinder.onChangeOfActiveDictionary( dictionary, VehicleDataSourceProtocol::RAW_SOCKET ); - - WAIT_ASSERT_FALSE( sendAndCheckSourceEmpty() ); - WAIT_ASSERT_FALSE( sendAndCheckConsumerEmpty() ); - ASSERT_TRUE( networkBinder.disconnect() ); -} diff --git a/src/datamanagement/datamanager/include/CollectionSchemeManager.h b/src/datamanagement/datamanager/include/CollectionSchemeManager.h index ddb99d09..ac6c7174 100644 --- a/src/datamanagement/datamanager/include/CollectionSchemeManager.h +++ b/src/datamanagement/datamanager/include/CollectionSchemeManager.h @@ -146,29 +146,6 @@ class CollectionSchemeManager : public ICollectionSchemeManager, mSchemaListenerPtr = collectionSchemeIngestionListenerPtr; } - /** - * @brief Used by the bootstrap to set the UseLocalDictionary Flag to ingore DM - * updates through MQTT connection - * - * @param useLocalDictionaryIn - */ - inline void - setLocalDictionaryFlag( bool useLocalDictionaryIn ) - { - mUseLocalDictionary = useLocalDictionaryIn; - } - - /** - * @brief Used by the bootstrap to set the DecoderManifestID when using local Dictionary - * - * @param currentDecoderManifestIDIn - */ - inline void - setDecoderManifestID( std::string currentDecoderManifestIDIn ) - { - currentDecoderManifestID = std::move( currentDecoderManifestIDIn ); - } - private: using ThreadListeners::notifyListeners; using ThreadListeners::notifyListeners; @@ -244,8 +221,7 @@ class CollectionSchemeManager : public ICollectionSchemeManager, * @param collectionScheme the collectionScheme where the info is retrieved * @param conditionData object ConditionWithCollectedData to be filled */ - void addConditionData( const ICollectionSchemePtr &collectionScheme, - struct ConditionWithCollectedData &conditionData ); + void addConditionData( const ICollectionSchemePtr &collectionScheme, ConditionWithCollectedData &conditionData ); /** * @brief initialize in mTimeLine to send checkin message @@ -307,9 +283,6 @@ class CollectionSchemeManager : public ICollectionSchemeManager, static constexpr int RETRY_CHECKIN_INTERVAL_IN_MILLISECOND = 5000; // checkIn ID in parallel to collectionScheme IDs static const std::string CHECKIN; - // Supported Network Protocol. This list will expand when new protocol added - static constexpr std::array SUPPORTED_NETWORK_PROTOCOL = { - { VehicleDataSourceProtocol::RAW_SOCKET, VehicleDataSourceProtocol::OBD } }; Thread mThread; // Atomic flag to signal the state of main thread. If true, we should stop @@ -331,7 +304,7 @@ class CollectionSchemeManager : public ICollectionSchemeManager, std::map mEnabledCollectionSchemeMap; // ID for the decoder manifest currently in use - std::string currentDecoderManifestID; + std::string mCurrentDecoderManifestID; // Time interval in ms to send checkin message uint64_t mCheckinIntervalInMsec{ DEFAULT_CHECKIN_INTERVAL_IN_MILLISECOND }; @@ -383,8 +356,6 @@ class CollectionSchemeManager : public ICollectionSchemeManager, bool mProcessDecoderManifest{ false }; // CacheAndPersist object passed from K-Engine std::shared_ptr mSchemaPersistency; - // flag used to check if local dictionary is available - bool mUseLocalDictionary{ false }; CANInterfaceIDTranslator mCANIDTranslator; }; diff --git a/src/datamanagement/datamanager/include/Schema.h b/src/datamanagement/datamanager/include/Schema.h index d4da0421..7242624e 100644 --- a/src/datamanagement/datamanager/include/Schema.h +++ b/src/datamanagement/datamanager/include/Schema.h @@ -81,6 +81,7 @@ class Schema : public ThreadListeners, publi */ bool sendCheckin( const std::vector &documentARNs ) override; +private: /** * @brief This struct is used to receive the callback from MQTT IoT Core on receipt of data on the DecoderManifest * topic @@ -123,7 +124,9 @@ class Schema : public ThreadListeners, publi mSchema.setDecoderManifest( decoderManifestPtr ); FWE_LOG_TRACE( "Received Decoder Manifest in PI DecoderManifestCb" ); } - } mDecoderManifestCb; + }; + + DecoderManifestCb mDecoderManifestCb; /** * @brief This struct is used to receive the callback from MQTT IoT Core on receipt of data on the @@ -167,9 +170,10 @@ class Schema : public ThreadListeners, publi mSchema.setCollectionSchemeList( collectionSchemeListPtr ); FWE_LOG_TRACE( "Received CollectionSchemeList" ); } - } mCollectionSchemeListCb; + }; + + CollectionSchemeListCb mCollectionSchemeListCb; -private: /** * @brief ISender object used to interface with cloud to send Checkins */ diff --git a/src/datamanagement/datamanager/src/CheckinAndPersistency.cpp b/src/datamanagement/datamanager/src/CheckinAndPersistency.cpp index 4b4e0e02..f592cb26 100644 --- a/src/datamanagement/datamanager/src/CheckinAndPersistency.cpp +++ b/src/datamanagement/datamanager/src/CheckinAndPersistency.cpp @@ -38,9 +38,9 @@ CollectionSchemeManager::sendCheckin() { checkinMsg.emplace_back( it->first ); } - if ( !currentDecoderManifestID.empty() ) + if ( !mCurrentDecoderManifestID.empty() ) { - checkinMsg.emplace_back( currentDecoderManifestID ); + checkinMsg.emplace_back( mCurrentDecoderManifestID ); } std::string checkinLogStr; for ( size_t i = 0; i < checkinMsg.size(); i++ ) @@ -122,6 +122,8 @@ CollectionSchemeManager::retrieve( DataType retrieveType ) // currently this if will be always true as it can be only DECODER_MANIFEST or COLLECTION_SCHEME_LIST but for // readability leave it as else if instead of else // coverity[autosar_cpp14_m0_1_2_violation] + // coverity[autosar_cpp14_m0_1_9_violation] + // coverity[misra_cpp_2008_rule_0_1_9_violation] else if ( retrieveType == DataType::DECODER_MANIFEST ) { // updating mDecoderManifest diff --git a/src/datamanagement/datamanager/src/CollectionSchemeManager.cpp b/src/datamanagement/datamanager/src/CollectionSchemeManager.cpp index ee21ca6b..c07447dc 100644 --- a/src/datamanagement/datamanager/src/CollectionSchemeManager.cpp +++ b/src/datamanagement/datamanager/src/CollectionSchemeManager.cpp @@ -19,7 +19,7 @@ namespace DataManagement const std::string CollectionSchemeManager::CHECKIN = "Checkin"; CollectionSchemeManager::CollectionSchemeManager( std::string dm_id ) - : currentDecoderManifestID( std::move( dm_id ) ) + : mCurrentDecoderManifestID( std::move( dm_id ) ) { } @@ -28,7 +28,7 @@ CollectionSchemeManager::CollectionSchemeManager( std::string dm_id, std::map mapIdle ) : mIdleCollectionSchemeMap( std::move( mapIdle ) ) , mEnabledCollectionSchemeMap( std::move( mapEnabled ) ) - , currentDecoderManifestID( std::move( dm_id ) ) + , mCurrentDecoderManifestID( std::move( dm_id ) ) { } @@ -168,37 +168,38 @@ CollectionSchemeManager::doWork( void *data ) collectionSchemeManager->prepareCheckinTimer(); // Retrieve collectionSchemeList and decoderManifest from persistent storage static_cast( collectionSchemeManager->retrieve( DataType::COLLECTION_SCHEME_LIST ) ); - // If we have an injected Decoder Manifest, we shall not try to load another one from - // the persistency module - if ( !collectionSchemeManager->mUseLocalDictionary ) - { - static_cast( collectionSchemeManager->retrieve( DataType::DECODER_MANIFEST ) ); - } - else - { - FWE_LOG_TRACE( " Using a locally defined decoder dictionary " ); - } - do + static_cast( collectionSchemeManager->retrieve( DataType::DECODER_MANIFEST ) ); + while ( true ) { if ( collectionSchemeManager->mProcessDecoderManifest ) { collectionSchemeManager->mProcessDecoderManifest = false; TraceModule::get().sectionBegin( TraceSection::MANAGER_DECODER_BUILD ); - enabledCollectionSchemeMapChanged |= collectionSchemeManager->processDecoderManifest(); + if ( collectionSchemeManager->processDecoderManifest() ) + { + enabledCollectionSchemeMapChanged = true; + } TraceModule::get().sectionEnd( TraceSection::MANAGER_DECODER_BUILD ); } if ( collectionSchemeManager->mProcessCollectionScheme ) { collectionSchemeManager->mProcessCollectionScheme = false; TraceModule::get().sectionBegin( TraceSection::MANAGER_COLLECTION_BUILD ); - enabledCollectionSchemeMapChanged |= collectionSchemeManager->processCollectionScheme(); + if ( collectionSchemeManager->processCollectionScheme() ) + { + enabledCollectionSchemeMapChanged = true; + } TraceModule::get().sectionEnd( TraceSection::MANAGER_COLLECTION_BUILD ); } auto checkTime = collectionSchemeManager->mClock->timeSinceEpoch(); - enabledCollectionSchemeMapChanged |= collectionSchemeManager->checkTimeLine( checkTime ); + if ( collectionSchemeManager->checkTimeLine( checkTime ) ) + { + enabledCollectionSchemeMapChanged = true; + } if ( enabledCollectionSchemeMapChanged ) { TraceModule::get().sectionBegin( TraceSection::MANAGER_EXTRACTION ); + TraceModule::get().sectionBegin( TraceSection::COLLECTION_SCHEME_CHANGE_TO_FIRST_DATA ); FWE_LOG_TRACE( "Start extraction because of changed active collection schemes at system time " + @@ -212,7 +213,7 @@ CollectionSchemeManager::doWork( void *data ) * Then, propagate inspection matrix to Inspection engine */ enabledCollectionSchemeMapChanged = false; - std::shared_ptr inspectionMatrixOutput = std::make_shared(); + auto inspectionMatrixOutput = std::make_shared(); collectionSchemeManager->inspectionMatrixExtractor( inspectionMatrixOutput ); collectionSchemeManager->inspectionMatrixUpdater( inspectionMatrixOutput ); /* @@ -222,39 +223,34 @@ CollectionSchemeManager::doWork( void *data ) * * the propagate the output to Vehicle Data Consumers */ - if ( !collectionSchemeManager->mUseLocalDictionary ) - { - std::map> decoderDictionaryMap; - collectionSchemeManager->decoderDictionaryExtractor( decoderDictionaryMap ); - // Publish decoder dictionaries update to all listeners - collectionSchemeManager->decoderDictionaryUpdater( decoderDictionaryMap ); - // coverity[check_return : SUPPRESS] - std::string decoderCanChannels = std::to_string( - ( decoderDictionaryMap.find( VehicleDataSourceProtocol::RAW_SOCKET ) != - decoderDictionaryMap.end() && - decoderDictionaryMap[VehicleDataSourceProtocol::RAW_SOCKET] != nullptr ) - ? decoderDictionaryMap[VehicleDataSourceProtocol::RAW_SOCKET]->canMessageDecoderMethod.size() - : 0 ); - std::string obdPids = std::to_string( - ( ( decoderDictionaryMap.find( VehicleDataSourceProtocol::OBD ) != decoderDictionaryMap.end() ) && - ( decoderDictionaryMap[VehicleDataSourceProtocol::OBD] != nullptr ) && - ( !decoderDictionaryMap[VehicleDataSourceProtocol::OBD]->canMessageDecoderMethod.empty() ) ) - ? decoderDictionaryMap[VehicleDataSourceProtocol::OBD] - ->canMessageDecoderMethod.cbegin() - ->second.size() - : 0 ); - FWE_LOG_INFO( "FWE activated Decoder Manifest:" + std::string( " using decoder manifest:" ) + - collectionSchemeManager->currentDecoderManifestID + " resulting in decoding rules for " + - std::to_string( decoderDictionaryMap.size() ) + - " protocols. Decoder CAN channels: " + decoderCanChannels + " and OBD PIDs:" + obdPids ); - } - std::string canInfo; + std::map> decoderDictionaryMap; + collectionSchemeManager->decoderDictionaryExtractor( decoderDictionaryMap ); + // Publish decoder dictionaries update to all listeners + collectionSchemeManager->decoderDictionaryUpdater( decoderDictionaryMap ); + // coverity[check_return : SUPPRESS] + std::string decoderCanChannels = std::to_string( + ( decoderDictionaryMap.find( VehicleDataSourceProtocol::RAW_SOCKET ) != decoderDictionaryMap.end() && + decoderDictionaryMap[VehicleDataSourceProtocol::RAW_SOCKET] != nullptr ) + ? decoderDictionaryMap[VehicleDataSourceProtocol::RAW_SOCKET]->canMessageDecoderMethod.size() + : 0 ); + std::string obdPids = std::to_string( + ( ( decoderDictionaryMap.find( VehicleDataSourceProtocol::OBD ) != decoderDictionaryMap.end() ) && + ( decoderDictionaryMap[VehicleDataSourceProtocol::OBD] != nullptr ) && + ( !decoderDictionaryMap[VehicleDataSourceProtocol::OBD]->canMessageDecoderMethod.empty() ) ) + ? decoderDictionaryMap[VehicleDataSourceProtocol::OBD] + ->canMessageDecoderMethod.cbegin() + ->second.size() + : 0 ); + FWE_LOG_INFO( "FWE activated Decoder Manifest:" + std::string( " using decoder manifest:" ) + + collectionSchemeManager->mCurrentDecoderManifestID + " resulting in decoding rules for " + + std::to_string( decoderDictionaryMap.size() ) + + " protocols. Decoder CAN channels: " + decoderCanChannels + " and OBD PIDs:" + obdPids ); std::string enabled; std::string idle; collectionSchemeManager->printExistingCollectionSchemes( enabled, idle ); // coverity[check_return : SUPPRESS] FWE_LOG_INFO( "FWE activated collection schemes:" + enabled + " using decoder manifest:" + - collectionSchemeManager->currentDecoderManifestID + " resulting in " + + collectionSchemeManager->mCurrentDecoderManifestID + " resulting in " + std::to_string( inspectionMatrixOutput->conditions.size() ) + " inspection conditions" ); TraceModule::get().sectionEnd( TraceSection::MANAGER_EXTRACTION ); } @@ -284,7 +280,11 @@ CollectionSchemeManager::doWork( void *data ) std::string wakeupStr; collectionSchemeManager->printWakeupStatus( wakeupStr ); FWE_LOG_TRACE( wakeupStr ); - } while ( !collectionSchemeManager->shouldStop() ); + if ( collectionSchemeManager->shouldStop() ) + { + break; + } + } } /* callback function */ @@ -388,20 +388,21 @@ CollectionSchemeManager::processDecoderManifest() if ( ( mDecoderManifest == nullptr ) || ( !mDecoderManifest->build() ) ) { FWE_LOG_ERROR( " Failed to process the upcoming DecoderManifest." ); + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::COLLECTION_SCHEME_ERROR ); return false; } // build is successful - if ( mDecoderManifest->getID() == currentDecoderManifestID ) + if ( mDecoderManifest->getID() == mCurrentDecoderManifestID ) { - FWE_LOG_TRACE( "Ignoring new decoder manifest with same name: " + currentDecoderManifestID ); + FWE_LOG_TRACE( "Ignoring new decoder manifest with same name: " + mCurrentDecoderManifestID ); // no change in decoder manifest return false; } - FWE_LOG_TRACE( "Replace decoder manifest " + currentDecoderManifestID + " with " + mDecoderManifest->getID() + + FWE_LOG_TRACE( "Replace decoder manifest " + mCurrentDecoderManifestID + " with " + mDecoderManifest->getID() + " while " + std::to_string( mEnabledCollectionSchemeMap.size() ) + " active and " + std::to_string( mIdleCollectionSchemeMap.size() ) + " idle collection schemes loaded" ); - // store the new DM, update currentDecoderManifestID - currentDecoderManifestID = mDecoderManifest->getID(); + // store the new DM, update mCurrentDecoderManifestID + mCurrentDecoderManifestID = mDecoderManifest->getID(); store( DataType::DECODER_MANIFEST ); // when DM changes, check if we have collectionScheme loaded if ( isCollectionSchemeLoaded() ) @@ -431,6 +432,7 @@ CollectionSchemeManager::processCollectionScheme() if ( ( mCollectionSchemeList == nullptr ) || ( !mCollectionSchemeList->build() ) ) { FWE_LOG_ERROR( "Incoming CollectionScheme does not exist or fails to build!" ); + TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::COLLECTION_SCHEME_ERROR ); return false; } // Build is successful. Store collectionScheme @@ -481,13 +483,14 @@ CollectionSchemeManager::rebuildMapsandTimeLine( const TimePoint &currTime ) /* Separate collectionSchemes into Enabled and Idle bucket */ for ( auto const &collectionScheme : collectionSchemeList ) { - if ( collectionScheme->getDecoderManifestID() != currentDecoderManifestID ) + if ( collectionScheme->getDecoderManifestID() != mCurrentDecoderManifestID ) { // Encounters a collectionScheme that does not have matching DM // Rebuild has to bail out. Call cleanupCollectionSchemes() before exiting. - FWE_LOG_TRACE( "CollectionScheme does not have matching DM ID. Current DM ID: " + currentDecoderManifestID + - " but collection scheme " + collectionScheme->getCollectionSchemeID() + " needs " + - collectionScheme->getDecoderManifestID() ); + FWE_LOG_TRACE( + "CollectionScheme does not have matching DM ID. Current DM ID: " + mCurrentDecoderManifestID + + " but collection scheme " + collectionScheme->getCollectionSchemeID() + " needs " + + collectionScheme->getDecoderManifestID() ); cleanupCollectionSchemes(); return false; @@ -542,11 +545,11 @@ CollectionSchemeManager::updateMapsandTimeLine( const TimePoint &currTime ) collectionSchemeList = mCollectionSchemeList->getCollectionSchemes(); for ( auto const &collectionScheme : collectionSchemeList ) { - if ( collectionScheme->getDecoderManifestID() != currentDecoderManifestID ) + if ( collectionScheme->getDecoderManifestID() != mCurrentDecoderManifestID ) { // Encounters a collectionScheme that does not have matching DM // Rebuild has to bail out. Call cleanupCollectionSchemes() before exiting. - FWE_LOG_TRACE( "CollectionScheme does not have matching DM ID: " + currentDecoderManifestID + " " + + FWE_LOG_TRACE( "CollectionScheme does not have matching DM ID: " + mCurrentDecoderManifestID + " " + collectionScheme->getDecoderManifestID() ); cleanupCollectionSchemes(); diff --git a/src/datamanagement/datamanager/src/DecoderDictionaryExtractor.cpp b/src/datamanagement/datamanager/src/DecoderDictionaryExtractor.cpp index adcb310e..604b7b15 100644 --- a/src/datamanagement/datamanager/src/DecoderDictionaryExtractor.cpp +++ b/src/datamanagement/datamanager/src/DecoderDictionaryExtractor.cpp @@ -15,12 +15,17 @@ namespace IoTFleetWise namespace DataManagement { -// NOLINT below due to C++17 warning of redundant declarations that are required to maintain C++14 compatibility -constexpr std::array CollectionSchemeManager::SUPPORTED_NETWORK_PROTOCOL; // NOLINT void CollectionSchemeManager::decoderDictionaryExtractor( std::map> &decoderDictionaryMap ) { + // Initialize the dictionary map with nullptr for each protocol, so that protocols are disabled if + // none of the collection schemes collect data for that protocol + decoderDictionaryMap.clear(); + for ( auto protocol : SUPPORTED_NETWORK_PROTOCOL ) + { + decoderDictionaryMap[protocol] = nullptr; + } // Iterate through enabled collectionScheme lists to locate the signals and CAN frames to be collected for ( auto it = mEnabledCollectionSchemeMap.begin(); it != mEnabledCollectionSchemeMap.end(); ++it ) { @@ -37,10 +42,10 @@ CollectionSchemeManager::decoderDictionaryExtractor( continue; } // Firstly we need to check if we already have dictionary created for this network - if ( decoderDictionaryMap.find( networkType ) == decoderDictionaryMap.end() ) + if ( decoderDictionaryMap[networkType] == nullptr ) { // Currently we don't have decoder dictionary for this type of network protocol, create one - decoderDictionaryMap.emplace( networkType, std::make_shared() ); + decoderDictionaryMap[networkType] = std::make_shared(); } if ( networkType == VehicleDataSourceProtocol::RAW_SOCKET ) @@ -140,11 +145,10 @@ CollectionSchemeManager::decoderDictionaryExtractor( // If some CAN Frame has signals to be decoded, we will set its collectType as RAW_AND_DECODE. if ( !collectionSchemePtr->getCollectRawCanFrames().empty() ) { - if ( decoderDictionaryMap.find( VehicleDataSourceProtocol::RAW_SOCKET ) == decoderDictionaryMap.end() ) + if ( decoderDictionaryMap[VehicleDataSourceProtocol::RAW_SOCKET] == nullptr ) { // Currently we don't have decoder dictionary for this type of network protocol, create one - decoderDictionaryMap.emplace( VehicleDataSourceProtocol::RAW_SOCKET, - std::make_shared() ); + decoderDictionaryMap[VehicleDataSourceProtocol::RAW_SOCKET] = std::make_shared(); } auto &canDecoderDictionaryPtr = decoderDictionaryMap[VehicleDataSourceProtocol::RAW_SOCKET]; for ( const auto &canFrameInfo : collectionSchemePtr->getCollectRawCanFrames() ) @@ -188,16 +192,6 @@ CollectionSchemeManager::decoderDictionaryExtractor( } } } - for ( VehicleDataSourceProtocol networkType : SUPPORTED_NETWORK_PROTOCOL ) - { - // check if the decoder dictionary has been created for this network type. If not, we need to explicity create - // an empty one to shutdown the decoding if it's on-going - if ( decoderDictionaryMap.find( networkType ) == decoderDictionaryMap.end() ) - { - // Currently we don't have decoder dictionary for this type of network protocol, create one - decoderDictionaryMap.emplace( networkType, std::make_shared() ); - } - } } // TODO: The collection scheme manager shall support generic decoder dictionary other than only can diff --git a/src/datamanagement/datamanager/test/DecoderDictionaryExtractorTest.cpp b/src/datamanagement/datamanager/test/DecoderDictionaryExtractorTest.cpp index 5cd60213..777d8bac 100644 --- a/src/datamanagement/datamanager/test/DecoderDictionaryExtractorTest.cpp +++ b/src/datamanagement/datamanager/test/DecoderDictionaryExtractorTest.cpp @@ -377,8 +377,7 @@ TEST( CollectionSchemeManagerTest, DecoderDictionaryExtractorTest ) // for OBD ASSERT_TRUE( decoderDictionaryMapNew.find( VehicleDataSourceProtocol::OBD ) != decoderDictionaryMapNew.end() ); decoderDictionary = decoderDictionaryMapNew[VehicleDataSourceProtocol::OBD]; - ASSERT_EQ( decoderDictionary->signalIDsToCollect.size(), 0 ); - ASSERT_EQ( decoderDictionary->canMessageDecoderMethod.size(), 0 ); + ASSERT_EQ( decoderDictionary, nullptr ); decoderDictionary = decoderDictionaryMapNew[VehicleDataSourceProtocol::RAW_SOCKET]; // Now dictionary shall not contain anything for Node 10 as CollectionScheme1 is retired @@ -438,42 +437,6 @@ TEST( CollectionSchemeManagerTest, DecoderDictionaryExtractorTest ) ASSERT_TRUE( decoderDictionaryMap2.find( VehicleDataSourceProtocol::OBD ) != decoderDictionaryMap2.end() ); } -/** @brief - * This test aims to test PM's functionality to invoke all Vehicle Data Consumer on Decoder Dictionary Update - * step1: - * Two Vehicle Data Consumer registered as listener - * step2: Invoke Decoder Dictionary Update - * check Both two network channel consumer has decoder dictionary updated - */ -TEST( CollectionSchemeManagerTest, DecoderDictionaryUpdaterTest ) -{ - auto testPtr = std::make_shared(); - CANInterfaceIDTranslator canIDTranslator; - testPtr->init( 0, nullptr, canIDTranslator ); - - // Mock the Vehicle Data Binder - std::shared_ptr binderMockPtr; - binderMockPtr = std::make_shared(); - - // Clear updater flag. Note this flag only exist in mock class for testing purpose - binderMockPtr->setUpdateFlag( false ); - testPtr->subscribeListener( binderMockPtr.get() ); - - std::map> decoderDictionaryMap; - decoderDictionaryMap.emplace( VehicleDataSourceProtocol::RAW_SOCKET, std::make_shared() ); - // Invoke the updater and check that the binder received the notification - testPtr->decoderDictionaryUpdater( decoderDictionaryMap ); - ASSERT_TRUE( binderMockPtr->getUpdateFlag() ); - - auto obdOverCANModulePtr = std::make_shared(); - testPtr->subscribeListener( static_cast( obdOverCANModulePtr.get() ) ); - - obdOverCANModulePtr->setUpdateFlag( false ); - decoderDictionaryMap.emplace( VehicleDataSourceProtocol::OBD, std::make_shared() ); - testPtr->decoderDictionaryUpdater( decoderDictionaryMap ); - // Verify OBD Module has received the decoder dictionary update - ASSERT_TRUE( obdOverCANModulePtr->getUpdateFlag() ); -} /** @brief * This test aims to test CollectionScheme Manager's Decoder Dictionary Extractor functionality * when only a raw can frame is provided and no signals diff --git a/src/datamanagement/datamanager/test/SchemaTest.cpp b/src/datamanagement/datamanager/test/SchemaTest.cpp index aad535fa..fb87f569 100644 --- a/src/datamanagement/datamanager/test/SchemaTest.cpp +++ b/src/datamanagement/datamanager/test/SchemaTest.cpp @@ -182,7 +182,7 @@ TEST( SchemaTest, DecoderManifestIngestion ) // Create a Decoder manifest protocol buffer and pack it with the data DecoderManifestMsg::DecoderManifest protoDM; - protoDM.set_arn( "arn:aws:iam::123456789012:user/Development/product_1234/*" ); + protoDM.set_sync_id( "arn:aws:iam::123456789012:user/Development/product_1234/*" ); // Create a Proto CANSignal DecoderManifestMsg::CANSignal *protoCANSignalA = protoDM.add_can_signals(); @@ -268,7 +268,7 @@ TEST( SchemaTest, DecoderManifestIngestion ) ASSERT_TRUE( testPIDM.build() ); ASSERT_TRUE( testPIDM.isReady() ); - ASSERT_EQ( testPIDM.getID(), protoDM.arn() ); + ASSERT_EQ( testPIDM.getID(), protoDM.sync_id() ); // Get a valid CANMessageFormat const CANMessageFormat &testCMF = @@ -347,7 +347,7 @@ TEST( SchemaTest, SchemaInvalidDecoderManifestTest ) // Create a Decoder manifest protocol buffer and pack it with the data DecoderManifestMsg::DecoderManifest protoDM; - protoDM.set_arn( "arn:aws:iam::123456789012:user/Development/product_1234/*" ); + protoDM.set_sync_id( "arn:aws:iam::123456789012:user/Development/product_1234/*" ); // Serialize the protocol buffer to a string to avoid malloc with cstyle arrays std::string protoSerializedBuffer; @@ -435,7 +435,7 @@ TEST( SchemaTest, CollectionSchemeIngestionHeartBeat ) // Create a collection scheme Proto Message CollectionSchemesMsg::CollectionScheme collectionSchemeTestMessage; collectionSchemeTestMessage.set_campaign_arn( "arn:aws:iam::2.23606797749:user/Development/product_1234/*" ); - collectionSchemeTestMessage.set_decoder_manifest_arn( "model_manifest_12" ); + collectionSchemeTestMessage.set_decoder_manifest_sync_id( "model_manifest_12" ); collectionSchemeTestMessage.set_start_time_ms_epoch( 1621448160000 ); collectionSchemeTestMessage.set_expiry_time_ms_epoch( 2621448160000 ); @@ -579,7 +579,7 @@ TEST( SchemaTest, SchemaCollectionEventBased ) // Create a collection scheme Proto Message CollectionSchemesMsg::CollectionScheme collectionSchemeTestMessage; collectionSchemeTestMessage.set_campaign_arn( "arn:aws:iam::2.23606797749:user/Development/product_1235/*" ); - collectionSchemeTestMessage.set_decoder_manifest_arn( "model_manifest_13" ); + collectionSchemeTestMessage.set_decoder_manifest_sync_id( "model_manifest_13" ); collectionSchemeTestMessage.set_start_time_ms_epoch( 162144816000 ); collectionSchemeTestMessage.set_expiry_time_ms_epoch( 262144816000 ); @@ -821,7 +821,7 @@ TEST( SchemaTest, SchemaGeohashFunctionNode ) // Create a collection scheme Proto Message CollectionSchemesMsg::CollectionScheme collectionSchemeTestMessage; collectionSchemeTestMessage.set_campaign_arn( "arn:aws:iam::2.23606797749:user/Development/product_1235/*" ); - collectionSchemeTestMessage.set_decoder_manifest_arn( "model_manifest_13" ); + collectionSchemeTestMessage.set_decoder_manifest_sync_id( "model_manifest_13" ); collectionSchemeTestMessage.set_start_time_ms_epoch( 162144816000 ); collectionSchemeTestMessage.set_expiry_time_ms_epoch( 262144816000 ); diff --git a/src/datamanagement/datamanager/test/include/CollectionSchemeManagerTest.h b/src/datamanagement/datamanager/test/include/CollectionSchemeManagerTest.h index b1bbb150..267fdd70 100644 --- a/src/datamanagement/datamanager/test/include/CollectionSchemeManagerTest.h +++ b/src/datamanagement/datamanager/test/include/CollectionSchemeManagerTest.h @@ -11,7 +11,6 @@ #include "DecoderManifestIngestion.h" #include "Listener.h" #include "OBDOverCANModule.h" -#include "VehicleDataSourceBinder.h" #include #include #include @@ -253,44 +252,6 @@ class CollectionSchemeManagerTestProducer } }; -/* mock the VehicleDataSourceBinder class that receive decoder dictionary update from PM */ -class VehicleDataSourceBinderMock : public VehicleDataSourceBinder -{ -public: - VehicleDataSourceBinderMock() - : mUpdateFlag( false ) - { - } - - ~VehicleDataSourceBinderMock() - { - } - - void - onChangeOfActiveDictionary( ConstDecoderDictionaryConstPtr &dictionary, - VehicleDataSourceProtocol networkProtocol ) override - { - VehicleDataSourceBinder::onChangeOfActiveDictionary( dictionary, networkProtocol ); - mUpdateFlag = true; - } - - void - setUpdateFlag( bool flag ) - { - mUpdateFlag = flag; - } - - bool - getUpdateFlag() - { - return mUpdateFlag; - } - -private: - // This flag is used for testing whether Vehicle Data Binder received the update - bool mUpdateFlag; -}; - /* mock OBDOverCANModule class that receive decoder dictionary update from PM */ class OBDOverCANModuleMock : public OBDOverCANModule { diff --git a/src/datamanagement/types/include/CANDataTypes.h b/src/datamanagement/types/include/CANDataTypes.h index bf2a3f2e..cc140e73 100644 --- a/src/datamanagement/types/include/CANDataTypes.h +++ b/src/datamanagement/types/include/CANDataTypes.h @@ -6,9 +6,7 @@ // Includes #include "SignalTypes.h" #include "TimeTypes.h" -#include "datatypes/VehicleDataSourceTypes.h" -#include -#include +#include namespace Aws { @@ -16,7 +14,6 @@ namespace IoTFleetWise { namespace DataManagement { -using namespace Aws::IoTFleetWise::VehicleNetwork; using namespace Aws::IoTFleetWise::Platform::Linux; union CANPhysicalValue { @@ -71,27 +68,11 @@ struct CANDecodedSignal SignalType mSignalType{ SignalType::DOUBLE }; }; -struct CANFrameInfo -{ - uint32_t mFrameID{ 0 }; - std::string mFrameRawData; - std::vector mSignals; -}; /** * @brief Cloud does not send information about each CAN message, so we set every CAN message size to the maximum. */ static constexpr uint8_t MAX_CAN_FRAME_BYTE_SIZE = 64; -struct CANDecodedMessage -{ - CANFrameInfo mFrameInfo; - Timestamp mReceptionTime{ 0 }; - Timestamp mDecodingTime{ 0 }; - VehicleDataSourceIfName mChannelIfName; - VehicleDataSourceType mChannelType; - VehicleDataSourceProtocol mChannelProtocol; -}; - } // namespace DataManagement } // namespace IoTFleetWise } // namespace Aws diff --git a/src/datamanagement/types/include/CollectionInspectionAPITypes.h b/src/datamanagement/types/include/CollectionInspectionAPITypes.h index 5fa2bdd1..cfbe53eb 100644 --- a/src/datamanagement/types/include/CollectionInspectionAPITypes.h +++ b/src/datamanagement/types/include/CollectionInspectionAPITypes.h @@ -127,12 +127,12 @@ struct CollectedCanRawFrame CollectedCanRawFrame() = default; CollectedCanRawFrame( CANRawFrameID frameIDIn, CANChannelNumericID channelIdIn, - Timestamp receiveTime, + Timestamp receiveTimeIn, std::array &dataIn, uint8_t sizeIn ) : frameID( frameIDIn ) , channelId( channelIdIn ) - , receiveTime( receiveTime ) + , receiveTime( receiveTimeIn ) , data( dataIn ) , size( sizeIn ) { @@ -243,12 +243,6 @@ struct SignalValueWrapper SignalValueWrapper( SignalValueWrapper && ) = default; SignalValueWrapper &operator=( SignalValueWrapper && ) = default; - template - SignalValueWrapper( T sigValue, SignalType sigType ) - { - setVal( sigValue, sigType ); - } - // For Backward Compatibility SignalValueWrapper & operator=( const double sigValue ) @@ -283,17 +277,17 @@ struct CollectedSignal // Backward Compatibility template - CollectedSignal( SignalID signalIDIn, Timestamp receiveTime, T sigValue ) + CollectedSignal( SignalID signalIDIn, Timestamp receiveTimeIn, T sigValue ) : signalID( signalIDIn ) - , receiveTime( receiveTime ) + , receiveTime( receiveTimeIn ) { value.setVal( static_cast( sigValue ), SignalType::DOUBLE ); } template - CollectedSignal( SignalID signalIDIn, Timestamp receiveTime, T sigValue, SignalType sigType ) + CollectedSignal( SignalID signalIDIn, Timestamp receiveTimeIn, T sigValue, SignalType sigType ) : signalID( signalIDIn ) - , receiveTime( receiveTime ) + , receiveTime( receiveTimeIn ) { switch ( sigType ) { diff --git a/src/datamanagement/types/include/OBDDataTypes.h b/src/datamanagement/types/include/OBDDataTypes.h index 5a259073..80707a04 100644 --- a/src/datamanagement/types/include/OBDDataTypes.h +++ b/src/datamanagement/types/include/OBDDataTypes.h @@ -55,7 +55,7 @@ struct OBDSignal }; // List of OBD Service IDs/ Modes -enum class SIDs : uint32_t +enum class SID : uint32_t { INVALID_SERVICE_MODE = 0x00, // invalid service mode CURRENT_STATS = 0x01, // Current Stats @@ -66,10 +66,10 @@ enum class SIDs : uint32_t OXGEN_SENSOR_MODE = 0x06, // Request Oxygen Sensor Monitoring PENDING_DTC = 0x07, // Request Pending DTCs TESTING = 0x08, // Testing related SID - VEHICLE_INFO = 0x09 // Request Vehicle Information + VEHICLE_INFO = 0x09, // Request Vehicle Information + MAX = 0x0A }; -using SID = SIDs; using PID = uint8_t; using SupportedPIDs = std::vector; constexpr PID INVALID_PID = UINT8_MAX; diff --git a/src/datamanagement/types/include/SignalTypes.h b/src/datamanagement/types/include/SignalTypes.h index 5b285334..63ec6f11 100644 --- a/src/datamanagement/types/include/SignalTypes.h +++ b/src/datamanagement/types/include/SignalTypes.h @@ -13,6 +13,9 @@ namespace IoTFleetWise namespace DataManagement { +/** @brief Number of bits in a byte */ +static constexpr uint8_t BYTE_SIZE = 8; + /** * @brief CAN Raw Frame ID is the arbitration ID of a CAN frame found on a bus. Paired with a NodeID its unique. */ diff --git a/src/executionmanagement/include/IoTFleetWiseEngine.h b/src/executionmanagement/include/IoTFleetWiseEngine.h index 7ba6d789..1c585a6e 100644 --- a/src/executionmanagement/include/IoTFleetWiseEngine.h +++ b/src/executionmanagement/include/IoTFleetWiseEngine.h @@ -6,6 +6,7 @@ // Includes #include "AwsIotChannel.h" #include "AwsIotConnectivityModule.h" +#include "CANDataSource.h" #include "CacheAndPersist.h" #include "ClockHandler.h" #include "CollectionInspectionWorkerThread.h" @@ -24,8 +25,6 @@ #include "Signal.h" #include "Thread.h" #include "Timer.h" -#include "VehicleDataSourceBinder.h" -#include "businterfaces/AbstractVehicleDataSource.h" #include #include #include @@ -114,9 +113,10 @@ class IoTFleetWiseEngine : public IDataReadyToPublishListener uint64_t mPrintMetricsCyclicPeriodMs{ 0 }; // default to 0 which means no cyclic printing std::shared_ptr mClock = ClockHandler::getClock(); - std::unique_ptr mVehicleDataSourceBinder; std::shared_ptr mOBDOverCANModule; + std::vector> mCANDataSources; + std::vector> mCANDataConsumers; std::shared_ptr mDataCollectionSender; std::shared_ptr mAwsIotModule; @@ -134,7 +134,6 @@ class IoTFleetWiseEngine : public IDataReadyToPublishListener std::unique_ptr mRemoteProfiler; std::shared_ptr mAwsIotChannelMetricsUpload; std::shared_ptr mAwsIotChannelLogsUpload; - VehicleDataSourcePtr mVehicleDataSource; #ifdef FWE_FEATURE_CAMERA // DDS Module std::shared_ptr mDataOverDDSModule; diff --git a/src/executionmanagement/src/IoTFleetWiseEngine.cpp b/src/executionmanagement/src/IoTFleetWiseEngine.cpp index b8cfbeac..ea987df0 100644 --- a/src/executionmanagement/src/IoTFleetWiseEngine.cpp +++ b/src/executionmanagement/src/IoTFleetWiseEngine.cpp @@ -4,12 +4,9 @@ // Includes #include "IoTFleetWiseEngine.h" #include "AwsBootstrap.h" -#include "CANDataConsumer.h" #include "CollectionInspectionAPITypes.h" #include "LoggingModule.h" #include "TraceModule.h" -#include "businterfaces/AbstractVehicleDataSource.h" -#include "businterfaces/CANDataSource.h" #include #include @@ -57,6 +54,22 @@ getFileContents( const std::string &p ) return ret; } +#ifdef FWE_EXAMPLE_IWAVEGPS +uint32_t +stringToU32( const std::string &value ) +{ + try + { + return static_cast( std::stoul( value ) ); + } + catch ( const std::exception &e ) + { + throw std::runtime_error( "Could not cast " + value + + " to integer, invalid input: " + std::string( e.what() ) ); + } +} +#endif + } // namespace IoTFleetWiseEngine::IoTFleetWiseEngine() @@ -176,7 +189,7 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) config["staticConfig"]["publishToCloudParameters"]["maxPublishMessageCount"].asUInt(), canIDTranslator ); - // Pass on the AWS SDK Bootsrap handle to the IoTModule. + // Pass on the AWS SDK Bootstrap handle to the IoTModule. auto bootstrapPtr = AwsBootstrap::getInstance().getClientBootStrap(); const auto privateKey = @@ -323,36 +336,16 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) // Allow CollectionSchemeManagement to send checkins through the Schema Object Callback mCollectionSchemeManagerPtr->setSchemaListenerPtr( mSchemaPtr ); - /********************************Vehicle Data Source Binder bootstrap start*******************************/ + /********************************Data source bootstrap start*******************************/ auto obdOverCANModuleInit = false; - // Start the vehicle data source binder - mVehicleDataSourceBinder = std::make_unique(); - if ( ( mVehicleDataSourceBinder == nullptr ) || ( !mVehicleDataSourceBinder->connect() ) ) - { - FWE_LOG_ERROR( "Failed to initialize the Vehicle DataSource binder" ); - return false; - } - - // Initialize for ( const auto &interfaceName : config["networkInterfaces"] ) { const auto &interfaceType = interfaceName["type"].asString(); if ( interfaceType == CAN_INTERFACE_TYPE ) { - std::vector canSourceConfigs( 1 ); - auto &canSourceConfig = canSourceConfigs.back(); - canSourceConfig.transportProperties.emplace( - "interfaceName", interfaceName[CAN_INTERFACE_TYPE]["interfaceName"].asString() ); - canSourceConfig.transportProperties.emplace( - "protocolName", interfaceName[CAN_INTERFACE_TYPE]["protocolName"].asString() ); - canSourceConfig.transportProperties.emplace( - "threadIdleTimeMs", - config["staticConfig"]["threadIdleTimes"]["socketCANThreadIdleTimeMs"].asString() ); - canSourceConfig.maxNumberOfVehicleDataMessages = - config["staticConfig"]["bufferSizes"]["socketCANBufferSize"].asUInt(); - CAN_TIMESTAMP_TYPE canTimestampType = CAN_TIMESTAMP_TYPE::KERNEL_SOFTWARE_TIMESTAMP; // default + CanTimestampType canTimestampType = CanTimestampType::KERNEL_SOFTWARE_TIMESTAMP; // default if ( interfaceName[CAN_INTERFACE_TYPE].isMember( "timestampType" ) ) { auto timestampTypeInput = interfaceName[CAN_INTERFACE_TYPE]["timestampType"].asString(); @@ -363,48 +356,29 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) " so default to Software" ); } } - auto canSourcePtr = std::make_shared( canTimestampType ); - auto canConsumerPtr = std::make_shared(); - - if ( canSourcePtr == nullptr || canConsumerPtr == nullptr ) - { - FWE_LOG_ERROR( "Failed to create consumer/producer" ); - return false; - } - // Initialize the consumer/producers - // Currently we limit 1 channel to a single consumer. We can always extend this - // if we want to process the data coming from 1 channel to multiple consumers. - if ( ( !canSourcePtr->init( canSourceConfigs ) ) || - ( !canConsumerPtr->init( - static_cast( - canIDTranslator.getChannelNumericID( interfaceName["interfaceId"].asString() ) ), - signalBufferPtr, - config["staticConfig"]["threadIdleTimes"]["canDecoderThreadIdleTimeMs"].asUInt() ) ) ) - { - FWE_LOG_ERROR( "Failed to initialize the producers/consumers" ); - return false; - } - else - { - // CAN Consumers require a RAW Buffer after init - // TODO: This is temporary change . Will need to think how this can be abstracted for all data - // sources. - canConsumerPtr->setCANBufferPtr( canRawBufferPtr ); - } - - // Handshake the binder and the channel - if ( !mVehicleDataSourceBinder->addVehicleDataSource( canSourcePtr ) ) + auto canChannelId = canIDTranslator.getChannelNumericID( interfaceName["interfaceId"].asString() ); + auto canConsumerPtr = + std::make_unique( canChannelId, signalBufferPtr, canRawBufferPtr ); + auto canSourcePtr = std::make_unique( + canChannelId, + canTimestampType, + interfaceName[CAN_INTERFACE_TYPE]["interfaceName"].asString(), + interfaceName[CAN_INTERFACE_TYPE]["protocolName"].asString() == "CAN-FD", + config["staticConfig"]["threadIdleTimes"]["socketCANThreadIdleTimeMs"].asUInt(), + *canConsumerPtr ); + if ( !canSourcePtr->init() ) { - FWE_LOG_ERROR( "Failed to add a network channel" ); + FWE_LOG_ERROR( "Failed to initialize CANDataSource" ); return false; } - - if ( !mVehicleDataSourceBinder->bindConsumerToVehicleDataSource( - canConsumerPtr, canSourcePtr->getVehicleDataSourceID() ) ) + if ( !mCollectionSchemeManagerPtr->subscribeListener( + static_cast( canSourcePtr.get() ) ) ) { - FWE_LOG_ERROR( "Failed to Bind Consumers to Producers" ); + FWE_LOG_ERROR( "Failed to register the CANDataSource to the CollectionScheme Manager" ); return false; } + mCANDataConsumers.push_back( std::move( canConsumerPtr ) ); + mCANDataSources.push_back( std::move( canSourcePtr ) ); } else if ( interfaceType == OBD_INTERFACE_TYPE ) { @@ -455,15 +429,8 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) FWE_LOG_ERROR( interfaceName["type"].asString() + " is not supported" ); } } - // Register Vehicle Data Source Binder as listener for CollectionScheme Manager - if ( !mCollectionSchemeManagerPtr->subscribeListener( mVehicleDataSourceBinder.get() ) ) - { - FWE_LOG_ERROR( - "Could not register the vehicle data source binder as a listener to the collection campaign manager" ); - return false; - } - /********************************Vehicle Data Source Binder bootstrap end*******************************/ + /********************************Data source bootstrap end*******************************/ // Only start the CollectionSchemeManager after all listeners have subscribed, otherwise // they will not be notified of the initial decoder manifest and collection schemes that are @@ -532,9 +499,9 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) // Only if there is at least one DDS Node, we should create the DDS Module if ( !ddsNodes.empty() ) { - mDataOverDDSModule.reset( new DataOverDDSModule() ); + mDataOverDDSModule = std::make_shared(); // Init the Module - if ( ( mDataOverDDSModule == nullptr ) || ( !mDataOverDDSModule->init( ddsNodes ) ) ) + if ( !mDataOverDDSModule->init( ddsNodes ) ) { FWE_LOG_ERROR( "Failed to initialize the DDS Module" ); return false; @@ -564,23 +531,15 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) if ( config["staticConfig"].isMember( "iWaveGpsExample" ) ) { FWE_LOG_TRACE( "Found 'iWaveGpsExample' section in config file" ); - std::vector iWaveGpsConfigs( 1 ); - for ( auto const &key : config["staticConfig"]["iWaveGpsExample"].getMemberNames() ) - { - if ( key == IWaveGpsSource::CAN_CHANNEL_NUMBER ) - { - iWaveGpsConfigs.back().transportProperties.emplace( - key, - std::to_string( canIDTranslator.getChannelNumericID( - config["staticConfig"]["iWaveGpsExample"][key].asString() ) ) ); - } - else - { - iWaveGpsConfigs.back().transportProperties.emplace( - key, config["staticConfig"]["iWaveGpsExample"][key].asString() ); - } - } - iWaveInitSuccessful = mIWaveGpsSource->init( iWaveGpsConfigs ); + iWaveInitSuccessful = mIWaveGpsSource->init( + config["staticConfig"]["iWaveGpsExample"][IWaveGpsSource::PATH_TO_NMEA].asString(), + canIDTranslator.getChannelNumericID( + config["staticConfig"]["iWaveGpsExample"][IWaveGpsSource::CAN_CHANNEL_NUMBER].asString() ), + stringToU32( config["staticConfig"]["iWaveGpsExample"][IWaveGpsSource::CAN_RAW_FRAME_ID].asString() ), + static_cast( stringToU32( + config["staticConfig"]["iWaveGpsExample"][IWaveGpsSource::LATITUDE_START_BIT].asString() ) ), + static_cast( stringToU32( + config["staticConfig"]["iWaveGpsExample"][IWaveGpsSource::LONGITUDE_START_BIT].asString() ) ) ); } else { @@ -671,11 +630,13 @@ IoTFleetWiseEngine::disconnect() return false; } - // Stop the Binder - if ( mVehicleDataSourceBinder && ( !mVehicleDataSourceBinder->disconnect() ) ) + for ( auto &source : mCANDataSources ) { - FWE_LOG_ERROR( "Could not disconnect the Binder" ); - return false; + if ( !source->disconnect() ) + { + FWE_LOG_ERROR( "Could not disconnect CAN data source" ); + return false; + } } if ( mAwsIotModule->isAlive() && ( !mAwsIotModule->disconnect() ) ) @@ -868,15 +829,29 @@ IoTFleetWiseEngine::doWork( void *data ) } } firstSignalValues += "]"; - FWE_LOG_INFO( "FWE data ready to send with eventID " + - std::to_string( triggeredCollectionSchemeDataPtr->eventID ) + " from " + - triggeredCollectionSchemeDataPtr->metaData.collectionSchemeID + - " Signals:" + std::to_string( triggeredCollectionSchemeDataPtr->signals.size() ) + " " + - firstSignalValues + firstSignalTimestamp + " raw CAN frames:" + - std::to_string( triggeredCollectionSchemeDataPtr->canFrames.size() ) + - " DTCs:" + std::to_string( triggeredCollectionSchemeDataPtr->mDTCInfo.mDTCCodes.size() ) + - " Geohash:" + triggeredCollectionSchemeDataPtr->mGeohashInfo.mGeohashString ); - engine->mDataCollectionSender->send( triggeredCollectionSchemeDataPtr ); + // Avoid invoking Data Collection Sender if there is nothing to send. + if ( triggeredCollectionSchemeDataPtr->signals.empty() && + triggeredCollectionSchemeDataPtr->canFrames.empty() && + triggeredCollectionSchemeDataPtr->mDTCInfo.mDTCCodes.empty() && + ( !triggeredCollectionSchemeDataPtr->mGeohashInfo.hasItems() ) ) + { + FWE_LOG_INFO( + "The trigger for Campaign: " + triggeredCollectionSchemeDataPtr->metaData.collectionSchemeID + + " activated eventID: " + std::to_string( triggeredCollectionSchemeDataPtr->eventID ) + + " but no data is available to ingest" ); + } + else + { + FWE_LOG_INFO( "FWE data ready to send with eventID " + + std::to_string( triggeredCollectionSchemeDataPtr->eventID ) + " from " + + triggeredCollectionSchemeDataPtr->metaData.collectionSchemeID + + " Signals:" + std::to_string( triggeredCollectionSchemeDataPtr->signals.size() ) + + " " + firstSignalValues + firstSignalTimestamp + " raw CAN frames:" + + std::to_string( triggeredCollectionSchemeDataPtr->canFrames.size() ) + " DTCs:" + + std::to_string( triggeredCollectionSchemeDataPtr->mDTCInfo.mDTCCodes.size() ) + + " Geohash:" + triggeredCollectionSchemeDataPtr->mGeohashInfo.mGeohashString ); + engine->mDataCollectionSender->send( triggeredCollectionSchemeDataPtr ); + } } ); TraceModule::get().setVariable( TraceVariable::QUEUE_INSPECTION_TO_SENDER, consumedElements ); @@ -888,10 +863,10 @@ IoTFleetWiseEngine::doWork( void *data ) IoTFleetWiseEngine::FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS ) ) ) { engine->mRetrySendingPersistedDataTimer.reset(); - if ( engine->mAwsIotModule->isAlive() ) + if ( engine->mAwsIotModule->isAlive() && engine->checkAndSendRetrievedData() ) { // Check if data was persisted, Retrieve all the data and send - uploadedPersistedDataOnce |= engine->checkAndSendRetrievedData(); + uploadedPersistedDataOnce = true; } } if ( ( engine->mPrintMetricsCyclicPeriodMs > 0 ) && @@ -900,7 +875,8 @@ IoTFleetWiseEngine::doWork( void *data ) { engine->mPrintMetricsCyclicTimer.reset(); TraceModule::get().print(); - TraceModule::get().startNewObservationWindow(); + TraceModule::get().startNewObservationWindow( + static_cast( engine->mPrintMetricsCyclicPeriodMs ) ); } } } diff --git a/src/executionmanagement/src/IoTFleetWiseVersion.cpp.in b/src/executionmanagement/src/IoTFleetWiseVersion.cpp.in index 459a45e1..1eaf0ed7 100644 --- a/src/executionmanagement/src/IoTFleetWiseVersion.cpp.in +++ b/src/executionmanagement/src/IoTFleetWiseVersion.cpp.in @@ -1,14 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// To use the variable #include "IoTFleetWiseVersion.h" +// coverity[autosar_cpp14_a16_2_2_violation] False positive - header is required to declare constants below +#include "IoTFleetWiseVersion.h" // This file will be filled by CMAKE configure_file with the right values and placed in the build folder -extern const char VERSION_GIT_COMMIT_SHA[] = "@VERSION_GIT_SHA@"; -extern const char VERSION_GIT_TAG[] = "@VERSION_GIT_TAG@"; -extern const char VERSION_BUILD_TIME[] = "@VERSION_CURRENT_TIME@"; -extern const char VERSION_PROJECT_VERSION[] = "@PROJECT_VERSION@"; +const char VERSION_GIT_COMMIT_SHA[] = "@VERSION_GIT_SHA@"; +const char VERSION_GIT_TAG[] = "@VERSION_GIT_TAG@"; +const char VERSION_BUILD_TIME[] = "@VERSION_CURRENT_TIME@"; +const char VERSION_PROJECT_VERSION[] = "@PROJECT_VERSION@"; -extern const char VERSION_PROJECT_VERSION_MAJOR[] = "@PROJECT_VERSION_MAJOR@"; -extern const char VERSION_PROJECT_VERSION_MINOR[] = "@PROJECT_VERSION_MINOR@"; -extern const char VERSION_PROJECT_VERSION_PATCH[] = "@PROJECT_VERSION_PATCH@"; +const char VERSION_PROJECT_VERSION_MAJOR[] = "@PROJECT_VERSION_MAJOR@"; +const char VERSION_PROJECT_VERSION_MINOR[] = "@PROJECT_VERSION_MINOR@"; +const char VERSION_PROJECT_VERSION_PATCH[] = "@PROJECT_VERSION_PATCH@"; diff --git a/src/executionmanagement/src/main.cpp b/src/executionmanagement/src/main.cpp index 44587641..9729f6f5 100644 --- a/src/executionmanagement/src/main.cpp +++ b/src/executionmanagement/src/main.cpp @@ -17,19 +17,24 @@ using namespace Aws::IoTFleetWise::ExecutionManagement; static volatile sig_atomic_t mSignal = 0; // volatile has to be used since it will be modified by a signal handler, // executed as result of an asynchronous interrupt -extern "C" void -signalHandler( int signum ) +extern "C" { - // Very few things are safe in a signal handler. So we never do anything other than set the atomic int, not even - // print a message: https://stackoverflow.com/a/16891799 - mSignal = signum; + // coverity[cert_msc54_cpp_violation] False positive - function does have C linkage + static void + signalHandler( int signum ) + { + // Very few things are safe in a signal handler. So we never do anything other than set the atomic int, not even + // print a message: https://stackoverflow.com/a/16891799 + mSignal = signum; + } } static void printVersion() { - std::cout << "Version: " << VERSION_PROJECT_VERSION << ", git tag: " << VERSION_GIT_TAG - << ", git commit sha: " << VERSION_GIT_COMMIT_SHA << ", Build time: " << VERSION_BUILD_TIME << std::endl; + std::cout << "Version: " << &VERSION_PROJECT_VERSION[0] << ", git tag: " << &VERSION_GIT_TAG[0] + << ", git commit sha: " << &VERSION_GIT_COMMIT_SHA[0] << ", Build time: " << &VERSION_BUILD_TIME[0] + << std::endl; } static void @@ -88,7 +93,7 @@ main( int argc, char *argv[] ) Json::Value config; if ( !IoTFleetWiseConfig::read( configFilename, config ) ) { - std::cout << " AWS IoT FleetWise Edge Service failed to read config file: " + configFilename << std::endl; + std::cout << "AWS IoT FleetWise Edge Service failed to read config file: " + configFilename << std::endl; return EXIT_FAILURE; } // Set system wide log level @@ -98,7 +103,7 @@ main( int argc, char *argv[] ) // Connect the Engine if ( engine.connect( config ) && engine.start() ) { - std::cout << " AWS IoT FleetWise Edge Service Started successfully " << std::endl; + std::cout << "AWS IoT FleetWise Edge Service Started successfully" << std::endl; } else { @@ -112,11 +117,11 @@ main( int argc, char *argv[] ) int exitCode = signalToExitCode( mSignal ); if ( engine.stop() && engine.disconnect() ) { - std::cout << " AWS IoT FleetWise Edge Service Stopped successfully " << std::endl; + std::cout << "AWS IoT FleetWise Edge Service Stopped successfully" << std::endl; return exitCode; } - std::cout << " AWS IoT FleetWise Edge Service Stopped with errors " << std::endl; + std::cout << "AWS IoT FleetWise Edge Service Stopped with errors" << std::endl; return EXIT_FAILURE; } catch ( const std::exception &e ) diff --git a/src/executionmanagement/test/IoTFleetWiseConfigTest.cpp b/src/executionmanagement/test/IoTFleetWiseConfigTest.cpp index 2af2d713..d4da9613 100644 --- a/src/executionmanagement/test/IoTFleetWiseConfigTest.cpp +++ b/src/executionmanagement/test/IoTFleetWiseConfigTest.cpp @@ -17,7 +17,6 @@ TEST( IoTFleetWiseConfigTest, ReadOk ) { Json::Value config; ASSERT_TRUE( IoTFleetWiseConfig::read( "em-example-config.json", config ) ); - ASSERT_EQ( 10000, config["staticConfig"]["bufferSizes"]["socketCANBufferSize"].asInt() ); ASSERT_EQ( 10000, config["staticConfig"]["bufferSizes"]["decodedSignalsBufferSize"].asInt() ); ASSERT_EQ( 10000, config["staticConfig"]["bufferSizes"]["rawCANFrameBufferSize"].asInt() ); ASSERT_EQ( "Trace", config["staticConfig"]["internalParameters"]["systemWideLogLevel"].asString() ); diff --git a/src/executionmanagement/test/em-example-config.json b/src/executionmanagement/test/em-example-config.json index c4c423ce..9728de54 100644 --- a/src/executionmanagement/test/em-example-config.json +++ b/src/executionmanagement/test/em-example-config.json @@ -24,7 +24,6 @@ "staticConfig": { "bufferSizes": { "dtcBufferSize": 100, - "socketCANBufferSize": 10000, "decodedSignalsBufferSize": 10000, "rawCANFrameBufferSize": 10000 }, diff --git a/src/offboardconnectivity/implementation/aws/CMakeLists.txt b/src/offboardconnectivity/implementation/aws/CMakeLists.txt index b8c2dba8..fac9b08f 100644 --- a/src/offboardconnectivity/implementation/aws/CMakeLists.txt +++ b/src/offboardconnectivity/implementation/aws/CMakeLists.txt @@ -11,8 +11,8 @@ if(FWE_AWS_SDK_EXTRA_LIBS STREQUAL "ON") find_library(CURL_LIBRARY NAMES curl) find_library(OPENSSL_SSL_LIBRARY NAMES ssl) find_library(OPENSSL_CRYPTO_LIBRARY NAMES crypto) - find_library(ZLIB_LIBRARY NAMES z) - set(FWE_AWS_SDK_EXTRA_LIBS "${CURL_LIBRARY} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${ZLIB_LIBRARY}") + find_package(ZLIB REQUIRED) + set(FWE_AWS_SDK_EXTRA_LIBS "${CURL_LIBRARY} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ZLIB::ZLIB") set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() elseif(FWE_AWS_SDK_EXTRA_LIBS STREQUAL "OFF") diff --git a/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsBootstrap.cpp b/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsBootstrap.cpp index b18096f5..d7455796 100644 --- a/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsBootstrap.cpp +++ b/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsBootstrap.cpp @@ -49,7 +49,7 @@ struct AwsBootstrap::Impl Aws::Crt::Io::DefaultHostResolver defaultHostResolver( eventLoopGroup, MAX_HOSTS, MAX_TTL ); constexpr char ALLOCATION_TAG[] = "AWS-SDK"; auto bootstrap = Aws::MakeShared( - ALLOCATION_TAG, eventLoopGroup, defaultHostResolver ); + &ALLOCATION_TAG[0], eventLoopGroup, defaultHostResolver ); mBootstrap = bootstrap.get(); return bootstrap; } diff --git a/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsSDKMemoryManager.cpp b/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsSDKMemoryManager.cpp index 248fe6df..5acd2d35 100644 --- a/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsSDKMemoryManager.cpp +++ b/src/offboardconnectivity/implementation/aws/bootstrap/src/AwsSDKMemoryManager.cpp @@ -22,7 +22,7 @@ using Byte = unsigned char; // Note: This however does not work for over-aligned types // https://en.cppreference.com/w/cpp/language/object#Alignment // We are OK at the moment to not handled over-aligned types since we do not have any usage of "alignas" -constexpr auto offset = alignof( std::max_align_t ); +constexpr auto ALIGN_OFFSET = alignof( std::max_align_t ); } // namespace @@ -51,9 +51,9 @@ AwsSDKMemoryManager::AllocateMemory( std::size_t blockSize, std::size_t alignmen (void)allocationTag; // Verify that the object fits into the memory block - static_assert( offset >= sizeof( std::size_t ), "too big memory size block" ); + static_assert( ALIGN_OFFSET >= sizeof( std::size_t ), "too big memory size block" ); - auto realSize = blockSize + offset; + auto realSize = blockSize + ALIGN_OFFSET; void *pMem = malloc( realSize ); // NOLINT(cppcoreguidelines-no-malloc) if ( pMem == nullptr ) @@ -66,7 +66,7 @@ AwsSDKMemoryManager::AllocateMemory( std::size_t blockSize, std::size_t alignmen mMemoryUsedAndReserved += realSize; // return a pointer to the block offset from the size storage location - return static_cast( pMem ) + offset; + return static_cast( pMem ) + ALIGN_OFFSET; } void @@ -78,7 +78,7 @@ AwsSDKMemoryManager::FreeMemory( void *memoryPtr ) } // go back to the memory location where stored the size - auto pMem = static_cast( static_cast( memoryPtr ) - offset ); + auto pMem = static_cast( static_cast( memoryPtr ) - ALIGN_OFFSET ); // read the size value auto realSize = *( static_cast( pMem ) ); @@ -92,14 +92,14 @@ AwsSDKMemoryManager::FreeMemory( void *memoryPtr ) std::size_t AwsSDKMemoryManager::reserveMemory( std::size_t bytes ) { - mMemoryUsedAndReserved += ( bytes + offset ); + mMemoryUsedAndReserved += ( bytes + ALIGN_OFFSET ); return mMemoryUsedAndReserved; } std::size_t AwsSDKMemoryManager::releaseReservedMemory( std::size_t bytes ) { - mMemoryUsedAndReserved -= ( bytes + offset ); + mMemoryUsedAndReserved -= ( bytes + ALIGN_OFFSET ); return mMemoryUsedAndReserved; } diff --git a/src/offboardconnectivity/implementation/aws/iotcpp/include/PayloadManager.h b/src/offboardconnectivity/implementation/aws/iotcpp/include/PayloadManager.h index 0a8795fd..3b98732a 100644 --- a/src/offboardconnectivity/implementation/aws/iotcpp/include/PayloadManager.h +++ b/src/offboardconnectivity/implementation/aws/iotcpp/include/PayloadManager.h @@ -81,7 +81,7 @@ class PayloadManager static bool preparePayload( uint8_t *const buf, size_t size, const std::string &data, - const struct CollectionSchemeParams &collectionSchemeParams ); + const CollectionSchemeParams &collectionSchemeParams ); }; } // namespace OffboardConnectivityAwsIot } // namespace IoTFleetWise diff --git a/src/offboardconnectivity/implementation/aws/iotcpp/include/RetryThread.h b/src/offboardconnectivity/implementation/aws/iotcpp/include/RetryThread.h index 458e60ec..503d43a7 100644 --- a/src/offboardconnectivity/implementation/aws/iotcpp/include/RetryThread.h +++ b/src/offboardconnectivity/implementation/aws/iotcpp/include/RetryThread.h @@ -83,15 +83,11 @@ class RetryThread static void doWork( void *data ); static std::atomic fInstanceCounter; - int fInstance; - IRetryable &fRetryable; - + int fInstance; const uint32_t fStartBackoffMs; const uint32_t fMaxBackoffMs; - uint32_t fCurrentWaitTime; - Thread fThread; std::atomic fShouldStop; std::mutex fThreadMutex; diff --git a/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotChannel.cpp b/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotChannel.cpp index 4a8f4d95..6e4c59d1 100644 --- a/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotChannel.cpp +++ b/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotChannel.cpp @@ -71,12 +71,13 @@ AwsIotChannel::subscribe() auto onMessage = [&]( Mqtt::MqttConnection &mqttConnection, const String &topic, const ByteBuf &byteBuf, - bool dup /*dup*/, - Mqtt::QOS /*qos*/, - bool retain /*retain*/ ) { + bool dup, + Mqtt::QOS qos, + bool retain ) { std::ostringstream os; (void)mqttConnection; (void)dup; + (void)qos; (void)retain; os << "Message received on topic " << topic << " payload length: " << byteBuf.len; FWE_LOG_TRACE( os.str() ); @@ -91,7 +92,7 @@ AwsIotChannel::subscribe() auto onSubAck = [&]( Mqtt::MqttConnection &mqttConnection, uint16_t packetId, const String &topic, - Mqtt::QOS QoS, + Mqtt::QOS qos, int errorCode ) { (void)mqttConnection; mSubscribed = false; @@ -104,7 +105,7 @@ AwsIotChannel::subscribe() } else { - if ( ( packetId == 0u ) || ( QoS == Mqtt::QOS::AWS_MQTT_QOS_FAILURE ) ) + if ( ( packetId == 0U ) || ( qos == Mqtt::QOS::AWS_MQTT_QOS_FAILURE ) ) { TraceModule::get().incrementAtomicVariable( TraceAtomicVariable::SUBSCRIBE_REJECT ); FWE_LOG_ERROR( "Subscribe rejected by the Remote broker" ); diff --git a/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotConnectivityModule.cpp b/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotConnectivityModule.cpp index 2ca3e300..c7b9d72b 100644 --- a/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotConnectivityModule.cpp +++ b/src/offboardconnectivity/implementation/aws/iotcpp/src/AwsIotConnectivityModule.cpp @@ -55,16 +55,16 @@ AwsIotConnectivityModule::releaseMemoryUsage( std::size_t bytes ) bool AwsIotConnectivityModule::connect( const std::string &privateKey, const std::string &certificate, - const std::string &endpointUrlRef, - const std::string &clientIdRef, + const std::string &endpointUrl, + const std::string &clientId, Aws::Crt::Io::ClientBootstrap *clientBootstrap, bool asynchronous ) { - mClientId = clientIdRef.c_str() != nullptr ? clientIdRef.c_str() : ""; + mClientId = clientId.c_str() != nullptr ? clientId.c_str() : ""; mConnected = false; mCertificate = Crt::ByteCursorFromCString( certificate.c_str() ); - mEndpointUrl = endpointUrlRef.c_str() != nullptr ? endpointUrlRef.c_str() : ""; + mEndpointUrl = endpointUrl.c_str() != nullptr ? endpointUrl.c_str() : ""; mPrivateKey = Crt::ByteCursorFromCString( privateKey.c_str() ); if ( !createMqttConnection( clientBootstrap ) ) @@ -95,8 +95,9 @@ std::shared_ptr AwsIotConnectivityModule::createNewChannel( const std::shared_ptr &payloadManager, std::size_t maximumIotSDKHeapMemoryBytes ) { - mChannels.emplace_back( new AwsIotChannel( this, payloadManager, maximumIotSDKHeapMemoryBytes ) ); - return mChannels.back(); + auto channel = std::make_shared( this, payloadManager, maximumIotSDKHeapMemoryBytes ); + mChannels.emplace_back( channel ); + return channel; } bool diff --git a/src/offboardconnectivity/implementation/aws/iotcpp/src/PayloadManager.cpp b/src/offboardconnectivity/implementation/aws/iotcpp/src/PayloadManager.cpp index 62b3cb21..8d1bb37a 100644 --- a/src/offboardconnectivity/implementation/aws/iotcpp/src/PayloadManager.cpp +++ b/src/offboardconnectivity/implementation/aws/iotcpp/src/PayloadManager.cpp @@ -20,7 +20,7 @@ bool PayloadManager::preparePayload( uint8_t *const buf, size_t size, const std::string &data, - const struct CollectionSchemeParams &collectionSchemeParams ) + const CollectionSchemeParams &collectionSchemeParams ) { if ( buf == nullptr ) { @@ -71,7 +71,7 @@ PayloadManager::storeData( const std::uint8_t *buf, { FWE_LOG_TRACE( "CollectionScheme does not activate compression, but will apply compression for local " "persistency anyway" ); - if ( snappy::Compress( payload.data(), payload.size(), &compressedData ) == 0u ) + if ( snappy::Compress( payload.data(), payload.size(), &compressedData ) == 0U ) { TraceModule::get().incrementVariable( TraceVariable::PM_COMPRESS_ERROR ); FWE_LOG_ERROR( "Error occurred when compressing the payload. The payload is likely corrupted." ); diff --git a/src/offboardconnectivity/implementation/aws/iotcpp/src/RemoteProfiler.cpp b/src/offboardconnectivity/implementation/aws/iotcpp/src/RemoteProfiler.cpp index 7cef4cf0..ef743f25 100644 --- a/src/offboardconnectivity/implementation/aws/iotcpp/src/RemoteProfiler.cpp +++ b/src/offboardconnectivity/implementation/aws/iotcpp/src/RemoteProfiler.cpp @@ -23,6 +23,7 @@ RemoteProfiler::RemoteProfiler( std::shared_ptr metricsSender, , fInitialLogMaxInterval( initialLogMaxInterval ) , fLastTimeMetricsSentOut( 0 ) , fLastTimeMLogsSentOut( 0 ) + , fLastTimeExecutionEnvironmentMetricsCollected( fClock->monotonicTimeSinceEpochMs() ) , fLogLevelThreshold( initialLogLevelThresholdToSend ) , fProfilerPrefix( std::move( profilerPrefix ) ) , fCurrentUserPayloadInLogRoot( 0 ) @@ -30,7 +31,6 @@ RemoteProfiler::RemoteProfiler( std::shared_ptr metricsSender, initLogStructure(); fLastCPURUsage.reportCPUUsageInfo(); Aws::IoTFleetWise::Platform::Linux::CPUUsageInfo::reportPerThreadUsageData( fLastThreadUsage ); - fLastTimeExecutionEnvironmentMetricsCollected = fClock->monotonicTimeSinceEpochMs(); } void @@ -61,23 +61,26 @@ RemoteProfiler::sendMetricsOut() void RemoteProfiler::sendLogsOut() { - std::string output; + if ( fCurrentUserPayloadInLogRoot > 0 ) { - // No logging in this area as this will deadlock - std::lock_guard lock( loggingMutex ); - Json::StreamWriterBuilder builder; - builder["indentation"] = ""; // If you want whitespace-less output - output = Json::writeString( builder, fLogRoot ); - initLogStructure(); - } + std::string output; + { + // No logging in this area as this will deadlock + std::lock_guard lock( loggingMutex ); + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; // If you want whitespace-less output + output = Json::writeString( builder, fLogRoot ); + initLogStructure(); + } - if ( ( fLogSender != nullptr ) && ( fCurrentUserPayloadInLogRoot > 0 ) ) - { - uint32_t ret = static_cast( - fLogSender->sendBuffer( reinterpret_cast( output.c_str() ), output.length() ) ); - if ( static_cast( ConnectivityError::Success ) != ret ) + if ( ( fLogSender != nullptr ) ) { - FWE_LOG_ERROR( " Send error " + std::to_string( static_cast( ret ) ) ); + uint32_t ret = static_cast( + fLogSender->sendBuffer( reinterpret_cast( output.c_str() ), output.length() ) ); + if ( static_cast( ConnectivityError::Success ) != ret ) + { + FWE_LOG_ERROR( " Send error " + std::to_string( static_cast( ret ) ) ); + } } } } @@ -135,7 +138,7 @@ RemoteProfiler::setMetric( const std::string &name, double value, const std::str } fCurrentMetricsPending++; Json::Value metric; - metric["name"] = fProfilerPrefix + name; + metric["name"] = fProfilerPrefix + "_" + name; metric["value"] = Json::Value( value ); metric["unit"] = unit; fMetricsRoot["metric" + std::to_string( fCurrentMetricsPending )] = metric; @@ -251,6 +254,7 @@ RemoteProfiler::doWork( void *data ) { profiler->fLastTimeMetricsSentOut = currentTime; TraceModule::get().forwardAllMetricsToMetricsReceiver( profiler ); + TraceModule::get().startNewObservationWindow( profiler->fInitialUploadInterval ); profiler->collectExecutionEnvironmentMetrics(); profiler->sendMetricsOut(); } diff --git a/src/offboardconnectivity/implementation/aws/iotcpp/src/RetryThread.cpp b/src/offboardconnectivity/implementation/aws/iotcpp/src/RetryThread.cpp index 00ab1951..a8c422b1 100644 --- a/src/offboardconnectivity/implementation/aws/iotcpp/src/RetryThread.cpp +++ b/src/offboardconnectivity/implementation/aws/iotcpp/src/RetryThread.cpp @@ -10,13 +10,14 @@ std::atomic RetryThread::fInstanceCounter( 0 ); RetryThread::RetryThread( IRetryable &retryable, uint32_t startBackoffMs, uint32_t maxBackoffMs ) : fRetryable( retryable ) + // coverity[misra_cpp_2008_rule_5_2_10_violation] For std::atomic this must be performed in a single statement + // coverity[autosar_cpp14_m5_2_10_violation] For std::atomic this must be performed in a single statement + , fInstance( fInstanceCounter++ ) , fStartBackoffMs( startBackoffMs ) , fMaxBackoffMs( maxBackoffMs ) , fCurrentWaitTime( 0 ) , fShouldStop( false ) { - // coverity[misra_cpp_2008_rule_5_2_10_violation] For std::atomic this must be performed in a single statement - fInstance = fInstanceCounter++; } bool diff --git a/src/platform/linux/logmanagement/include/ConsoleLogger.h b/src/platform/linux/logmanagement/include/ConsoleLogger.h index 5c14a33b..7d343359 100644 --- a/src/platform/linux/logmanagement/include/ConsoleLogger.h +++ b/src/platform/linux/logmanagement/include/ConsoleLogger.h @@ -69,7 +69,7 @@ class ConsoleLogger : public ILogger * */ const std::string &levelToColor( LogLevel level ) const; - bool mColorEnabled; + bool mColorEnabled{ false }; }; enum class LogColorOption diff --git a/src/platform/linux/logmanagement/include/TraceModule.h b/src/platform/linux/logmanagement/include/TraceModule.h index a0b1ec6a..308fc1e8 100644 --- a/src/platform/linux/logmanagement/include/TraceModule.h +++ b/src/platform/linux/logmanagement/include/TraceModule.h @@ -5,6 +5,7 @@ // Includes #include "EnumUtility.h" +#include "Timer.h" #include #include #include @@ -22,7 +23,6 @@ using namespace Aws::IoTFleetWise::Platform::Utility; /** * Different Variables defined at compile time used by all other modules * For verbose print to work it needs to be also added to getVariableName() - * Only add items at the end and do not delete items * */ enum class TraceVariable { @@ -46,26 +46,6 @@ enum class TraceVariable READ_SOCKET_FRAMES_17, READ_SOCKET_FRAMES_18, READ_SOCKET_FRAMES_19, // If you add more, update references to this - QUEUE_SOCKET_TO_CONSUMER_0, - QUEUE_SOCKET_TO_CONSUMER_1, - QUEUE_SOCKET_TO_CONSUMER_2, - QUEUE_SOCKET_TO_CONSUMER_3, - QUEUE_SOCKET_TO_CONSUMER_4, - QUEUE_SOCKET_TO_CONSUMER_5, - QUEUE_SOCKET_TO_CONSUMER_6, - QUEUE_SOCKET_TO_CONSUMER_7, - QUEUE_SOCKET_TO_CONSUMER_8, - QUEUE_SOCKET_TO_CONSUMER_9, - QUEUE_SOCKET_TO_CONSUMER_10, - QUEUE_SOCKET_TO_CONSUMER_11, - QUEUE_SOCKET_TO_CONSUMER_12, - QUEUE_SOCKET_TO_CONSUMER_13, - QUEUE_SOCKET_TO_CONSUMER_14, - QUEUE_SOCKET_TO_CONSUMER_15, - QUEUE_SOCKET_TO_CONSUMER_16, - QUEUE_SOCKET_TO_CONSUMER_17, - QUEUE_SOCKET_TO_CONSUMER_18, - QUEUE_SOCKET_TO_CONSUMER_19, // If you add more, update references to this QUEUE_INSPECTION_TO_SENDER, MAX_SYSTEMTIME_KERNELTIME_DIFF, PM_MEMORY_NULL, @@ -88,6 +68,8 @@ enum class TraceVariable CE_TRIGGERS, OBD_POSSIBLE_PRECISION_LOSS_UINT64, OBD_POSSIBLE_PRECISION_LOSS_INT64, + + // If you add more, remember to add the name to TraceModule::getVariableName TRACE_VARIABLE_SIZE }; @@ -105,13 +87,14 @@ enum class TraceAtomicVariable CONNECTION_REJECTED, CONNECTION_INTERRUPTED, CONNECTION_RESUMED, + COLLECTION_SCHEME_ERROR, + // If you add more, remember to add the name to TraceModule::getAtomicVariableName TRACE_ATOMIC_VARIABLE_SIZE }; /** * Different Sections defined at compile time used by all other modules * For verbose print to work it needs to be also added to getSectionName() - * Only add items at the end and do not delete items * */ enum class TraceSection { @@ -141,6 +124,9 @@ enum class TraceSection CAN_DECODER_CYCLE_17, CAN_DECODER_CYCLE_18, CAN_DECODER_CYCLE_19, // If you add more, update references to this + COLLECTION_SCHEME_CHANGE_TO_FIRST_DATA, + + // If you add more, remember to add the name to TraceModule::getSectionName TRACE_SECTION_SIZE }; /** @@ -378,8 +364,10 @@ class TraceModule * of a traced variable for every second. Additionally the overall max is still saved. * It is independent of the startNewObservationWindow so at any time you know what was * the maximum since starting FWE. + * @param minimumObservationWindowTime if set higher than 0 the observation window will only be started if the + * current observation window is longer than the value */ - void startNewObservationWindow(); + void startNewObservationWindow( uint32_t minimumObservationWindowTime = 0 ); /** * @brief prints all variables and section in a fixed format to stdout @@ -437,6 +425,8 @@ class TraceModule struct AtomicVariableData mAtomicVariableData[toUType( TraceAtomicVariable::TRACE_ATOMIC_VARIABLE_SIZE )]; struct SectionData mSectionData[toUType( TraceSection::TRACE_SECTION_SIZE )]; + + Timer mTimeSinceLastObservationWindow; }; } // namespace Linux } // namespace Platform diff --git a/src/platform/linux/logmanagement/src/ConsoleLogger.cpp b/src/platform/linux/logmanagement/src/ConsoleLogger.cpp index 557db60c..3223dba9 100644 --- a/src/platform/linux/logmanagement/src/ConsoleLogger.cpp +++ b/src/platform/linux/logmanagement/src/ConsoleLogger.cpp @@ -44,7 +44,7 @@ setLogForwarding( ILogger *logForwarder ) gLogForwarder = logForwarder; } -void +static void forwardLog( LogLevel level, const std::string &filename, const uint32_t lineNumber, @@ -74,10 +74,6 @@ ConsoleLogger::ConsoleLogger() { mColorEnabled = true; } - else - { - mColorEnabled = false; - } } void diff --git a/src/platform/linux/logmanagement/src/TraceModule.cpp b/src/platform/linux/logmanagement/src/TraceModule.cpp index d4799885..26caeb10 100644 --- a/src/platform/linux/logmanagement/src/TraceModule.cpp +++ b/src/platform/linux/logmanagement/src/TraceModule.cpp @@ -5,7 +5,6 @@ #include "TraceModule.h" #include "LoggingModule.h" -#include #include namespace Aws @@ -53,138 +52,101 @@ TraceModule::sectionEnd( TraceSection section ) /* * return the name that should be as short as possible but still meaningful + * Used in metrics.md as reference */ const char * TraceModule::getVariableName( TraceVariable variable ) { switch ( variable ) { + // The _idX suffix is to match the legacy naming, which should not be changed as they are used + // in dashbaords. case TraceVariable::READ_SOCKET_FRAMES_0: - return "RFrames0"; + return "RFrames0_id0"; case TraceVariable::READ_SOCKET_FRAMES_1: - return "RFrames1"; + return "RFrames1_id1"; case TraceVariable::READ_SOCKET_FRAMES_2: - return "RFrames2"; + return "RFrames2_id2"; case TraceVariable::READ_SOCKET_FRAMES_3: - return "RFrames3"; + return "RFrames3_id3"; case TraceVariable::READ_SOCKET_FRAMES_4: - return "RFrames4"; + return "RFrames4_id4"; case TraceVariable::READ_SOCKET_FRAMES_5: - return "RFrames5"; + return "RFrames5_id5"; case TraceVariable::READ_SOCKET_FRAMES_6: - return "RFrames6"; + return "RFrames6_id6"; case TraceVariable::READ_SOCKET_FRAMES_7: - return "RFrames7"; + return "RFrames7_id7"; case TraceVariable::READ_SOCKET_FRAMES_8: - return "RFrames8"; + return "RFrames8_id8"; case TraceVariable::READ_SOCKET_FRAMES_9: - return "RFrames9"; + return "RFrames9_id9"; case TraceVariable::READ_SOCKET_FRAMES_10: - return "RFrames10"; + return "RFrames10_id10"; case TraceVariable::READ_SOCKET_FRAMES_11: - return "RFrames11"; + return "RFrames11_id11"; case TraceVariable::READ_SOCKET_FRAMES_12: - return "RFrames12"; + return "RFrames12_id12"; case TraceVariable::READ_SOCKET_FRAMES_13: - return "RFrames13"; + return "RFrames13_id13"; case TraceVariable::READ_SOCKET_FRAMES_14: - return "RFrames14"; + return "RFrames14_id14"; case TraceVariable::READ_SOCKET_FRAMES_15: - return "RFrames15"; + return "RFrames15_id15"; case TraceVariable::READ_SOCKET_FRAMES_16: - return "RFrames16"; + return "RFrames16_id16"; case TraceVariable::READ_SOCKET_FRAMES_17: - return "RFrames17"; + return "RFrames17_id17"; case TraceVariable::READ_SOCKET_FRAMES_18: - return "RFrames18"; + return "RFrames18_id18"; case TraceVariable::READ_SOCKET_FRAMES_19: - return "RFrames19"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_0: - return "QStC0"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_1: - return "QStC1"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_2: - return "QStC2"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_3: - return "QStC3"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_4: - return "QStC4"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_5: - return "QStC5"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_6: - return "QStC6"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_7: - return "QStC7"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_8: - return "QStC8"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_9: - return "QStC9"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_10: - return "QStC10"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_11: - return "QStC11"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_12: - return "QStC12"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_13: - return "QStC13"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_14: - return "QStC14"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_15: - return "QStC15"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_16: - return "QStC16"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_17: - return "QStC17"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_18: - return "QStC18"; - case TraceVariable::QUEUE_SOCKET_TO_CONSUMER_19: - return "QStC19"; + return "RFrames19_id19"; case TraceVariable::QUEUE_INSPECTION_TO_SENDER: - return "QStS"; + return "QStS_id40"; case TraceVariable::MAX_SYSTEMTIME_KERNELTIME_DIFF: - return "SysKerTimeDiff"; + return "SysKerTimeDiff_id41"; case TraceVariable::PM_MEMORY_NULL: - return "PmE0"; + return "PmE0_id42"; case TraceVariable::PM_MEMORY_INSUFFICIENT: - return "PmE1"; + return "PmE1_id43"; case TraceVariable::PM_COMPRESS_ERROR: - return "PmE2"; + return "PmE2_id44"; case TraceVariable::PM_STORE_ERROR: - return "PmE3"; + return "PmE3_id45"; case TraceVariable::CE_TOO_MANY_CONDITIONS: - return "CeE0"; + return "CeE0_id46"; case TraceVariable::CE_SIGNAL_ID_OUTBOUND: - return "CeE1"; + return "CeE1_id47"; case TraceVariable::CE_SAMPLE_SIZE_ZERO: - return "CeE2"; + return "CeE2_id48"; case TraceVariable::GE_COMPARE_PRECISION_ERROR: - return "GeE0"; + return "GeE0_id49"; case TraceVariable::GE_EVALUATE_ERROR_LAT_LON: - return "GeE1"; + return "GeE1_id50"; case TraceVariable::OBD_VIN_ERROR: - return "ObdE0"; + return "ObdE0_id51"; case TraceVariable::OBD_ENG_PID_REQ_ERROR: - return "ObdE1"; + return "ObdE1_id52"; case TraceVariable::OBD_TRA_PID_REQ_ERROR: - return "ObdE2"; + return "ObdE2_id53"; case TraceVariable::OBD_KEEP_ALIVE_ERROR: - return "ObdE3"; + return "ObdE3_id54"; case TraceVariable::DISCARDED_FRAMES: - return "FrmE0"; + return "FrmE0_id55"; case TraceVariable::CAN_POLLING_TIMESTAMP_COUNTER: - return "CanPollTCnt"; + return "CanPollTCnt_id56"; case TraceVariable::CE_PROCESSED_SIGNALS: - return "CeSCnt"; + return "CeSCnt_id57"; case TraceVariable::CE_PROCESSED_CAN_FRAMES: - return "CeCCnt"; + return "CeCCnt_id58"; case TraceVariable::CE_TRIGGERS: - return "CeTrgCnt"; + return "CeTrgCnt_id59"; case TraceVariable::OBD_POSSIBLE_PRECISION_LOSS_UINT64: - return "ObdPrecU64"; + return "ObdPrecU64_id60"; case TraceVariable::OBD_POSSIBLE_PRECISION_LOSS_INT64: - return "ObdPrecI64"; + return "ObdPrecI64_id61"; default: - return "UNKNOWN"; + return nullptr; } } @@ -193,26 +155,30 @@ TraceModule::getAtomicVariableName( TraceAtomicVariable variable ) { switch ( variable ) { + // The _idX suffix is to match the legacy naming, which should not be changed as they are used + // in dashbaords. case TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_SIGNALS: - return "QUEUE_CONSUMER_TO_INSPECTION_SIGNALS"; + return "QUEUE_CONSUMER_TO_INSPECTION_SIGNALS_id0"; case TraceAtomicVariable::QUEUE_CONSUMER_TO_INSPECTION_CAN: - return "QUEUE_CONSUMER_TO_INSPECTION_CAN"; + return "QUEUE_CONSUMER_TO_INSPECTION_CAN_id1"; case TraceAtomicVariable::NOT_TIME_MONOTONIC_FRAMES: - return "nTime"; + return "nTime_id2"; case TraceAtomicVariable::SUBSCRIBE_ERROR: - return "SubErr"; + return "SubErr_id3"; case TraceAtomicVariable::SUBSCRIBE_REJECT: - return "SubRej"; + return "SubRej_id4"; case TraceAtomicVariable::CONNECTION_FAILED: - return "ConFail"; + return "ConFail_id5"; case TraceAtomicVariable::CONNECTION_REJECTED: - return "ConRej"; + return "ConRej_id6"; case TraceAtomicVariable::CONNECTION_INTERRUPTED: - return "ConInt"; + return "ConInt_id7"; case TraceAtomicVariable::CONNECTION_RESUMED: - return "ConRes"; + return "ConRes_id8"; + case TraceAtomicVariable::COLLECTION_SCHEME_ERROR: + return "CampaignFailures"; default: - return "UNKNOWN"; + return nullptr; } } @@ -221,60 +187,64 @@ TraceModule::getSectionName( TraceSection section ) { switch ( section ) { + // The _idX suffix is to match the legacy naming, which should not be changed as they are used + // in dashbaords. case TraceSection::BUILD_MQTT: - return "BUILD_MQTT"; + return "BUILD_MQTT_id0"; case TraceSection::FWE_STARTUP: - return "FWE_STARTUP"; + return "FWE_STARTUP_id1"; case TraceSection::FWE_SHUTDOWN: - return "FWE_SHUTDOWN"; + return "FWE_SHUTDOWN_id2"; case TraceSection::MANAGER_DECODER_BUILD: - return "DEC_BUILD"; + return "DEC_BUILD_id3"; case TraceSection::MANAGER_COLLECTION_BUILD: - return "COL_BUILD"; + return "COL_BUILD_id4"; case TraceSection::MANAGER_EXTRACTION: - return "EXTRACT"; + return "EXTRACT_id5"; case TraceSection::CAN_DECODER_CYCLE_0: - return "CD_0"; + return "CD_0_id6"; case TraceSection::CAN_DECODER_CYCLE_1: - return "CD_1"; + return "CD_1_id7"; case TraceSection::CAN_DECODER_CYCLE_2: - return "CD_2"; + return "CD_2_id8"; case TraceSection::CAN_DECODER_CYCLE_3: - return "CD_3"; + return "CD_3_id9"; case TraceSection::CAN_DECODER_CYCLE_4: - return "CD_4"; + return "CD_4_id10"; case TraceSection::CAN_DECODER_CYCLE_5: - return "CD_5"; + return "CD_5_id11"; case TraceSection::CAN_DECODER_CYCLE_6: - return "CD_6"; + return "CD_6_id12"; case TraceSection::CAN_DECODER_CYCLE_7: - return "CD_7"; + return "CD_7_id13"; case TraceSection::CAN_DECODER_CYCLE_8: - return "CD_8"; + return "CD_8_id14"; case TraceSection::CAN_DECODER_CYCLE_9: - return "CD_9"; + return "CD_9_id15"; case TraceSection::CAN_DECODER_CYCLE_10: - return "CD_10"; + return "CD_10_id16"; case TraceSection::CAN_DECODER_CYCLE_11: - return "CD_11"; + return "CD_11_id17"; case TraceSection::CAN_DECODER_CYCLE_12: - return "CD_12"; + return "CD_12_id18"; case TraceSection::CAN_DECODER_CYCLE_13: - return "CD_13"; + return "CD_13_id19"; case TraceSection::CAN_DECODER_CYCLE_14: - return "CD_14"; + return "CD_14_id20"; case TraceSection::CAN_DECODER_CYCLE_15: - return "CD_15"; + return "CD_15_id21"; case TraceSection::CAN_DECODER_CYCLE_16: - return "CD_16"; + return "CD_16_id22"; case TraceSection::CAN_DECODER_CYCLE_17: - return "CD_17"; + return "CD_17_id23"; case TraceSection::CAN_DECODER_CYCLE_18: - return "CD_18"; + return "CD_18_id24"; case TraceSection::CAN_DECODER_CYCLE_19: - return "CD_19"; + return "CD_19_id25"; + case TraceSection::COLLECTION_SCHEME_CHANGE_TO_FIRST_DATA: + return "CampaignRxToDataTx"; default: - return "UNKNOWN"; + return nullptr; } } @@ -300,8 +270,13 @@ TraceModule::updateAllTimeData() } void -TraceModule::startNewObservationWindow() +TraceModule::startNewObservationWindow( uint32_t minimumObservationWindowTime ) { + if ( mTimeSinceLastObservationWindow.getElapsedMs().count() < minimumObservationWindowTime ) + { + return; + } + mTimeSinceLastObservationWindow.reset(); updateAllTimeData(); for ( auto i = 0; i < toUType( TraceVariable::TRACE_VARIABLE_SIZE ); i++ ) { @@ -334,13 +309,11 @@ TraceModule::forwardAllMetricsToMetricsReceiver( IMetricsReceiver *profiler ) { auto &v = mVariableData[i]; auto variableNamePtr = getVariableName( static_cast( i ) ); - auto variableName = variableNamePtr != nullptr ? variableNamePtr : "Unknown variable name"; - profiler->setMetric( std::string( "variableMaxSinceStartup_" ) + variableName + std::string( "_id" ) + - std::to_string( i ), - static_cast( v.mMaxValue ), - "Count" ); - profiler->setMetric( std::string( "variableMaxSinceLast_" ) + variableName + std::string( "_id" ) + - std::to_string( i ), + auto variableName = + variableNamePtr != nullptr ? variableNamePtr : std::string( "UnknownVariable_id" ) + std::to_string( i ); + profiler->setMetric( + std::string( "variableMaxSinceLast_" ) + variableName, static_cast( v.mMaxValue ), "Count" ); + profiler->setMetric( std::string( "variableMaxSinceStartup_" ) + variableName, static_cast( v.mMaxValueAllTime ), "Count" ); } @@ -348,14 +321,13 @@ TraceModule::forwardAllMetricsToMetricsReceiver( IMetricsReceiver *profiler ) { auto &v = mAtomicVariableData[i]; auto atomicVariableNamePtr = getAtomicVariableName( static_cast( i ) ); - auto atomicVariableName = - atomicVariableNamePtr != nullptr ? atomicVariableNamePtr : "Unknown atomic variable name"; - profiler->setMetric( std::string( "variableMaxSinceStartup_atomic_" ) + atomicVariableName + - std::string( "_id" ) + std::to_string( i ), + auto atomicVariableName = atomicVariableNamePtr != nullptr + ? atomicVariableNamePtr + : std::string( "UnknownVariable_id" ) + std::to_string( i ); + profiler->setMetric( std::string( "variableMaxSinceStartup_atomic_" ) + atomicVariableName, static_cast( v.mMaxValueAllTime ), "Count" ); - profiler->setMetric( std::string( "variableMaxSinceLast_atomic_" ) + atomicVariableName + std::string( "_id" ) + - std::to_string( i ), + profiler->setMetric( std::string( "variableMaxSinceLast_atomic_" ) + atomicVariableName, static_cast( v.mMaxValue ), "Count" ); } @@ -363,23 +335,14 @@ TraceModule::forwardAllMetricsToMetricsReceiver( IMetricsReceiver *profiler ) { auto &v = mSectionData[i]; auto sectionNamePtr = getSectionName( static_cast( i ) ); - auto sectionName = sectionNamePtr != nullptr ? sectionNamePtr : "Unknown section name"; - profiler->setMetric( std::string( "sectionAvgSinceStartup_" ) + sectionName + std::string( "_id" ) + - std::to_string( i ), + auto sectionName = + sectionNamePtr != nullptr ? sectionNamePtr : std::string( "UnknownSection_id" ) + std::to_string( i ); + profiler->setMetric( std::string( "sectionAvgSinceStartup_" ) + sectionName, ( v.mHitCounter == 0 ? 0 : v.mTimeSpentSum / v.mHitCounter ), "Seconds" ); - profiler->setMetric( std::string( "sectionMaxSinceStartup_" ) + sectionName + std::string( "_id" ) + - std::to_string( i ), - v.mMaxSpentAllTime, - "Seconds" ); - profiler->setMetric( std::string( "sectionMaxSinceLast_" ) + sectionName + std::string( "_id" ) + - std::to_string( i ), - v.mMaxSpent, - "Seconds" ); - profiler->setMetric( std::string( "sectionCountSinceStartup_" ) + sectionName + std::string( "_id" ) + - std::to_string( i ), - v.mHitCounter, - "Seconds" ); + profiler->setMetric( std::string( "sectionMaxSinceStartup_" ) + sectionName, v.mMaxSpentAllTime, "Seconds" ); + profiler->setMetric( std::string( "sectionMaxSinceLast_" ) + sectionName, v.mMaxSpent, "Seconds" ); + profiler->setMetric( std::string( "sectionCountSinceStartup_" ) + sectionName, v.mHitCounter, "Seconds" ); } } @@ -391,48 +354,42 @@ TraceModule::print() { auto &v = mVariableData[i]; auto variableNamePtr = getVariableName( static_cast( i ) ); - auto variableName = variableNamePtr != nullptr ? variableNamePtr : "Unknown variable name"; + auto variableName = + variableNamePtr != nullptr ? variableNamePtr : std::string( "UnknownVariable_id" ) + std::to_string( i ); FWE_LOG_TRACE( std::string{ " TraceModule-ConsoleLogging-Variable '" } + variableName + "' [" + std::to_string( i ) + "] current value: [" + std::to_string( v.mCurrentValue ) + - "] max value since last print: " - "[" + - std::to_string( v.mMaxValue ) + "] overall max value: [" + std::to_string( v.mMaxValueAllTime ) + - "]" ); + "] max value since last print: [" + std::to_string( v.mMaxValue ) + "] overall max value: [" + + std::to_string( v.mMaxValueAllTime ) + "]" ); } for ( auto i = 0; i < toUType( TraceAtomicVariable::TRACE_ATOMIC_VARIABLE_SIZE ); i++ ) { auto &v = mAtomicVariableData[i]; auto atomicVariableNamePtr = getAtomicVariableName( static_cast( i ) ); - auto atomicVariableName = - atomicVariableNamePtr != nullptr ? atomicVariableNamePtr : "Unknown atomic variable name"; + auto atomicVariableName = atomicVariableNamePtr != nullptr + ? atomicVariableNamePtr + : std::string( "UnknownVariable_id" ) + std::to_string( i ); FWE_LOG_TRACE( std::string{ " TraceModule-ConsoleLogging-TraceAtomicVariable '" } + atomicVariableName + "' [" + std::to_string( i ) + "] current value: [" + std::to_string( v.mCurrentValue.load() ) + - "] max value since " - "last print: [" + - std::to_string( v.mMaxValue ) + "] overall max value: [" + std::to_string( v.mMaxValueAllTime ) + - "]" ); + "] max value since last print: [" + std::to_string( v.mMaxValue ) + "] overall max value: [" + + std::to_string( v.mMaxValueAllTime ) + "]" ); } for ( auto i = 0; i < toUType( TraceSection::TRACE_SECTION_SIZE ); i++ ) { auto &v = mSectionData[i]; auto currentHitCounter = v.mHitCounter - ( v.mCurrentlyActive ? 0 : 1 ); auto sectionNamePtr = getSectionName( static_cast( i ) ); - auto sectionName = sectionNamePtr != nullptr ? sectionNamePtr : "Unknown section name"; + auto sectionName = + sectionNamePtr != nullptr ? sectionNamePtr : std::string( "UnknownSection_id" ) + std::to_string( i ); FWE_LOG_TRACE( std::string{ " TraceModule-ConsoleLogging-Section '" } + sectionName + "' [" + std::to_string( i ) + "] times section executed: [" + std::to_string( v.mHitCounter ) + - "] avg execution time: " - "[" + + "] avg execution time: [" + std::to_string( ( v.mHitCounter == 0 ? 0 : v.mTimeSpentSum / v.mHitCounter ) ) + "] max execution time since last print: [" + std::to_string( v.mMaxSpent ) + "] overall: [" + - std::to_string( v.mMaxSpentAllTime ) + - "] avg interval between execution: " - "[" + + std::to_string( v.mMaxSpentAllTime ) + "] avg interval between execution: [" + std::to_string( ( currentHitCounter == 0 ? 0 : v.mIntervalSum / currentHitCounter ) ) + "] max interval since last print: [" + std::to_string( v.mMaxInterval ) + "] overall: [" + std::to_string( v.mMaxIntervalAllTime ) + "]" ); } - - std::fflush( stdout ); } } // namespace Linux diff --git a/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp b/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp index 685c0868..848168cf 100644 --- a/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp +++ b/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp @@ -13,13 +13,11 @@ using namespace Aws::IoTFleetWise::Platform::Linux::PersistencyManagement; CacheAndPersist::CacheAndPersist( const std::string &partitionPath, size_t maxPartitionSize ) + : mDecoderManifestFile{ partitionPath + DECODER_MANIFEST_FILE } + , mCollectionSchemeListFile{ partitionPath + COLLECTION_SCHEME_LIST_FILE } + , mCollectedDataFile{ partitionPath + COLLECTED_DATA_FILE } + , mMaxPersistencePartitionSize{ maxPartitionSize } { - // Define the file paths - mDecoderManifestFile = partitionPath + DECODER_MANIFEST_FILE; - mCollectionSchemeListFile = partitionPath + COLLECTION_SCHEME_LIST_FILE; - mCollectedDataFile = partitionPath + COLLECTED_DATA_FILE; - - mMaxPersistencePartitionSize = maxPartitionSize; } bool diff --git a/src/platform/linux/resourcemanagement/src/CPUUsageInfo.cpp b/src/platform/linux/resourcemanagement/src/CPUUsageInfo.cpp index 868cde0b..94af28ff 100644 --- a/src/platform/linux/resourcemanagement/src/CPUUsageInfo.cpp +++ b/src/platform/linux/resourcemanagement/src/CPUUsageInfo.cpp @@ -66,7 +66,6 @@ CPUUsageInfo::reportPerThreadUsageData( CPUUsageInfo::ThreadCPUUsageInfos &threa // Iterate through all the tasks, and extracts each task info and push it into the // a structure per task. Each task is a thread in this context. DIR *taskDir = nullptr; - struct dirent *dp = nullptr; taskDir = opendir( "/proc/self/task/." ); if ( taskDir == nullptr ) { @@ -76,8 +75,13 @@ CPUUsageInfo::reportPerThreadUsageData( CPUUsageInfo::ThreadCPUUsageInfos &threa threadCPUUsageInfos.clear(); // Clock Frequency is needed to compute the user and system execution cpu cycles double clockTickFrequency = 1.0 / static_cast( sysconf( _SC_CLK_TCK ) ); - while ( ( dp = readdir( taskDir ) ) != nullptr ) + while ( true ) { + auto dp = readdir( taskDir ); + if ( dp == nullptr ) + { + break; + } std::string taskFileName = dp->d_name; if ( ( taskFileName.length() > 0 ) && ( taskFileName[0] != '.' ) ) { @@ -90,7 +94,7 @@ CPUUsageInfo::reportPerThreadUsageData( CPUUsageInfo::ThreadCPUUsageInfos &threa CPUUsageInfo::ThreadId tid = static_cast( strtol( taskFileName.c_str(), nullptr, 10 ) ); char statContent[MAX_PROC_STAT_FILE_SIZE_READ]; - if ( fgets( statContent, MAX_PROC_STAT_FILE_SIZE_READ - 1, fp ) != nullptr ) + if ( fgets( &statContent[0], MAX_PROC_STAT_FILE_SIZE_READ - 1, fp ) != nullptr ) { statContent[MAX_PROC_STAT_FILE_SIZE_READ - 1] = '\0'; // fgets should already null terminate string char *c = &statContent[0]; diff --git a/src/platform/linux/resourcemanagement/src/MemoryUsageInfo.cpp b/src/platform/linux/resourcemanagement/src/MemoryUsageInfo.cpp index 16691be5..e39b0015 100644 --- a/src/platform/linux/resourcemanagement/src/MemoryUsageInfo.cpp +++ b/src/platform/linux/resourcemanagement/src/MemoryUsageInfo.cpp @@ -61,7 +61,7 @@ MemoryUsageInfo::reportMemoryUsageInfo() } else { - mMaxResidentMemorySize = static_cast( selfMemoryUsage.ru_maxrss * 1024U ); + mMaxResidentMemorySize = static_cast( selfMemoryUsage.ru_maxrss ) * 1024U; return true; } } diff --git a/src/platform/linux/threadingmanagement/include/Listener.h b/src/platform/linux/threadingmanagement/include/Listener.h index bb970101..5255bb0d 100644 --- a/src/platform/linux/threadingmanagement/include/Listener.h +++ b/src/platform/linux/threadingmanagement/include/Listener.h @@ -105,17 +105,6 @@ class ThreadListeners } } - /** - * @brief Mutable size of the listeners count. - */ - size_t - size() const - { - MutexLock lock( mMutex ); - - return mContainer.size(); - } - private: // Container to store the list of listeners to this thread using ListenerContainer = std::vector; diff --git a/src/platform/linux/threadingmanagement/include/Signal.h b/src/platform/linux/threadingmanagement/include/Signal.h index 26baa50b..22a5e007 100644 --- a/src/platform/linux/threadingmanagement/include/Signal.h +++ b/src/platform/linux/threadingmanagement/include/Signal.h @@ -26,10 +26,7 @@ namespace Linux class Signal { public: - Signal() - { - mNotify = false; - } + Signal() = default; ~Signal() = default; /** @@ -56,8 +53,10 @@ class Signal void wait( uint32_t timeoutMs ) { - // Predicate, returns true if the wakeup signal is set. - auto predicate = [this]() -> bool { return mNotify; }; + auto predicate = [this]() -> bool { + // Predicate, returns true if the wakeup signal is set. + return mNotify; + }; std::unique_lock mWaitMutex( mMutex ); if ( !predicate() ) { @@ -89,7 +88,7 @@ class Signal private: std::condition_variable mSignalCondition; - std::atomic mNotify; + std::atomic mNotify{ false }; std::mutex mMutex; }; diff --git a/src/platform/linux/threadingmanagement/include/Thread.h b/src/platform/linux/threadingmanagement/include/Thread.h index cf6142e7..32b726e8 100644 --- a/src/platform/linux/threadingmanagement/include/Thread.h +++ b/src/platform/linux/threadingmanagement/include/Thread.h @@ -82,7 +82,7 @@ class Thread unsigned long getThreadID() const; - pthread_t mThread{ 0 }; + pthread_t mThread{ 0U }; ThreadSettings mExecParams; unsigned long mThreadId{}; diff --git a/src/platform/linux/threadingmanagement/src/Thread.cpp b/src/platform/linux/threadingmanagement/src/Thread.cpp index 52a5c41b..1869eaab 100644 --- a/src/platform/linux/threadingmanagement/src/Thread.cpp +++ b/src/platform/linux/threadingmanagement/src/Thread.cpp @@ -62,7 +62,7 @@ Thread::release() // Wait till the Predicate mTerminateSignal->wait( Signal::WaitWithPredicate ); - if ( ( mThread != 0u ) && ( pthread_join( mThread, nullptr ) != 0 ) ) + if ( ( mThread != 0U ) && ( pthread_join( mThread, nullptr ) != 0 ) ) { return false; } diff --git a/src/platform/linux/timemanagement/include/Timer.h b/src/platform/linux/timemanagement/include/Timer.h index d5a29c28..d7a435b7 100644 --- a/src/platform/linux/timemanagement/include/Timer.h +++ b/src/platform/linux/timemanagement/include/Timer.h @@ -85,7 +85,8 @@ inline Timer::Timer() inline void Timer::reset() { - mStart = mResume = Clock::now(); + mStart = Clock::now(); + mResume = mStart; mElapsed = Duration{ 0 }; mIsTimerRunning = true; } diff --git a/src/testingsupport/include/WaitUntil.h b/src/testingsupport/include/WaitUntil.h index 6ae6594e..18def6a1 100644 --- a/src/testingsupport/include/WaitUntil.h +++ b/src/testingsupport/include/WaitUntil.h @@ -14,8 +14,14 @@ The following WAIT_ASSERT_* are adding extra retrial capability above the GoogleTest framework. Some test cases need longer time to process before get expected outcome, so we need to wait and retry. */ -#define WAIT_ASSERT_TRUE( val ) ASSERT_TRUE( waitUntil( [&] { return ( val ); } ) ) -#define WAIT_ASSERT_FALSE( val ) ASSERT_TRUE( waitUntil( [&] { return !( val ); } ) ) +#define WAIT_ASSERT_TRUE( val ) \ + ASSERT_TRUE( waitUntil( [&] { \ + return ( val ); \ + } ) ) +#define WAIT_ASSERT_FALSE( val ) \ + ASSERT_TRUE( waitUntil( [&] { \ + return !( val ); \ + } ) ) #define WAIT_ASSERT_EQ( val1, val2 ) \ auto WAIT_VAR( val1_, __LINE__ ) = val1; \ @@ -60,8 +66,14 @@ The following DELAYED_ASSERT_* are adding extra retrial capability above the GoogleTest framework. Some test cases need to be continuously evaluated, and asserted only after the timeout occurs. */ -#define DELAY_ASSERT_TRUE( val ) ASSERT_TRUE( waitDelay( [&] { return ( val ); } ) ) -#define DELAY_ASSERT_FALSE( val ) ASSERT_FALSE( waitDelay( [&] { return ( val ); } ) ) +#define DELAY_ASSERT_TRUE( val ) \ + ASSERT_TRUE( waitDelay( [&] { \ + return ( val ); \ + } ) ) +#define DELAY_ASSERT_FALSE( val ) \ + ASSERT_FALSE( waitDelay( [&] { \ + return ( val ); \ + } ) ) namespace Aws { diff --git a/src/testingsupport/test/WaitUntilTest.cpp b/src/testingsupport/test/WaitUntilTest.cpp index 8bcee975..bc892637 100644 --- a/src/testingsupport/test/WaitUntilTest.cpp +++ b/src/testingsupport/test/WaitUntilTest.cpp @@ -13,13 +13,17 @@ TEST( TimerTest, waitUntilTest ) { // A determent function with negative return, it will retry for `WAIT_TIME_OUT` auto startTime = std::chrono::steady_clock::now(); - waitUntil( [] { return 1 == 2; } ); + waitUntil( [] { + return 1 == 2; + } ); auto elapsedTime = std::chrono::steady_clock::now() - startTime; ASSERT_LT( WAIT_TIME_OUT, elapsedTime ); // A determent function with positive return, it won't retry or wait for `WAIT_TIME_OUT` startTime = std::chrono::steady_clock::now(); - waitUntil( [] { return 1 == 1; } ); + waitUntil( [] { + return 1 == 1; + } ); elapsedTime = std::chrono::steady_clock::now() - startTime; ASSERT_GT( WAIT_TIME_OUT, elapsedTime ); } diff --git a/src/vehiclenetwork/CMakeLists.txt b/src/vehiclenetwork/CMakeLists.txt index c7f41ffa..92f11752 100644 --- a/src/vehiclenetwork/CMakeLists.txt +++ b/src/vehiclenetwork/CMakeLists.txt @@ -6,7 +6,6 @@ set(libraryAliasName IoTFleetWise::Vehiclenetwork) set(SRCS - src/CANDataSource.cpp src/ISOTPOverCANReceiver.cpp src/ISOTPOverCANSender.cpp src/ISOTPOverCANSenderReceiver.cpp @@ -27,7 +26,7 @@ add_library( # library, for example: #include "businterfaces/VehicleDataSource.h" target_include_directories(${libraryTargetName} PUBLIC include) -find_package(Boost 1.65.1 REQUIRED COMPONENTS thread) +find_package(Boost 1.71.0 REQUIRED COMPONENTS thread) if(FWE_FEATURE_CAMERA) find_package(fastrtps REQUIRED) find_package(fastcdr REQUIRED) @@ -60,17 +59,12 @@ install( include/businterfaces/ISOTPOverCANReceiver.h include/businterfaces/ISOTPOverCANSender.h include/businterfaces/ISOTPOverCANSenderReceiver.h - include/businterfaces/AbstractVehicleDataSource.h - include/businterfaces/VehicleDataSourceListener.h - include/businterfaces/CANDataSource.h DESTINATION include ) install( FILES - include/datatypes/VehicleDataMessage.h - include/datatypes/VehicleDataSourceConfig.h include/datatypes/VehicleDataSourceTypes.h include/datatypes/ISOTPOverCANOptions.h DESTINATION @@ -97,8 +91,6 @@ endif() set( testSources test/ISOTPOverCANProtocolTest.cpp - test/VehicleDataMessageTest.cpp - test/CANDataSourceTest.cpp ) if(FWE_FEATURE_CAMERA) diff --git a/src/vehiclenetwork/include/businterfaces/AbstractVehicleDataSource.h b/src/vehiclenetwork/include/businterfaces/AbstractVehicleDataSource.h deleted file mode 100644 index af1cb45f..00000000 --- a/src/vehiclenetwork/include/businterfaces/AbstractVehicleDataSource.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -// Includes -#include "Listener.h" -#include "VehicleDataSourceListener.h" -#include "datatypes/VehicleDataMessage.h" -#include "datatypes/VehicleDataSourceConfig.h" -#include -#include -#include - -namespace Aws -{ -namespace IoTFleetWise -{ -namespace VehicleNetwork -{ -using namespace Aws::IoTFleetWise::Platform::Linux; -// Single Producer/Consumer buffer. Used for data processing between the source and the consumer. -using VehicleMessageCircularBuffer = boost::lockfree::spsc_queue; -using VehicleMessageCircularBufferPtr = std::shared_ptr; -/** - * @brief Abstract Interface for a Vehicle Data Source. A data source maps to exactly one Transport - * Connector implementation. The Transport protocol details are abstracted away so that users of - * the data sources only act on the output of the source i.e. the Circular buffer. - * This interface does not mandate any threading model on implementers but all APIs are expected to - * be thread safe. - */ -class AbstractVehicleDataSource : public ThreadListeners -{ -public: - ~AbstractVehicleDataSource() override = default; - - /** - * @brief Initializes the Vehicle Data Source. - * @param sourceConfigs The source configuration. One source can handle - * multiple configs at a time if composition is wanted e.g. multiple - * raw network sources consolidated into one outbound vehicle data source. - * Configurations used to init the source must be of the same RAW Network protocol type. - * @return True if the Init succeeds. - */ - virtual bool init( const std::vector &sourceConfigs ) = 0; - - /** - * @brief Connect the Data Source to the underlying Transport. - * @return True if the connection has been setup. - */ - virtual bool connect() = 0; - - /** - * @brief Connect the Data Source to the underlying Transport. - * @return True if connection has been closed. - */ - virtual bool disconnect() = 0; - - /** - * @brief Checks if the connection to the underlying Transport and data - * is being consumed, - * Implementation varies among Transports. - * @return True if the connection is healthy. - */ - virtual bool isAlive() = 0; - - /** - * @brief Ask the source to suspend the data acquisition from the Transport. - */ - virtual void suspendDataAcquisition() = 0; - - /** - * @brief Ask the source to resume the data acquisition from the Transport. - */ - virtual void resumeDataAcquisition() = 0; - - /** - * @brief Handle of the Vehicle Data Source circular buffer. User of the Data Source - * can use this object to consume data. The buffer can ONLY be consume - * from one single consumer thread. - * @return shared object pointer to the circular buffer. - */ - inline VehicleMessageCircularBufferPtr - getBuffer() - { - return mCircularBuffPtr; - } - /** - * @brief Provide the Transport Protocol Type used by the source. - * If multiple source configs are provided, they are assumed have the same - * Transport protocol. - * @return returns the transport type. - */ - inline VehicleDataSourceProtocol - getVehicleDataSourceProtocol() - { - return mNetworkProtocol; - } - /** - * @return the unique ID of the Source. - */ - inline VehicleDataSourceID - getVehicleDataSourceID() const - { - return mID; - } - /** - * @return the data source type. - */ - inline VehicleDataSourceType - getVehicleDataSourceType() - { - return mType; - } - - /** - * @return the network interface name. In case multiple - * network interfaces ( e.g. multiple CAN IFs ) are aggregated in one source, - * only one IF Name will be returned. - */ - inline VehicleDataSourceIfName - getVehicleDataSourceIfName() - { - return mIfName; - } - -protected: - /** - * @brief Thread safe Source ID generator - * @return returns a unique identifier of a source. - */ - static VehicleDataSourceID - generateSourceID() - { - static std::atomic sourceID( INVALID_DATA_SOURCE_ID ); - return ++sourceID; - } - // A FIFO queue holding the currently acquired vehicle data messages. - VehicleMessageCircularBufferPtr mCircularBuffPtr; - // Current active Data Source Configurations - std::vector mConfigs; - // Unique Identifier of the source. - VehicleDataSourceID mID; - // A data source has one single Transport even if we are dealing with - // multiple raw data sources at the network level. - VehicleDataSourceProtocol mNetworkProtocol; - // The data source network interface name. - VehicleDataSourceIfName mIfName; - // Type of the source - VehicleDataSourceType mType; -}; -using VehicleDataSourcePtr = std::shared_ptr; -} // namespace VehicleNetwork -} // namespace IoTFleetWise -} // namespace Aws diff --git a/src/vehiclenetwork/include/businterfaces/VehicleDataSourceListener.h b/src/vehiclenetwork/include/businterfaces/VehicleDataSourceListener.h deleted file mode 100644 index 692cb8f1..00000000 --- a/src/vehiclenetwork/include/businterfaces/VehicleDataSourceListener.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -// Includes -#include "datatypes/VehicleDataSourceTypes.h" - -namespace Aws -{ -namespace IoTFleetWise -{ -namespace VehicleNetwork -{ - -/** - * @brief Vehicle Data Source Listener callback. - * These events are raised when the Connector got connected or - * disconnected from the Network Interface it listens to. - */ -struct VehicleDataSourceListener -{ - /** - * @brief Default Destructor. - */ - virtual ~VehicleDataSourceListener() = default; - - /** - * @brief Callback raised if the Data Source is connected - */ - virtual void onVehicleDataSourceConnected( const VehicleDataSourceID &vehicleDataSourceId ) = 0; - - /** - * @brief Callback raised if the Data Source is disconnected - */ - virtual void onVehicleDataSourceDisconnected( const VehicleDataSourceID &vehicleDataSourceId ) = 0; -}; - -} // namespace VehicleNetwork -} // namespace IoTFleetWise -} // namespace Aws diff --git a/src/vehiclenetwork/include/datatypes/VehicleDataMessage.h b/src/vehiclenetwork/include/datatypes/VehicleDataMessage.h deleted file mode 100644 index d824804d..00000000 --- a/src/vehiclenetwork/include/datatypes/VehicleDataMessage.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -// Includes -#include "TimeTypes.h" - -#include -#include -#include -#include -namespace Aws -{ -namespace IoTFleetWise -{ -namespace VehicleNetwork -{ - -using namespace Aws::IoTFleetWise::Platform::Linux; - -/** - * @brief Generic Structure of a Vehicle Data Message. - * Every Message has two sub structures i.e. Raw and Synthetic. - */ -class VehicleDataMessage -{ -public: - /** - * @brief Constructor/destructor - */ - inline VehicleDataMessage() = default; - - inline ~VehicleDataMessage() = default; - - /** - * @brief Unique identifier of the Vehicle Data Message. - * @return ID - */ - inline std::uint64_t - getMessageID() const - { - return mID; - } - - /** - * @brief Raw representation of the Message - * @return Byte array of the raw data - */ - // TODO: This API implies a copy of the data from the Network. - // In certain Protocol stacks, the raw data can be a shared object. - // Instead of Copying, we can use the shared object and work on it instead. - // This interface is also not optimized for Standard CAN frames, as the size does not - // exceed 8 Bytes. Using a Vector introduces an overhead of 8 extra bytes for the vector handle. - inline const std::vector & - getRawData() - { - return mRawData; - } - - /** - * @brief Synthetic representation of the Message - * @return Array of Any type - */ - inline const std::vector & - getSyntheticData() - { - return mSyntheticData; - } - - /** - * @brief Timepoint when the Message was acquired from the vehicle - * @return Timestamp - */ - inline const Timestamp & - getReceptionTimestamp() const - { - return mTimestamp; - } - - /** - * @brief Checks if the Message is valid or not. - * A message is valid if it have either a synthetic or a raw representation - * @return True if Valid, False if not. - */ - inline bool - isValid() const - { - return ( !mSyntheticData.empty() ) || ( !mRawData.empty() ); - } - - /** - * @brief Routine to setup a Vehicle Data Message. - */ - inline void - setup( const std::uint32_t &id, - const std::vector &rawData, - const std::vector &syntheticData, - const Timestamp ×tamp ) - { - mTimestamp = timestamp; - mID = id; - mRawData = rawData; - mSyntheticData = syntheticData; - } - -private: - std::uint64_t mID{}; - std::vector mRawData; - std::vector mSyntheticData; - Timestamp mTimestamp{}; -}; -} // namespace VehicleNetwork -} // namespace IoTFleetWise -} // namespace Aws diff --git a/src/vehiclenetwork/include/datatypes/VehicleDataSourceConfig.h b/src/vehiclenetwork/include/datatypes/VehicleDataSourceConfig.h deleted file mode 100644 index f30f393b..00000000 --- a/src/vehiclenetwork/include/datatypes/VehicleDataSourceConfig.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -// Includes -#include "VehicleDataSourceTypes.h" -#include -#include -#include -#include - -namespace Aws -{ -namespace IoTFleetWise -{ -namespace VehicleNetwork -{ - -/** - * @brief A structure representing the configuration of a Vehicle Data Source. - * @param maxNumberOfVehicleDataMessages The Maximum number of vehicle data messages that this - * source can make available at a given point in time. - * @param vehicleDataMessageFilter Regular expression representing a filter for the source to - * discard messages by e.g. message ID, topic. - * @param transportProperties Container for any other Transport properties eg. CAN Bus interface - * Topic Name. - */ -struct VehicleDataSourceConfig -{ - uint32_t maxNumberOfVehicleDataMessages; - std::regex vehicleDataMessageFilter; - std::map transportProperties; -}; - -} // namespace VehicleNetwork -} // namespace IoTFleetWise -} // namespace Aws diff --git a/src/vehiclenetwork/include/datatypes/VehicleDataSourceTypes.h b/src/vehiclenetwork/include/datatypes/VehicleDataSourceTypes.h index ce04d1f2..45f1189e 100644 --- a/src/vehiclenetwork/include/datatypes/VehicleDataSourceTypes.h +++ b/src/vehiclenetwork/include/datatypes/VehicleDataSourceTypes.h @@ -3,9 +3,7 @@ #pragma once -// Includes -#include -#include +#include namespace Aws { @@ -13,55 +11,18 @@ namespace IoTFleetWise { namespace VehicleNetwork { -// Identifier of the Vehicle Data Source -using VehicleDataSourceID = uint32_t; -const VehicleDataSourceID INVALID_DATA_SOURCE_ID = 0; - -using VehicleDataSourceIfName = std::string; -// Types of the the Vehicle Data Source -enum class VehicleDataSourceType -{ - INVALID_SOURCE_TYPE, - CAN_SOURCE, - CAN_FD_SOURCE, - FLEXRAY_SOURCE, - LIN_SOURCE, - ETHERNET_SOURCE, - IPC_SOURCE -}; // Transport Protocol used by the Vehicle Data Source enum class VehicleDataSourceProtocol { INVALID_PROTOCOL, - CAN_TP, - SOMEIP, OBD, - RAW_SOCKET, - DOIP, - AVB, - DDS -}; - -// Vehicle Data Source States -enum class VehicleDataSourceState -{ - INVALID_STATE, - CONNECTED, - DISCONNECTED + RAW_SOCKET + // Add any new protocols to the list of supported protocols below }; -// ECU IDs -enum class ECUID -{ - INVALID_ECU_ID = 0, - BROADCAST_ID = 0x7DF, - BROADCAST_EXTENDED_ID = 0x18DB33F1, - LOWEST_ECU_EXTENDED_RX_ID = 0x18DAF100, - LOWEST_ECU_RX_ID = 0x7E8, - HIGHEST_ECU_EXTENDED_RX_ID = 0x18DAF1FF, - HIGHEST_ECU_RX_ID = 0x7EF, -}; +constexpr std::array SUPPORTED_NETWORK_PROTOCOL = { + { VehicleDataSourceProtocol::RAW_SOCKET, VehicleDataSourceProtocol::OBD } }; } // namespace VehicleNetwork } // namespace IoTFleetWise diff --git a/src/vehiclenetwork/include/dds/CameraDataSubscriber.h b/src/vehiclenetwork/include/dds/CameraDataSubscriber.h index c3c3d5cc..9815f467 100644 --- a/src/vehiclenetwork/include/dds/CameraDataSubscriber.h +++ b/src/vehiclenetwork/include/dds/CameraDataSubscriber.h @@ -85,7 +85,7 @@ class CameraDataSubscriber : public IDDSSubscriber // frames again/Split the file into frames. We want to do this correctly in future versions // of this code, where we would store as a metadata the frame size and/or store the frames // in separate artifacts. - static bool persistToStorage( const std::vector &buffer, const std::string &fileName ); + static bool persistToStorage( const std::vector &frameBuffer, const std::string &fileName ); Thread mThread; std::atomic mShouldStop{ false }; diff --git a/src/vehiclenetwork/include/dds/IDDSPublisher.h b/src/vehiclenetwork/include/dds/IDDSPublisher.h index aa0b8d4e..8d5bdb00 100644 --- a/src/vehiclenetwork/include/dds/IDDSPublisher.h +++ b/src/vehiclenetwork/include/dds/IDDSPublisher.h @@ -79,24 +79,6 @@ class IDDSPublisher : public DataWriterListener */ virtual void publishDataRequest( const DDSDataRequest &dataRequest ) = 0; - /** - * @return the unique ID of the channel. - */ - inline VehicleDataSourceID - getChannelID() const - { - return mID; - } - - /** - * @return the Network Protocol Type - */ - inline VehicleDataSourceProtocol - getChannelProtocol() const - { - return mNetworkProtocol; - } - /** * @return the DDS Domain ID */ @@ -141,16 +123,15 @@ class IDDSPublisher : public DataWriterListener static uint32_t generateChannelID() { - static std::atomic channelID( INVALID_DATA_SOURCE_ID ); + static std::atomic channelID{ 0 }; return ++channelID; } SensorSourceType mType; - VehicleDataSourceID mID; + uint32_t mID; DDSDomainID mDDSDomainID; DDSTopicName mDDSTopic; DDSWriterName mDDSwriterName; - VehicleDataSourceProtocol mNetworkProtocol; }; using DDSPublisherPtr = std::unique_ptr; } // namespace VehicleNetwork diff --git a/src/vehiclenetwork/include/dds/IDDSSubscriber.h b/src/vehiclenetwork/include/dds/IDDSSubscriber.h index 8f2b23df..89f9b22c 100644 --- a/src/vehiclenetwork/include/dds/IDDSSubscriber.h +++ b/src/vehiclenetwork/include/dds/IDDSSubscriber.h @@ -66,24 +66,6 @@ class IDDSSubscriber : public ThreadListeners, public DataRe */ virtual bool isAlive() = 0; - /** - * @return the unique ID of the channel. - */ - inline VehicleDataSourceID - getChannelID() const - { - return mID; - } - - /** - * @return the Network Protocol Type - */ - inline VehicleDataSourceProtocol - getChannelProtocol() const - { - return mNetworkProtocol; - } - /** * @return the DDS Domain ID */ @@ -137,16 +119,15 @@ class IDDSSubscriber : public ThreadListeners, public DataRe static uint32_t generateChannelID() { - static std::atomic channelID( INVALID_DATA_SOURCE_ID ); + static std::atomic channelID{ 0 }; return ++channelID; } SensorSourceType mType; - VehicleDataSourceID mID; + uint32_t mID; DDSDomainID mDDSDomainID; DDSTopicName mDDSTopic; DDSWriterName mDDSReaderName; - VehicleDataSourceProtocol mNetworkProtocol; std::string mTemporaryCacheLocation; DDSTopicQoS mTopicQoS; }; diff --git a/src/vehiclenetwork/src/CameraDataPublisher.cpp b/src/vehiclenetwork/src/CameraDataPublisher.cpp index c12417d1..c0ccf91b 100644 --- a/src/vehiclenetwork/src/CameraDataPublisher.cpp +++ b/src/vehiclenetwork/src/CameraDataPublisher.cpp @@ -17,7 +17,6 @@ namespace VehicleNetwork { CameraDataPublisher::CameraDataPublisher() { - mNetworkProtocol = VehicleDataSourceProtocol::DDS; mID = generateChannelID(); } diff --git a/src/vehiclenetwork/src/CameraDataSubscriber.cpp b/src/vehiclenetwork/src/CameraDataSubscriber.cpp index b0007c08..e4298170 100644 --- a/src/vehiclenetwork/src/CameraDataSubscriber.cpp +++ b/src/vehiclenetwork/src/CameraDataSubscriber.cpp @@ -18,7 +18,6 @@ namespace VehicleNetwork { CameraDataSubscriber::CameraDataSubscriber() { - mNetworkProtocol = VehicleDataSourceProtocol::DDS; mID = generateChannelID(); } diff --git a/src/vehiclenetwork/test/CANDataSourceTest.cpp b/src/vehiclenetwork/test/CANDataSourceTest.cpp deleted file mode 100644 index afda7302..00000000 --- a/src/vehiclenetwork/test/CANDataSourceTest.cpp +++ /dev/null @@ -1,402 +0,0 @@ - -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#include "businterfaces/CANDataSource.h" -#include "WaitUntil.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Aws::IoTFleetWise::TestingSupport; -using namespace Aws::IoTFleetWise::VehicleNetwork; - -static void -cleanUp( int socketFD ) -{ - close( socketFD ); -} - -static int -setup( bool fd = false ) -{ - // Setup a socket - std::string socketCANIFName( "vcan0" ); - struct sockaddr_can interfaceAddress; - struct ifreq interfaceRequest; - - int type = SOCK_RAW | SOCK_NONBLOCK; - int socketFD = socket( PF_CAN, type, CAN_RAW ); - if ( socketFD < 0 ) - { - return -1; - } - if ( fd ) - { - int canfd_on = 1; - if ( setsockopt( socketFD, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof( canfd_on ) ) != 0 ) - { - return -1; - } - } - - if ( socketCANIFName.size() >= sizeof( interfaceRequest.ifr_name ) ) - { - cleanUp( socketFD ); - return -1; - } - (void)strncpy( interfaceRequest.ifr_name, socketCANIFName.c_str(), sizeof( interfaceRequest.ifr_name ) - 1U ); - - if ( ioctl( socketFD, SIOCGIFINDEX, &interfaceRequest ) ) - { - cleanUp( socketFD ); - return -1; - } - - memset( &interfaceAddress, 0, sizeof( interfaceAddress ) ); - interfaceAddress.can_family = AF_CAN; - interfaceAddress.can_ifindex = interfaceRequest.ifr_ifindex; - - if ( bind( socketFD, (struct sockaddr *)&interfaceAddress, sizeof( interfaceAddress ) ) < 0 ) - { - cleanUp( socketFD ); - return -1; - } - - return socketFD; -} - -class LocalDataSourceEventListener : public VehicleDataSourceListener -{ -public: - LocalDataSourceEventListener() - : gotConnectCallback( false ) - , gotDisConnectCallback( false ) - { - } - - inline void - onVehicleDataSourceConnected( const VehicleDataSourceID &Id ) - { - static_cast( Id ); // Ignore parameter - gotConnectCallback = true; - } - - inline void - onVehicleDataSourceDisconnected( const VehicleDataSourceID &Id ) - { - static_cast( Id ); // Ignore parameter - gotDisConnectCallback = true; - } - - bool gotConnectCallback; - bool gotDisConnectCallback; -}; - -static bool -sendTestMessage( int socketFD ) -{ - struct can_frame frame = {}; - frame.can_id = 0x123; - frame.can_dlc = 4; - for ( uint8_t i = 0; i < 3; ++i ) - { - frame.data[i] = i; - } - ssize_t bytesWritten = write( socketFD, &frame, sizeof( struct can_frame ) ); - EXPECT_EQ( bytesWritten, sizeof( struct can_frame ) ); - return true; -} - -static bool -sendTestFDMessage( int socketFD ) -{ - struct canfd_frame frame = {}; - frame.can_id = 0x123; - frame.len = 64; - for ( uint8_t i = 0; i < 64; ++i ) - { - frame.data[i] = i; - } - ssize_t bytesWritten = write( socketFD, &frame, sizeof( struct canfd_frame ) ); - EXPECT_EQ( bytesWritten, sizeof( struct canfd_frame ) ); - return true; -} - -static bool -sendTestMessageExtendedID( int socketFD ) -{ - struct can_frame frame = {}; - frame.can_id = 0x123 | CAN_EFF_FLAG; - - frame.can_dlc = 4; - for ( uint8_t i = 0; i < 3; ++i ) - { - frame.data[i] = i; - } - ssize_t bytesWritten = write( socketFD, &frame, sizeof( struct can_frame ) ); - EXPECT_EQ( bytesWritten, sizeof( struct can_frame ) ); - return true; -} - -class CANDataSourceTest : public ::testing::Test -{ -public: - int socketFD; - -protected: - void - SetUp() override - { - socketFD = setup(); - if ( socketFD == -1 ) - { - GTEST_SKIP() << "Skipping test fixture due to unavailability of socket"; - } - } - - void - TearDown() override - { - cleanUp( socketFD ); - } -}; - -TEST_F( CANDataSourceTest, testAquireDataFromNetwork ) -{ - LocalDataSourceEventListener listener; - ASSERT_TRUE( socketFD != -1 ); - - static_cast( socketFD >= 0 ); - VehicleDataSourceConfig sourceConfig; - sourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - sourceConfig.transportProperties.emplace( "protocolName", "CAN" ); - sourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - sourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector sourceConfigs = { sourceConfig }; - CANDataSource dataSource; - ASSERT_TRUE( dataSource.init( sourceConfigs ) ); - ASSERT_TRUE( dataSource.subscribeListener( &listener ) ); - - ASSERT_TRUE( dataSource.connect() ); - ASSERT_TRUE( listener.gotConnectCallback ); - ASSERT_TRUE( dataSource.isAlive() ); - // Set the Channel in an active acquire state - dataSource.resumeDataAcquisition(); - ASSERT_EQ( dataSource.getVehicleDataSourceIfName(), "vcan0" ); - ASSERT_EQ( dataSource.getVehicleDataSourceProtocol(), VehicleDataSourceProtocol::RAW_SOCKET ); - ASSERT_EQ( dataSource.getVehicleDataSourceType(), VehicleDataSourceType::CAN_SOURCE ); - VehicleDataMessage msg; - WAIT_ASSERT_TRUE( sendTestMessage( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - ASSERT_TRUE( dataSource.disconnect() ); - ASSERT_TRUE( dataSource.unSubscribeListener( &listener ) ); - ASSERT_TRUE( listener.gotDisConnectCallback ); -} - -TEST_F( CANDataSourceTest, testDoNotAcquireDataFromNetwork ) -{ - LocalDataSourceEventListener listener; - int socketFD = setup(); - ASSERT_TRUE( socketFD != -1 ); - - static_cast( socketFD >= 0 ); - VehicleDataSourceConfig sourceConfig; - sourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - sourceConfig.transportProperties.emplace( "protocolName", "CAN" ); - sourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - sourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector sourceConfigs = { sourceConfig }; - CANDataSource dataSource; - ASSERT_TRUE( dataSource.init( sourceConfigs ) ); - ASSERT_TRUE( dataSource.subscribeListener( &listener ) ); - - ASSERT_TRUE( dataSource.connect() ); - ASSERT_TRUE( listener.gotConnectCallback ); - ASSERT_TRUE( dataSource.isAlive() ); - // The channel is not acquiring data from the network by default - // We should test that although data is available in the socket, - // the channel buffer must be empty - ASSERT_EQ( dataSource.getVehicleDataSourceIfName(), "vcan0" ); - ASSERT_EQ( dataSource.getVehicleDataSourceProtocol(), VehicleDataSourceProtocol::RAW_SOCKET ); - ASSERT_EQ( dataSource.getVehicleDataSourceType(), VehicleDataSourceType::CAN_SOURCE ); - VehicleDataMessage msg; - // No messages should be in the buffer - DELAY_ASSERT_FALSE( sendTestMessage( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - ASSERT_TRUE( dataSource.disconnect() ); // Here the frame will be read from the socket - ASSERT_TRUE( dataSource.unSubscribeListener( &listener ) ); - ASSERT_TRUE( listener.gotDisConnectCallback ); -} - -TEST_F( CANDataSourceTest, testNetworkDataAquisitionStateChange ) -{ - // In this test, we want to start the channel with the default settings i.e. sleep mode, - // then activate data acquisition and check that the channel buffer effectively has a message, - // then interrupt the consumption and make sure that the channel is in sleep mode. - LocalDataSourceEventListener listener; - ASSERT_TRUE( socketFD != -1 ); - - static_cast( socketFD >= 0 ); - VehicleDataSourceConfig sourceConfig; - sourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - sourceConfig.transportProperties.emplace( "protocolName", "CAN" ); - sourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - sourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector sourceConfigs = { sourceConfig }; - CANDataSource dataSource; - ASSERT_TRUE( dataSource.init( sourceConfigs ) ); - ASSERT_TRUE( dataSource.subscribeListener( &listener ) ); - - ASSERT_TRUE( dataSource.connect() ); - ASSERT_TRUE( listener.gotConnectCallback ); - ASSERT_TRUE( dataSource.isAlive() ); - // The channel is not acquiring data from the network by default - // We should test that although data is available in the socket, - // the channel buffer must be empty - ASSERT_EQ( dataSource.getVehicleDataSourceIfName(), "vcan0" ); - ASSERT_EQ( dataSource.getVehicleDataSourceProtocol(), VehicleDataSourceProtocol::RAW_SOCKET ); - ASSERT_EQ( dataSource.getVehicleDataSourceType(), VehicleDataSourceType::CAN_SOURCE ); - // Send a message on the bus. - VehicleDataMessage msg; - // No messages should be in the buffer - DELAY_ASSERT_FALSE( sendTestMessage( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - - // Activate consumption on the bus and make sure the channel buffer has items. - dataSource.resumeDataAcquisition(); - - // Send a message on the bus. - // 1 message should be in the buffer as the channel is active. - WAIT_ASSERT_TRUE( sendTestMessage( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - - // Interrupt data acquisition and make sure that the channel now does not consume data - // anymore. - dataSource.suspendDataAcquisition(); - // Send a message on the bus. - // No messages should be in the buffer - DELAY_ASSERT_FALSE( sendTestMessage( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - - ASSERT_TRUE( dataSource.disconnect() ); - ASSERT_TRUE( dataSource.unSubscribeListener( &listener ) ); - ASSERT_TRUE( listener.gotDisConnectCallback ); -} - -TEST_F( CANDataSourceTest, testSourceIdsAreUnique ) -{ - ASSERT_TRUE( socketFD != -1 ); - static_cast( socketFD >= 0 ); - - constexpr auto NUM_SOURCES = 5; - std::unordered_set sourceIDs; - for ( auto i = 0; i < NUM_SOURCES; ++i ) - { - CANDataSource source; - sourceIDs.insert( source.getVehicleDataSourceID() ); - } - ASSERT_EQ( NUM_SOURCES, sourceIDs.size() ); -} - -TEST_F( CANDataSourceTest, testCanFDSocketMode ) -{ - LocalDataSourceEventListener listener; - int socketFD = setup( true ); - ASSERT_TRUE( socketFD != -1 ); - - static_cast( socketFD >= 0 ); - VehicleDataSourceConfig sourceConfig; - sourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - sourceConfig.transportProperties.emplace( "protocolName", "CAN-FD" ); - sourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - sourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector sourceConfigs = { sourceConfig }; - CANDataSource dataSource; - ASSERT_TRUE( dataSource.init( sourceConfigs ) ); - ASSERT_TRUE( dataSource.subscribeListener( &listener ) ); - - ASSERT_TRUE( dataSource.connect() ); - ASSERT_TRUE( listener.gotConnectCallback ); - ASSERT_TRUE( dataSource.isAlive() ); - // Set the Channel in an active acquire state - dataSource.resumeDataAcquisition(); - - ASSERT_EQ( dataSource.getVehicleDataSourceIfName(), "vcan0" ); - ASSERT_EQ( dataSource.getVehicleDataSourceProtocol(), VehicleDataSourceProtocol::RAW_SOCKET ); - ASSERT_EQ( dataSource.getVehicleDataSourceType(), VehicleDataSourceType::CAN_SOURCE ); - - // Send a CAN-FD message on the bus. - VehicleDataMessage msg; - WAIT_ASSERT_TRUE( sendTestFDMessage( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - ASSERT_TRUE( dataSource.disconnect() ); - ASSERT_TRUE( dataSource.unSubscribeListener( &listener ) ); - ASSERT_TRUE( listener.gotDisConnectCallback ); -} - -TEST_F( CANDataSourceTest, testSendRegularID ) -{ - LocalDataSourceEventListener listener; - ASSERT_TRUE( socketFD != -1 ); - - static_cast( socketFD >= 0 ); - VehicleDataSourceConfig sourceConfig; - sourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - sourceConfig.transportProperties.emplace( "protocolName", "CAN" ); - sourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - sourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector sourceConfigs = { sourceConfig }; - CANDataSource dataSource; - ASSERT_TRUE( dataSource.init( sourceConfigs ) ); - ASSERT_TRUE( dataSource.subscribeListener( &listener ) ); - - ASSERT_TRUE( dataSource.connect() ); - ASSERT_TRUE( listener.gotConnectCallback ); - ASSERT_TRUE( dataSource.isAlive() ); - // Set the Channel in an active acquire state - dataSource.resumeDataAcquisition(); - ASSERT_EQ( dataSource.getVehicleDataSourceIfName(), "vcan0" ); - ASSERT_EQ( dataSource.getVehicleDataSourceProtocol(), VehicleDataSourceProtocol::RAW_SOCKET ); - ASSERT_EQ( dataSource.getVehicleDataSourceType(), VehicleDataSourceType::CAN_SOURCE ); - VehicleDataMessage msg; - WAIT_ASSERT_TRUE( sendTestMessage( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - ASSERT_EQ( msg.getMessageID(), 0x123 ); - ASSERT_TRUE( dataSource.disconnect() ); - ASSERT_TRUE( dataSource.unSubscribeListener( &listener ) ); - ASSERT_TRUE( listener.gotDisConnectCallback ); -} - -TEST_F( CANDataSourceTest, testExtractExtendedID ) -{ - LocalDataSourceEventListener listener; - ASSERT_TRUE( socketFD != -1 ); - - static_cast( socketFD >= 0 ); - VehicleDataSourceConfig sourceConfig; - sourceConfig.transportProperties.emplace( "interfaceName", "vcan0" ); - sourceConfig.transportProperties.emplace( "protocolName", "CAN" ); - sourceConfig.transportProperties.emplace( "threadIdleTimeMs", "100" ); - sourceConfig.maxNumberOfVehicleDataMessages = 1000; - std::vector sourceConfigs = { sourceConfig }; - CANDataSource dataSource; - ASSERT_TRUE( dataSource.init( sourceConfigs ) ); - ASSERT_TRUE( dataSource.subscribeListener( &listener ) ); - - ASSERT_TRUE( dataSource.connect() ); - ASSERT_TRUE( listener.gotConnectCallback ); - ASSERT_TRUE( dataSource.isAlive() ); - // Set the Channel in an active acquire state - dataSource.resumeDataAcquisition(); - ASSERT_EQ( dataSource.getVehicleDataSourceIfName(), "vcan0" ); - ASSERT_EQ( dataSource.getVehicleDataSourceProtocol(), VehicleDataSourceProtocol::RAW_SOCKET ); - ASSERT_EQ( dataSource.getVehicleDataSourceType(), VehicleDataSourceType::CAN_SOURCE ); - VehicleDataMessage msg; - WAIT_ASSERT_TRUE( sendTestMessageExtendedID( socketFD ) && dataSource.getBuffer()->pop( msg ) ); - ASSERT_EQ( msg.getMessageID(), 0x80000123 ); - ASSERT_TRUE( dataSource.disconnect() ); - ASSERT_TRUE( dataSource.unSubscribeListener( &listener ) ); - ASSERT_TRUE( listener.gotDisConnectCallback ); -} diff --git a/src/vehiclenetwork/test/VehicleDataMessageTest.cpp b/src/vehiclenetwork/test/VehicleDataMessageTest.cpp deleted file mode 100644 index 3098d565..00000000 --- a/src/vehiclenetwork/test/VehicleDataMessageTest.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#include "datatypes/VehicleDataMessage.h" -#include - -using namespace Aws::IoTFleetWise::VehicleNetwork; -using namespace Aws::IoTFleetWise::Platform::Linux; - -TEST( VehicleDataMessageTest, SetupTest ) -{ - // Test for a normal message. - // The message should be set up correctly. - VehicleDataMessage message; - - std::uint32_t id = 0xFFU; - const std::vector rawData = { 0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U }; - const std::vector syntheticData = { 0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U }; - Timestamp timestamp = static_cast( 12345678 ); - message.setup( id, rawData, syntheticData, timestamp ); - ASSERT_EQ( message.getMessageID(), id ); - ASSERT_TRUE( message.isValid() ); - // Raw Data - for ( size_t i = 0; i < rawData.size(); i++ ) - { - ASSERT_EQ( rawData[i], ( message.getRawData() )[i] ); - } - - // Synthetic Data - for ( size_t i = 0; i < syntheticData.size(); i++ ) - { - ASSERT_EQ( boost::any_cast( syntheticData[i] ), boost::any_cast( message.getSyntheticData()[i] ) ); - } - // Timestamp - ASSERT_EQ( message.getReceptionTimestamp(), timestamp ); -} diff --git a/tools/cfn-templates/fwdemo.yml b/tools/cfn-templates/fwdemo.yml index 47b62e7c..214d8a7a 100644 --- a/tools/cfn-templates/fwdemo.yml +++ b/tools/cfn-templates/fwdemo.yml @@ -87,139 +87,153 @@ Resources: Properties: Roles: - !Ref Ec2ServiceRole - Ec2Instance: - Type: AWS::EC2::Instance - CreationPolicy: - ResourceSignal: - Count: 1 - Timeout: PT30M + Ec2LaunchTemplate: + Type: AWS::EC2::LaunchTemplate Properties: - ImageId: !FindInMap [AMIRegionMap, !Ref "AWS::Region", AMIID] - KeyName: !If [KeyPairSpecifiedCondition, !Ref Ec2KeyPair, !Ref "AWS::NoValue"] - InstanceType: !FindInMap [FleetSizeEc2InstanceTypeMap, !Ref "FleetSize", InstanceType] - IamInstanceProfile: !Ref Ec2InstanceProfile - SecurityGroupIds: !Split [",", !GetAtt Ec2SecurityGroup.GroupId] - Tags: - - Key: Name - Value: !Sub ${AWS::StackName}-Ec2-Instance - UserData: - Fn::Base64: !Sub | - #!/bin/bash - set -euo pipefail + LaunchTemplateName: !Sub ${AWS::StackName}-Ec2LaunchTemplate + LaunchTemplateData: + ImageId: !FindInMap [AMIRegionMap, !Ref "AWS::Region", AMIID] + KeyName: !If [KeyPairSpecifiedCondition, !Ref Ec2KeyPair, !Ref "AWS::NoValue"] + InstanceType: !FindInMap [FleetSizeEc2InstanceTypeMap, !Ref "FleetSize", InstanceType] + IamInstanceProfile: + Name: !Ref Ec2InstanceProfile + SecurityGroupIds: !Split [",", !GetAtt Ec2SecurityGroup.GroupId] + TagSpecifications: + - ResourceType: instance + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Ec2-Instance + MetadataOptions: + HttpEndpoint: "enabled" + HttpTokens: "required" + UserData: + Fn::Base64: !Sub | + #!/bin/bash + set -euo pipefail - # Wait for any existing package install to finish - i=0 - while true; do - if sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; then - i=0 - else - i=`expr $i + 1` - if expr $i \>= 10 > /dev/null; then - break + # Wait for any existing package install to finish + i=0 + while true; do + if sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; then + i=0 + else + i=`expr $i + 1` + if expr $i \>= 10 > /dev/null; then + break + fi fi - fi - sleep 1 - done + sleep 1 + done - # Upgrade system and reboot if required - apt update && apt upgrade -y - if [ -f /var/run/reboot-required ]; then - # Delete the UserData info file so that we run again after reboot - rm -f /var/lib/cloud/instances/*/sem/config_scripts_user - reboot - exit - fi + # Upgrade system and reboot if required + apt update && apt upgrade -y + if [ -f /var/run/reboot-required ]; then + # Delete the UserData info file so that we run again after reboot + rm -f /var/lib/cloud/instances/*/sem/config_scripts_user + reboot + exit + fi - # Install helper scripts: - apt update && apt install -y python3-setuptools - mkdir -p /opt/aws/bin - wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - python3 -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-py3-latest.tar.gz - rm aws-cfn-bootstrap-py3-latest.tar.gz + # Install helper scripts: + apt update && apt install -y python3-setuptools + mkdir -p /opt/aws/bin + wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz + python3 -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-py3-latest.tar.gz + rm aws-cfn-bootstrap-py3-latest.tar.gz - # On error, signal back to cfn: - error_handler() { - /opt/aws/bin/cfn-signal --success false --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} - } - trap error_handler ERR + # On error, signal back to cfn: + error_handler() { + /opt/aws/bin/cfn-signal --success false --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} + } + trap error_handler ERR - # Increase pid_max: - echo 1048576 > /proc/sys/kernel/pid_max - # Disable syslog: - systemctl stop syslog.socket rsyslog.service - # Remove journald rate limiting and set max size: - printf "RateLimitBurst=0\nSystemMaxUse=1G\n" >> /etc/systemd/journald.conf + # Increase pid_max: + echo 1048576 > /proc/sys/kernel/pid_max + # Disable syslog: + systemctl stop syslog.socket rsyslog.service + # Remove journald rate limiting and set max size: + printf "RateLimitBurst=0\nSystemMaxUse=1G\n" >> /etc/systemd/journald.conf - # Install packages - apt update && apt install -y wget ec2-instance-connect htop jq unzip + # Install packages + apt update && apt install -y wget ec2-instance-connect htop jq unzip - # Install AWS CLI: - curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" - unzip -q awscliv2.zip - ./aws/install - rm awscliv2.zip + # Install AWS CLI: + curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" + unzip -q awscliv2.zip + ./aws/install + rm awscliv2.zip - # Download agent: - cd /home/ubuntu - if echo ${BinaryUrl} | grep -q 's3://'; then - sudo -u ubuntu aws s3 cp ${BinaryUrl} aws-iot-fleetwise-edge.tar.gz - else - sudo -u ubuntu wget ${BinaryUrl} -O aws-iot-fleetwise-edge.tar.gz - fi - sudo -u ubuntu mkdir dist && cd dist - sudo -u ubuntu tar -zxf ../aws-iot-fleetwise-edge.tar.gz - sudo -u ubuntu mkdir -p build/src/executionmanagement - sudo -u ubuntu mv aws-iot-fleetwise-edge build/src/executionmanagement + # Download agent: + cd /home/ubuntu + if echo ${BinaryUrl} | grep -q 's3://'; then + sudo -u ubuntu aws s3 cp ${BinaryUrl} aws-iot-fleetwise-edge.tar.gz + else + sudo -u ubuntu wget ${BinaryUrl} -O aws-iot-fleetwise-edge.tar.gz + fi + sudo -u ubuntu mkdir dist && cd dist + sudo -u ubuntu tar -zxf ../aws-iot-fleetwise-edge.tar.gz + sudo -u ubuntu mkdir -p build/src/executionmanagement + sudo -u ubuntu mv aws-iot-fleetwise-edge build/src/executionmanagement - # Install SocketCAN modules: - ./tools/install-socketcan.sh --bus-count ${FleetSize} + # Install SocketCAN modules: + ./tools/install-socketcan.sh --bus-count ${FleetSize} - # Install CAN Simulator - ./tools/install-cansim.sh --bus-count ${FleetSize} + # Install CAN Simulator + ./tools/install-cansim.sh --bus-count ${FleetSize} - # Install FWE credentials and config file - mkdir /etc/aws-iot-fleetwise - mkdir /var/aws-iot-fleetwise - echo -n "${IoTThing.certificatePem}" > /etc/aws-iot-fleetwise/certificate.pem - echo -n "${IoTThing.privateKey}" > /etc/aws-iot-fleetwise/private-key.key - if ((${FleetSize}==1)); then - echo "Configuring ${AWS::StackName}..." - ./tools/configure-fwe.sh \ - --input-config-file "configuration/static-config.json" \ - --output-config-file "/etc/aws-iot-fleetwise/config-0.json" \ - --vehicle-name "${AWS::StackName}" \ - --endpoint-url "${IoTThing.iotEndpoint}" \ - --topic-prefix '${IoTMqttTopicPrefix}' \ - --can-bus0 "vcan0" - else - BATCH_SIZE=$((`nproc`*4)) - for ((i=0; i<${FleetSize}; i+=${!BATCH_SIZE})); do - for ((j=0; j<${!BATCH_SIZE} && i+j<${FleetSize}; j++)); do - # This output group is run in a background process. Note that stderr is redirected to stream 3 and back, - # to print stderr from the output group, but not info about the background process. - { \ - echo "Configuring ${AWS::StackName}-$((i+j))..."; \ - mkdir /var/aws-iot-fleetwise/fwe$((i+j)); \ - ./tools/configure-fwe.sh \ - --input-config-file "configuration/static-config.json" \ - --output-config-file "/etc/aws-iot-fleetwise/config-$((i+j)).json" \ - --vehicle-name "${AWS::StackName}-$((i+j))" \ - --endpoint-url "${IoTThing.iotEndpoint}" \ - --topic-prefix '${IoTMqttTopicPrefix}' \ - --can-bus0 "vcan$((i+j))" \ - --persistency-path "/var/aws-iot-fleetwise/fwe$((i+j))"; \ - 2>&3 &} 3>&2 2>/dev/null + # Install FWE credentials and config file + mkdir /etc/aws-iot-fleetwise + mkdir /var/aws-iot-fleetwise + echo -n "${IoTThing.certificatePem}" > /etc/aws-iot-fleetwise/certificate.pem + echo -n "${IoTThing.privateKey}" > /etc/aws-iot-fleetwise/private-key.key + if ((${FleetSize}==1)); then + echo "Configuring ${AWS::StackName}..." + ./tools/configure-fwe.sh \ + --input-config-file "configuration/static-config.json" \ + --output-config-file "/etc/aws-iot-fleetwise/config-0.json" \ + --vehicle-name "${AWS::StackName}" \ + --endpoint-url "${IoTThing.iotEndpoint}" \ + --topic-prefix '${IoTMqttTopicPrefix}' \ + --can-bus0 "vcan0" + else + BATCH_SIZE=$((`nproc`*4)) + for ((i=0; i<${FleetSize}; i+=${!BATCH_SIZE})); do + for ((j=0; j<${!BATCH_SIZE} && i+j<${FleetSize}; j++)); do + # This output group is run in a background process. Note that stderr is redirected to stream 3 and back, + # to print stderr from the output group, but not info about the background process. + { \ + echo "Configuring ${AWS::StackName}-$((i+j))..."; \ + mkdir /var/aws-iot-fleetwise/fwe$((i+j)); \ + ./tools/configure-fwe.sh \ + --input-config-file "configuration/static-config.json" \ + --output-config-file "/etc/aws-iot-fleetwise/config-$((i+j)).json" \ + --vehicle-name "${AWS::StackName}-$((i+j))" \ + --endpoint-url "${IoTThing.iotEndpoint}" \ + --topic-prefix '${IoTMqttTopicPrefix}' \ + --can-bus0 "vcan$((i+j))" \ + --persistency-path "/var/aws-iot-fleetwise/fwe$((i+j))"; \ + 2>&3 &} 3>&2 2>/dev/null + done + # Wait for all background processes to finish + wait done - # Wait for all background processes to finish - wait - done - fi + fi - # Install FWE - ./tools/install-fwe.sh + # Install FWE + ./tools/install-fwe.sh - # Signal init complete: - /opt/aws/bin/cfn-signal --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} + # Signal init complete: + /opt/aws/bin/cfn-signal --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} + Ec2Instance: + Type: AWS::EC2::Instance + CreationPolicy: + ResourceSignal: + Count: 1 + Timeout: PT30M + Properties: + LaunchTemplate: + LaunchTemplateId: !Ref Ec2LaunchTemplate + Version: !GetAtt Ec2LaunchTemplate.LatestVersionNumber IoTThing: Type: Custom::IoTThing Properties: diff --git a/tools/cfn-templates/fwdev.yml b/tools/cfn-templates/fwdev.yml index c1f2fb42..f8589c0b 100644 --- a/tools/cfn-templates/fwdev.yml +++ b/tools/cfn-templates/fwdev.yml @@ -72,79 +72,93 @@ Resources: Properties: Roles: - !Ref Ec2ServiceRole - Ec2Instance: - Type: AWS::EC2::Instance - CreationPolicy: - ResourceSignal: - Count: 1 - Timeout: PT15M + Ec2LaunchTemplate: + Type: AWS::EC2::LaunchTemplate Properties: - ImageId: !FindInMap [AMIRegionMap, !Ref "AWS::Region", AMIID] - KeyName: !Ref Ec2KeyPair - InstanceType: !Ref Ec2InstanceType - IamInstanceProfile: !Ref Ec2InstanceProfile - SecurityGroupIds: !Split [",", !GetAtt Ec2SecurityGroup.GroupId] - Tags: - - Key: Name - Value: !Sub ${AWS::StackName}-Ec2-Instance - UserData: - Fn::Base64: !Sub | - #!/bin/bash - set -euo pipefail + LaunchTemplateName: !Sub ${AWS::StackName}-EC2-LaunchTemplate + LaunchTemplateData: + ImageId: !FindInMap [AMIRegionMap, !Ref "AWS::Region", AMIID] + KeyName: !Ref Ec2KeyPair + InstanceType: !Ref Ec2InstanceType + IamInstanceProfile: + Name: !Ref Ec2InstanceProfile + SecurityGroupIds: !Split [",", !GetAtt Ec2SecurityGroup.GroupId] + TagSpecifications: + - ResourceType: instance + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Ec2-Instance + MetadataOptions: + HttpEndpoint: "enabled" + HttpTokens: "required" + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: !Ref Ec2VolumeSize + DeleteOnTermination: true + Encrypted: true + UserData: + Fn::Base64: !Sub | + #!/bin/bash + set -euo pipefail - # Wait for any existing package install to finish - i=0 - while true; do - if sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; then - i=0 - else - i=`expr $i + 1` - if expr $i \>= 10 > /dev/null; then - break + # Wait for any existing package install to finish + i=0 + while true; do + if sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; then + i=0 + else + i=`expr $i + 1` + if expr $i \>= 10 > /dev/null; then + break + fi fi - fi - sleep 1 - done + sleep 1 + done - # Upgrade system and reboot if required - apt update && apt upgrade -y - if [ -f /var/run/reboot-required ]; then - # Delete the UserData info file so that we run again after reboot - rm -f /var/lib/cloud/instances/*/sem/config_scripts_user - reboot - exit - fi + # Upgrade system and reboot if required + apt update && apt upgrade -y + if [ -f /var/run/reboot-required ]; then + # Delete the UserData info file so that we run again after reboot + rm -f /var/lib/cloud/instances/*/sem/config_scripts_user + reboot + exit + fi - # Install helper scripts: - apt update && apt install -y python3-setuptools - mkdir -p /opt/aws/bin - wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - python3 -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-py3-latest.tar.gz - rm aws-cfn-bootstrap-py3-latest.tar.gz + # Install helper scripts: + apt update && apt install -y python3-setuptools + mkdir -p /opt/aws/bin + wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz + python3 -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-py3-latest.tar.gz + rm aws-cfn-bootstrap-py3-latest.tar.gz - # On error, signal back to cfn: - error_handler() { - /opt/aws/bin/cfn-signal --success false --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} - } - trap error_handler ERR + # On error, signal back to cfn: + error_handler() { + /opt/aws/bin/cfn-signal --success false --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} + } + trap error_handler ERR - # Install packages - apt update && apt install -y ec2-instance-connect htop jq unzip zip + # Install packages + apt update && apt install -y ec2-instance-connect htop jq unzip zip - # Install AWS CLI: - curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" - unzip -q awscliv2.zip - ./aws/install - rm awscliv2.zip + # Install AWS CLI: + curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" + unzip -q awscliv2.zip + ./aws/install + rm awscliv2.zip - # Signal init complete: - /opt/aws/bin/cfn-signal --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} - BlockDeviceMappings: - - DeviceName: /dev/sda1 - Ebs: - VolumeSize: !Ref Ec2VolumeSize - DeleteOnTermination: "true" - Encrypted: "true" + # Signal init complete: + /opt/aws/bin/cfn-signal --stack ${AWS::StackName} --resource Ec2Instance --region ${AWS::Region} + Ec2Instance: + Type: AWS::EC2::Instance + CreationPolicy: + ResourceSignal: + Count: 1 + Timeout: PT15M + Properties: + LaunchTemplate: + LaunchTemplateId: !Ref Ec2LaunchTemplate + Version: !GetAtt Ec2LaunchTemplate.LatestVersionNumber Mappings: # Ubuntu 20.04 arm64 AMIs AMIRegionMap: diff --git a/tools/cfn-templates/fwremoteprofiler.yml b/tools/cfn-templates/fwremoteprofiler.yml new file mode 100644 index 00000000..70de49ac --- /dev/null +++ b/tools/cfn-templates/fwremoteprofiler.yml @@ -0,0 +1,112 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +AWSTemplateFormatVersion: "2010-09-09" + +Resources: + RemoteProfilerCloudwatchRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: iot.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: !Sub ${AWS::StackName}-RemoteProfilerCloudwatchRole + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - "cloudwatch:PutMetricData" + - "logs:CreateLogStream" + - "logs:DescribeLogStreams" + - "logs:PutLogEvents" + Resource: + - "*" + RemoteProfilerMetricsRule: + Type: AWS::IoT::TopicRule + Properties: + RuleName: AwsIotFleetwiseMetricsUpload + TopicRulePayload: + Actions: + - CloudwatchMetric: + MetricName: "${metric1.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric1.unit}" + MetricValue: "${metric1.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric2.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric2.unit}" + MetricValue: "${metric2.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric3.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric3.unit}" + MetricValue: "${metric3.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric4.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric4.unit}" + MetricValue: "${metric4.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric5.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric5.unit}" + MetricValue: "${metric5.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric6.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric6.unit}" + MetricValue: "${metric6.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric7.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric7.unit}" + MetricValue: "${metric7.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric8.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric8.unit}" + MetricValue: "${metric8.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric9.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric9.unit}" + MetricValue: "${metric9.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + - CloudwatchMetric: + MetricName: "${metric10.name}" + MetricNamespace: AWSIotFleetWiseEdge + MetricUnit: "${metric10.unit}" + MetricValue: "${metric10.value}" + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + Description: Used by the Remote Profiler of the AWS IoT Fleetwise Edge Client see METRICS.md + Sql: SELECT * FROM 'aws-iot-fleetwise-metrics-upload' + RemoteProfilerLogRule: + Type: AWS::IoT::TopicRule + Properties: + RuleName: AwsIotFleetwiseLogsUpload + TopicRulePayload: + Actions: + - CloudwatchLogs: + LogGroupName: AWSIotFleetWiseEdge + RoleArn: !GetAtt RemoteProfilerCloudwatchRole.Arn + Description: Used by the Remote Profiler of the AWS IoT Fleetwise Edge Client see METRICS.md + Sql: SELECT * FROM 'aws-iot-fleetwise-logging-upload' + RemoteProfilerLogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: AWSIotFleetWiseEdge diff --git a/tools/install-deps-versions.sh b/tools/install-deps-versions.sh index b3ff82e8..e3887dcc 100755 --- a/tools/install-deps-versions.sh +++ b/tools/install-deps-versions.sh @@ -1,10 +1,10 @@ #!/bin/bash -export VERSION_JSON_CPP="1.7.4" +export VERSION_JSON_CPP="1.9.5" export VERSION_PROTOBUF="3.21.7" export VERSION_PROTOBUF_RELEASE="v21.7" export VERSION_CURL="7.86.0" export VERSION_CURL_RELEASE="curl-7_86_0" -export VERSION_AWS_SDK_CPP="1.9.253" +export VERSION_AWS_SDK_CPP="1.11.46" export VERSION_TINYXML2="6.0.0" export VERSION_FOONATHAN_MEMORY_VENDOR="v1.1.0" export VERSION_FAST_CDR="v1.0.21"