diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index e6bb0e7b..27314b72 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -7,19 +7,118 @@ on: push: branches: - main + pull_request: + permissions: contents: write jobs: + build_firmware_size: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v4 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Run PlatformIO + run: pio ci --lib="." --build-dir="${{ github.workspace }}/../build" --keep-build-dir --project-conf="./tests/benchmarks/firmware_size/platformio.ini" ./tests/benchmarks/firmware_size/main.cpp + - name: Move firmware files # change path to location without parent dir ('..') statement (to make upload-artifact happy) + run: | + mkdir firmware + mv "${{ github.workspace }}/../build/.pio/build/v16/firmware.elf" firmware/firmware_v16.elf + mv "${{ github.workspace }}/../build/.pio/build/v201/firmware.elf" firmware/firmware_v201.elf + - name: Upload firmware linker files + uses: actions/upload-artifact@v4 + with: + name: Firmware linker files + path: firmware + if-no-files-found: error + retention-days: 1 + + evaluate_firmware: + needs: build_firmware_size + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - uses: actions/cache@v2 + with: + key: ${{ github.ref }} + path: .cache + - name: Install Python dependencies + run: pip install pandas + - name: Get build tools + run: | + sudo apt update + sudo apt install build-essential cmake ninja-build + sudo apt -y install gcc-9 g++-9 + g++ --version + - name: Check out bloaty + uses: actions/checkout@v3 + with: + repository: google/bloaty + ref: 379d5305670c00c36a57e608079fd253f13bde63 + path: tools/bloaty + submodules: recursive + - name: Install bloaty + run: | + cmake -B tools/bloaty/build -G Ninja -S tools/bloaty + cmake --build tools/bloaty/build -j 32 + - name: Get firmware linker files + uses: actions/download-artifact@v4 + with: + name: Firmware linker files + path: firmware + - name: Run bloaty + run: | + mkdir docs/assets + mkdir docs/assets/tables + tools/bloaty/build/bloaty firmware/firmware_v16.elf -d compileunits --csv -n 0 > docs/assets/tables/bloaty_v16.csv + tools/bloaty/build/bloaty firmware/firmware_v201.elf -d compileunits --csv -n 0 > docs/assets/tables/bloaty_v201.csv + - name: Evaluate and create reports + run: python tests/benchmarks/scripts/eval_firmware_size.py + - name: Upload reports + uses: actions/upload-artifact@v4 + with: + name: Firmware size reports CSV + path: docs/assets/tables + if-no-files-found: error + deploy: + needs: evaluate_firmware runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.x - - uses: actions/cache@v2 - with: - key: ${{ github.ref }} - path: .cache - - run: pip install mkdocs-material - - run: mkdocs gh-deploy --force + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - uses: actions/cache@v2 + with: + key: ${{ github.ref }} + path: .cache + - name: Install Python dependencies + run: pip install pandas mkdocs-material mkdocs-table-reader-plugin + - name: Get firmware size reports + uses: actions/download-artifact@v4 + with: + name: Firmware size reports CSV + path: docs/assets/tables + - name: Run mkdocs + run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index 56868dff..e04a3bf0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ src/ArduinoJson* src/main.cpp tests/helpers/ArduinoJson* coverage.info +docs/assets diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0a8467..426e1afe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Support for TransactionMessageAttempts/-RetryInterval ([#345](https://github.com/matth-x/MicroOcpp/pull/345)) - Heap profiler and custom allocator support ([#350](https://github.com/matth-x/MicroOcpp/pull/350)) - Migration of persistent storage ([#355](https://github.com/matth-x/MicroOcpp/pull/355)) +- Benchmarks pipeline ([#369](https://github.com/matth-x/MicroOcpp/pull/369)) ### Removed diff --git a/docs/benchmarks.md b/docs/benchmarks.md new file mode 100644 index 00000000..f304dd6f --- /dev/null +++ b/docs/benchmarks.md @@ -0,0 +1,47 @@ +# Benchmarks + +Microcontrollers have tight hardware constraints which affect how much resources the firmware can demand. It is important to make sure that the available resources are not depleted to allow for robust operation and that there is sufficient flash head room to allow for future software upgrades. + +In general, microcontrollers have three relevant hardware constraints: + +- Limited processing speed +- Limited memory size +- Limited flash size + +For OCPP, the relevant bottlenecks are especially the memory and flash size. The processing speed is no concern, since OCPP is not computationally complex and does not include any extensive planning algorithms on the charger size. A previous [benchmark on the ESP-IDF](https://github.com/matth-x/MicroOcpp-benchmark) showed that the processing times are in the lower milliseconds range and are probably outweighed by IO times and network round trip times. + +However, the memory and flash requirements are important figures, because the device model of OCPP has a significant size. The microcontroller needs to keep the model data in the heap memory for the largest part and the firmware which covers the corresponding processing routines needs to have sufficient space on flash. + +This chapter presents benchmarks of the memory and flash requirements. They should help to determine the required microcontroller capabilities, or to give general insights for taking further action on optimizing the firmware. + +## Firmware size + +When compiling a firmware with MicroOCPP, the resulting binary will contain functionality which is not related to OCPP, like hardware drivers, modules which are shared, like MbedTLS and the actual MicroOCPP object files. The size of the latter is the final flash requirement of MicroOCPP. + +For the flash benchmark, the profiler compiles a [dummy OCPP firmware](https://github.com/matth-x/MicroOcpp/tree/main/tests/benchmarks/firmware_size/main.cpp), analyzes the size of the compilation units using [bloaty](https://github.com/google/bloaty) and evaluates the bloaty report using a [Python script](https://github.com/matth-x/MicroOcpp/tree/main/tests/benchmarks/scripts/eval_firmware_size.py). To give realistic results, the firwmare is compiled with `-Os`, no RTTI or exceptions and newlib as the standard C library. The following tables show the results. + +### OCPP 1.6 + +The following table shows the cumulated size of the objects files per module. The Module category consists of the OCPP 2.0.1 functional blocks, OCPP 1.6 feature profiles and general functionality which is shared accross the library. If a feature of the implementation falls under both an OCPP 2.0.1 functional block and OCPP 1.6 feature profile definition, it is preferrably assigned to the OCPP 2.0.1 category. This allows for better comparability between both OCPP versions. + +**Table 1: Firmware size per Module** + +{{ read_csv('modules_v16.csv') }} + +### OCPP 2.0.1 + +**Table 2: Firmware size per Module** + +{{ read_csv('modules_v201.csv') }} + +## Full data sets + +This section contains the raw data which is the basis for the evaluations above. + +**Table 3: All compilation units for OCPP 1.6 firmware** + +{{ read_csv('compile_units_v16.csv') }} + +**Table 4: All compilation units for OCPP 2.0.1 firmware** + +{{ read_csv('compile_units_v201.csv') }} diff --git a/mkdocs.yml b/mkdocs.yml index 0bc66499..22027d12 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,3 +48,8 @@ theme: extra_css: - stylesheets/extra.css + +plugins: + - search + - table-reader: + data_path: "docs/assets/tables" diff --git a/tests/benchmarks/firmware_size/main.cpp b/tests/benchmarks/firmware_size/main.cpp new file mode 100644 index 00000000..f1d07cd1 --- /dev/null +++ b/tests/benchmarks/firmware_size/main.cpp @@ -0,0 +1,68 @@ +// matth-x/MicroOcpp +// Copyright Matthias Akstaller 2019 - 2024 +// MIT License + +#include +#include +#include +#include + +MicroOcpp::LoopbackConnection g_loopback; + +void setup() { + + ocpp_deinitialize(); + +#if MO_ENABLE_V201 + mocpp_initialize(g_loopback, ChargerCredentials::v201(),MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail),true,MicroOcpp::ProtocolVersion(2,0,1)); +#else + mocpp_initialize(g_loopback, ChargerCredentials()); +#endif + + ocpp_beginTransaction(""); + ocpp_beginTransaction_authorized("",""); + ocpp_endTransaction("",""); + ocpp_endTransaction_authorized("",""); + ocpp_isTransactionActive(); + ocpp_isTransactionRunning(); + ocpp_getTransactionIdTag(); + ocpp_getTransaction(); + ocpp_ocppPermitsCharge(); + ocpp_getChargePointStatus(); + ocpp_setConnectorPluggedInput([] () {return false;}); + ocpp_setEnergyMeterInput([] () {return 0;}); + ocpp_setPowerMeterInput([] () {return 0.f;}); + ocpp_setSmartChargingPowerOutput([] (float) {}); + ocpp_setSmartChargingCurrentOutput([] (float) {}); + ocpp_setSmartChargingOutput([] (float,float,int) {}); + ocpp_setEvReadyInput([] () {return false;}); + ocpp_setEvseReadyInput([] () {return false;}); + ocpp_addErrorCodeInput([] () {return (const char*)nullptr;}); + addErrorDataInput([] () {return MicroOcpp::ErrorData("");}); + ocpp_addMeterValueInputFloat([] () {return 0.f;},"","","",""); + ocpp_setOccupiedInput([] () {return false;}); + ocpp_setStartTxReadyInput([] () {return false;}); + ocpp_setStopTxReadyInput([] () {return false;}); + ocpp_setTxNotificationOutput([] (OCPP_Transaction*, OCPP_TxNotification) {}); + +#if MO_ENABLE_CONNECTOR_LOCK + ocpp_setOnUnlockConnectorInOut([] () {return UnlockConnectorResult_UnlockFailed;}); +#endif + + isOperative(); + setOnResetNotify([] (bool) {return false;}); + setOnResetExecute([] (bool) {return false;}); + getFirmwareService()->getFirmwareStatus(); + getDiagnosticsService()->getDiagnosticsStatus(); + +#if MO_ENABLE_CERT_MGMT + setCertificateStore(nullptr); +#endif + + getOcppContext(); + +} + +void loop() { + mocpp_loop(); +} diff --git a/tests/benchmarks/firmware_size/platformio.ini b/tests/benchmarks/firmware_size/platformio.ini new file mode 100644 index 00000000..121a8de5 --- /dev/null +++ b/tests/benchmarks/firmware_size/platformio.ini @@ -0,0 +1,38 @@ +; matth-x/MicroOcpp +; Copyright Matthias Akstaller 2019 - 2024 +; MIT License + +[common] +platform = espressif32@6.8.1 +board = esp-wrover-kit +framework = arduino +lib_deps = + bblanchon/ArduinoJson@6.20.1 +build_flags= + -D MO_DBG_LEVEL=MO_DL_NONE ; don't take debug messages into account + -D MO_CUSTOM_WS + +[env:v16] +platform = ${common.platform} +board = ${common.board} +framework = ${common.framework} +lib_deps = ${common.lib_deps} +build_flags = + ${common.build_flags} + -D MO_ENABLE_MBEDTLS=1 + -D MO_ENABLE_CERT_MGMT=1 + -D MO_ENABLE_RESERVATION=1 + -D MO_ENABLE_LOCAL_AUTH=1 + -D MO_REPORT_NOERROR=1 + -D MO_ENABLE_CONNECTOR_LOCK=1 + +[env:v201] +platform = ${common.platform} +board = ${common.board} +framework = ${common.framework} +lib_deps = ${common.lib_deps} +build_flags = + ${common.build_flags} + -D MO_ENABLE_V201=1 + -D MO_ENABLE_MBEDTLS=1 + -D MO_ENABLE_CERT_MGMT=1 diff --git a/tests/benchmarks/scripts/eval_firmware_size.py b/tests/benchmarks/scripts/eval_firmware_size.py new file mode 100755 index 00000000..653cf00f --- /dev/null +++ b/tests/benchmarks/scripts/eval_firmware_size.py @@ -0,0 +1,350 @@ +import sys +import numpy as np +import pandas as pd + +# load data + +COLUMN_BINSIZE = 'Binary size (Bytes)' + +def load_compilation_units(fn): + df = pd.read_csv(fn, index_col="compileunits").filter(like="lib/MicroOcpp/src/MicroOcpp", axis=0).filter(['Module','v16','v201','vmsize'], axis=1).sort_index() + df.index.names = ['Compile Unit'] + df.index = df.index.map(lambda s: s[len("lib/MicroOcpp/src/"):] if s.startswith("lib/MicroOcpp/src/") else s) + df.index = df.index.map(lambda s: s[len("MicroOcpp/"):] if s.startswith("MicroOcpp/") else s) + df.rename(columns={'vmsize': COLUMN_BINSIZE}, inplace=True) + return df + +cunits_v16 = load_compilation_units('docs/assets/tables/bloaty_v16.csv') +cunits_v201 = load_compilation_units('docs/assets/tables/bloaty_v201.csv') + +# categorize data + +def categorize_table(df): + + df["v16"] = ' ' + df["v201"] = ' ' + df["Module"] = '' + + TICK = 'x' + + MODULE_GENERAL = 'General' + MODULE_HAL = 'General - Hardware Abstraction Layer' + MODULE_RPC = 'General - RPC framework' + MODULE_API = 'General - API' + MODULE_CORE = 'Core' + MODULE_CONFIGURATION = 'Configuration' + MODULE_FW_MNGT = 'Firmware Management' + MODULE_TRIGGERMESSAGE = 'TriggerMessage' + MODULE_SECURITY = 'A - Security' + MODULE_PROVISIONING = 'B - Provisioning' + MODULE_PROVISIONING_VARS = 'B - Provisioning - Variables' + MODULE_AUTHORIZATION = 'C - Authorization' + MODULE_LOCALAUTH = 'D - Local Authorization List Management' + MODULE_TX = 'E - Transactions' + MODULE_AVAILABILITY = 'G - Availability' + MODULE_RESERVATION = 'H - Reservation' + MODULE_METERVALUES = 'J - MeterValues' + MODULE_SMARTCHARGING = 'K - SmartCharging' + MODULE_CERTS = 'M - Certificate Management' + + df.at['MicroOcpp.cpp', 'v16'] = TICK + df.at['MicroOcpp.cpp', 'v201'] = TICK + df.at['MicroOcpp.cpp', 'Module'] = MODULE_API + df.at['Core/Configuration.cpp', 'v16'] = TICK + df.at['Core/Configuration.cpp', 'v201'] = TICK + df.at['Core/Configuration.cpp', 'Module'] = MODULE_CONFIGURATION + if 'Core/Configuration_c.cpp' in df.index: + df.at['Core/Configuration_c.cpp', 'v16'] = TICK + df.at['Core/Configuration_c.cpp', 'v201'] = TICK + df.at['Core/Configuration_c.cpp', 'Module'] = MODULE_CONFIGURATION + df.at['Core/ConfigurationContainer.cpp', 'v16'] = TICK + df.at['Core/ConfigurationContainer.cpp', 'v201'] = TICK + df.at['Core/ConfigurationContainer.cpp', 'Module'] = MODULE_CONFIGURATION + df.at['Core/ConfigurationContainerFlash.cpp', 'v16'] = TICK + df.at['Core/ConfigurationContainerFlash.cpp', 'v201'] = TICK + df.at['Core/ConfigurationContainerFlash.cpp', 'Module'] = MODULE_CONFIGURATION + df.at['Core/ConfigurationKeyValue.cpp', 'v16'] = TICK + df.at['Core/ConfigurationKeyValue.cpp', 'v201'] = TICK + df.at['Core/ConfigurationKeyValue.cpp', 'Module'] = MODULE_CONFIGURATION + df.at['Core/Connection.cpp', 'v16'] = TICK + df.at['Core/Connection.cpp', 'v201'] = TICK + df.at['Core/Connection.cpp', 'Module'] = MODULE_HAL + df.at['Core/Context.cpp', 'v16'] = TICK + df.at['Core/Context.cpp', 'v201'] = TICK + df.at['Core/Context.cpp', 'Module'] = MODULE_GENERAL + df.at['Core/FilesystemAdapter.cpp', 'v16'] = TICK + df.at['Core/FilesystemAdapter.cpp', 'v201'] = TICK + df.at['Core/FilesystemAdapter.cpp', 'Module'] = MODULE_HAL + df.at['Core/FilesystemUtils.cpp', 'v16'] = TICK + df.at['Core/FilesystemUtils.cpp', 'v201'] = TICK + df.at['Core/FilesystemUtils.cpp', 'Module'] = MODULE_GENERAL + df.at['Core/FtpMbedTLS.cpp', 'v16'] = TICK + df.at['Core/FtpMbedTLS.cpp', 'v201'] = TICK + df.at['Core/FtpMbedTLS.cpp', 'Module'] = MODULE_GENERAL + df.at['Core/Memory.cpp', 'v16'] = TICK + df.at['Core/Memory.cpp', 'v201'] = TICK + df.at['Core/Memory.cpp', 'Module'] = MODULE_GENERAL + df.at['Core/Operation.cpp', 'v16'] = TICK + df.at['Core/Operation.cpp', 'v201'] = TICK + df.at['Core/Operation.cpp', 'Module'] = MODULE_RPC + df.at['Core/OperationRegistry.cpp', 'v16'] = TICK + df.at['Core/OperationRegistry.cpp', 'v201'] = TICK + df.at['Core/OperationRegistry.cpp', 'Module'] = MODULE_RPC + df.at['Core/Request.cpp', 'v16'] = TICK + df.at['Core/Request.cpp', 'v201'] = TICK + df.at['Core/Request.cpp', 'Module'] = MODULE_RPC + df.at['Core/RequestQueue.cpp', 'v16'] = TICK + df.at['Core/RequestQueue.cpp', 'v201'] = TICK + df.at['Core/RequestQueue.cpp', 'Module'] = MODULE_RPC + df.at['Core/Time.cpp', 'v16'] = TICK + df.at['Core/Time.cpp', 'v201'] = TICK + df.at['Core/Time.cpp', 'Module'] = MODULE_GENERAL + if 'Debug.cpp' in df.index: + df.at['Debug.cpp', 'v16'] = TICK + df.at['Debug.cpp', 'v201'] = TICK + df.at['Debug.cpp', 'Module'] = MODULE_HAL + df.at['Model/Authorization/AuthorizationData.cpp', 'v16'] = TICK + df.at['Model/Authorization/AuthorizationData.cpp', 'Module'] = MODULE_LOCALAUTH + df.at['Model/Authorization/AuthorizationList.cpp', 'v16'] = TICK + df.at['Model/Authorization/AuthorizationList.cpp', 'Module'] = MODULE_LOCALAUTH + df.at['Model/Authorization/AuthorizationService.cpp', 'v16'] = TICK + df.at['Model/Authorization/AuthorizationService.cpp', 'Module'] = MODULE_LOCALAUTH + if 'Model/Authorization/IdToken.cpp' in df.index: + df.at['Model/Authorization/IdToken.cpp', 'v201'] = TICK + df.at['Model/Authorization/IdToken.cpp', 'Module'] = MODULE_AUTHORIZATION + if 'Model/Availability/AvailabilityService.cpp' in df.index: + df.at['Model/Availability/AvailabilityService.cpp', 'v16'] = TICK + df.at['Model/Availability/AvailabilityService.cpp', 'v201'] = TICK + df.at['Model/Availability/AvailabilityService.cpp', 'Module'] = MODULE_AVAILABILITY + df.at['Model/Boot/BootService.cpp', 'v16'] = TICK + df.at['Model/Boot/BootService.cpp', 'v201'] = TICK + df.at['Model/Boot/BootService.cpp', 'Module'] = MODULE_PROVISIONING + df.at['Model/Certificates/Certificate.cpp', 'v16'] = TICK + df.at['Model/Certificates/Certificate.cpp', 'v201'] = TICK + df.at['Model/Certificates/Certificate.cpp', 'Module'] = MODULE_CERTS + df.at['Model/Certificates/CertificateMbedTLS.cpp', 'v16'] = TICK + df.at['Model/Certificates/CertificateMbedTLS.cpp', 'v201'] = TICK + df.at['Model/Certificates/CertificateMbedTLS.cpp', 'Module'] = MODULE_CERTS + if 'Model/Certificates/Certificate_c.cpp' in df.index: + df.at['Model/Certificates/Certificate_c.cpp', 'v16'] = TICK + df.at['Model/Certificates/Certificate_c.cpp', 'v201'] = TICK + df.at['Model/Certificates/Certificate_c.cpp', 'Module'] = MODULE_CERTS + df.at['Model/Certificates/CertificateService.cpp', 'v16'] = TICK + df.at['Model/Certificates/CertificateService.cpp', 'v201'] = TICK + df.at['Model/Certificates/CertificateService.cpp', 'Module'] = MODULE_CERTS + df.at['Model/ConnectorBase/Connector.cpp', 'v16'] = TICK + df.at['Model/ConnectorBase/Connector.cpp', 'Module'] = MODULE_CORE + df.at['Model/ConnectorBase/ConnectorsCommon.cpp', 'v16'] = TICK + df.at['Model/ConnectorBase/ConnectorsCommon.cpp', 'Module'] = MODULE_CORE + if 'Model/ConnectorBase/Notification.cpp' in df.index: + df.at['Model/ConnectorBase/Notification.cpp', 'v16'] = TICK + df.at['Model/ConnectorBase/Notification.cpp', 'Module'] = MODULE_CORE + df.at['Model/Diagnostics/DiagnosticsService.cpp', 'v16'] = TICK + df.at['Model/Diagnostics/DiagnosticsService.cpp', 'Module'] = MODULE_FW_MNGT + df.at['Model/FirmwareManagement/FirmwareService.cpp', 'v16'] = TICK + df.at['Model/FirmwareManagement/FirmwareService.cpp', 'Module'] = MODULE_FW_MNGT + df.at['Model/Heartbeat/HeartbeatService.cpp', 'v16'] = TICK + df.at['Model/Heartbeat/HeartbeatService.cpp', 'v201'] = TICK + df.at['Model/Heartbeat/HeartbeatService.cpp', 'Module'] = MODULE_AVAILABILITY + df.at['Model/Metering/MeteringConnector.cpp', 'v16'] = TICK + df.at['Model/Metering/MeteringConnector.cpp', 'Module'] = MODULE_METERVALUES + df.at['Model/Metering/MeteringService.cpp', 'v16'] = TICK + df.at['Model/Metering/MeteringService.cpp', 'Module'] = MODULE_METERVALUES + df.at['Model/Metering/MeterStore.cpp', 'v16'] = TICK + df.at['Model/Metering/MeterStore.cpp', 'Module'] = MODULE_METERVALUES + df.at['Model/Metering/MeterValue.cpp', 'v16'] = TICK + df.at['Model/Metering/MeterValue.cpp', 'Module'] = MODULE_METERVALUES + df.at['Model/Metering/SampledValue.cpp', 'v16'] = TICK + df.at['Model/Metering/SampledValue.cpp', 'Module'] = MODULE_METERVALUES + df.at['Model/Model.cpp', 'v16'] = TICK + df.at['Model/Model.cpp', 'v201'] = TICK + df.at['Model/Model.cpp', 'Module'] = MODULE_GENERAL + df.at['Model/Reservation/Reservation.cpp', 'v16'] = TICK + df.at['Model/Reservation/Reservation.cpp', 'Module'] = MODULE_RESERVATION + df.at['Model/Reservation/ReservationService.cpp', 'v16'] = TICK + df.at['Model/Reservation/ReservationService.cpp', 'Module'] = MODULE_RESERVATION + df.at['Model/Reset/ResetService.cpp', 'v16'] = TICK + df.at['Model/Reset/ResetService.cpp', 'v201'] = TICK + df.at['Model/Reset/ResetService.cpp', 'Module'] = MODULE_PROVISIONING + df.at['Model/SmartCharging/SmartChargingModel.cpp', 'v16'] = TICK + df.at['Model/SmartCharging/SmartChargingModel.cpp', 'Module'] = MODULE_SMARTCHARGING + df.at['Model/SmartCharging/SmartChargingService.cpp', 'v16'] = TICK + df.at['Model/SmartCharging/SmartChargingService.cpp', 'Module'] = MODULE_SMARTCHARGING + df.at['Model/Transactions/Transaction.cpp', 'v16'] = TICK + df.at['Model/Transactions/Transaction.cpp', 'v201'] = TICK + df.at['Model/Transactions/Transaction.cpp', 'Module'] = MODULE_TX + df.at['Model/Transactions/TransactionDeserialize.cpp', 'v16'] = TICK + df.at['Model/Transactions/TransactionDeserialize.cpp', 'Module'] = MODULE_TX + if 'Model/Transactions/TransactionService.cpp' in df.index: + df.at['Model/Transactions/TransactionService.cpp', 'v201'] = TICK + df.at['Model/Transactions/TransactionService.cpp', 'Module'] = MODULE_TX + df.at['Model/Transactions/TransactionStore.cpp', 'v16'] = TICK + df.at['Model/Transactions/TransactionStore.cpp', 'Module'] = MODULE_TX + if 'Model/Variables/Variable.cpp' in df.index: + df.at['Model/Variables/Variable.cpp', 'v201'] = TICK + df.at['Model/Variables/Variable.cpp', 'Module'] = MODULE_PROVISIONING_VARS + if 'Model/Variables/VariableContainer.cpp' in df.index: + df.at['Model/Variables/VariableContainer.cpp', 'v201'] = TICK + df.at['Model/Variables/VariableContainer.cpp', 'Module'] = MODULE_PROVISIONING_VARS + if 'Model/Variables/VariableService.cpp' in df.index: + df.at['Model/Variables/VariableService.cpp', 'v201'] = TICK + df.at['Model/Variables/VariableService.cpp', 'Module'] = MODULE_PROVISIONING_VARS + df.at['Operations/Authorize.cpp', 'v16'] = TICK + df.at['Operations/Authorize.cpp', 'v201'] = TICK + df.at['Operations/Authorize.cpp', 'Module'] = MODULE_AUTHORIZATION + df.at['Operations/BootNotification.cpp', 'v16'] = TICK + df.at['Operations/BootNotification.cpp', 'v201'] = TICK + df.at['Operations/BootNotification.cpp', 'Module'] = MODULE_PROVISIONING + df.at['Operations/CancelReservation.cpp', 'v16'] = TICK + df.at['Operations/CancelReservation.cpp', 'Module'] = MODULE_RESERVATION + df.at['Operations/ChangeAvailability.cpp', 'v16'] = TICK + df.at['Operations/ChangeAvailability.cpp', 'Module'] = MODULE_AVAILABILITY + df.at['Operations/ChangeConfiguration.cpp', 'v16'] = TICK + df.at['Operations/ChangeConfiguration.cpp', 'Module'] = MODULE_CONFIGURATION + df.at['Operations/ClearCache.cpp', 'v16'] = TICK + df.at['Operations/ClearCache.cpp', 'Module'] = MODULE_CORE + df.at['Operations/ClearChargingProfile.cpp', 'v16'] = TICK + df.at['Operations/ClearChargingProfile.cpp', 'Module'] = MODULE_SMARTCHARGING + df.at['Operations/CustomOperation.cpp', 'v16'] = TICK + df.at['Operations/CustomOperation.cpp', 'v201'] = TICK + df.at['Operations/CustomOperation.cpp', 'Module'] = MODULE_RPC + df.at['Operations/DataTransfer.cpp', 'v16'] = TICK + df.at['Operations/DataTransfer.cpp', 'Module'] = MODULE_CORE + df.at['Operations/DeleteCertificate.cpp', 'v16'] = TICK + df.at['Operations/DeleteCertificate.cpp', 'v201'] = TICK + df.at['Operations/DeleteCertificate.cpp', 'Module'] = MODULE_CERTS + df.at['Operations/DiagnosticsStatusNotification.cpp', 'v16'] = TICK + df.at['Operations/DiagnosticsStatusNotification.cpp', 'Module'] = MODULE_FW_MNGT + df.at['Operations/FirmwareStatusNotification.cpp', 'v16'] = TICK + df.at['Operations/FirmwareStatusNotification.cpp', 'Module'] = MODULE_FW_MNGT + if 'Operations/GetBaseReport.cpp' in df.index: + df.at['Operations/GetBaseReport.cpp', 'v201'] = TICK + df.at['Operations/GetBaseReport.cpp', 'Module'] = MODULE_PROVISIONING_VARS + df.at['Operations/GetCompositeSchedule.cpp', 'v16'] = TICK + df.at['Operations/GetCompositeSchedule.cpp', 'Module'] = MODULE_SMARTCHARGING + df.at['Operations/GetConfiguration.cpp', 'v16'] = TICK + df.at['Operations/GetConfiguration.cpp', 'v201'] = TICK + df.at['Operations/GetConfiguration.cpp', 'Module'] = MODULE_CONFIGURATION + df.at['Operations/GetDiagnostics.cpp', 'v16'] = TICK + df.at['Operations/GetDiagnostics.cpp', 'Module'] = MODULE_FW_MNGT + df.at['Operations/GetInstalledCertificateIds.cpp', 'v16'] = TICK + df.at['Operations/GetInstalledCertificateIds.cpp', 'Module'] = MODULE_SMARTCHARGING + df.at['Operations/GetLocalListVersion.cpp', 'v16'] = TICK + df.at['Operations/GetLocalListVersion.cpp', 'Module'] = MODULE_LOCALAUTH + if 'Operations/GetVariables.cpp' in df.index: + df.at['Operations/GetVariables.cpp', 'v201'] = TICK + df.at['Operations/GetVariables.cpp', 'Module'] = MODULE_PROVISIONING_VARS + df.at['Operations/Heartbeat.cpp', 'v16'] = TICK + df.at['Operations/Heartbeat.cpp', 'v201'] = TICK + df.at['Operations/Heartbeat.cpp', 'Module'] = MODULE_AVAILABILITY + df.at['Operations/InstallCertificate.cpp', 'v16'] = TICK + df.at['Operations/InstallCertificate.cpp', 'v201'] = TICK + df.at['Operations/InstallCertificate.cpp', 'Module'] = MODULE_CERTS + df.at['Operations/MeterValues.cpp', 'v16'] = TICK + df.at['Operations/MeterValues.cpp', 'Module'] = MODULE_METERVALUES + if 'Operations/NotifyReport.cpp' in df.index: + df.at['Operations/NotifyReport.cpp', 'v201'] = TICK + df.at['Operations/NotifyReport.cpp', 'Module'] = MODULE_PROVISIONING_VARS + df.at['Operations/RemoteStartTransaction.cpp', 'v16'] = TICK + df.at['Operations/RemoteStartTransaction.cpp', 'Module'] = MODULE_TX + df.at['Operations/RemoteStopTransaction.cpp', 'v16'] = TICK + df.at['Operations/RemoteStopTransaction.cpp', 'Module'] = MODULE_TX + if 'Operations/RequestStartTransaction.cpp' in df.index: + df.at['Operations/RequestStartTransaction.cpp', 'v201'] = TICK + df.at['Operations/RequestStartTransaction.cpp', 'Module'] = MODULE_TX + if 'Operations/RequestStopTransaction.cpp' in df.index: + df.at['Operations/RequestStopTransaction.cpp', 'v201'] = TICK + df.at['Operations/RequestStopTransaction.cpp', 'Module'] = MODULE_TX + df.at['Operations/ReserveNow.cpp', 'v16'] = TICK + df.at['Operations/ReserveNow.cpp', 'Module'] = MODULE_RESERVATION + df.at['Operations/Reset.cpp', 'v16'] = TICK + df.at['Operations/Reset.cpp', 'v201'] = TICK + df.at['Operations/Reset.cpp', 'Module'] = MODULE_PROVISIONING + if 'Operations/SecurityEventNotification.cpp' in df.index: + df.at['Operations/SecurityEventNotification.cpp', 'v201'] = TICK + df.at['Operations/SecurityEventNotification.cpp', 'Module'] = MODULE_SECURITY + df.at['Operations/SendLocalList.cpp', 'v16'] = TICK + df.at['Operations/SendLocalList.cpp', 'Module'] = MODULE_LOCALAUTH + df.at['Operations/SetChargingProfile.cpp', 'v16'] = TICK + df.at['Operations/SetChargingProfile.cpp', 'Module'] = MODULE_SMARTCHARGING + if 'Operations/SetVariables.cpp' in df.index: + df.at['Operations/SetVariables.cpp', 'v201'] = TICK + df.at['Operations/SetVariables.cpp', 'Module'] = MODULE_PROVISIONING_VARS + df.at['Operations/StartTransaction.cpp', 'v16'] = TICK + df.at['Operations/StartTransaction.cpp', 'Module'] = MODULE_TX + df.at['Operations/StatusNotification.cpp', 'v16'] = TICK + df.at['Operations/StatusNotification.cpp', 'v201'] = TICK + df.at['Operations/StatusNotification.cpp', 'Module'] = MODULE_AVAILABILITY + df.at['Operations/StopTransaction.cpp', 'v16'] = TICK + df.at['Operations/StopTransaction.cpp', 'Module'] = MODULE_TX + if 'Operations/TransactionEvent.cpp' in df.index: + df.at['Operations/TransactionEvent.cpp', 'v201'] = TICK + df.at['Operations/TransactionEvent.cpp', 'Module'] = MODULE_TX + df.at['Operations/TriggerMessage.cpp', 'v16'] = TICK + df.at['Operations/TriggerMessage.cpp', 'Module'] = MODULE_TRIGGERMESSAGE + df.at['Operations/UnlockConnector.cpp', 'v16'] = TICK + df.at['Operations/UnlockConnector.cpp', 'Module'] = MODULE_CORE + df.at['Operations/UpdateFirmware.cpp', 'v16'] = TICK + df.at['Operations/UpdateFirmware.cpp', 'Module'] = MODULE_FW_MNGT + if 'MicroOcpp_c.cpp' in df.index: + df.at['MicroOcpp_c.cpp', 'v16'] = TICK + df.at['MicroOcpp_c.cpp', 'v201'] = TICK + df.at['MicroOcpp_c.cpp', 'Module'] = MODULE_API + + print(df) + +categorize_table(cunits_v16) +categorize_table(cunits_v201) + +categorize_success = True + +if cunits_v16[COLUMN_BINSIZE].isnull().any(): + print('Error: categorized the following compilation units erroneously (v16):\n') + print(cunits_v16.loc[cunits_v16[COLUMN_BINSIZE].isnull()]) + categorize_success = False + +if cunits_v201[COLUMN_BINSIZE].isnull().any(): + print('Error: categorized the following compilation units erroneously (v201):\n') + print(cunits_v201.loc[cunits_v201[COLUMN_BINSIZE].isnull()]) + categorize_success = False + +if (cunits_v16['Module'].values == '').sum() > 0: + print('Error: did not categorize the following compilation units (v16):\n') + print(cunits_v16.loc[cunits_v16['Module'].values == '']) + categorize_success = False + +if (cunits_v201['Module'].values == '').sum() > 0: + print('Error: did not categorize the following compilation units (v201):\n') + print(cunits_v201.loc[cunits_v201['Module'].values == '']) + categorize_success = False + +if not categorize_success: + sys.exit('\nError categorizing compilation units') + +# store csv with all details + +print('Uncategorized compile units (v16): ', (cunits_v16['Module'].values == '').sum()) +print('Uncategorized compile units (v201): ', (cunits_v201['Module'].values == '').sum()) + +cunits_v16.to_csv("docs/assets/tables/compile_units_v16.csv") +cunits_v201.to_csv("docs/assets/tables/compile_units_v201.csv") + +# store csv with size by Module for v16 + +modules_v16 = cunits_v16.loc[cunits_v16['v16'].values == 'x'].sort_index() +modules_v16_by_module = modules_v16[['Module', COLUMN_BINSIZE]].groupby('Module').sum() +modules_v16_by_module.loc['**Total**'] = [modules_v16_by_module[COLUMN_BINSIZE].sum()] + +print(modules_v16_by_module) + +modules_v16_by_module.to_csv('docs/assets/tables/modules_v16.csv') + +# store csv with size by Module for v201 + +modules_v201 = cunits_v201.loc[cunits_v201['v201'].values == 'x'].sort_index() +modules_v201_by_module = modules_v201[['Module', COLUMN_BINSIZE]].groupby('Module').sum() +modules_v201_by_module.loc['**Total**'] = [modules_v201_by_module[COLUMN_BINSIZE].sum()] + +print(modules_v201_by_module) + +modules_v201_by_module.to_csv('docs/assets/tables/modules_v201.csv')