diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bdc27f4..b1c0a602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,37 @@ # Change Log +## v0.1.1 (January 25, 2022) +https://s3.console.aws.amazon.com/s3/object/aws-iot-fleetwise?prefix=v0.1.1/aws-iot-fleetwise-edge.zip + +Features: +* No new features. + +Bugfixes/Improvements: +* Edge agent source code: + * Fixed bug in `PayloadManager.cpp` that caused corruption of the persisted data. + * Improved the documentation of the Protobuf schemas. + * Added retry with exponential back-off for making initial connection to AWS IoT Core. + * Added retry for uploading previously-collected persistent data. +* Edge agent developer guide and associated scripts: + * Fixed bug in `install-socketcan.sh` that caused the `can-gw` kernel module not to be loaded, + which prevented data from being generated when the fleet size was greater than one. + * Edge agent developer guide now available in HTML format as well as PDF format. + * Cloud demo script `demo.sh`: + * Added retry loop if registration fails due to eventual-consistency of IAM. + * Added `--force-registration` option to allow re-creation of Timestream database or service + role, if these resources have been manually deleted. + * Updated `iotfleetwise-2021-06-17.json` to current released version, which improves the + parameter validation and help documentation. + * CloudFormation templates `fwdemo.yml` and `fwdev.yml`: + * Kernel updated and SocketCAN modules installed from `linux-modules-extra-aws` to avoid + modules becoming unavailable after system upgrade of EC2 instance. + * Edge agent now compiled and run on the same EC2 instance, rather than using CodePipeline. + ## v0.1.0 (November 29, 2021) https://s3.console.aws.amazon.com/s3/object/aws-iot-fleetwise?prefix=v0.1.0/aws-iot-fleetwise-edge.zip Features: * Initial preview release - -Bugfixes/Improvements: - N/A +Bugfixes/Improvements: +* N/A diff --git a/CMakeLists.txt b/CMakeLists.txt index e3fc7dff..f62d5db1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10.2) -project(iotfleetwise VERSION 0.1.0) +project(iotfleetwise VERSION 0.1.1) # Due to autosar rules, AWS IoT FleetWise Edge will use C++11 set(CMAKE_CXX_STANDARD 11) @@ -22,6 +22,7 @@ option(FWE_BUILD_DOC "Build documentation" ON) option(FWE_STRIP_SYMBOLS "Strips symbols from output binaries" OFF) option(FWE_FEATURE_CAMERA "Enable Camera Data Collection feature" OFF) + option(FWE_SECURITY_COMPILE_FLAGS "Add security related compile options" OFF) # Define the default build type diff --git a/README.md b/README.md index 35dd5df6..c4f6adf8 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ The following documents provide more information about AWS IoT FleetWise Edge. 1. [Change Log](./CHANGELOG.md) provides a summary of feature enhancements, updates, and resolved and known issues. 2. [AWS IoT FleetWise Edge Offboarding](./assets/AWS-IoTFleetWiseOffboarding.md) provides a summary of the steps needed on the Client side to off board from the service. -For more information on the below topics please refer to [AWS_IoT_FleetWise_Edge_Agent_Developer_Guide.pdf](https://console.aws.amazon.com/s3/object/aws-iot-fleetwise?prefix=latest%2FAWS_IoT_FleetWise_Edge_Agent_Developer_Guide.pdf) +For more information on the below topics please refer to [AWS_IoT_FleetWise_Edge_Agent_Developer_Guide.html](https://console.aws.amazon.com/s3/object/aws-iot-fleetwise?prefix=latest%2FAWS_IoT_FleetWise_Edge_Agent_Developer_Guide.html) * AWS IoT FleetWise Getting Started. * Deploy AWS IoT FleetWise demo. diff --git a/appspec.yml b/appspec.yml deleted file mode 100644 index ae8759e6..00000000 --- a/appspec.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: 0.0 -os: linux -files: - - source: build/src/executionmanagement/aws-iot-fleetwise-edge - destination: /usr/bin/ - - source: tools/deploy/fwe@.service - destination: /lib/systemd/system/ -hooks: - ApplicationStop: - - location: tools/deploy/stop-fwe.sh - timeout: 60 - runas: root - ApplicationStart: - - location: tools/deploy/start-and-enable-fwe.sh - timeout: 60 - runas: root diff --git a/buildspec.yml b/buildspec.yml deleted file mode 100644 index 888d1202..00000000 --- a/buildspec.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: 0.2 - -phases: - install: - commands: - - bash ./tools/install-deps-cross.sh - build: - commands: - - bash ./tools/build-fwe-cross.sh -artifacts: - files: - - build/src/executionmanagement/aws-iot-fleetwise-edge - - appspec.yml - - configuration/static-config.json - - tools/deploy/* - name: aws-iot-fleetwise-edge diff --git a/configuration/static-config.json b/configuration/static-config.json index 639af1a6..b6fddf1f 100644 --- a/configuration/static-config.json +++ b/configuration/static-config.json @@ -38,7 +38,8 @@ }, "persistency": { "persistencyPath": "/path/to/collection-scheme-and-data-persistency", - "persistencyPartitionMaxSize": 524288 + "persistencyPartitionMaxSize": 524288, + "persistencyUploadRetryInterval" : 10000 }, "internalParameters": { "readyToPublishDataBufferSize": 10000, diff --git a/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto b/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto index 4039f84f..f2953d55 100644 --- a/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto +++ b/interfaces/protobuf/schemas/cloudToEdge/collection_schemes.proto @@ -17,10 +17,7 @@ option java_package = "com.amazonaws.iot.autobahn.schemas"; package Aws.IoTFleetWise.Schemas.CollectionSchemesMsg; /* - * List of collectionSchemes to be enacted by Edge - * - * TODO: This schema requires all collectionSchemes every time a collectionSchemes changes. This is to be optimized after Alpha - * to allow for sending or revoking individual + * List of collectionSchemes to be enacted by AWS IoT FleetWise Edge */ message CollectionSchemes { @@ -37,8 +34,8 @@ message CollectionSchemes { /* * A definition of an individual collectionScheme containing what/when/how to send vehicle data to cloud. A - * collectionScheme can be condition based, where data is sent whenever a condition evaluates to true, or it - * can be time based, where data is sent up at periodic intervals. + * collectionScheme can be condition based, with data sent whenever a condition evaluates to true, or it can be time + * based, with data sent at periodic intervals. */ message CollectionScheme { @@ -60,7 +57,7 @@ message CollectionScheme { /* * When collectionScheme should expire in milliseconds since the Unix epoch. This collectionScheme expiration date * is meant to serve as an end date for a collectionScheme so it does not keep running forever in the case - * that the vehicle permanently loses internet connection to the cloud + * that a vehicle permanently loses internet connection to the cloud */ uint64 expiry_time_ms_epoch = 4; @@ -74,15 +71,13 @@ message CollectionScheme { } /* - * This specifies how much time to spend collecting data after a condition evaluates to true. - * When after_duration_ms elapses whatever data collected up to that point ( if any was present - * on the vehicle ) is sent to the cloud. + * This specifies how much time to spend collecting data after a condition evaluates to true. When after_duration_ms + * elapses whatever data collected up to that point ( if any was present on the vehicle ) is sent to the cloud. */ uint32 after_duration_ms = 7; /* - * All active DTCs including the time they were first seen active will be sent when the collectionScheme - * triggers. TODO: After alpha allow customer to select which DTCs to send back. + * All active DTCs including the time they were first seen active will be sent when the collectionScheme triggers. */ bool include_active_dtcs = 8; @@ -97,32 +92,24 @@ message CollectionScheme { repeated RawCanFrame raw_can_frames_to_collect = 10; /* - * When true all data will be written to persistant storage when vehicle doesn't not have an - * internet connection + * When true, all data will be written to persistent storage when vehicle doesn't not have an internet connection */ bool persist_all_collected_data = 11; /* - * When true, collected data will be compressed and then sent to cloud. TODO: Add type of - * compression used. + * When true, collected data will be compressed and then sent to cloud. */ bool compress_collected_data = 12; /* - * An integer between describing the priority for the data collection. CollectionSchemes with low - * priority numbers will have higher priority and will be processed first. + * An integer between describing the priority for the data collection. CollectionSchemes with low priority numbers + * will have higher priority and will be processed first. */ uint32 priority = 13; /* - * Only as of protobuf 3.12 the optional keyword was reintroduced into proto3 and since 3.15 the flag - * --experimental_allow_proto3_optional has been made default for proto3. As we use an older - * version of protobuf the presence check if a field is present is not possible for singular - * numeric fields. And the default value for doubles returned if a field is not present is 0. - * So to make the presence check possible move the probability in its own message. For singular - * message the presence check is always possible. - * https://github.com/protocolbuffers/protobuf/blob/master/docs/field_presence.md - */ + * An optional probabilities message indicating the probability that a CollectionScheme be enacted. + */ Probabilities probabilities = 14; /* @@ -133,13 +120,8 @@ message CollectionScheme { message Probabilities{ /* - * Double between 0 and 1 giving the probability after the condition is met - * and the minimum interval is over that the message should be actually be sent out - * 0: send never, 1: send always. A uniform random number (0-1) is generated before sending the - * data to cloud and compared to this probability. If lower then data will be sent out. - * The minimum interval will start again even when the random number decides to not sent out - * the data. - * It is both useable for condition and time based collectionSchemes. + * Double between 0 and 1 giving the probability after a condition is met. + * 0: never send, 1: send always. It is usable for both condition and time based collectionSchemes. */ double probability_to_send = 1; } @@ -150,8 +132,8 @@ message Probabilities{ message TimeBasedCollectionScheme { /* - * Time in milliseconds that will be interval of a time based collectionScheme if is_time_based_collection_scheme is - * set to true. This is not used if is_time_based_collection_scheme is set false. + * Time in milliseconds that will be the interval of a time based collectionScheme if is_time_based_collection_scheme is + * set to true. This field is unused if is_time_based_collection_scheme is set false. */ uint32 time_based_collection_scheme_period_ms = 1; } @@ -162,14 +144,14 @@ message TimeBasedCollectionScheme { message ConditionBasedCollectionScheme { /* - * The minimum time in milliseconds required to elapse between conditions that evaluate to true - * for data to be sent to the cloud. + * The minimum time in milliseconds required to elapse between conditions that evaluate to true for data to be sent + * to the cloud. */ uint32 condition_minimum_interval_ms = 1; /* - * The version number associated with the event condition language used in the abstract syntax - * tree. We are starting at 0 for alpha and we will increment as we add features + * The version number associated with the event condition language used in the abstract syntax tree. We are starting + * at 0 for alpha and we will increment as we add features */ uint32 condition_language_version = 2; @@ -179,8 +161,8 @@ message ConditionBasedCollectionScheme { ConditionNode condition_tree = 3; /* - * Edge can monitor the previous state of a condition and use this information to allow the - * customer to set a trigger mode similar to an oscillascope trigger. + * Edge can monitor the previous state of a condition and use this information to allow the customer to set a + * trigger mode similar to an oscilloscope trigger. */ enum ConditionTriggerMode { @@ -190,29 +172,26 @@ message ConditionBasedCollectionScheme { TRIGGER_ALWAYS = 0; /* - * Condition will evaluate to true only when it previously evaulated to false + * Condition will evaluate to true only when it previously evaluated to false */ TRIGGER_ONLY_ON_RISING_EDGE = 1; - - //TODO: Add support to triggering on falling edge } /* - * A triggering mode can be applied to the condition to take in account the previous state of - * the condition. + * A triggering mode can be applied to the condition to take in account the previous state of the condition. */ ConditionTriggerMode condition_trigger_mode = 4; } /* - * This message contains information of signals that are to be collected and sent to cloud, or are - * part of the condition logic and require attribute information. + * This message contains information of signals that are to be collected and sent to cloud, or are part of the condition + * logic and require attribute information. */ message SignalInformation { /* - * Unique identifier of a Signal. Maps directly to a signal defined in the decoder manifest. - * Signal can also be an OBD PID. + * Unique identifier of a Signal. Maps directly to a signal defined in the decoder manifest. Signal can also be an + * OBDII PID. */ uint32 signal_id = 1; @@ -222,21 +201,20 @@ message SignalInformation { uint32 sample_buffer_size = 2; /* - * Minimum time period in milliseconds that must elapse between collecting samples. Samples - * arriving faster than this period will be dropped. A value of 0 will collect samples as fast - * as they arrive. + * Minimum time period in milliseconds that must elapse between collecting samples. Samples arriving faster than + * this period will be dropped. A value of 0 will collect samples as fast as they arrive. */ uint32 minimum_sample_period_ms = 3; /* - * The size of a fixed window in milliseconds which will be used by aggregate condition - * functions to calculate min/max/avg etc. + * The size of a fixed window in milliseconds which will be used by aggregate condition functions to calculate + * min/max/avg etc. */ uint32 fixed_window_period_ms = 4; /* - * When true, this signal will not be collected and sent to cloud. It will only be used in the - * condition logic with its associated fixed_window_period_ms. Default is false. + * When true, this signal will not be collected and sent to cloud. It will only be used in the condition logic with + * its associated fixed_window_period_ms. Default is false. */ bool condition_only_signal = 5; } @@ -250,6 +228,7 @@ message ConditionNode { * Each Abstract Syntax Tree node can be one of the following types */ oneof node { + /* * An operator node can perform an operation or comparisons on its child node(s) */ @@ -261,27 +240,24 @@ message ConditionNode { NodeFunction node_function = 2; /* - * A node containing a floating point constant which can be used as a child node to operator - * nodes. + * A node containing a floating point constant which can be used as a child node to operator nodes. */ double node_double_value = 3; /* - * A node containing a signal id, whose value will be evaluated every time that signal is - * received on the vehicle network bus. + * A node containing a signal id, whose value will be evaluated every time that signal is received on the + * vehicle network bus. */ uint32 node_signal_id = 4; /* - * A node containing a boolean constant which can be used as a child node to an operator - * node. + * A node containing a boolean constant which can be used as a child node to an operator node. */ - bool node_boolean_value = 5; - } + bool node_boolean_value = 5; } /* - * Operator node types contain one or two children. If they are unary operator type nodes, only - * the left child will be used + * Operator node types contain one or two children. If they are unary operator type nodes, only the left child will + * be used */ message NodeOperator{ @@ -333,25 +309,25 @@ message ConditionNode { } /* - * Function node is a self-contained module that accomplish a specific task. - * It takes inputs provided here and output based on specific logic + * Function node is a self-contained module that accomplish a specific task. It takes inputs provided here and + * output based on specific logic */ message NodeFunction{ /* - * The function node could be one of the following funtion types. + * The function node could be one of the following function types. */ oneof functionType { /* - * A Window function node will sample a signal for the duration specifed by fixed_window_period_ms - * and then run an aggregation funcion over the samples and evaluate to a double. + * A Window function node will sample a signal for the duration specified by fixed_window_period_ms and then + * run an aggregation function over the samples and evaluate to a double. */ WindowFunction window_function = 1; /* - * Geohash function Node that evaluates whether Edge has changed Geohash - * It returns true if the Geohash has changed at given precision. Otherwise return false + * Geohash function Node that evaluates whether Edge has changed Geohash. It returns true if the Geohash + * has changed at given precision and otherwise return false */ GeohashFunction geohash_function = 2; } @@ -374,23 +350,21 @@ message ConditionNode { uint32 longitude_signal_id = 2; /* - * The geohash precision for dynamic data collection - * Note geohash precision is defined as the length of hash characters (base 32 encoding). - * Longer hash will have higher precision than shorter hash. + * The geohash precision for dynamic data collection Note geohash precision is defined as the length of hash + * characters (base 32 encoding). Longer hash will have higher precision than shorter hash. * see more details: https://en.wikipedia.org/wiki/Geohash */ uint32 geohash_precision = 3; /* - * The unit for decoded latitude / longitude signal. GPS Signal might be decoded into different unit - * according to the DBC file. For instance, DBC for Chevy Equinox is using - * milliarcsecond. 1 milliarcsecond = 1 / 3600000 degree. + * The unit for decoded latitude / longitude signal. GPS Signal might be decoded into different unit + * according to the DBC file. */ GPSUnitType gps_unit = 4; /* - * The unit type for decoded latitude / longitude signal. This list might be extended in - * future to accommodate different vehicle models. + * The unit type for decoded latitude / longitude signal. This list might be extended in future to + * accommodate different vehicle models. */ enum GPSUnitType { DECIMAL_DEGREE = 0; @@ -406,8 +380,8 @@ message ConditionNode { message WindowFunction{ /* - * signal id of value to run a function on. The fixed_window_period_ms associated in - * signalInformation will be used. + * signal id of value to run a function on. The fixed_window_period_ms associated in signalInformation will + * be used. */ uint32 signal_id = 1; @@ -444,15 +418,14 @@ message ConditionNode { message RawCanFrame { /* - * The interface ID speficied by the Decoder Manifest. This will contain the physical channel - * id of the hardware CAN Bus the frame is present on. + * The interface ID specified by the Decoder Manifest. This will contain the physical channel id of the hardware CAN + * Bus the frame is present on. */ - string can_interface_id = 1; /* - * CAN Message ID to collect. This Raw CAN message will be collected. Whatever number of bytes - * present on the bus for this message ID will be collected. + * CAN Message ID to collect. This Raw CAN message will be collected. Whatever number of bytes present on the bus + * for this message ID will be collected. */ uint32 can_message_id = 2; @@ -462,9 +435,8 @@ message RawCanFrame { uint32 sample_buffer_size = 3; /* - * Minimum time period in milliseconds that must elapse between collecting samples. Samples - * arriving faster than this period will be dropped. A value of 0 will collect samples as fast - * as they arrive. + * Minimum time period in milliseconds that must elapse between collecting samples. Samples arriving faster than + * this period will be dropped. A value of 0 will collect samples as fast as they arrive. */ uint32 minimum_sample_period_ms = 4; } @@ -474,7 +446,6 @@ message ImageData{ /* * Image Source Node ID which contain network interface information needed to access the image source. * - * TODO: Add an ImageSourceInterfaceNode in the DecoderManifest. Currently FWE will only use one image source node */ uint32 image_source_node_id = 1; @@ -489,7 +460,7 @@ message ImageData{ oneof image_collection_method { /* - * A timebased window of before and after time relative to an event trigger. + * A time based window of before and after time relative to an event trigger. */ TimeBasedImageData time_based_image_data = 3; } diff --git a/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto b/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto index 7f74294f..ac7e49c9 100644 --- a/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto +++ b/interfaces/protobuf/schemas/cloudToEdge/decoder_manifest.proto @@ -42,9 +42,8 @@ message CANSignal { uint32 signal_id = 1; /* - * Interface ID for CAN network interface this signal is found on. - * The CAN network interface details are provided as a part of the edge static - * configuration file. + * Interface ID for CAN network interface this signal is found on. The CAN network interface details are provided as + * a part of the edge static configuration file. */ string interface_id = 2; @@ -85,8 +84,8 @@ message CANSignal { } /* - * This is the OBDII-PID signal decoding rule. One OBDII-PID could contain - * multiple signals. Below section is the decoder rule per signal, not per PID + * This is the OBDII-PID signal decoding rule. One OBDII-PID could contain multiple signals. Below section is the + * decoder rule per signal, not per PID */ message OBDPIDSignal { @@ -96,15 +95,13 @@ message OBDPIDSignal { uint32 signal_id = 1; /* - * Interface ID for CAN network interface this signal is found on. - * The CAN network interface details are provided as a part of the edge static - * configuration file. + * Interface ID for CAN network interface this signal is found on. The CAN network interface details are provided as + * a part of the edge static configuration file. */ string interface_id = 2; /* - * Length of the PID response. Note this is not the signal byte length as PID - * might contain multiple signals + * Length of the PID response. Note this is not the signal byte length as PID might contain multiple signals */ uint32 pid_response_length = 3; @@ -120,7 +117,7 @@ message OBDPIDSignal { /* * scaling to decode OBD from raw bytes to double value - * e.g. A * 0.0125 - 40. scaling is 0.01 + * e.g. A * 0.0125 - 40. Scaling is 0.0125 */ double scaling = 6; @@ -131,9 +128,8 @@ message OBDPIDSignal { double offset = 7; /* - * the start byte order (starting from 0th) for this signal in its PID query - * response e.g. PID 0x14 contains two signals. SHRFT is the second byte. Its - * startByte is 1 + * the start byte order (starting from 0th) for this signal in its PID query response + * e.g. PID 0x14 contains two signals, with Signal B located in the 2nd byte startByte is 1 */ uint32 start_byte = 8; @@ -144,18 +140,16 @@ message OBDPIDSignal { uint32 byte_length = 9; /* - * Right shift on bits to decode this signal from raw bytes. Note the bit - * manipulation is only performed when byteLength is 1. e.g. Boost Pressure B - * Control Status is bit 2, 3 on byte J. The right shift shall be 2 For + * Right shift on bits to decode this signal from raw bytes. Note the bit manipulation is only performed when + * byteLength is 1. e.g. Boost Pressure B Control Status is bit 2, 3 on byte J. The right shift shall be 2 For * non-bitmask signals, the right shift shall always be 0 */ uint32 bit_right_shift = 10; /* - * bit Mask Length to be applied to decode this signal from raw byte. Note the - * bit manipulation is only performed when byteLength is 1. e.g. Boost - * Pressure B Control Status is bit 2, 3 on byte J. The bit Mask Length would - * be 2 For non-bitmask signals, the bit Mask Length shall always be 8. + * bit Mask Length to be applied to decode this signal from raw byte. Note the bit manipulation is only performed when + * byteLength is 1. e.g. Boost Pressure B Control Status is bit 2, 3 on byte J. The bit Mask Length would be 2 For + * non-bitmask signals, the bit Mask Length shall always be 8. */ uint32 bit_mask_length = 11; } diff --git a/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json b/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json index e2b5bab4..472284b4 100644 --- a/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json +++ b/interfaces/protobuf/schemas/edgeConfiguration/staticConfiguration.json @@ -178,6 +178,10 @@ "persistencyPartitionMaxSize": { "type": "integer", "description": "Maximum size allocated for persistency (Bytes)" + }, + "persistencyUploadRetryIntervalMs": { + "type": "integer", + "description": "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. Defaults to 10 seconds." } }, "required": [ diff --git a/interfaces/protobuf/schemas/edgeToCloud/checkin.proto b/interfaces/protobuf/schemas/edgeToCloud/checkin.proto index 792db25a..e95d77bd 100644 --- a/interfaces/protobuf/schemas/edgeToCloud/checkin.proto +++ b/interfaces/protobuf/schemas/edgeToCloud/checkin.proto @@ -17,10 +17,10 @@ option java_package = "com.amazonaws.iot.autobahn.schemas"; package Aws.IoTFleetWise.Schemas.CheckinMsg; message Checkin { - /** - * List of document arn's the Edge currently has enacted including collectionSchemes (both idle and active) and decoder - * manifest. - */ + /* + * List of document arn's the Edge currently has enacted including collectionSchemes (both idle and active) and + * decoder manifest. + */ repeated string document_arns = 1; /* diff --git a/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto b/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto index 9358da5e..1661683a 100644 --- a/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto +++ b/interfaces/protobuf/schemas/edgeToCloud/vehicle_data.proto @@ -17,8 +17,8 @@ option java_package = "com.amazonaws.iot.autobahn.schemas"; package Aws.IoTFleetWise.Schemas.VehicleDataMsg; /* - * VehicleData payload from IoTFleetWise Edge for all events triggered by CollectionSchemes including - * condition based events and periodic time based events. + * VehicleData payload from IoTFleetWise Edge for all events triggered by CollectionSchemes including condition based + * events and periodic time based events. */ message VehicleData { @@ -27,7 +27,8 @@ message VehicleData { */ string campaign_arn = 1; - /** Amazon Resource Name of the decoder manifest used to decode the signals in this message + /* + * Amazon Resource Name of the decoder manifest used to decode the signals in this message */ string decoder_arn = 2; @@ -73,15 +74,14 @@ message CapturedSignal { sint64 relative_time_ms = 1; /* - * The signal id of the physical value of a signal that was captured. This maps directly to - * signal IDs provided in the collection schemes and decoder manifest. Signals can come from - * normal CAN traffic or OBD-II PIDs. In the case of OBD-II PIDs, Signal ID will only map to one - * of those signals, as per the decoder manifest. + * The signal id of the physical value of a signal that was captured. This maps directly to signal IDs provided in + * the collection schemes and decoder manifest. Signals can come from normal CAN traffic or OBD-II PIDs. In the case + * of OBD-II PIDs, Signal ID will only map to one of those signals, as per the decoder manifest. */ uint32 signal_id = 2; /* - * Datatypes of physical signal values. This can be extended to add other dataypes. + * Data types of physical signal values. */ oneof SignalValue { @@ -135,16 +135,13 @@ message DtcData { message Geohash { /* - * Geohash in string format. It's encoded in base 32 format. - * Currently we always upload the maximum resolution of geohash. - * The string is always 9 characters long. + * Geohash in string format. It's encoded in base 32 format. The maximum resolution of geohash is used and the string is always 9 characters long. */ string geohash_string = 1; /* - * Previous Geohash in string format. It's encoded in base 32 format. - * Currently we always upload the maximum resolution of geohash. - * The string is always 9 characters long. + * Previous Geohash in string format encoded in base 32 format. The maximum resolution of geohash is used and the + * string is always 9 characters long. */ string prev_reported_geohash_string = 2; } diff --git a/src/datamanagement/CMakeLists.txt b/src/datamanagement/CMakeLists.txt index ae661d63..9fab0745 100644 --- a/src/datamanagement/CMakeLists.txt +++ b/src/datamanagement/CMakeLists.txt @@ -19,7 +19,10 @@ add_library( src/CollectionSchemeIngestion.cpp src/DecoderManifestIngestion.cpp src/CollectionSchemeIngestionList.cpp - src/CollectionSchemeManager.cpp + src/CollectionSchemeManager/CollectionSchemeManager.cpp + src/CollectionSchemeManager/CheckinAndPersistency.cpp + src/CollectionSchemeManager/DecoderDictionaryExtractor.cpp + src/CollectionSchemeManager/InspectionMatrixExtractor.cpp ) find_path(JSONCPP_INCLUDE_DIR "json/json.h" PATH_SUFFIXES "jsoncpp") diff --git a/src/datamanagement/include/CollectionSchemeManagementListener.h b/src/datamanagement/include/CollectionSchemeManagementListener.h index db77da40..e0cf08db 100644 --- a/src/datamanagement/include/CollectionSchemeManagementListener.h +++ b/src/datamanagement/include/CollectionSchemeManagementListener.h @@ -34,7 +34,7 @@ struct CollectionSchemeManagementListener * arrives from the Cloud. * * @param collectionSchemeList ICollectionSchemeList from CollectionScheme Ingestion - **/ + */ virtual void onCollectionSchemeUpdate( const ICollectionSchemeListPtr &collectionSchemeList ) = 0; /** @@ -42,7 +42,7 @@ struct CollectionSchemeManagementListener * Manifest arrives from the Cloud. * * @param decoderManifest IDecoderManifest from CollectionScheme Ingestion - **/ + */ virtual void onDecoderManifestUpdate( const IDecoderManifestPtr &decoderManifest ) = 0; }; diff --git a/src/datamanagement/include/CollectionSchemeManager.h b/src/datamanagement/include/CollectionSchemeManager.h index cbac9cfd..5e5c9bad 100644 --- a/src/datamanagement/include/CollectionSchemeManager.h +++ b/src/datamanagement/include/CollectionSchemeManager.h @@ -51,13 +51,13 @@ using TimeData = std::pair; * @brief main CollectionScheme Management entity - responsible for the following: * 1. Listens to collectionScheme ingestion to get CollectionSchemeList and DecoderManifest * 2. Process CollectionSchemeList to generate timeLine in chronological order, organize CollectionSchemeList into -Enabled and Idle lists; + Enabled and Idle lists; * 3. Wait for timer to elapse on TimePointInMsecond along timeLine chronologically, re-org Enabled and Idle list; * 4. Extract decoding dictionary and propagate to Network Channel Consumer; * 5. Extract Inspection Matrix and propagate to Inspection Engine; * 6. Delete expired collectionSchemes from Enabled list, or removed collectionScheme from existing list per Cloud -request. - **/ + request. + */ class CollectionSchemeManager : public ICollectionSchemeManager, public CollectionSchemeManagementListener, diff --git a/src/datamanagement/src/CollectionSchemeManager/CheckinAndPersistency.cpp b/src/datamanagement/src/CollectionSchemeManager/CheckinAndPersistency.cpp new file mode 100644 index 00000000..34818264 --- /dev/null +++ b/src/datamanagement/src/CollectionSchemeManager/CheckinAndPersistency.cpp @@ -0,0 +1,194 @@ +/** + * Copyright 2020 Amazon.com, Inc. and its affiliates. All Rights Reserved. + * SPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0 + * Licensed under the Amazon Software License (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * http://aws.amazon.com/asl/ + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +// Includes +#include "CollectionSchemeIngestionList.h" +#include "CollectionSchemeManager.h" +#include "DecoderManifestIngestion.h" +#include +#include + +namespace Aws +{ +namespace IoTFleetWise +{ +namespace DataManagement +{ +void +CollectionSchemeManager::prepareCheckinTimer() +{ + TimePointInMsec currTime = mClock->timeSinceEpochMs(); + TimeData checkinData = std::make_pair( currTime, CHECKIN ); + mTimeLine.push( checkinData ); +} + +bool +CollectionSchemeManager::sendCheckin() +{ + // Create a list of active collectionSchemes and the current decoder manifest and send it to cloud + std::vector checkinMsg; + std::string checkinLogStr; + for ( auto it = mEnabledCollectionSchemeMap.begin(); it != mEnabledCollectionSchemeMap.end(); it++ ) + { + checkinMsg.emplace_back( it->first ); + checkinLogStr += it->first + ' '; + } + for ( auto it = mIdleCollectionSchemeMap.begin(); it != mIdleCollectionSchemeMap.end(); it++ ) + { + checkinMsg.emplace_back( it->first ); + checkinLogStr += it->first + ' '; + } + if ( !currentDecoderManifestID.empty() ) + { + checkinMsg.emplace_back( currentDecoderManifestID ); + checkinLogStr += currentDecoderManifestID; + } + mLogger.trace( "CollectionSchemeManager::sendCheckin ", "CHECKIN " + checkinLogStr ); + + if ( mSchemaListenerPtr == nullptr ) + { + mLogger.error( "CollectionSchemeManager::sendCheckin", "Cannot set the checkin message " ); + return false; + } + else + { + return mSchemaListenerPtr->sendCheckin( checkinMsg ); + } +} + +bool +CollectionSchemeManager::retrieve( DataType retrieveType ) +{ + size_t protoSize = 0; + ErrorCode ret = SUCCESS; + std::vector protoOutput; + std::string infoStr = ""; + std::string errStr = ""; + + if ( mSchemaPersistency == nullptr ) + { + mLogger.error( "CollectionSchemeManager::retrieve", + "Failed to acquire a valid handle on the scheme local persistency module " ); + return false; + } + switch ( retrieveType ) + { + case COLLECTION_SCHEME_LIST: + infoStr = "Retrieved a CollectionSchemeList of size "; + errStr = "Failed to retrieve the CollectionSchemeList from the persistency module due to an error :"; + break; + case DECODER_MANIFEST: + infoStr = "Retrieved a DecoderManifest of size "; + errStr = "Failed to retrieve the DecoderManifest from the persistency module due to an error :"; + break; + default: + mLogger.error( "CollectionSchemeManager::retrieve", " unknown error : " + std::to_string( retrieveType ) ); + return false; + } + + protoSize = mSchemaPersistency->getSize( retrieveType ); + if ( protoSize <= 0 ) + { + mLogger.info( "CollectionSchemeManager::retrieve", infoStr + "zero." ); + return false; + } + protoOutput.resize( protoSize ); + ret = mSchemaPersistency->read( protoOutput.data(), protoSize, retrieveType ); + if ( ret != SUCCESS ) + { + mLogger.error( "CollectionSchemeManager::retrieve", errStr + mSchemaPersistency->getErrorString( ret ) ); + return false; + } + mLogger.info( "CollectionSchemeManager::retrieve", infoStr + std::to_string( protoSize ) + " successfully." ); + if ( retrieveType == COLLECTION_SCHEME_LIST ) + { + // updating mCollectionSchemeList + if ( mCollectionSchemeList == nullptr ) + { + mCollectionSchemeList = std::make_shared(); + } + mCollectionSchemeList->copyData( protoOutput.data(), protoSize ); + mProcessCollectionScheme = true; + } + else + { + // updating mDecoderManifest + if ( mDecoderManifest == nullptr ) + { + mDecoderManifest = std::make_shared(); + } + mDecoderManifest->copyData( protoOutput.data(), protoSize ); + mProcessDecoderManifest = true; + } + return true; +} + +void +CollectionSchemeManager::store( DataType storeType ) +{ + ErrorCode ret = SUCCESS; + std::vector protoInput; + std::string logStr; + + if ( mSchemaPersistency == nullptr ) + { + mLogger.error( "CollectionSchemeManager::store", + "Failed to acquire a valid handle on the scheme local persistency module" ); + return; + } + if ( storeType == COLLECTION_SCHEME_LIST && mCollectionSchemeList == nullptr ) + { + mLogger.error( "CollectionSchemeManager::store", "Invalid CollectionSchemeList" ); + return; + } + if ( storeType == DECODER_MANIFEST && mDecoderManifest == nullptr ) + { + mLogger.error( "CollectionSchemeManager::store", "Invalid DecoderManifest" ); + return; + } + switch ( storeType ) + { + case COLLECTION_SCHEME_LIST: + protoInput = mCollectionSchemeList->getData(); + logStr = "The CollectionSchemeList"; + break; + case DECODER_MANIFEST: + protoInput = mDecoderManifest->getData(); + logStr = "The DecoderManifest"; + break; + default: + mLogger.error( "CollectionSchemeManager::store", + "cannot store unsupported type of " + std::to_string( storeType ) ); + return; + } + + if ( protoInput.size() <= 0 ) + { + mLogger.error( "CollectionSchemeManager::store", logStr + " data size is zero." ); + return; + } + ret = mSchemaPersistency->write( protoInput.data(), protoInput.size(), storeType ); + if ( ret != SUCCESS ) + { + mLogger.error( "CollectionSchemeManager::store", + "failed to persist " + logStr + + " because of this error: " + mSchemaPersistency->getErrorString( ret ) ); + } + else + { + mLogger.trace( "CollectionSchemeManager::store", logStr + " persisted successfully." ); + } +} +} // namespace DataManagement +} // namespace IoTFleetWise +} // namespace Aws diff --git a/src/datamanagement/src/CollectionSchemeManager.cpp b/src/datamanagement/src/CollectionSchemeManager/CollectionSchemeManager.cpp similarity index 59% rename from src/datamanagement/src/CollectionSchemeManager.cpp rename to src/datamanagement/src/CollectionSchemeManager/CollectionSchemeManager.cpp index 97a4a399..85512801 100644 --- a/src/datamanagement/src/CollectionSchemeManager.cpp +++ b/src/datamanagement/src/CollectionSchemeManager/CollectionSchemeManager.cpp @@ -13,10 +13,7 @@ // Includes #include "CollectionSchemeManager.h" -#include "CollectionSchemeIngestionList.h" -#include "DecoderManifestIngestion.h" #include "TraceModule.h" -#include #include #include #include @@ -163,14 +160,6 @@ CollectionSchemeManager::printWakeupStatus( std::string &wakeupStr ) wakeupStr += mProcessDecoderManifest ? "Yes." : "No."; } -void -CollectionSchemeManager::prepareCheckinTimer() -{ - TimePointInMsec currTime = mClock->timeSinceEpochMs(); - TimeData checkinData = std::make_pair( currTime, CHECKIN ); - mTimeLine.push( checkinData ); -} - // Clears both enabled collectionScheme map and idle collectionScheme map // removes all dataPair from mTimeLine except for CHECKIN void @@ -200,130 +189,6 @@ CollectionSchemeManager::cleanupCollectionSchemes() } } -bool -CollectionSchemeManager::retrieve( DataType retrieveType ) -{ - size_t protoSize = 0; - ErrorCode ret = SUCCESS; - std::vector protoOutput; - std::string infoStr = ""; - std::string errStr = ""; - - if ( mSchemaPersistency == nullptr ) - { - mLogger.error( "CollectionSchemeManager::retrieve", - "Failed to acquire a valid handle on the scheme local persistency module " ); - return false; - } - switch ( retrieveType ) - { - case COLLECTION_SCHEME_LIST: - infoStr = "Retrieved a CollectionSchemeList of size "; - errStr = "Failed to retrieve the CollectionSchemeList from the persistency module due to an error :"; - break; - case DECODER_MANIFEST: - infoStr = "Retrieved a DecoderManifest of size "; - errStr = "Failed to retrieve the DecoderManifest from the persistency module due to an error :"; - break; - default: - mLogger.error( "CollectionSchemeManager::retrieve", " unknown error : " + std::to_string( retrieveType ) ); - return false; - } - - protoSize = mSchemaPersistency->getSize( retrieveType ); - if ( protoSize <= 0 ) - { - mLogger.info( "CollectionSchemeManager::retrieve", infoStr + "zero." ); - return false; - } - protoOutput.resize( protoSize ); - ret = mSchemaPersistency->read( protoOutput.data(), protoSize, retrieveType ); - if ( ret != SUCCESS ) - { - mLogger.error( "CollectionSchemeManager::retrieve", errStr + mSchemaPersistency->getErrorString( ret ) ); - return false; - } - mLogger.info( "CollectionSchemeManager::retrieve", infoStr + std::to_string( protoSize ) + " successfully." ); - if ( retrieveType == COLLECTION_SCHEME_LIST ) - { - // updating mCollectionSchemeList - if ( mCollectionSchemeList == nullptr ) - { - mCollectionSchemeList = std::make_shared(); - } - mCollectionSchemeList->copyData( protoOutput.data(), protoSize ); - mProcessCollectionScheme = true; - } - else - { - // updating mDecoderManifest - if ( mDecoderManifest == nullptr ) - { - mDecoderManifest = std::make_shared(); - } - mDecoderManifest->copyData( protoOutput.data(), protoSize ); - mProcessDecoderManifest = true; - } - return true; -} - -void -CollectionSchemeManager::store( DataType storeType ) -{ - ErrorCode ret = SUCCESS; - std::vector protoInput; - std::string logStr; - - if ( mSchemaPersistency == nullptr ) - { - mLogger.error( "CollectionSchemeManager::store", - "Failed to acquire a valid handle on the scheme local persistency module" ); - return; - } - if ( storeType == COLLECTION_SCHEME_LIST && mCollectionSchemeList == nullptr ) - { - mLogger.error( "CollectionSchemeManager::store", "Invalid CollectionSchemeList" ); - return; - } - if ( storeType == DECODER_MANIFEST && mDecoderManifest == nullptr ) - { - mLogger.error( "CollectionSchemeManager::store", "Invalid DecoderManifest" ); - return; - } - switch ( storeType ) - { - case COLLECTION_SCHEME_LIST: - protoInput = mCollectionSchemeList->getData(); - logStr = "The CollectionSchemeList"; - break; - case DECODER_MANIFEST: - protoInput = mDecoderManifest->getData(); - logStr = "The DecoderManifest"; - break; - default: - mLogger.error( "CollectionSchemeManager::store", - "cannot store unsupported type of " + std::to_string( storeType ) ); - return; - } - - if ( protoInput.size() <= 0 ) - { - mLogger.error( "CollectionSchemeManager::store", logStr + " data size is zero." ); - return; - } - ret = mSchemaPersistency->write( protoInput.data(), protoInput.size(), storeType ); - if ( ret != SUCCESS ) - { - mLogger.error( "CollectionSchemeManager::store", - "failed to persist " + logStr + - " because of this error: " + mSchemaPersistency->getErrorString( ret ) ); - } - else - { - mLogger.trace( "CollectionSchemeManager::store", logStr + " persisted successfully." ); - } -} - /* main thread */ void CollectionSchemeManager::doWork( void *data ) @@ -601,190 +466,6 @@ CollectionSchemeManager::processCollectionScheme() } } -void -CollectionSchemeManager::decoderDictionaryExtractor( - std::map> &decoderDictionaryMap ) -{ - // Iterate through enabled collectionScheme lists to locate the signals and CAN frames to be collected - for ( auto it = mEnabledCollectionSchemeMap.begin(); it != mEnabledCollectionSchemeMap.end(); ++it ) - { - const auto &collectionSchemePtr = it->second; - // first iterate through the signalID lists - for ( const auto &signalInfo : collectionSchemePtr->getCollectSignals() ) - { - // get the Network Protocol Type: CAN, OBD, SOMEIP, etc - auto networkType = mDecoderManifest->getNetworkProtocol( signalInfo.signalID ); - if ( networkType == INVALID_PROTOCOL ) - { - mLogger.warn( "CollectionSchemeManager::decoderDictionaryExtractor", - "Invalid protocol provided for signal : " + std::to_string( signalInfo.signalID ) ); - // This signal contains invalid network protocol, cannot include it onto decoder dictionary - continue; - } - // Firstly we need to check if we already have dictionary created for this network - 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() ); - } - - if ( networkType == RAW_SOCKET ) - { - auto canRawFrameID = mDecoderManifest->getCANFrameAndInterfaceID( signalInfo.signalID ).first; - auto interfaceId = mDecoderManifest->getCANFrameAndInterfaceID( signalInfo.signalID ).second; - - auto canChannelID = mCANIDTranslator.getChannelNumericID( interfaceId ); - if ( canChannelID == INVALID_CAN_CHANNEL_NUMERIC_ID ) - { - mLogger.warn( "CollectionSchemeManager::decoderDictionaryExtractor", - "Invalid Interface ID provided: " + interfaceId ); - } - else - { - auto &canDecoderDictionaryPtr = decoderDictionaryMap[networkType]; - // Add signalID to the set of this decoder dictionary - canDecoderDictionaryPtr->signalIDsToCollect.insert( signalInfo.signalID ); - // firstly check if we have canChannelID entry at dictionary top layer - if ( canDecoderDictionaryPtr->canMessageDecoderMethod.find( canChannelID ) == - canDecoderDictionaryPtr->canMessageDecoderMethod.end() ) - { - // create an entry for canChannelID if it's not existed yet - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID] = - std::unordered_map(); - } - // check if this CAN Frame already exits in dictionary, if so, update if its a raw can decoder - // method. - // If not, we need to create an entry for this CAN Frame which will include decoder - // format for all signals defined in decoder manifest - if ( canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].find( canRawFrameID ) == - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].end() ) - { - CANMessageDecoderMethod decoderMethod; - // We set the collect Type to DECODE at this stage. In the second half of this function, we will - // examine the CAN Frames. If there's any CAN Frame to have both signal and raw bytes to be - // collected, the type will be updated to RAW_AND_DECODE - decoderMethod.collectType = CANMessageCollectType::DECODE; - decoderMethod.format = mDecoderManifest->getCANMessageFormat( canRawFrameID, interfaceId ); - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID] = decoderMethod; - } - else if ( canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID] - .collectType == CANMessageCollectType::RAW ) - { - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID].collectType = - CANMessageCollectType::RAW_AND_DECODE; - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID].format = - mDecoderManifest->getCANMessageFormat( canRawFrameID, interfaceId ); - } - } - } - else if ( networkType == OBD ) - { - auto pidDecoderFormat = mDecoderManifest->getPIDSignalDecoderFormat( signalInfo.signalID ); - // There's only one OBD Channel, this is just a place holder to maintain the generic dictionary - // structure - CANChannelNumericID canChannelID = 0; - auto &obdPidCanDecoderDictionaryPtr = decoderDictionaryMap[networkType]; - obdPidCanDecoderDictionaryPtr->signalIDsToCollect.insert( signalInfo.signalID ); - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.emplace( - canChannelID, std::unordered_map() ); - if ( obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.find( canChannelID ) == - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.end() ) - { - // create an entry for canChannelID if it's not existed yet - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID] = - std::unordered_map(); - } - if ( obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) - .find( pidDecoderFormat.mPID ) == - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ).end() ) - { - // There's no Dictionary Entry created for this PID yet, create one - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) - .emplace( pidDecoderFormat.mPID, CANMessageDecoderMethod() ); - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) - .at( pidDecoderFormat.mPID ) - .format.mMessageID = pidDecoderFormat.mPID; - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) - .at( pidDecoderFormat.mPID ) - .format.mSizeInBytes = static_cast( pidDecoderFormat.mPidResponseLength ); - } - // Below is the OBD Signal format represented in generic Signal Format - CANSignalFormat format; - format.mSignalID = signalInfo.signalID; - format.mFirstBitPosition = - static_cast( pidDecoderFormat.mStartByte * BYTE_SIZE + pidDecoderFormat.mBitRightShift ); - format.mSizeInBits = static_cast( ( pidDecoderFormat.mByteLength - 1 ) * BYTE_SIZE + - pidDecoderFormat.mBitMaskLength ); - format.mFactor = pidDecoderFormat.mScaling; - format.mOffset = pidDecoderFormat.mOffset; - obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) - .at( pidDecoderFormat.mPID ) - .format.mSignals.emplace_back( format ); - } - } - // Next let's iterate through the CAN Frames that collectionScheme wants to collect. - // 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( RAW_SOCKET ) == decoderDictionaryMap.end() ) - { - // Currently we don't have decoder dictionary for this type of network protocol, create one - decoderDictionaryMap.emplace( RAW_SOCKET, std::make_shared() ); - } - auto &canDecoderDictionaryPtr = decoderDictionaryMap[RAW_SOCKET]; - for ( const auto &canFrameInfo : collectionSchemePtr->getCollectRawCanFrames() ) - { - auto canChannelID = mCANIDTranslator.getChannelNumericID( canFrameInfo.interfaceID ); - if ( canChannelID == INVALID_CAN_CHANNEL_NUMERIC_ID ) - { - mLogger.warn( "CollectionSchemeManager::decoderDictionaryExtractor", - "Invalid Interface ID provided:" + canFrameInfo.interfaceID ); - } - else - { - if ( canDecoderDictionaryPtr->canMessageDecoderMethod.find( canChannelID ) == - canDecoderDictionaryPtr->canMessageDecoderMethod.end() ) - { - // create an entry for canChannelID if the dictionary doesn't have one - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID] = - std::unordered_map(); - } - // check if we already have entry for CAN Frame. If not, it means this CAN Frame doesn't contain any - // Signals to decode, hence the collectType will be RAW only. - if ( canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].find( canFrameInfo.frameID ) == - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].end() ) - { - // there's entry for CANChannelNumericID but no corresponding canFrameID - CANMessageDecoderMethod canMessageDecoderMethod; - canMessageDecoderMethod.collectType = CANMessageCollectType::RAW; - canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canFrameInfo.frameID] = - canMessageDecoderMethod; - } - else - { - // This CAN Frame contains signal to be decoded. As we need to collect both CAN Frame and - // signal, set the collectType as RAW_AND_DECODE - canDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) - .at( canFrameInfo.frameID ) - .collectType = CANMessageCollectType::RAW_AND_DECODE; - } - } - } - } - } -} - -void -CollectionSchemeManager::decoderDictionaryUpdater( - std::map> &decoderDictionaryMap ) -{ - for ( auto const &dict : decoderDictionaryMap ) - { - notifyListeners &>( - &IActiveDecoderDictionaryListener::onChangeOfActiveDictionary, dict.second, dict.first ); - } -} - /* * This function rebuild enableCollectionScheme map, idle collectionScheme map, and timeline. * In case a collectionScheme needs to start immediately, this function builds mEnableCollectionSchemeMap and returns @@ -1008,39 +689,6 @@ CollectionSchemeManager::updateMapsandTimeLine( const TimePointInMsec &currTime return ret; } -bool -CollectionSchemeManager::sendCheckin() -{ - // Create a list of active collectionSchemes and the current decoder manifest and send it to cloud - std::vector checkinMsg; - std::string checkinLogStr; - for ( auto it = mEnabledCollectionSchemeMap.begin(); it != mEnabledCollectionSchemeMap.end(); it++ ) - { - checkinMsg.emplace_back( it->first ); - checkinLogStr += it->first + ' '; - } - for ( auto it = mIdleCollectionSchemeMap.begin(); it != mIdleCollectionSchemeMap.end(); it++ ) - { - checkinMsg.emplace_back( it->first ); - checkinLogStr += it->first + ' '; - } - if ( !currentDecoderManifestID.empty() ) - { - checkinMsg.emplace_back( currentDecoderManifestID ); - checkinLogStr += currentDecoderManifestID; - } - mLogger.trace( "CollectionSchemeManager::sendCheckin ", "CHECKIN " + checkinLogStr ); - - if ( mSchemaListenerPtr == nullptr ) - { - mLogger.error( "CollectionSchemeManager::sendCheckin", "Cannot set the checkin message " ); - return false; - } - else - { - return mSchemaListenerPtr->sendCheckin( checkinMsg ); - } -} /* * This function checks timeline, * 1. Timer has not expired but main thread wakes up because of PI updates, @@ -1227,179 +875,6 @@ CollectionSchemeManager::checkTimeLine( const TimePointInMsec &currTime ) } return ret; } - -void -CollectionSchemeManager::addConditionData( const ICollectionSchemePtr &collectionScheme, - ConditionWithCollectedData &conditionData ) -{ - conditionData.minimumPublishInterval = collectionScheme->getMinimumPublishIntervalMs(); - conditionData.afterDuration = collectionScheme->getAfterDurationMs(); - conditionData.includeActiveDtcs = collectionScheme->isActiveDTCsIncluded(); - conditionData.triggerOnlyOnRisingEdge = collectionScheme->isTriggerOnlyOnRisingEdge(); - conditionData.probabilityToSend = collectionScheme->getProbabilityToSend(); - - /* - * use for loop to copy signalInfo and CANframe over to avoid error or memory issue - * This is probably not the fastest way to get things done, but the safest way - * since the object is not big, so not really slow - */ - const std::vector &collectionSignals = collectionScheme->getCollectSignals(); - for ( uint32_t i = 0; i < collectionSignals.size(); i++ ) - { - InspectionMatrixSignalCollectionInfo inspectionSignal = {}; - inspectionSignal.signalID = collectionSignals[i].signalID; - inspectionSignal.sampleBufferSize = collectionSignals[i].sampleBufferSize; - inspectionSignal.minimumSampleIntervalMs = collectionSignals[i].minimumSampleIntervalMs; - inspectionSignal.fixedWindowPeriod = collectionSignals[i].fixedWindowPeriod; - inspectionSignal.isConditionOnlySignal = collectionSignals[i].isConditionOnlySignal; - conditionData.signals.emplace_back( inspectionSignal ); - } - - const std::vector &collectionCANFrames = collectionScheme->getCollectRawCanFrames(); - for ( uint32_t i = 0; i < collectionCANFrames.size(); i++ ) - { - InspectionMatrixCanFrameCollectionInfo CANFrame = {}; - CANFrame.frameID = collectionCANFrames[i].frameID; - CANFrame.channelID = mCANIDTranslator.getChannelNumericID( collectionCANFrames[i].interfaceID ); - CANFrame.sampleBufferSize = collectionCANFrames[i].sampleBufferSize; - CANFrame.minimumSampleIntervalMs = collectionCANFrames[i].minimumSampleIntervalMs; - if ( CANFrame.channelID == INVALID_CAN_CHANNEL_NUMERIC_ID ) - { - mLogger.warn( "CollectionSchemeManager::addConditionData", - "Invalid Interface ID provided:" + collectionCANFrames[i].interfaceID ); - } - else - { - conditionData.canFrames.emplace_back( CANFrame ); - } - } - // Image capture data - const std::vector &imageCollectionInfos = collectionScheme->getImageCaptureData(); - for ( const auto &imageInfo : imageCollectionInfos ) - { - InspectionMatrixImageCollectionInfo imageSettings = {}; - imageSettings.deviceID = imageInfo.deviceID; - switch ( imageInfo.collectionType ) - { - case ImageCollectionType::TIME_BASED: - imageSettings.collectionType = InspectionMatrixImageCollectionType::TIME_BASED; - imageSettings.beforeDurationMs = imageInfo.beforeDurationMs; - break; - case ImageCollectionType::FRAME_BASED: - imageSettings.collectionType = InspectionMatrixImageCollectionType::FRAME_BASED; - break; - - default: - break; - } - imageSettings.imageFormat = imageInfo.imageFormat; - conditionData.imageCollectionInfos.emplace_back( imageSettings ); - } - conditionData.includeImageCapture = !conditionData.imageCollectionInfos.empty(); - // The rest - conditionData.metaData.compress = collectionScheme->isCompressionNeeded(); - conditionData.metaData.persist = collectionScheme->isPersistNeeded(); - conditionData.metaData.priority = collectionScheme->getPriority(); - conditionData.metaData.decoderID = collectionScheme->getDecoderManifestID(); - conditionData.metaData.collectionSchemeID = collectionScheme->getCollectionSchemeID(); -} - -void -CollectionSchemeManager::inspectionMatrixExtractor( const std::shared_ptr &inspectionMatrix ) -{ - std::stack nodeStack; - std::map nodeToIndexMap; - std::vector nodes; - uint32_t index = 0; - const ExpressionNode *currNode = nullptr; - - for ( auto it = mEnabledCollectionSchemeMap.begin(); it != mEnabledCollectionSchemeMap.end(); it++ ) - { - ICollectionSchemePtr collectionScheme = it->second; - ConditionWithCollectedData conditionData; - addConditionData( collectionScheme, conditionData ); - - currNode = collectionScheme->getCondition(); - /* save the old root of this tree */ - conditionData.condition = currNode; - inspectionMatrix->conditions.emplace_back( conditionData ); - - /* - * The following lines traverse each tree and pack the node addresses into a vector - * and build a map - * any order to traverse the tree is OK, here we use in-order. - */ - while ( currNode != nullptr ) - { - nodeStack.push( currNode ); - currNode = currNode->left; - } - while ( !nodeStack.empty() ) - { - currNode = nodeStack.top(); - nodeStack.pop(); - nodeToIndexMap[currNode] = index; - nodes.emplace_back( currNode ); - index++; - if ( currNode->right != nullptr ) - { - currNode = currNode->right; - while ( currNode != nullptr ) - { - nodeStack.push( currNode ); - currNode = currNode->left; - } - } - } - } - - size_t count = nodes.size(); - /* now we have the count of all nodes from all collectionSchemes, allocate a vector for the output */ - inspectionMatrix->expressionNodeStorage.resize( count ); - /* copy from the old tree node and update left and right children pointers */ - for ( uint32_t i = 0; i < count; i++ ) - { - inspectionMatrix->expressionNodeStorage[i].nodeType = nodes[i]->nodeType; - inspectionMatrix->expressionNodeStorage[i].floatingValue = nodes[i]->floatingValue; - inspectionMatrix->expressionNodeStorage[i].booleanValue = nodes[i]->booleanValue; - inspectionMatrix->expressionNodeStorage[i].signalID = nodes[i]->signalID; - inspectionMatrix->expressionNodeStorage[i].function = nodes[i]->function; - - if ( nodes[i]->left != nullptr ) - { - uint32_t leftIndex = nodeToIndexMap[nodes[i]->left]; - inspectionMatrix->expressionNodeStorage[i].left = &inspectionMatrix->expressionNodeStorage[leftIndex]; - } - else - { - inspectionMatrix->expressionNodeStorage[i].left = nullptr; - } - - if ( nodes[i]->right != nullptr ) - { - uint32_t rightIndex = nodeToIndexMap[nodes[i]->right]; - inspectionMatrix->expressionNodeStorage[i].right = &inspectionMatrix->expressionNodeStorage[rightIndex]; - } - else - { - inspectionMatrix->expressionNodeStorage[i].right = nullptr; - } - } - /* update the root of tree with new address */ - for ( uint32_t i = 0; i < inspectionMatrix->conditions.size(); i++ ) - { - uint32_t newIndex = nodeToIndexMap[inspectionMatrix->conditions[i].condition]; - inspectionMatrix->conditions[i].condition = &inspectionMatrix->expressionNodeStorage[newIndex]; - } -} - -void -CollectionSchemeManager::inspectionMatrixUpdater( const std::shared_ptr &inspectionMatrix ) -{ - notifyListeners &>( - &IActiveConditionProcessor::onChangeInspectionMatrix, inspectionMatrix ); -} - } // namespace DataManagement } // namespace IoTFleetWise } // namespace Aws diff --git a/src/datamanagement/src/CollectionSchemeManager/DecoderDictionaryExtractor.cpp b/src/datamanagement/src/CollectionSchemeManager/DecoderDictionaryExtractor.cpp new file mode 100644 index 00000000..5926935c --- /dev/null +++ b/src/datamanagement/src/CollectionSchemeManager/DecoderDictionaryExtractor.cpp @@ -0,0 +1,211 @@ +/** + * Copyright 2020 Amazon.com, Inc. and its affiliates. All Rights Reserved. + * SPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0 + * Licensed under the Amazon Software License (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * http://aws.amazon.com/asl/ + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +// Includes +#include "CollectionSchemeManager.h" +#include "TraceModule.h" +#include +#include + +namespace Aws +{ +namespace IoTFleetWise +{ +namespace DataManagement +{ +void +CollectionSchemeManager::decoderDictionaryExtractor( + std::map> &decoderDictionaryMap ) +{ + // Iterate through enabled collectionScheme lists to locate the signals and CAN frames to be collected + for ( auto it = mEnabledCollectionSchemeMap.begin(); it != mEnabledCollectionSchemeMap.end(); ++it ) + { + const auto &collectionSchemePtr = it->second; + // first iterate through the signalID lists + for ( const auto &signalInfo : collectionSchemePtr->getCollectSignals() ) + { + // get the Network Protocol Type: CAN, OBD, SOMEIP, etc + auto networkType = mDecoderManifest->getNetworkProtocol( signalInfo.signalID ); + if ( networkType == INVALID_PROTOCOL ) + { + mLogger.warn( "CollectionSchemeManager::decoderDictionaryExtractor", + "Invalid protocol provided for signal : " + std::to_string( signalInfo.signalID ) ); + // This signal contains invalid network protocol, cannot include it onto decoder dictionary + continue; + } + // Firstly we need to check if we already have dictionary created for this network + 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() ); + } + + if ( networkType == RAW_SOCKET ) + { + auto canRawFrameID = mDecoderManifest->getCANFrameAndInterfaceID( signalInfo.signalID ).first; + auto interfaceId = mDecoderManifest->getCANFrameAndInterfaceID( signalInfo.signalID ).second; + + auto canChannelID = mCANIDTranslator.getChannelNumericID( interfaceId ); + if ( canChannelID == INVALID_CAN_CHANNEL_NUMERIC_ID ) + { + mLogger.warn( "CollectionSchemeManager::decoderDictionaryExtractor", + "Invalid Interface ID provided: " + interfaceId ); + } + else + { + auto &canDecoderDictionaryPtr = decoderDictionaryMap[networkType]; + // Add signalID to the set of this decoder dictionary + canDecoderDictionaryPtr->signalIDsToCollect.insert( signalInfo.signalID ); + // firstly check if we have canChannelID entry at dictionary top layer + if ( canDecoderDictionaryPtr->canMessageDecoderMethod.find( canChannelID ) == + canDecoderDictionaryPtr->canMessageDecoderMethod.end() ) + { + // create an entry for canChannelID if it's not existed yet + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID] = + std::unordered_map(); + } + // check if this CAN Frame already exits in dictionary, if so, update if its a raw can decoder + // method. + // If not, we need to create an entry for this CAN Frame which will include decoder + // format for all signals defined in decoder manifest + if ( canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].find( canRawFrameID ) == + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].end() ) + { + CANMessageDecoderMethod decoderMethod; + // We set the collect Type to DECODE at this stage. In the second half of this function, we will + // examine the CAN Frames. If there's any CAN Frame to have both signal and raw bytes to be + // collected, the type will be updated to RAW_AND_DECODE + decoderMethod.collectType = CANMessageCollectType::DECODE; + decoderMethod.format = mDecoderManifest->getCANMessageFormat( canRawFrameID, interfaceId ); + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID] = decoderMethod; + } + else if ( canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID] + .collectType == CANMessageCollectType::RAW ) + { + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID].collectType = + CANMessageCollectType::RAW_AND_DECODE; + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canRawFrameID].format = + mDecoderManifest->getCANMessageFormat( canRawFrameID, interfaceId ); + } + } + } + else if ( networkType == OBD ) + { + auto pidDecoderFormat = mDecoderManifest->getPIDSignalDecoderFormat( signalInfo.signalID ); + // There's only one OBD Channel, this is just a place holder to maintain the generic dictionary + // structure + CANChannelNumericID canChannelID = 0; + auto &obdPidCanDecoderDictionaryPtr = decoderDictionaryMap[networkType]; + obdPidCanDecoderDictionaryPtr->signalIDsToCollect.insert( signalInfo.signalID ); + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.emplace( + canChannelID, std::unordered_map() ); + if ( obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.find( canChannelID ) == + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.end() ) + { + // create an entry for canChannelID if it's not existed yet + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID] = + std::unordered_map(); + } + if ( obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) + .find( pidDecoderFormat.mPID ) == + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ).end() ) + { + // There's no Dictionary Entry created for this PID yet, create one + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) + .emplace( pidDecoderFormat.mPID, CANMessageDecoderMethod() ); + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) + .at( pidDecoderFormat.mPID ) + .format.mMessageID = pidDecoderFormat.mPID; + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) + .at( pidDecoderFormat.mPID ) + .format.mSizeInBytes = static_cast( pidDecoderFormat.mPidResponseLength ); + } + // Below is the OBD Signal format represented in generic Signal Format + CANSignalFormat format; + format.mSignalID = signalInfo.signalID; + format.mFirstBitPosition = + static_cast( pidDecoderFormat.mStartByte * BYTE_SIZE + pidDecoderFormat.mBitRightShift ); + format.mSizeInBits = static_cast( ( pidDecoderFormat.mByteLength - 1 ) * BYTE_SIZE + + pidDecoderFormat.mBitMaskLength ); + format.mFactor = pidDecoderFormat.mScaling; + format.mOffset = pidDecoderFormat.mOffset; + obdPidCanDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) + .at( pidDecoderFormat.mPID ) + .format.mSignals.emplace_back( format ); + } + } + // Next let's iterate through the CAN Frames that collectionScheme wants to collect. + // 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( RAW_SOCKET ) == decoderDictionaryMap.end() ) + { + // Currently we don't have decoder dictionary for this type of network protocol, create one + decoderDictionaryMap.emplace( RAW_SOCKET, std::make_shared() ); + } + auto &canDecoderDictionaryPtr = decoderDictionaryMap[RAW_SOCKET]; + for ( const auto &canFrameInfo : collectionSchemePtr->getCollectRawCanFrames() ) + { + auto canChannelID = mCANIDTranslator.getChannelNumericID( canFrameInfo.interfaceID ); + if ( canChannelID == INVALID_CAN_CHANNEL_NUMERIC_ID ) + { + mLogger.warn( "CollectionSchemeManager::decoderDictionaryExtractor", + "Invalid Interface ID provided:" + canFrameInfo.interfaceID ); + } + else + { + if ( canDecoderDictionaryPtr->canMessageDecoderMethod.find( canChannelID ) == + canDecoderDictionaryPtr->canMessageDecoderMethod.end() ) + { + // create an entry for canChannelID if the dictionary doesn't have one + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID] = + std::unordered_map(); + } + // check if we already have entry for CAN Frame. If not, it means this CAN Frame doesn't contain any + // Signals to decode, hence the collectType will be RAW only. + if ( canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].find( canFrameInfo.frameID ) == + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID].end() ) + { + // there's entry for CANChannelNumericID but no corresponding canFrameID + CANMessageDecoderMethod canMessageDecoderMethod; + canMessageDecoderMethod.collectType = CANMessageCollectType::RAW; + canDecoderDictionaryPtr->canMessageDecoderMethod[canChannelID][canFrameInfo.frameID] = + canMessageDecoderMethod; + } + else + { + // This CAN Frame contains signal to be decoded. As we need to collect both CAN Frame and + // signal, set the collectType as RAW_AND_DECODE + canDecoderDictionaryPtr->canMessageDecoderMethod.at( canChannelID ) + .at( canFrameInfo.frameID ) + .collectType = CANMessageCollectType::RAW_AND_DECODE; + } + } + } + } + } +} + +void +CollectionSchemeManager::decoderDictionaryUpdater( + std::map> &decoderDictionaryMap ) +{ + for ( auto const &dict : decoderDictionaryMap ) + { + notifyListeners &>( + &IActiveDecoderDictionaryListener::onChangeOfActiveDictionary, dict.second, dict.first ); + } +} +} // namespace DataManagement +} // namespace IoTFleetWise +} // namespace Aws diff --git a/src/datamanagement/src/CollectionSchemeManager/InspectionMatrixExtractor.cpp b/src/datamanagement/src/CollectionSchemeManager/InspectionMatrixExtractor.cpp new file mode 100644 index 00000000..405c6555 --- /dev/null +++ b/src/datamanagement/src/CollectionSchemeManager/InspectionMatrixExtractor.cpp @@ -0,0 +1,200 @@ +/** + * Copyright 2020 Amazon.com, Inc. and its affiliates. All Rights Reserved. + * SPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0 + * Licensed under the Amazon Software License (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * http://aws.amazon.com/asl/ + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +// Includes +#include "CollectionSchemeManager.h" +#include "TraceModule.h" +#include +#include +#include + +namespace Aws +{ +namespace IoTFleetWise +{ +namespace DataManagement +{ +void +CollectionSchemeManager::addConditionData( const ICollectionSchemePtr &collectionScheme, + ConditionWithCollectedData &conditionData ) +{ + conditionData.minimumPublishInterval = collectionScheme->getMinimumPublishIntervalMs(); + conditionData.afterDuration = collectionScheme->getAfterDurationMs(); + conditionData.includeActiveDtcs = collectionScheme->isActiveDTCsIncluded(); + conditionData.triggerOnlyOnRisingEdge = collectionScheme->isTriggerOnlyOnRisingEdge(); + conditionData.probabilityToSend = collectionScheme->getProbabilityToSend(); + + /* + * use for loop to copy signalInfo and CANframe over to avoid error or memory issue + * This is probably not the fastest way to get things done, but the safest way + * since the object is not big, so not really slow + */ + const std::vector &collectionSignals = collectionScheme->getCollectSignals(); + for ( uint32_t i = 0; i < collectionSignals.size(); i++ ) + { + InspectionMatrixSignalCollectionInfo inspectionSignal = {}; + inspectionSignal.signalID = collectionSignals[i].signalID; + inspectionSignal.sampleBufferSize = collectionSignals[i].sampleBufferSize; + inspectionSignal.minimumSampleIntervalMs = collectionSignals[i].minimumSampleIntervalMs; + inspectionSignal.fixedWindowPeriod = collectionSignals[i].fixedWindowPeriod; + inspectionSignal.isConditionOnlySignal = collectionSignals[i].isConditionOnlySignal; + conditionData.signals.emplace_back( inspectionSignal ); + } + + const std::vector &collectionCANFrames = collectionScheme->getCollectRawCanFrames(); + for ( uint32_t i = 0; i < collectionCANFrames.size(); i++ ) + { + InspectionMatrixCanFrameCollectionInfo CANFrame = {}; + CANFrame.frameID = collectionCANFrames[i].frameID; + CANFrame.channelID = mCANIDTranslator.getChannelNumericID( collectionCANFrames[i].interfaceID ); + CANFrame.sampleBufferSize = collectionCANFrames[i].sampleBufferSize; + CANFrame.minimumSampleIntervalMs = collectionCANFrames[i].minimumSampleIntervalMs; + if ( CANFrame.channelID == INVALID_CAN_CHANNEL_NUMERIC_ID ) + { + mLogger.warn( "CollectionSchemeManager::addConditionData", + "Invalid Interface ID provided:" + collectionCANFrames[i].interfaceID ); + } + else + { + conditionData.canFrames.emplace_back( CANFrame ); + } + } + // Image capture data + const std::vector &imageCollectionInfos = collectionScheme->getImageCaptureData(); + for ( const auto &imageInfo : imageCollectionInfos ) + { + InspectionMatrixImageCollectionInfo imageSettings = {}; + imageSettings.deviceID = imageInfo.deviceID; + switch ( imageInfo.collectionType ) + { + case ImageCollectionType::TIME_BASED: + imageSettings.collectionType = InspectionMatrixImageCollectionType::TIME_BASED; + imageSettings.beforeDurationMs = imageInfo.beforeDurationMs; + break; + case ImageCollectionType::FRAME_BASED: + imageSettings.collectionType = InspectionMatrixImageCollectionType::FRAME_BASED; + break; + + default: + break; + } + imageSettings.imageFormat = imageInfo.imageFormat; + conditionData.imageCollectionInfos.emplace_back( imageSettings ); + } + conditionData.includeImageCapture = !conditionData.imageCollectionInfos.empty(); + // The rest + conditionData.metaData.compress = collectionScheme->isCompressionNeeded(); + conditionData.metaData.persist = collectionScheme->isPersistNeeded(); + conditionData.metaData.priority = collectionScheme->getPriority(); + conditionData.metaData.decoderID = collectionScheme->getDecoderManifestID(); + conditionData.metaData.collectionSchemeID = collectionScheme->getCollectionSchemeID(); +} + +void +CollectionSchemeManager::inspectionMatrixExtractor( const std::shared_ptr &inspectionMatrix ) +{ + std::stack nodeStack; + std::map nodeToIndexMap; + std::vector nodes; + uint32_t index = 0; + const ExpressionNode *currNode = nullptr; + + for ( auto it = mEnabledCollectionSchemeMap.begin(); it != mEnabledCollectionSchemeMap.end(); it++ ) + { + ICollectionSchemePtr collectionScheme = it->second; + ConditionWithCollectedData conditionData; + addConditionData( collectionScheme, conditionData ); + + currNode = collectionScheme->getCondition(); + /* save the old root of this tree */ + conditionData.condition = currNode; + inspectionMatrix->conditions.emplace_back( conditionData ); + + /* + * The following lines traverse each tree and pack the node addresses into a vector + * and build a map + * any order to traverse the tree is OK, here we use in-order. + */ + while ( currNode != nullptr ) + { + nodeStack.push( currNode ); + currNode = currNode->left; + } + while ( !nodeStack.empty() ) + { + currNode = nodeStack.top(); + nodeStack.pop(); + nodeToIndexMap[currNode] = index; + nodes.emplace_back( currNode ); + index++; + if ( currNode->right != nullptr ) + { + currNode = currNode->right; + while ( currNode != nullptr ) + { + nodeStack.push( currNode ); + currNode = currNode->left; + } + } + } + } + + size_t count = nodes.size(); + /* now we have the count of all nodes from all collectionSchemes, allocate a vector for the output */ + inspectionMatrix->expressionNodeStorage.resize( count ); + /* copy from the old tree node and update left and right children pointers */ + for ( uint32_t i = 0; i < count; i++ ) + { + inspectionMatrix->expressionNodeStorage[i].nodeType = nodes[i]->nodeType; + inspectionMatrix->expressionNodeStorage[i].floatingValue = nodes[i]->floatingValue; + inspectionMatrix->expressionNodeStorage[i].booleanValue = nodes[i]->booleanValue; + inspectionMatrix->expressionNodeStorage[i].signalID = nodes[i]->signalID; + inspectionMatrix->expressionNodeStorage[i].function = nodes[i]->function; + + if ( nodes[i]->left != nullptr ) + { + uint32_t leftIndex = nodeToIndexMap[nodes[i]->left]; + inspectionMatrix->expressionNodeStorage[i].left = &inspectionMatrix->expressionNodeStorage[leftIndex]; + } + else + { + inspectionMatrix->expressionNodeStorage[i].left = nullptr; + } + + if ( nodes[i]->right != nullptr ) + { + uint32_t rightIndex = nodeToIndexMap[nodes[i]->right]; + inspectionMatrix->expressionNodeStorage[i].right = &inspectionMatrix->expressionNodeStorage[rightIndex]; + } + else + { + inspectionMatrix->expressionNodeStorage[i].right = nullptr; + } + } + /* update the root of tree with new address */ + for ( uint32_t i = 0; i < inspectionMatrix->conditions.size(); i++ ) + { + uint32_t newIndex = nodeToIndexMap[inspectionMatrix->conditions[i].condition]; + inspectionMatrix->conditions[i].condition = &inspectionMatrix->expressionNodeStorage[newIndex]; + } +} + +void +CollectionSchemeManager::inspectionMatrixUpdater( const std::shared_ptr &inspectionMatrix ) +{ + notifyListeners &>( + &IActiveConditionProcessor::onChangeInspectionMatrix, inspectionMatrix ); +} +} // namespace DataManagement +} // namespace IoTFleetWise +} // namespace Aws diff --git a/src/executionmanagement/include/IoTFleetWiseEngine.h b/src/executionmanagement/include/IoTFleetWiseEngine.h index ade36369..620d34d1 100644 --- a/src/executionmanagement/include/IoTFleetWiseEngine.h +++ b/src/executionmanagement/include/IoTFleetWiseEngine.h @@ -68,7 +68,10 @@ using namespace Aws::IoTFleetWise::OffboardConnectivityAwsIot; class IoTFleetWiseEngine : public IDataReadyToPublishListener { public: - static const uint32_t MAX_NUMBER_OF_SIGNAL_TO_TRACE_LOG = 6; + static const uint32_t MAX_NUMBER_OF_SIGNAL_TO_TRACE_LOG; + static const uint64_t FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS; // retry every second + static const uint64_t DEFAULT_RETRY_UPLOAD_PERSISTED_INTERVAL_MS; // retry every 10 second + IoTFleetWiseEngine(); virtual ~IoTFleetWiseEngine(); bool connect( const Json::Value &config ); @@ -86,9 +89,9 @@ class IoTFleetWiseEngine : public IDataReadyToPublishListener /** * @brief Check if the data was persisted in the last cycle due to no offboardconnectivity, * retrieve all the data and send - * + * @return true if either no data persisted or all persisted data was handed over to connectivity */ - void checkAndSendRetrievedData(); + bool checkAndSendRetrievedData(); private: // atomic state of the bus. If true, we should stop @@ -108,6 +111,9 @@ class IoTFleetWiseEngine : public IDataReadyToPublishListener Platform::Signal mWait; Timer mTimer; + Timer mRetrySendingPersistedDataTimer; + uint64_t mPersistencyUploadRetryIntervalMs; + LoggingModule mLogger; std::shared_ptr mClock = ClockHandler::getClock(); std::unique_ptr mBinder; @@ -116,9 +122,6 @@ class IoTFleetWiseEngine : public IDataReadyToPublishListener std::shared_ptr mOBDOverCANModule; std::shared_ptr mDataCollectionSender; - EventTriggers mSignalEventTriggers; - EventTrigger mTimerBasedTrigger; - std::shared_ptr mAwsIotModule; std::shared_ptr mAwsIotChannelSendCanData; std::shared_ptr mAwsIotChannelSendCheckin; diff --git a/src/executionmanagement/src/IoTFleetWiseEngine.cpp b/src/executionmanagement/src/IoTFleetWiseEngine.cpp index f1f18985..4fc653f6 100644 --- a/src/executionmanagement/src/IoTFleetWiseEngine.cpp +++ b/src/executionmanagement/src/IoTFleetWiseEngine.cpp @@ -31,8 +31,13 @@ using namespace Aws::IoTFleetWise::Platform::PersistencyManagement; using Aws::IoTFleetWise::OffboardConnectivity::CollectionSchemeParams; using Aws::IoTFleetWise::OffboardConnectivity::ConnectivityError; +const uint32_t IoTFleetWiseEngine::MAX_NUMBER_OF_SIGNAL_TO_TRACE_LOG = 6; +const uint64_t IoTFleetWiseEngine::FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS = 1000; +const uint64_t IoTFleetWiseEngine::DEFAULT_RETRY_UPLOAD_PERSISTED_INTERVAL_MS = 10000; + IoTFleetWiseEngine::IoTFleetWiseEngine() - : mAwsIotChannelMetricsUpload( nullptr ) + : mPersistencyUploadRetryIntervalMs( 0 ) + , mAwsIotChannelMetricsUpload( nullptr ) , mAwsIotChannelLogsUpload( nullptr ) { TraceModule::get().sectionBegin( FWE_STARTUP ); @@ -68,7 +73,15 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) { mLogger.error( "IoTFleetWiseEngine::connect", " Failed to init persistency library " ); } - + if ( config["staticConfig"]["persistency"].isMember( "PersistencyUploadRetryIntervalMs" ) ) + { + mPersistencyUploadRetryIntervalMs = + static_cast( config["staticConfig"]["PersistencyUploadRetryIntervalMs"].asInt() ); + } + else + { + mPersistencyUploadRetryIntervalMs = DEFAULT_RETRY_UPLOAD_PERSISTED_INTERVAL_MS; + } // Payload Manager for offline data management mPayloadManager = std::make_shared( mPersistDecoderManifestCollectionSchemesAndData ); @@ -90,11 +103,6 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) /**************************Connectivity bootstrap begin*******************************/ mAwsIotModule = std::make_shared(); - mAwsIotModule->connect( config["staticConfig"]["mqttConnection"]["privateKeyFilename"].asString(), - config["staticConfig"]["mqttConnection"]["certificateFilename"].asString(), - config["staticConfig"]["mqttConnection"]["endpointUrl"].asString(), - config["staticConfig"]["mqttConnection"]["clientId"].asString(), - true ); // Only CAN data channel needs a payloadManager object for persistency and compression support, // for other components this will be nullptr @@ -143,6 +151,13 @@ IoTFleetWiseEngine::connect( const Json::Value &config ) config["staticConfig"]["publishToCloudParameters"]["maxPublishMessageCount"].asUInt(), canIDTranslator ); + // For asynchronous connect the call needs to be done after all channels created and setTopic calls + mAwsIotModule->connect( config["staticConfig"]["mqttConnection"]["privateKeyFilename"].asString(), + config["staticConfig"]["mqttConnection"]["certificateFilename"].asString(), + config["staticConfig"]["mqttConnection"]["endpointUrl"].asString(), + config["staticConfig"]["mqttConnection"]["clientId"].asString(), + true ); + /*************************Connectivity bootstrap end***************************************/ /*************************Remote Profiling bootstrap begin**********************************/ @@ -627,10 +642,10 @@ IoTFleetWiseEngine::doWork( void *data ) IoTFleetWiseEngine *engine = static_cast( data ); // Time in seconds double timeTrigger = 0; + bool uploadedPersistedDataOnce = false; TraceModule::get().sectionEnd( FWE_STARTUP ); - // Check if data was persisted, Retrieve all the data and send - engine->checkAndSendRetrievedData(); + engine->mRetrySendingPersistedDataTimer.reset(); while ( !engine->shouldStop() ) { @@ -644,10 +659,34 @@ IoTFleetWiseEngine::doWork( void *data ) engine->mTimer.reset(); uint32_t elapsedTimeUs = 0; + if ( !uploadedPersistedDataOnce ) + { + // Minimum delay one tenth of FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS + uint64_t timeToWaitMs = + IoTFleetWiseEngine::FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS - + std::min( static_cast( engine->mRetrySendingPersistedDataTimer.getElapsedMs().count() ), + IoTFleetWiseEngine::FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS ); + timeTrigger = static_cast( std::max( + IoTFleetWiseEngine::FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS / 10, timeToWaitMs ) ) / + 1000.0; + } + else if ( engine->mPersistencyUploadRetryIntervalMs > 0 ) + { + uint64_t timeToWaitMs = + engine->mPersistencyUploadRetryIntervalMs - + std::min( static_cast( engine->mRetrySendingPersistedDataTimer.getElapsedMs().count() ), + engine->mPersistencyUploadRetryIntervalMs ); + timeTrigger = static_cast( + std::max( IoTFleetWiseEngine::FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS, timeToWaitMs ) ) / + 1000.0; + } if ( timeTrigger > 0 ) { - engine->mLogger.trace( "IoTFleetWiseEngine::doWork", - "Waiting for :" + std::to_string( timeTrigger ) + "seconds" ); + engine->mLogger.trace( + "IoTFleetWiseEngine::doWork", + "Waiting for :" + std::to_string( timeTrigger ) + "seconds " + + std::to_string( engine->mPersistencyUploadRetryIntervalMs ) + " config" + + std::to_string( engine->mRetrySendingPersistedDataTimer.getElapsedMs().count() ) + " timer" ); engine->mWait.wait( static_cast( timeTrigger * 1000 ) ); } else @@ -687,6 +726,21 @@ IoTFleetWiseEngine::doWork( void *data ) engine->mDataCollectionSender->send( triggeredCollectionSchemeDataPtr ); } ); TraceModule::get().setVariable( QUEUE_INSPECTION_TO_SENDER, consumedElements ); + + if ( ( engine->mPersistencyUploadRetryIntervalMs > 0 && + ( static_cast( engine->mRetrySendingPersistedDataTimer.getElapsedMs().count() ) >= + engine->mPersistencyUploadRetryIntervalMs ) ) || + ( !uploadedPersistedDataOnce && + ( static_cast( engine->mRetrySendingPersistedDataTimer.getElapsedMs().count() ) >= + IoTFleetWiseEngine::FAST_RETRY_UPLOAD_PERSISTED_INTERVAL_MS ) ) ) + { + engine->mRetrySendingPersistedDataTimer.reset(); + if ( engine->mAwsIotModule->isAlive() ) + { + // Check if data was persisted, Retrieve all the data and send + uploadedPersistedDataOnce |= engine->checkAndSendRetrievedData(); + } + } } } @@ -696,7 +750,7 @@ IoTFleetWiseEngine::onDataReadyToPublish() mWait.notify(); } -void +bool IoTFleetWiseEngine::checkAndSendRetrievedData() { std::vector payloads; @@ -707,8 +761,8 @@ IoTFleetWiseEngine::checkAndSendRetrievedData() if ( status == SUCCESS ) { ConnectivityError res = ConnectivityError::Success; - mLogger.info( "IoTFleetWiseEngine::checkAndSendRetrievedData", - "Number of Payloads to transmit : " + std::to_string( payloads.size() ) ); + mLogger.trace( "IoTFleetWiseEngine::checkAndSendRetrievedData", + "Number of Payloads to transmit : " + std::to_string( payloads.size() ) ); for ( const auto &payload : payloads ) { @@ -732,16 +786,20 @@ IoTFleetWiseEngine::checkAndSendRetrievedData() // All the stored data has been transmitted, erase the file contents mPersistDecoderManifestCollectionSchemesAndData->erase( EDGE_TO_CLOUD_PAYLOAD ); mLogger.info( "IoTFleetWiseEngine::checkAndSendRetrievedData", - "All Payloads successfully successfully sent to the backend" ); + "All " + std::to_string( payloads.size() ) + " Payloads successfully sent to the backend" ); + return true; } + return false; } else if ( status == EMPTY ) { - mLogger.info( "IoTFleetWiseEngine::checkAndSendRetrievedData", "No Payloads to Retrieve" ); + mLogger.trace( "IoTFleetWiseEngine::checkAndSendRetrievedData", "No Payloads to Retrieve" ); + return true; } else { mLogger.error( "IoTFleetWiseEngine::checkAndSendRetrievedData", "Payload Retrieval Failed" ); + return false; } } diff --git a/src/offboardconnectivity/implementation/awsiotcpp/include/AwsIotConnectivityModule.h b/src/offboardconnectivity/implementation/awsiotcpp/include/AwsIotConnectivityModule.h index 34667a7d..d0205e18 100644 --- a/src/offboardconnectivity/implementation/awsiotcpp/include/AwsIotConnectivityModule.h +++ b/src/offboardconnectivity/implementation/awsiotcpp/include/AwsIotConnectivityModule.h @@ -63,7 +63,7 @@ class AwsIotConnectivityModule : public IRetryable, public IConnectivityModule * @param endpointUrl the endpoint URL normally in the format like * "[YOUR-THING]-ats.iot.us-west-2.amazonaws.com" * @param clientId the id that is used to identify this connection instance - * @param asynchronous if true launch a background thread + * @param asynchronous if true launch a background thread. * @return True if connecting was successful in the synchronous case or if asynchronous true if the establish * connection retry thread was successfully started */ @@ -111,6 +111,16 @@ class AwsIotConnectivityModule : public IRetryable, public IConnectivityModule void onFinished( RetryStatus code ) override; + /** + * @brief create a new channel sharing the connection of this module + * This call needs to be done before calling connect for all asynchronous subscribe channel + * @param payloadManager the payload manager used by the new channel, + * @param maximumIotSDKHeapMemoryBytes the iot sdk heap threshold in bytes after which this channel will stop + * sending data + * + * @return a pointer to the newly created channel. A reference to the newly created channel is also hold inside this + * module. + */ std::shared_ptr createNewChannel( const std::shared_ptr &payloadManager, uint64_t maximumIotSDKHeapMemoryBytes = AwsIotChannel::MAXIMUM_IOT_SDK_HEAP_MEMORY_BYTES ); diff --git a/src/offboardconnectivity/implementation/awsiotcpp/src/PayloadManager.cpp b/src/offboardconnectivity/implementation/awsiotcpp/src/PayloadManager.cpp index a06e88e4..3c32519f 100644 --- a/src/offboardconnectivity/implementation/awsiotcpp/src/PayloadManager.cpp +++ b/src/offboardconnectivity/implementation/awsiotcpp/src/PayloadManager.cpp @@ -58,7 +58,7 @@ PayloadManager::preparePayload( uint8_t *const buf, payloadHdr.compressionRequired = collectionSchemeParams.compression; memcpy( &buf[0], &payloadHdr, hdrSize ); - strncpy( reinterpret_cast( &buf[hdrSize] ), data.c_str(), payloadHdr.size ); + memcpy( reinterpret_cast( &buf[hdrSize] ), data.data(), payloadHdr.size ); return true; } @@ -113,8 +113,8 @@ PayloadManager::storeData( const std::uint8_t *buf, // set the successful storage flag to true isDataPersisted = true; mLogger.trace( "PayloadManager::storeData", - "Payload of size : " + std::to_string( totalWriteSize ) + - "Bytes has been successfully persisted" ); + "Payload of size : " + std::to_string( totalWriteSize ) + " Bytes (header: " + + std::to_string( sizeof( PayloadHeader ) ) + ") has been successfully persisted" ); } else { @@ -178,8 +178,10 @@ PayloadManager::retrieveData( std::vector &data ) if ( !payloadHdr.compressionRequired ) { mLogger.trace( "PayloadManager::retrieveData", - "CollectionScheme does not require compression, uncompress before transmitting the " - "persisted data." ); + "CollectionScheme does not require compression, uncompress " + + std::to_string( dataString.size() ) + + " bytes before transmitting the " + "persisted data." ); if ( !snappy::Uncompress( dataString.data(), dataString.size(), &payloadData ) ) { mLogger.error( diff --git a/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp b/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp index 4dbcb48c..a61d460a 100644 --- a/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp +++ b/src/platform/linux/persistencymanagement/src/CacheAndPersist.cpp @@ -239,7 +239,7 @@ CacheAndPersist::read( uint8_t *const readBufPtr, size_t size, DataType dataType } else { - size_t fileSize = getSize( dataType ); + size_t fileSize = std::min( mMaxPersistencePartitionSize, getSize( dataType ) ); if ( fileSize == 0 ) { status = EMPTY; diff --git a/tools/configure-fwe.sh b/tools/configure-fwe.sh index a6328c67..d5978ca7 100755 --- a/tools/configure-fwe.sh +++ b/tools/configure-fwe.sh @@ -20,6 +20,7 @@ CAN_BUS0="vcan0" USE_EXTENDED_IDS=false HAS_TRANSMISSION_ECU=false PERSISTENCY_PATH="/var/aws-iot-fleetwise/" +TOPIC_PREFIX="\$aws/iotfleetwise/" parse_args() { while [ "$#" -gt 0 ]; do @@ -48,16 +49,20 @@ parse_args() { --persistency-path) PERSISTENCY_PATH=$2 ;; + --topic-prefix) + TOPIC_PREFIX=$2 + ;; --help) echo "Usage: $0 [OPTION]" echo " --input-config-file Input JSON config file" echo " --output-config-file Output JSON config file" echo " --vehicle-id Vehicle ID" echo " --endpoint-url IoT Core MQTT endpoint URL" - echo " --can-bus0 CAN bus 0, default vcan0" + echo " --can-bus0 CAN bus 0, default: ${CAN_BUS0}" echo " --use-extended-ids Use extended CAN IDs for diagnostic communication" echo " --has-transmission-ecu Vehicle has automatic transmission" - echo " --persistency-path Desired persistency path, default $PERSISTENCY_PATH" + echo " --persistency-path Desired persistency path, default: ${PERSISTENCY_PATH}" + echo " --topic-prefix IoT MQTT topic prefix, default: ${TOPIC_PREFIX}" exit 0 ;; esac @@ -93,10 +98,10 @@ fi # Create the config file: jq ".staticConfig.mqttConnection.endpointUrl=\"${ENDPOINT_URL}\"" ${INPUT_CONFIG_FILE} \ | jq ".staticConfig.mqttConnection.clientId=\"${VEHICLE_ID}\"" \ - | jq ".staticConfig.mqttConnection.collectionSchemeListTopic=\"\$aws/iotfleetwise/vehicles/${VEHICLE_ID}/collection_schemes\"" \ - | jq ".staticConfig.mqttConnection.decoderManifestTopic=\"\$aws/iotfleetwise/vehicles/${VEHICLE_ID}/decoder_manifests\"" \ - | jq ".staticConfig.mqttConnection.canDataTopic=\"\$aws/iotfleetwise/vehicles/${VEHICLE_ID}/signals\"" \ - | jq ".staticConfig.mqttConnection.checkinTopic=\"\$aws/iotfleetwise/vehicles/${VEHICLE_ID}/checkins\"" \ + | jq ".staticConfig.mqttConnection.collectionSchemeListTopic=\"${TOPIC_PREFIX}vehicles/${VEHICLE_ID}/collection_schemes\"" \ + | jq ".staticConfig.mqttConnection.decoderManifestTopic=\"${TOPIC_PREFIX}vehicles/${VEHICLE_ID}/decoder_manifests\"" \ + | jq ".staticConfig.mqttConnection.canDataTopic=\"${TOPIC_PREFIX}vehicles/${VEHICLE_ID}/signals\"" \ + | jq ".staticConfig.mqttConnection.checkinTopic=\"${TOPIC_PREFIX}vehicles/${VEHICLE_ID}/checkins\"" \ | jq ".staticConfig.mqttConnection.certificateFilename=\"/etc/aws-iot-fleetwise/certificate.pem\"" \ | jq ".staticConfig.mqttConnection.privateKeyFilename=\"/etc/aws-iot-fleetwise/private-key.key\"" \ | jq ".staticConfig.internalParameters.systemWideLogLevel=\"Info\"" \ diff --git a/tools/install-deps-cross.sh b/tools/install-deps-cross.sh index 5f2cda74..e84610a7 100755 --- a/tools/install-deps-cross.sh +++ b/tools/install-deps-cross.sh @@ -93,6 +93,7 @@ git clone https://github.com/hartkopp/can-isotp.git cd can-isotp git checkout beb4650660179963a8ed5b5cbf2085cc1b34f608 cp include/uapi/linux/can/isotp.h /usr/include/linux/can +cd .. git clone -b v1.10.9 --recursive https://github.com/aws/aws-iot-device-sdk-cpp-v2.git cd aws-iot-device-sdk-cpp-v2 diff --git a/tools/install-socketcan.sh b/tools/install-socketcan.sh index 51bc5199..532ec074 100755 --- a/tools/install-socketcan.sh +++ b/tools/install-socketcan.sh @@ -33,13 +33,22 @@ parse_args() { parse_args "$@" # Install packages -apt update && apt install -y build-essential dkms can-utils git linux-modules-extra-`uname -r` +apt update && apt install -y build-essential dkms can-utils git + +# For EC2, the SocketCAN modules vcan and can-gw are included in a separate package: +if uname -r | grep -q aws; then + apt install -y linux-modules-extra-aws +fi # Install can-isotp kernel module: git clone https://github.com/hartkopp/can-isotp.git cd can-isotp git checkout beb4650660179963a8ed5b5cbf2085cc1b34f608 cd .. +if [ -d /usr/src/can-isotp-1.0 ]; then + sudo dkms remove can-isotp/1.0 --all + sudo rm -rf /usr/src/can-isotp-1.0 +fi sudo mv can-isotp /usr/src/can-isotp-1.0 sudo sed -e s/else// -e s/shell\ uname\ \-r/KERNELRELEASE/ -i /usr/src/can-isotp-1.0/Makefile sudo tee /usr/src/can-isotp-1.0/dkms.conf > /dev/null < /dev/null # Install setup-socketcan diff --git a/tools/provision.sh b/tools/provision.sh index fd7e348a..88af172e 100755 --- a/tools/provision.sh +++ b/tools/provision.sh @@ -50,12 +50,12 @@ parse_args() { --help) echo "Usage: $0 [OPTION]" echo " --vehicle-id Vehicle ID" - echo " --certificate-pem-outfile Certificate output file, default certificate.pem" - echo " --private-key-outfile Private key output file, default private-key.key" + echo " --certificate-pem-outfile Certificate output file, default: ${CERT_OUT_FILE}" + echo " --private-key-outfile Private key output file, default: ${PRIVATE_KEY_OUT_FILE}" echo " --endpoint-url-outfile Endpoint URL for MQTT connections output file" echo " --vehicle-id-outfile Vehicle ID output file" echo " --endpoint-url The endpoint URL used for AWS CLI calls" - echo " --region The region used for AWS CLI calls. Default: us-east-1" + echo " --region The region used for AWS CLI calls, default: ${REGION}" exit 0 ;; esac