merge V23.12.16 (#556)

* Optimize Sun data calculation

* Remove not required enum

* Split config struct into different sub structs

* Feature: Allow configuration of LWT QoS

* Made resetreason methods static

* Feature: Implement offset cache for "YieldDay"

Thanks to @broth-itk for the idea!
Fix: #1258 #1397

* Add Esp32-Stick-PoE-A

* remove broken LilyGO_T_ETH_POE config, use device profile instead

* Feature: High resolution Icon and PWA (Progressive Web App) functionality

Fix: #1289

* webapp: Update dependencies

* Initialize TaskScheduler

* Migrate SunPosition to TaskScheduler

* Migrate Datastore to TaskScheduler

* Migrate MqttHandleInverterTotal to TaskSchedule

* Migrate MqttHandleHass to TaskScheduler

* Migrate MqttHandleDtu to TaskScheduler

* Migrate MqttHandleInverter to TaskScheduler

* Migrate LedSingle to TaskScheduler

* Migrate NetworkSettings to TaskScheduler

* Migrate InverterSettings to TaskScheduler

* Migrate MessageOutput to TaskScheduler

* Migrate Display_Graphic to TaskScheduler

* Migrate WebApi to TaskScheduler

* Split InverterSettings into multiple tasks

* Calculate SunPosition only every 5 seconds

* Split LedSingle into multiple tasks

* Upgrade espMqttClient from 1.4.5 to 1.5.0

* Doc: Correct amount of MPP-Tracker

* Added HMT-1600-4T and HMT-1800-4T to DevInfoParser

Fix #1524

* Adjusted inverter names for HMS-1600/1800/2000-4T

* Add channel count to description of detected inverter type (DevInfoParser)

* Adjust device web api endpoint for dynamic led count

* Feature: Added ability to change the brightness of the LEDs

Based on the idea of @moritzlerch with several modifications like pwmTable and structure

* webapp: Update dependencies

* Update olikraus/U8g2 from 2.35.7 to 2.35.8

* Remove not required onWebsocketEvent

* Remove code nesting

* Introduce several const statements

* Remove not required AsyncEventSource

* Doc: Added byte specification to each command

* Feature: Added basic Grid Profile parser which shows the used profile and version

Other values are still outstanding.

* Optimize AlarmLogParser to save memory

* Add libfrozen to project to create constexpr maps

* Feature: First version of GridProfile Parser which shows all values contained in the profile.

* webapp: Update dependencies

* Apply better variable names

* Remove not required casts

* Add additional compiler flags to prevent errors

* Add const statement to several variables

* Replace NULL by nullptr

* Update bblanchon/ArduinoJson from 6.21.3 to 6.21.4

* Add const keyword to method parameters

* Add const keyword to methods

* Use references instead of pointers whenver possible

* Adjust member variable names in MqttSettings

* Adjust member variable names in NetworkSettings

* webapp: Update timezone database to latest version

* webapp: Beautify and unify form footers

* Feature: Allow setting of an inverter limit of 0% and 0W

Thanks to @madmartin in #1270

* Feature: Allow links in device profiles

These links will be shown on the hardware settings page.

* Doc: Added hint regarding HMS-xxxx-xT-NA inverters

* Feature: Added DeviceProfile for CASmo-DTU

Based on #1565

* Upgrade actions/upload-artifact from v3 to v4

* Upgrade actions/download-artifact from v3 to v4

* webapp: add app.js.gz

* Gridprofileparser: Added latest known values

Thanks to @stefan123t and @noone2k

* webapp: Fix lint errors

* Feature: Add DTU to Home Assistant Auto Discovery

This is based on PR 1365 from @CFenner with several fixes and optimizations

* Fix: Remove debug output as it floods the console

* Fix: Gridprofileparser: Add additional error handling if profile is unknown

* webapp: add app.js.gz

* Fix: Offset cache for "YieldDay" did not work correctly

* webapp: update dependencies

* webapp: add app.js.gz

* Fix: yarn.lock was outdated

* Fix: yarn build error

* Fix: Reset Yield day correction in combination with Zero Yield Day on Midnight lead to wrong values.

* Fix: Allow negative values in GridProfileParser

* Correct variable name

* Fix #1579: Static IP in Ethernet mode did not work correctly

* Feature: Added diagram to display

This is based on the idea of @Henrik-Ingenieur and was discussed in #1504

* webapp: update dependencies

* webapp: add app.js.gz

---------

Co-authored-by: Thomas Basler <thomas@familie-basler.net>
Co-authored-by: Pierre Kancir <pierre.kancir.emn@gmail.com>
This commit is contained in:
helgeerbe 2023-12-27 11:49:57 +01:00 committed by GitHub
parent fb2ca28692
commit d494810975
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
264 changed files with 7693 additions and 2976 deletions

View File

@ -106,7 +106,7 @@ jobs:
- name: Rename Factory Firmware
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: opendtu-onbattery-${{ matrix.environment }}
path: |
@ -149,7 +149,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts/

58
.vscode/settings.json vendored
View File

@ -2,6 +2,62 @@
"C_Cpp.clang_format_style": "WebKit",
"files.associations": {
"*.tcc": "cpp",
"algorithm": "cpp"
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"functional": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
}
}

View File

@ -0,0 +1,20 @@
[
{
"name": "CASmo-DTU",
"links": [
{"name": "Information", "url": "https://casmo.info/product-details/?product=2"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
"clk": 18,
"irq": 16,
"en": 4,
"cs": 5
},
"led": {
"led0": 25,
"led1": 26
}
}
]

View File

@ -1,6 +1,12 @@
[
{
"name": "NRF, LEDs, Display",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
@ -21,6 +27,12 @@
},
{
"name": "CMT, LEDs, Display",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": -1,
"mosi": -1,
@ -49,6 +61,12 @@
},
{
"name": "NRF, Display",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
@ -65,6 +83,12 @@
},
{
"name": "CMT, Display",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": -1,
"mosi": -1,
@ -89,6 +113,12 @@
},
{
"name": "NRF, LEDs",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
@ -104,6 +134,12 @@
},
{
"name": "CMT, LEDs",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": -1,
"mosi": -1,
@ -127,6 +163,12 @@
},
{
"name": "NRF",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
@ -138,6 +180,12 @@
},
{
"name": "CMT",
"links": [
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
],
"nrf24": {
"miso": -1,
"mosi": -1,

View File

@ -0,0 +1,25 @@
[
{
"name": "Esp32-Stick-PoE-A",
"links": [
{"name": "Information", "url": "https://github.com/allexoK/Esp32-Stick-Boards-Docs"}
],
"nrf24": {
"miso": 2,
"mosi": 15,
"clk": 14,
"irq": 34,
"en": 12,
"cs": 4
},
"eth": {
"enabled": true,
"phy_addr": 1,
"power": -1,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 3
}
}
]

View File

@ -1,6 +1,9 @@
[
{
"name": "LILYGO TTGO T-Internet-POE",
"links": [
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-internet-poe"}
],
"nrf24": {
"miso": 2,
"mosi": 15,
@ -21,6 +24,9 @@
},
{
"name": "LILYGO TTGO T-Internet-POE, nrf24 direct solder",
"links": [
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-internet-poe"}
],
"nrf24": {
"miso": 12,
"mosi": 4,
@ -41,6 +47,9 @@
},
{
"name": "LILYGO TTGO T-Internet-POE, nrf24 direct solder, SSD1306",
"links": [
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-internet-poe"}
],
"nrf24": {
"miso": 12,
"mosi": 4,

View File

@ -1,6 +1,9 @@
[
{
"name": "Olimex ESP32-EVB",
"links": [
{ "name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware" }
],
"nrf24": {
"miso": 15,
"mosi": 2,

View File

@ -1,6 +1,9 @@
[
{
"name": "Olimex ESP32-POE",
"links": [
{"name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware"}
],
"nrf24": {
"miso": 15,
"mosi": 2,
@ -21,6 +24,9 @@
},
{
"name": "Olimex ESP32-POE with SSD1306",
"links": [
{"name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware"}
],
"nrf24": {
"miso": 15,
"mosi": 2,
@ -46,6 +52,9 @@
},
{
"name": "Olimex ESP32-POE with SH1106",
"links": [
{"name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware"}
],
"nrf24": {
"miso": 15,
"mosi": 2,

View File

@ -1,6 +1,9 @@
[
{
"name": "WT32-ETH01",
"links": [
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
],
"nrf24": {
"miso": 4,
"mosi": 2,
@ -21,6 +24,9 @@
},
{
"name": "WT32-ETH01 with SSD1306",
"links": [
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
],
"nrf24": {
"miso": 4,
"mosi": 2,

View File

@ -4,6 +4,7 @@
#include <stdint.h>
#include <memory>
#include <mutex>
#include <TaskSchedulerDeclarations.h>
#include "BatteryStats.h"
@ -19,12 +20,15 @@ class BatteryProvider {
class BatteryClass {
public:
void init();
void loop();
void init(Scheduler&);
void updateSettings();
std::shared_ptr<BatteryStats const> getStats() const;
private:
void loop();
Task _loopTask;
uint32_t _lastMqttPublish = 0;
mutable std::mutex _mutex;
std::unique_ptr<BatteryProvider> _upProvider = nullptr;

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "PinMapping.h"
#include <cstdint>
#define CONFIG_FILENAME "/config.json"
@ -57,6 +58,7 @@ struct INVERTER_CONFIG_T {
uint8_t ReachableThreshold;
bool ZeroRuntimeDataIfUnrechable;
bool ZeroYieldDayOnMidnight;
bool YieldDayCorrection;
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
};
@ -74,128 +76,171 @@ struct POWERMETER_HTTP_PHASE_CONFIG_T {
};
struct CONFIG_T {
uint32_t Cfg_Version;
uint32_t Cfg_SaveCount;
struct {
uint32_t Version;
uint32_t SaveCount;
} Cfg;
char WiFi_Ssid[WIFI_MAX_SSID_STRLEN + 1];
char WiFi_Password[WIFI_MAX_PASSWORD_STRLEN + 1];
uint8_t WiFi_Ip[4];
uint8_t WiFi_Netmask[4];
uint8_t WiFi_Gateway[4];
uint8_t WiFi_Dns1[4];
uint8_t WiFi_Dns2[4];
bool WiFi_Dhcp;
char WiFi_Hostname[WIFI_MAX_HOSTNAME_STRLEN + 1];
uint32_t WiFi_ApTimeout;
struct {
char Ssid[WIFI_MAX_SSID_STRLEN + 1];
char Password[WIFI_MAX_PASSWORD_STRLEN + 1];
uint8_t Ip[4];
uint8_t Netmask[4];
uint8_t Gateway[4];
uint8_t Dns1[4];
uint8_t Dns2[4];
bool Dhcp;
char Hostname[WIFI_MAX_HOSTNAME_STRLEN + 1];
uint32_t ApTimeout;
} WiFi;
bool Mdns_Enabled;
struct {
bool Enabled;
} Mdns;
char Ntp_Server[NTP_MAX_SERVER_STRLEN + 1];
char Ntp_Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
char Ntp_TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1];
double Ntp_Longitude;
double Ntp_Latitude;
uint8_t Ntp_SunsetType;
struct {
char Server[NTP_MAX_SERVER_STRLEN + 1];
char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
char TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1];
double Longitude;
double Latitude;
uint8_t SunsetType;
} Ntp;
struct {
bool Enabled;
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
bool VerboseLogging;
uint32_t Port;
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
char Password[MQTT_MAX_PASSWORD_STRLEN + 1];
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
bool Retain;
uint32_t PublishInterval;
bool CleanSession;
struct {
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
char Value_Online[MQTT_MAX_LWTVALUE_STRLEN + 1];
char Value_Offline[MQTT_MAX_LWTVALUE_STRLEN + 1];
uint8_t Qos;
} Lwt;
struct {
bool Enabled;
bool Retain;
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
bool IndividualPanels;
bool Expire;
} Hass;
struct {
bool Enabled;
char RootCaCert[MQTT_MAX_CERT_STRLEN + 1];
bool CertLogin;
char ClientCert[MQTT_MAX_CERT_STRLEN + 1];
char ClientKey[MQTT_MAX_CERT_STRLEN + 1];
} Tls;
} Mqtt;
struct {
uint64_t Serial;
uint32_t PollInterval;
struct {
uint8_t PaLevel;
} Nrf;
struct {
int8_t PaLevel;
uint32_t Frequency;
} Cmt;
bool VerboseLogging;
} Dtu;
struct {
char Password[WIFI_MAX_PASSWORD_STRLEN + 1];
bool AllowReadonly;
} Security;
struct {
bool PowerSafe;
bool ScreenSaver;
uint8_t Rotation;
uint8_t Contrast;
uint8_t Language;
uint32_t DiagramDuration;
} Display;
struct {
uint8_t Brightness;
} Led_Single[PINMAPPING_LED_COUNT];
struct {
bool Enabled;
bool VerboseLogging;
bool UpdatesOnly;
} Vedirect;
struct {
bool Enabled;
bool VerboseLogging;
uint32_t Interval;
uint32_t Source;
char MqttTopicPowerMeter1[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttTopicPowerMeter2[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttTopicPowerMeter3[MQTT_MAX_TOPIC_STRLEN + 1];
uint32_t SdmBaudrate;
uint32_t SdmAddress;
uint32_t HttpInterval;
bool HttpIndividualRequests;
POWERMETER_HTTP_PHASE_CONFIG_T Http_Phase[POWERMETER_MAX_PHASES];
} PowerMeter;
struct {
bool Enabled;
bool VerboseLogging;
bool SolarPassThroughEnabled;
uint8_t SolarPassThroughLosses;
uint8_t BatteryDrainStategy;
uint32_t Interval;
bool IsInverterBehindPowerMeter;
uint8_t InverterId;
uint8_t InverterChannelId;
int32_t TargetPowerConsumption;
int32_t TargetPowerConsumptionHysteresis;
int32_t LowerPowerLimit;
int32_t UpperPowerLimit;
uint32_t BatterySocStartThreshold;
uint32_t BatterySocStopThreshold;
float VoltageStartThreshold;
float VoltageStopThreshold;
float VoltageLoadCorrectionFactor;
int8_t RestartHour;
uint32_t FullSolarPassThroughSoc;
float FullSolarPassThroughStartVoltage;
float FullSolarPassThroughStopVoltage;
} PowerLimiter;
struct {
bool Enabled;
bool VerboseLogging;
uint8_t Provider;
uint8_t JkBmsInterface;
uint8_t JkBmsPollingInterval;
} Battery;
struct {
bool Enabled;
uint32_t CAN_Controller_Frequency;
bool Auto_Power_Enabled;
float Auto_Power_Voltage_Limit;
float Auto_Power_Enable_Voltage_Limit;
float Auto_Power_Lower_Power_Limit;
float Auto_Power_Upper_Power_Limit;
} Huawei;
bool Mqtt_Enabled;
char Mqtt_Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
bool Mqtt_VerboseLogging;
uint32_t Mqtt_Port;
char Mqtt_Username[MQTT_MAX_USERNAME_STRLEN + 1];
char Mqtt_Password[MQTT_MAX_PASSWORD_STRLEN + 1];
char Mqtt_Topic[MQTT_MAX_TOPIC_STRLEN + 1];
bool Mqtt_Retain;
char Mqtt_LwtTopic[MQTT_MAX_TOPIC_STRLEN + 1];
char Mqtt_LwtValue_Online[MQTT_MAX_LWTVALUE_STRLEN + 1];
char Mqtt_LwtValue_Offline[MQTT_MAX_LWTVALUE_STRLEN + 1];
uint32_t Mqtt_PublishInterval;
bool Mqtt_CleanSession;
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
uint64_t Dtu_Serial;
uint32_t Dtu_PollInterval;
bool Dtu_VerboseLogging;
uint8_t Dtu_NrfPaLevel;
int8_t Dtu_CmtPaLevel;
uint32_t Dtu_CmtFrequency;
bool Mqtt_Hass_Enabled;
bool Mqtt_Hass_Retain;
char Mqtt_Hass_Topic[MQTT_MAX_TOPIC_STRLEN + 1];
bool Mqtt_Hass_IndividualPanels;
bool Mqtt_Hass_Expire;
bool Mqtt_Tls;
char Mqtt_RootCaCert[MQTT_MAX_CERT_STRLEN + 1];
bool Mqtt_TlsCertLogin;
char Mqtt_ClientCert[MQTT_MAX_CERT_STRLEN + 1];
char Mqtt_ClientKey[MQTT_MAX_CERT_STRLEN +1];
bool Vedirect_Enabled;
bool Vedirect_VerboseLogging;
bool Vedirect_UpdatesOnly;
bool PowerMeter_Enabled;
bool PowerMeter_VerboseLogging;
uint32_t PowerMeter_Interval;
uint32_t PowerMeter_Source;
char PowerMeter_MqttTopicPowerMeter1[MQTT_MAX_TOPIC_STRLEN + 1];
char PowerMeter_MqttTopicPowerMeter2[MQTT_MAX_TOPIC_STRLEN + 1];
char PowerMeter_MqttTopicPowerMeter3[MQTT_MAX_TOPIC_STRLEN + 1];
uint32_t PowerMeter_SdmBaudrate;
uint32_t PowerMeter_SdmAddress;
uint32_t PowerMeter_HttpInterval;
bool PowerMeter_HttpIndividualRequests;
POWERMETER_HTTP_PHASE_CONFIG_T Powermeter_Http_Phase[POWERMETER_MAX_PHASES];
bool PowerLimiter_Enabled;
bool PowerLimiter_VerboseLogging;
bool PowerLimiter_SolarPassThroughEnabled;
uint8_t PowerLimiter_SolarPassThroughLosses;
uint8_t PowerLimiter_BatteryDrainStategy;
uint32_t PowerLimiter_Interval;
bool PowerLimiter_IsInverterBehindPowerMeter;
uint8_t PowerLimiter_InverterId;
uint8_t PowerLimiter_InverterChannelId;
int32_t PowerLimiter_TargetPowerConsumption;
int32_t PowerLimiter_TargetPowerConsumptionHysteresis;
int32_t PowerLimiter_LowerPowerLimit;
int32_t PowerLimiter_UpperPowerLimit;
uint32_t PowerLimiter_BatterySocStartThreshold;
uint32_t PowerLimiter_BatterySocStopThreshold;
float PowerLimiter_VoltageStartThreshold;
float PowerLimiter_VoltageStopThreshold;
float PowerLimiter_VoltageLoadCorrectionFactor;
int8_t PowerLimiter_RestartHour;
uint32_t PowerLimiter_FullSolarPassThroughSoc;
float PowerLimiter_FullSolarPassThroughStartVoltage;
float PowerLimiter_FullSolarPassThroughStopVoltage;
bool Battery_Enabled;
bool Battery_VerboseLogging;
uint8_t Battery_Provider;
uint8_t Battery_JkBmsInterface;
uint8_t Battery_JkBmsPollingInterval;
bool Huawei_Enabled;
uint32_t Huawei_CAN_Controller_Frequency;
bool Huawei_Auto_Power_Enabled;
float Huawei_Auto_Power_Voltage_Limit;
float Huawei_Auto_Power_Enable_Voltage_Limit;
float Huawei_Auto_Power_Lower_Power_Limit;
float Huawei_Auto_Power_Upper_Power_Limit;
char Security_Password[WIFI_MAX_PASSWORD_STRLEN + 1];
bool Security_AllowReadonly;
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
bool Display_PowerSafe;
bool Display_ScreenSaver;
uint8_t Display_Rotation;
uint8_t Display_Contrast;
uint8_t Display_Language;
};
class ConfigurationClass {
@ -207,7 +252,7 @@ public:
CONFIG_T& get();
INVERTER_CONFIG_T* getFreeInverterSlot();
INVERTER_CONFIG_T* getInverterConfig(uint64_t serial);
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
};
extern ConfigurationClass Configuration;

View File

@ -1,13 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TimeoutHelper.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex>
class DatastoreClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
float getTotalAcYieldTotalEnabled();
@ -58,7 +57,10 @@ public:
bool getIsAllEnabledReachable();
private:
TimeoutHelper _updateTimeout;
void loop();
Task _loopTask;
std::mutex _mutex;
float _totalAcYieldTotalEnabled = 0;

View File

@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Display_Graphic_Diagram.h"
#include "defaults.h"
#include <TaskSchedulerDeclarations.h>
#include <U8g2lib.h>
enum DisplayType_t {
@ -16,23 +18,28 @@ public:
DisplayGraphicClass();
~DisplayGraphicClass();
void init(DisplayType_t type, uint8_t data, uint8_t clk, uint8_t cs, uint8_t reset);
void loop();
void setContrast(uint8_t contrast);
void setStatus(bool turnOn);
void setOrientation(uint8_t rotation = DISPLAY_ROTATION);
void setLanguage(uint8_t language);
void init(Scheduler& scheduler, const DisplayType_t type, const uint8_t data, const uint8_t clk, const uint8_t cs, const uint8_t reset);
void setContrast(const uint8_t contrast);
void setStatus(const bool turnOn);
void setOrientation(const uint8_t rotation = DISPLAY_ROTATION);
void setLanguage(const uint8_t language);
void setStartupDisplay();
DisplayGraphicDiagramClass& Diagram();
bool enablePowerSafe = true;
bool enableScreensaver = true;
private:
void printText(const char* text, uint8_t line);
void loop();
void printText(const char* text, const uint8_t line);
void calcLineHeights();
void setFont(uint8_t line);
void setFont(const uint8_t line);
Task _loopTask;
U8G2* _display;
DisplayGraphicDiagramClass _diagram;
bool _displayTurnedOn;
@ -41,7 +48,6 @@ private:
uint8_t _mExtra;
uint16_t _period = 1000;
uint16_t _interval = 60000; // interval at which to power save (milliseconds)
uint32_t _lastDisplayUpdate = 0;
uint32_t _previousMillis = 0;
char _fmtText[32];
bool _isLarge = false;

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TaskSchedulerDeclarations.h>
#include <U8g2lib.h>
#include <array>
#define CHART_HEIGHT 20 // chart area hight in pixels
#define CHART_WIDTH 47 // chart area width in pixels
#define DIAG_POSX 80 // position were Diag is drawn at
#define DIAG_POSY 0
class DisplayGraphicDiagramClass {
public:
DisplayGraphicDiagramClass();
void init(Scheduler& scheduler, U8G2* display);
void redraw();
void updatePeriod();
private:
void averageLoop();
void dataPointLoop();
static uint32_t getSecondsPerDot();
Task _averageTask;
Task _dataPointTask;
U8G2* _display = nullptr;
std::array<float, CHART_WIDTH> _graphValues = {};
uint8_t _graphValuesCount = 0;
float _iRunningAverage = 0;
uint16_t _iRunningAverageCnt = 0;
uint8_t _graphPosX = DIAG_POSX;
};

View File

@ -5,6 +5,7 @@
#include "SPI.h"
#include <mcp_can.h>
#include <mutex>
#include <TaskSchedulerDeclarations.h>
#ifndef HUAWEI_PIN_MISO
#define HUAWEI_PIN_MISO 12
@ -118,8 +119,8 @@ private:
class HuaweiCanClass {
public:
void init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
void loop();
void init(Scheduler& scheduler, uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
void updateSettings(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
void setValue(float in, uint8_t parameterType);
void setMode(uint8_t mode);
@ -128,9 +129,12 @@ public:
bool getAutoPowerStatus();
private:
void loop();
void processReceivedParameters();
void _setValue(float in, uint8_t parameterType);
Task _loopTask;
TaskHandle_t _HuaweiCanCommunicationTaskHdl = NULL;
bool _initialized = false;
uint8_t _huaweiPower; // Power pin

View File

@ -1,17 +1,21 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TaskSchedulerDeclarations.h>
#include <cstdint>
#define INVERTER_UPDATE_SETTINGS_INTERVAL 60000l
class InverterSettingsClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
private:
uint32_t _lastUpdate = 0;
void settingsLoop();
void hoyLoop();
Task _settingsTask;
Task _hoyTask;
};
extern InverterSettingsClass InverterSettings;

View File

@ -2,38 +2,38 @@
#pragma once
#include "PinMapping.h"
#include <TaskSchedulerDeclarations.h>
#include <TimeoutHelper.h>
#define LEDSINGLE_UPDATE_INTERVAL 2000
enum eLedFunction {
CONNECTED_NETWORK,
CONNECTED_MQTT,
INV_REACHABLE,
INV_PRODUCING,
};
class LedSingleClass {
public:
LedSingleClass();
void init();
void loop();
void init(Scheduler& scheduler);
void turnAllOff();
void turnAllOn();
private:
void setLoop();
void outputLoop();
void setLed(const uint8_t ledNo, const bool ledState);
Task _setTask;
Task _outputTask;
enum class LedState_t {
On,
Off,
Blink,
};
LedState_t _ledState[PINMAPPING_LED_COUNT];
LedState_t _allState;
TimeoutHelper _updateTimeout;
LedState_t _ledMode[PINMAPPING_LED_COUNT];
LedState_t _allMode;
bool _ledStateCurrent[PINMAPPING_LED_COUNT];
TimeoutHelper _blinkTimeout;
uint8_t _ledActive = 0;
};
extern LedSingleClass LedSingle;

View File

@ -2,34 +2,32 @@
#pragma once
#include <AsyncWebSocket.h>
#include <Print.h>
#include <freertos/task.h>
#include <HardwareSerial.h>
#include <Stream.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex>
#include <vector>
#include <unordered_map>
#include <queue>
#define BUFFER_SIZE 500
class MessageOutputClass : public Print {
public:
void loop();
void init(Scheduler& scheduler);
size_t write(uint8_t c) override;
size_t write(const uint8_t* buffer, size_t size) override;
void register_ws_output(AsyncWebSocket* output);
private:
using message_t = std::vector<uint8_t>;
void loop();
// we keep a buffer for every task and only write complete lines to the
// serial output and then move them to be pushed through the websocket.
// this way we prevent mangling of messages from different contexts.
std::unordered_map<TaskHandle_t, message_t> _task_messages;
std::queue<message_t> _lines;
Task _loopTask;
AsyncWebSocket* _ws = nullptr;
char _buffer[BUFFER_SIZE];
uint16_t _buff_pos = 0;
uint32_t _lastSend = 0;
bool _forceSend = false;
std::mutex _msgLock;
void serialWrite(message_t const& m);
};
extern MessageOutputClass MessageOutput;

View File

@ -1,15 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TaskSchedulerDeclarations.h>
#include <cstdint>
class MqttHandleDtuClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
private:
uint32_t _lastPublish = 0;
void loop();
Task _loopTask;
};
extern MqttHandleDtuClass MqttHandleDtu;

View File

@ -3,6 +3,7 @@
#include <ArduinoJson.h>
#include <Hoymiles.h>
#include <TaskSchedulerDeclarations.h>
// mqtt discovery device classes
enum {
@ -50,18 +51,29 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
class MqttHandleHassClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
void publishConfig();
void forceUpdate();
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishField(std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, byteAssign_fieldDeviceClass_t fieldType, bool clear = false);
void publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic);
void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = "");
void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, int16_t min = 1, int16_t max = 100);
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100);
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
void createDeviceInfo(JsonObject& object, std::shared_ptr<InverterAbstract> inv);
static void createInverterInfo(DynamicJsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
static void createDtuInfo(DynamicJsonDocument& doc);
static void createDeviceInfo(DynamicJsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
static String getDtuUniqueId();
static String getDtuUrl();
Task _loopTask;
bool _wasConnected = false;
bool _updateForced = false;

View File

@ -4,15 +4,18 @@
#include "Configuration.h"
#include <Huawei_can.h>
#include <espMqttClient.h>
#include <TaskSchedulerDeclarations.h>
class MqttHandleHuaweiClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
private:
void loop();
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
Task _loopTask;
uint32_t _lastPublishStats;
uint32_t _lastPublish;

View File

@ -3,22 +3,23 @@
#include "Configuration.h"
#include <Hoymiles.h>
#include <TimeoutHelper.h>
#include <TaskSchedulerDeclarations.h>
#include <espMqttClient.h>
class MqttHandleInverterClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
static String getTopic(std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
static String getTopic(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
private:
void publishField(std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
void loop();
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
Task _loopTask;
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
uint32_t _lastPublish = 0;
FieldId_t _publishFields[14] = {
FLD_UDC,

View File

@ -1,15 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TimeoutHelper.h>
#include <TaskSchedulerDeclarations.h>
class MqttHandleInverterTotalClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
private:
TimeoutHelper _lastPublish;
void loop();
Task _loopTask;
};
extern MqttHandleInverterTotalClass MqttHandleInverterTotal;

View File

@ -3,15 +3,18 @@
#include "Configuration.h"
#include <espMqttClient.h>
#include <TaskSchedulerDeclarations.h>
class MqttHandlePowerLimiterClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
private:
void loop();
void onCmdMode(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
Task _loopTask;
uint32_t _lastPublishStats;
uint32_t _lastPublish;

View File

@ -2,20 +2,23 @@
#pragma once
#include <ArduinoJson.h>
#include <TaskSchedulerDeclarations.h>
class MqttHandlePylontechHassClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
void publishConfig();
void forceUpdate();
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off);
void publishSensor(const char* caption, const char* icon, const char* subTopic, const char* deviceClass = NULL, const char* stateClass = NULL, const char* unitOfMeasurement = NULL);
void createDeviceInfo(JsonObject& object);
Task _loopTask;
bool _wasConnected = false;
bool _updateForced = false;
String serial = "0001"; // pseudo-serial, can be replaced in future with real serialnumber

View File

@ -4,6 +4,7 @@
#include "VeDirectMpptController.h"
#include "Configuration.h"
#include <Arduino.h>
#include <TaskSchedulerDeclarations.h>
#ifndef VICTRON_PIN_RX
#define VICTRON_PIN_RX 22
@ -15,12 +16,14 @@
class MqttHandleVedirectClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
void forceUpdate();
private:
void loop();
VeDirectMpptController::veMpptStruct _kvFrame{};
Task _loopTask;
// point of time in millis() when updated values will be published
uint32_t _nextPublishUpdatesOnly = 0;

View File

@ -3,20 +3,23 @@
#include <ArduinoJson.h>
#include "VeDirectMpptController.h"
#include <TaskSchedulerDeclarations.h>
class MqttHandleVedirectHassClass {
public:
void init();
void loop();
void init(Scheduler& scheduler);
void publishConfig();
void forceUpdate();
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off);
void publishSensor(const char* caption, const char* icon, const char* subTopic, const char* deviceClass = NULL, const char* stateClass = NULL, const char* unitOfMeasurement = NULL);
void createDeviceInfo(JsonObject& object);
Task _loopTask;
bool _wasConnected = false;
bool _updateForced = false;
};

View File

@ -15,29 +15,27 @@ public:
void performReconnect();
bool getConnected();
void publish(const String& subtopic, const String& payload);
void publishGeneric(const String& topic, const String& payload, bool retain, uint8_t qos = 0);
void publishGeneric(const String& topic, const String& payload, const bool retain, const uint8_t qos = 0);
void subscribe(const String& topic, uint8_t qos, const espMqttClientTypes::OnMessageCallback& cb);
void subscribe(const String& topic, const uint8_t qos, const espMqttClientTypes::OnMessageCallback& cb);
void unsubscribe(const String& topic);
String getPrefix();
String getPrefix() const;
private:
void NetworkEvent(network_event event);
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
void onMqttConnect(bool sessionPresent);
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
void onMqttConnect(const bool sessionPresent);
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
void performConnect();
void performDisconnect();
void createMqttClientObject();
MqttClient* mqttClient = nullptr;
String clientId;
String willTopic;
Ticker mqttReconnectTimer;
MqttClient* _mqttClient = nullptr;
Ticker _mqttReconnectTimer;
MqttSubscribeParser _mqttSubscribeParser;
std::mutex _clientLock;
bool _verboseLogging = true;

View File

@ -2,6 +2,7 @@
#pragma once
#include <DNSServer.h>
#include <TaskSchedulerDeclarations.h>
#include <WiFi.h>
#include <vector>
@ -29,7 +30,7 @@ typedef struct NetworkEventCbList {
network_event event;
NetworkEventCbList()
: cb(NULL)
: cb(nullptr)
, event(network_event::NETWORK_UNKNOWN)
{
}
@ -38,46 +39,50 @@ typedef struct NetworkEventCbList {
class NetworkSettingsClass {
public:
NetworkSettingsClass();
void init();
void loop();
void init(Scheduler& scheduler);
void applyConfig();
void enableAdminMode();
String getApName();
String getApName() const;
IPAddress localIP();
IPAddress subnetMask();
IPAddress gatewayIP();
IPAddress dnsIP(uint8_t dns_no = 0);
String macAddress();
IPAddress localIP() const;
IPAddress subnetMask() const;
IPAddress gatewayIP() const;
IPAddress dnsIP(const uint8_t dns_no = 0) const;
String macAddress() const;
static String getHostname();
bool isConnected();
network_mode NetworkMode();
bool isConnected() const;
network_mode NetworkMode() const;
bool onEvent(NetworkEventCb cbEvent, network_event event = network_event::NETWORK_EVENT_MAX);
void raiseEvent(network_event event);
bool onEvent(NetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
void raiseEvent(const network_event event);
private:
void loop();
void setHostname();
void setStaticIp();
void handleMDNS();
void setupMode();
void NetworkEvent(WiFiEvent_t event);
bool adminEnabled = true;
bool forceDisconnection = false;
uint32_t adminTimeoutCounter = 0;
uint32_t adminTimeoutCounterMax = 0;
uint32_t connectTimeoutTimer = 0;
uint32_t connectRedoTimer = 0;
uint32_t lastTimerCall = 0;
const byte DNS_PORT = 53;
IPAddress apIp;
IPAddress apNetmask;
std::unique_ptr<DNSServer> dnsServer;
bool dnsServerStatus = false;
void NetworkEvent(const WiFiEvent_t event);
Task _loopTask;
static constexpr byte DNS_PORT = 53;
bool _adminEnabled = true;
bool _forceDisconnection = false;
uint32_t _adminTimeoutCounter = 0;
uint32_t _adminTimeoutCounterMax = 0;
uint32_t _connectTimeoutTimer = 0;
uint32_t _connectRedoTimer = 0;
uint32_t _lastTimerCall = 0;
IPAddress _apIp;
IPAddress _apNetmask;
std::unique_ptr<DNSServer> _dnsServer;
bool _dnsServerStatus = false;
network_mode _networkMode = network_mode::Undefined;
bool _ethConnected = false;
std::vector<NetworkEventCbList_t> _cbEventList;
bool lastMdnsEnabled = false;
bool _lastMdnsEnabled = false;
};
extern NetworkSettingsClass NetworkSettings;

View File

@ -59,9 +59,9 @@ public:
bool init(const String& deviceMapping);
PinMapping_t& get();
bool isValidNrf24Config();
bool isValidCmt2300Config();
bool isValidEthConfig();
bool isValidNrf24Config() const;
bool isValidCmt2300Config() const;
bool isValidEthConfig() const;
bool isValidHuaweiConfig();
private:

View File

@ -7,6 +7,7 @@
#include <Hoymiles.h>
#include <memory>
#include <functional>
#include <TaskSchedulerDeclarations.h>
#define PL_UI_STATE_INACTIVE 0
#define PL_UI_STATE_CHARGING 1
@ -47,8 +48,7 @@ public:
Stable,
};
void init();
void loop();
void init(Scheduler& scheduler);
uint8_t getPowerLimiterState();
int32_t getLastRequestedPowerLimit();
@ -63,6 +63,10 @@ public:
void calcNextInverterRestart();
private:
void loop();
Task _loopTask;
int32_t _lastRequestedPowerLimit = 0;
uint32_t _lastPowerLimitMillis = 0;
uint32_t _shutdownTimeout = 0;

View File

@ -8,6 +8,7 @@
#include <list>
#include "SDM.h"
#include "sml.h"
#include <TaskSchedulerDeclarations.h>
#ifndef SDM_RX_PIN
#define SDM_RX_PIN 13
@ -36,17 +37,19 @@ public:
SOURCE_HTTP = 3,
SOURCE_SML = 4
};
void init();
void loop();
void init(Scheduler& scheduler);
float getPowerTotal(bool forceUpdate = true);
uint32_t getLastPowerMeterUpdate();
private:
void loop();
void mqtt();
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties,
const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
Task _loopTask;
bool _verboseLogging = true;
uint32_t _lastPowerMeterCheck;
// Used in Power limiter for safety check

6
include/Scheduler.h Normal file
View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TaskSchedulerDeclarations.h>
extern Scheduler scheduler;

View File

@ -1,34 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <TaskSchedulerDeclarations.h>
#include <atomic>
#include <sunset.h>
class SunPositionClass {
public:
SunPositionClass();
void init();
void loop();
void init(Scheduler& scheduler);
bool isDayPeriod();
bool isSunsetAvailable();
bool sunsetTime(struct tm* info);
bool sunriseTime(struct tm* info);
void setDoRecalc(bool doRecalc);
bool isDayPeriod() const;
bool isSunsetAvailable() const;
bool sunsetTime(struct tm* info) const;
bool sunriseTime(struct tm* info) const;
void setDoRecalc(const bool doRecalc);
private:
void loop();
void updateSunData();
bool checkRecalcDayChanged();
bool getDoRecalc();
bool checkRecalcDayChanged() const;
bool getSunTime(struct tm* info, const uint32_t offset) const;
Task _loopTask;
SunSet _sun;
bool _isSunsetAvailable = true;
uint32_t _sunriseMinutes = 0;
uint32_t _sunsetMinutes = 0;
bool _isValidInfo = false;
bool _doRecalc = true;
std::mutex _recalcLock;
std::atomic_bool _doRecalc = true;
uint32_t _lastSunPositionCalculatedYMD = 0;
};

View File

@ -5,14 +5,15 @@
#include <memory>
#include "VeDirectMpptController.h"
#include <TaskSchedulerDeclarations.h>
class VictronMpptClass {
public:
VictronMpptClass() = default;
~VictronMpptClass() = default;
void init();
void loop();
void init(Scheduler& scheduler);
void updateSettings();
bool isDataValid() const;
@ -35,11 +36,14 @@ public:
double getYieldDay() const;
private:
void loop();
VictronMpptClass(VictronMpptClass const& other) = delete;
VictronMpptClass(VictronMpptClass&& other) = delete;
VictronMpptClass& operator=(VictronMpptClass const& other) = delete;
VictronMpptClass& operator=(VictronMpptClass&& other) = delete;
Task _loopTask;
mutable std::mutex _mutex;
using controller_t = std::unique_ptr<VeDirectMpptController>;
std::vector<controller_t> _controllers;

View File

@ -30,12 +30,12 @@
#include "WebApi_Huawei.h"
#include "WebApi_ws_battery.h"
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiClass {
public:
WebApiClass();
void init();
void loop();
void init(Scheduler& scheduler);
static bool checkCredentials(AsyncWebServerRequest* request);
static bool checkCredentialsReadonly(AsyncWebServerRequest* request);
@ -43,8 +43,11 @@ public:
static void sendTooManyRequests(AsyncWebServerRequest* request);
private:
void loop();
Task _loopTask;
AsyncWebServer _server;
AsyncEventSource _events;
WebApiBatteryClass _webApiBattery;
WebApiConfigClass _webApiConfig;

View File

@ -6,7 +6,7 @@
class WebApiHuaweiClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
void getJsonData(JsonObject& root);
private:

View File

@ -6,7 +6,7 @@
class WebApiBatteryClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiConfigClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiDeviceClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiDevInfoClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiDtuClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -56,6 +56,7 @@ enum WebApiError {
MqttPublishInterval,
MqttHassTopicLength,
MqttHassTopicCharacter,
MqttLwtQos,
NetworkBase = 8000,
NetworkIpInvalid,

View File

@ -5,7 +5,7 @@
class WebApiEventlogClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiFirmwareClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,11 +5,12 @@
class WebApiGridProfileClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:
void onGridProfileStatus(AsyncWebServerRequest* request);
void onGridProfileRawdata(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -5,7 +5,7 @@
class WebApiInverterClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiLimitClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiMaintenanceClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -7,7 +7,7 @@
class WebApiMqttClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiNetworkClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiNtpClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiPowerClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -6,7 +6,7 @@
class WebApiPowerLimiterClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -6,7 +6,7 @@
class WebApiPowerMeterClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -7,15 +7,15 @@
class WebApiPrometheusClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:
void onPrometheusMetricsGet(AsyncWebServerRequest* request);
void addField(AsyncResponseStream* stream, String& serial, uint8_t idx, std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, const char* metricName, const char* channelName = NULL);
void addField(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, const char* metricName, const char* channelName = nullptr);
void addPanelInfo(AsyncResponseStream* stream, String& serial, uint8_t idx, std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel);
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
AsyncWebServer* _server;

View File

@ -5,7 +5,7 @@
class WebApiSecurityClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiSysstatusClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -6,7 +6,7 @@
class WebApiVedirectClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -5,7 +5,7 @@
class WebApiWebappClass {
public:
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -8,7 +8,7 @@
class WebApiWsHuaweiLiveClass {
public:
WebApiWsHuaweiLiveClass();
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -7,7 +7,7 @@
class WebApiWsBatteryLiveClass {
public:
WebApiWsBatteryLiveClass();
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -6,12 +6,10 @@
class WebApiWsConsoleClass {
public:
WebApiWsConsoleClass();
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebServer* _server;
AsyncWebSocket _ws;

View File

@ -8,13 +8,13 @@
class WebApiWsLiveClass {
public:
WebApiWsLiveClass();
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:
void generateJsonResponse(JsonVariant& root);
void addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, String topic = "");
void addTotalField(JsonObject& root, String name, float value, String unit, uint8_t digits);
void addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
void onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);

View File

@ -8,7 +8,7 @@
class WebApiWsVedirectLiveClass {
public:
WebApiWsVedirectLiveClass();
void init(AsyncWebServer* server);
void init(AsyncWebServer& server);
void loop();
private:

View File

@ -75,6 +75,7 @@
#define MQTT_LWT_TOPIC "dtu/status"
#define MQTT_LWT_ONLINE "online"
#define MQTT_LWT_OFFLINE "offline"
#define MQTT_LWT_QOS 2U
#define MQTT_PUBLISH_INTERVAL 5U
#define MQTT_CLEAN_SESSION true
@ -97,6 +98,7 @@
#define DISPLAY_ROTATION 2U
#define DISPLAY_CONTRAST 60U
#define DISPLAY_LANGUAGE 0U
#define DISPLAY_DIAGRAM_DURATION (10UL * 60UL * 60UL)
#define REACHABLE_THRESHOLD 2U
@ -146,3 +148,7 @@
#define HUAWEI_AUTO_POWER_UPPER_POWER_LIMIT 2000
#define VERBOSE_LOGGING true
#define LED_BRIGHTNESS 100U
#define MAX_INVERTER_LIMIT 2250

3
lib/Frozen/AUTHORS Normal file
View File

@ -0,0 +1,3 @@
serge-sans-paille <sguelton@quarkslab.com>
Jérôme Dumesnil <jerome.dumesnil@gmail.com>
Chris Beck <chbeck@tesla.com>

202
lib/Frozen/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 Quarkslab
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License 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.

245
lib/Frozen/README.rst Normal file
View File

@ -0,0 +1,245 @@
Frozen
######
.. image:: https://travis-ci.org/serge-sans-paille/frozen.svg?branch=master
:target: https://travis-ci.org/serge-sans-paille/frozen
Header-only library that provides 0 cost initialization for immutable containers, fixed-size containers, and various algorithms.
Frozen provides:
- immutable (a.k.a. frozen), ``constexpr``-compatible versions of ``std::set``,
``std::unordered_set``, ``std::map`` and ``std::unordered_map``.
- fixed-capacity, ``constinit``-compatible versions of ``std::map`` and
``std::unordered_map`` with immutable, compile-time selected keys mapped
to mutable values.
- 0-cost initialization version of ``std::search`` for frozen needles using
Boyer-Moore or Knuth-Morris-Pratt algorithms.
The ``unordered_*`` containers are guaranteed *perfect* (a.k.a. no hash
collision) and the extra storage is linear with respect to the number of keys.
Once initialized, the container keys cannot be updated, and in exchange, lookups
are faster. And initialization is free when ``constexpr`` or ``constinit`` is
used :-).
Installation
------------
Just copy the ``include/frozen`` directory somewhere and points to it using the ``-I`` flag. Alternatively, using CMake:
.. code:: sh
> mkdir build
> cd build
> cmake -D CMAKE_BUILD_TYPE=Release ..
> make install
Installation via CMake populates configuration files into the ``/usr/local/share``
directory which can be consumed by CMake's ``find_package`` instrinsic function.
Requirements
------------
A C++ compiler that supports C++14. Clang version 5 is a good pick, GCC version
6 lags behind in terms of ``constexpr`` compilation time (At least on my
setup), but compiles correctly. Visual Studio 2017 also works correctly!
Note that gcc 5 isn't supported. (Here's an `old compat branch`_ where a small amount of stuff was ported.)
.. _old compat branch: https://github.com/cbeck88/frozen/tree/gcc5-support
Usage
-----
Compiled with ``-std=c++14`` flag:
.. code:: C++
#include <frozen/set.h>
constexpr frozen::set<int, 4> some_ints = {1,2,3,5};
constexpr bool letitgo = some_ints.count(8);
extern int n;
bool letitgoooooo = some_ints.count(n);
As the constructor and some methods are ``constexpr``, it's also possible to write weird stuff like:
.. code:: C++
#include <frozen/set.h>
template<std::size_t N>
std::enable_if_t< frozen::set<int, 3>{{1,11,111}}.count(N), int> foo();
String support is built-in:
.. code:: C++
#include <frozen/unordered_map.h>
#include <frozen/string.h>
constexpr frozen::unordered_map<frozen::string, int, 2> olaf = {
{"19", 19},
{"31", 31},
};
constexpr auto val = olaf.at("19");
The associative containers have different functionality with and without ``constexpr``.
With ``constexpr``, frozen maps have immutable keys and values. Without ``constexpr``, the
values can be updated in runtime (the keys, however, remain immutable):
.. code:: C++
#include <frozen/unordered_map.h>
#include <frozen/string.h>
static constinit frozen::unordered_map<frozen::string, frozen::string, 2> voice = {
{"Anna", "???"},
{"Elsa", "???"}
};
int main() {
voice.at("Anna") = "Kristen";
voice.at("Elsa") = "Idina";
}
You may also prefer a slightly more DRY initialization syntax:
.. code:: C++
#include <frozen/set.h>
constexpr auto some_ints = frozen::make_set<int>({1,2,3,5});
There are similar ``make_X`` functions for all frozen containers.
Exception Handling
------------------
For compatibility with STL's API, Frozen may eventually throw exceptions, as in
``frozen::map::at``. If you build your code without exception support, or
define the ``FROZEN_NO_EXCEPTIONS`` macro variable, they will be turned into an
``std::abort``.
Extending
---------
Just like the regular C++14 container, you can specialize the hash function,
the key equality comparator for ``unordered_*`` containers, and the comparison
functions for the ordered version.
It's also possible to specialize the ``frozen::elsa`` structure used for
hashing. Note that unlike `std::hash`, the hasher also takes a seed in addition
to the value being hashed.
.. code:: C++
template <class T> struct elsa {
// in case of collisions, different seeds are tried
constexpr std::size_t operator()(T const &value, std::size_t seed) const;
};
Ideally, the hash function should have nice statistical properties like *pairwise-independence*:
If ``x`` and ``y`` are different values, the chance that ``elsa<T>{}(x, seed) == elsa<T>{}(y, seed)``
should be very low for a random value of ``seed``.
Note that frozen always ultimately produces a perfect hash function, and you will always have ``O(1)``
lookup with frozen. It's just that if the input hasher performs poorly, the search will take longer and
your project will take longer to compile.
Troubleshooting
---------------
If you hit a message like this:
.. code:: none
[...]
note: constexpr evaluation hit maximum step limit; possible infinite loop?
Then either you've got a very big container and you should increase Clang's
thresholds, using ``-fconstexpr-steps=1000000000`` for instance, or the hash
functions used by frozen do not suit your data, and you should change them, as
in the following:
.. code:: c++
struct olaf {
constexpr std::size_t operator()(frozen::string const &value, std::size_t seed) const { return seed ^ value[0];}
};
constexpr frozen::unordered_set<frozen::string, 2, olaf/*custom hash*/> hans = { "a", "b" };
Tests and Benchmarks
--------------------
Using hand-written Makefiles crafted with love and care:
.. code:: sh
> # running tests
> make -C tests check
> # running benchmarks
> make -C benchmarks GOOGLE_BENCHMARK_PREFIX=<GOOGLE-BENCHMARK_INSTALL_DIR>
Using CMake to generate a static configuration build system:
.. code:: sh
> mkdir build
> cd build
> cmake -D CMAKE_BUILD_TYPE=Release \
-D frozen.benchmark=ON \
-G <"Unix Makefiles" or "Ninja"> ..
> # building the tests and benchmarks...
> make # ... with make
> ninja # ... with ninja
> cmake --build . # ... with cmake
> # running the tests...
> make test # ... with make
> ninja test # ... with ninja
> cmake --build . --target test # ... with cmake
> ctest # ... with ctest
> # running the benchmarks...
> make benchmark # ... with make
> ninja benchmark # ... with ninja
> cmake --build . --target benchmark # ... with cmake
Using CMake to generate an IDE build system with test and benchmark targets
.. code:: sh
> mkdir build
> cd build
> cmake -D frozen.benchmark=ON -G <"Xcode" or "Visual Studio 15 2017"> ..
> # using cmake to drive the IDE build, test, and benchmark
> cmake --build . --config Release
> cmake --build . --target test
> cmake --build . --target benchmark
Credits
-------
The perfect hashing is strongly inspired by the blog post `Throw away the keys:
Easy, Minimal Perfect Hashing <http://stevehanov.ca/blog/index.php?id=119>`_.
Thanks a lot to Jérôme Dumesnil for his high-quality reviews, and to Chris Beck
for his contributions on perfect hashing.
Contact
-------
Serge sans Paille ``<serge.guelton@telecom-bretagne.eu>``

View File

@ -0,0 +1,12 @@
target_sources(frozen-headers INTERFACE
"${prefix}/frozen/algorithm.h"
"${prefix}/frozen/map.h"
"${prefix}/frozen/random.h"
"${prefix}/frozen/set.h"
"${prefix}/frozen/string.h"
"${prefix}/frozen/unordered_map.h"
"${prefix}/frozen/unordered_set.h"
"${prefix}/frozen/bits/algorithms.h"
"${prefix}/frozen/bits/basic_types.h"
"${prefix}/frozen/bits/elsa.h"
"${prefix}/frozen/bits/pmh.h")

View File

@ -0,0 +1,198 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_ALGORITHM_H
#define FROZEN_LETITGO_ALGORITHM_H
#include "frozen/bits/basic_types.h"
#include "frozen/bits/version.h"
#include "frozen/string.h"
namespace frozen {
// 'search' implementation if C++17 is not available
// https://en.cppreference.com/w/cpp/algorithm/search
template<class ForwardIterator, class Searcher>
ForwardIterator search(ForwardIterator first, ForwardIterator last, const Searcher & searcher)
{
return searcher(first, last).first;
}
// text book implementation from
// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
template <std::size_t size> class knuth_morris_pratt_searcher {
bits::carray<std::ptrdiff_t, size> step_;
bits::carray<char, size> needle_;
static constexpr bits::carray<std::ptrdiff_t, size>
build_kmp_cache(char const (&needle)[size + 1]) {
std::ptrdiff_t cnd = 0;
bits::carray<std::ptrdiff_t, size> cache(-1);
for (std::size_t pos = 1; pos < size; ++pos) {
if (needle[pos] == needle[cnd]) {
cache[pos] = cache[cnd];
cnd += 1;
} else {
cache[pos] = cnd;
cnd = cache[cnd];
while (cnd >= 0 && needle[pos] != needle[cnd])
cnd = cache[cnd];
cnd += 1;
}
}
return cache;
}
public:
constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1])
: step_{build_kmp_cache(needle)}, needle_(needle) {}
template <class ForwardIterator>
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(ForwardIterator first, ForwardIterator last) const {
std::size_t i = 0;
ForwardIterator iter = first;
while (iter != last) {
if (needle_[i] == *iter) {
if (i == (size - 1))
return { iter - i, iter - i + size };
++i;
++iter;
} else {
if (step_[i] > -1) {
i = step_[i];
} else {
++iter;
i = 0;
}
}
}
return { last, last };
}
};
template <std::size_t N>
constexpr knuth_morris_pratt_searcher<N - 1> make_knuth_morris_pratt_searcher(char const (&needle)[N]) {
return {needle};
}
// text book implementation from
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
template <std::size_t size> class boyer_moore_searcher {
using skip_table_type = bits::carray<std::ptrdiff_t, sizeof(char) << 8>;
using suffix_table_type = bits::carray<std::ptrdiff_t, size>;
skip_table_type skip_table_;
suffix_table_type suffix_table_;
bits::carray<char, size> needle_;
constexpr auto build_skip_table(char const (&needle)[size + 1]) {
skip_table_type skip_table(size);
for (std::size_t i = 0; i < size - 1; ++i)
skip_table[needle[i]] -= i + 1;
return skip_table;
}
constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) {
std::size_t suffixlen = size - pos;
for (std::size_t i = 0; i < suffixlen; i++) {
if (needle[i] != needle[pos + i])
return false;
}
return true;
}
constexpr std::size_t suffix_length(char const (&needle)[size + 1],
std::size_t pos) {
// increment suffix length slen to the first mismatch or beginning
// of the word
for (std::size_t slen = 0; slen < pos ; slen++)
if (needle[pos - slen] != needle[size - 1 - slen])
return slen;
return pos;
}
constexpr auto build_suffix_table(char const (&needle)[size + 1]) {
suffix_table_type suffix;
std::ptrdiff_t last_prefix_index = size - 1;
// first loop
for (std::ptrdiff_t p = size - 1; p >= 0; p--) {
if (is_prefix(needle, p + 1))
last_prefix_index = p + 1;
suffix[p] = last_prefix_index + (size - 1 - p);
}
// second loop
for (std::size_t p = 0; p < size - 1; p++) {
auto slen = suffix_length(needle, p);
if (needle[p - slen] != needle[size - 1 - slen])
suffix[size - 1 - slen] = size - 1 - p + slen;
}
return suffix;
}
public:
constexpr boyer_moore_searcher(char const (&needle)[size + 1])
: skip_table_{build_skip_table(needle)},
suffix_table_{build_suffix_table(needle)},
needle_(needle) {}
template <class RandomAccessIterator>
constexpr std::pair<RandomAccessIterator, RandomAccessIterator> operator()(RandomAccessIterator first, RandomAccessIterator last) const {
if (size == 0)
return { first, first };
if (size > size_t(last - first))
return { last, last };
RandomAccessIterator iter = first + size - 1;
while (true) {
std::ptrdiff_t j = size - 1;
while (j > 0 && (*iter == needle_[j])) {
--iter;
--j;
}
if (j == 0 && *iter == needle_[0])
return { iter, iter + size};
std::ptrdiff_t jump = std::max(skip_table_[*iter], suffix_table_[j]);
if (jump >= last - iter)
return { last, last };
iter += jump;
}
}
};
template <std::size_t N>
constexpr boyer_moore_searcher<N - 1> make_boyer_moore_searcher(char const (&needle)[N]) {
return {needle};
}
} // namespace frozen
#endif

View File

@ -0,0 +1,235 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_BITS_ALGORITHMS_H
#define FROZEN_LETITGO_BITS_ALGORITHMS_H
#include "frozen/bits/basic_types.h"
#include <limits>
#include <tuple>
namespace frozen {
namespace bits {
auto constexpr next_highest_power_of_two(std::size_t v) {
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
constexpr auto trip_count = std::numeric_limits<decltype(v)>::digits;
v--;
for(std::size_t i = 1; i < trip_count; i <<= 1)
v |= v >> i;
v++;
return v;
}
template<class T>
auto constexpr log(T v) {
std::size_t n = 0;
while (v > 1) {
n += 1;
v >>= 1;
}
return n;
}
constexpr std::size_t bit_weight(std::size_t n) {
return (n <= 8*sizeof(unsigned int))
+ (n <= 8*sizeof(unsigned long))
+ (n <= 8*sizeof(unsigned long long))
+ (n <= 128);
}
unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
template<std::size_t N>
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
static_assert(N < 2, "unsupported type size");
return {};
}
template<std::size_t N>
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>()));
template <typename Iter, typename Compare>
constexpr auto min_element(Iter begin, const Iter end,
Compare const &compare) {
auto result = begin;
while (begin != end) {
if (compare(*begin, *result)) {
result = begin;
}
++begin;
}
return result;
}
template <class T>
constexpr void cswap(T &a, T &b) {
auto tmp = a;
a = b;
b = tmp;
}
template <class T, class U>
constexpr void cswap(std::pair<T, U> & a, std::pair<T, U> & b) {
cswap(a.first, b.first);
cswap(a.second, b.second);
}
template <class... Tys, std::size_t... Is>
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b, std::index_sequence<Is...>) {
using swallow = int[];
(void) swallow{(cswap(std::get<Is>(a), std::get<Is>(b)), 0)...};
}
template <class... Tys>
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b) {
cswap(a, b, std::make_index_sequence<sizeof...(Tys)>());
}
template <typename Iter>
constexpr void iter_swap(Iter a, Iter b) {
cswap(*a, *b);
}
template <typename Iterator, class Compare>
constexpr Iterator partition(Iterator left, Iterator right, Compare const &compare) {
auto pivot = left + (right - left) / 2;
iter_swap(right, pivot);
pivot = right;
for (auto it = left; 0 < right - it; ++it) {
if (compare(*it, *pivot)) {
iter_swap(it, left);
left++;
}
}
iter_swap(pivot, left);
pivot = left;
return pivot;
}
template <typename Iterator, class Compare>
constexpr void quicksort(Iterator left, Iterator right, Compare const &compare) {
while (0 < right - left) {
auto new_pivot = bits::partition(left, right, compare);
quicksort(left, new_pivot, compare);
left = new_pivot + 1;
}
}
template <typename Container, class Compare>
constexpr Container quicksort(Container const &array,
Compare const &compare) {
Container res = array;
quicksort(res.begin(), res.end() - 1, compare);
return res;
}
template <class T, class Compare> struct LowerBound {
T const &value_;
Compare const &compare_;
constexpr LowerBound(T const &value, Compare const &compare)
: value_(value), compare_(compare) {}
template <class ForwardIt>
inline constexpr ForwardIt doit_fast(ForwardIt first,
std::integral_constant<std::size_t, 0>) {
return first;
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doit_fast(ForwardIt first,
std::integral_constant<std::size_t, N>) {
auto constexpr step = N / 2;
static_assert(N/2 == N - N / 2 - 1, "power of two minus 1");
auto it = first + step;
auto next_it = compare_(*it, value_) ? it + 1 : first;
return doit_fast(next_it, std::integral_constant<std::size_t, N / 2>{});
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, true>) {
return doit_fast(first, std::integral_constant<std::size_t, N>{});
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, false>) {
auto constexpr next_power = next_highest_power_of_two(N);
auto constexpr next_start = next_power / 2 - 1;
auto it = first + next_start;
if (compare_(*it, value_)) {
auto constexpr next = N - next_start - 1;
return doitfirst(it + 1, std::integral_constant<std::size_t, next>{}, std::integral_constant<bool, next_highest_power_of_two(next) - 1 == next>{});
}
else
return doit_fast(first, std::integral_constant<std::size_t, next_start>{});
}
template <class ForwardIt>
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, 1>, std::integral_constant<bool, false>) {
return doit_fast(first, std::integral_constant<std::size_t, 1>{});
}
};
template <std::size_t N, class ForwardIt, class T, class Compare>
constexpr ForwardIt lower_bound(ForwardIt first, const T &value, Compare const &compare) {
return LowerBound<T, Compare>{value, compare}.doitfirst(first, std::integral_constant<std::size_t, N>{}, std::integral_constant<bool, next_highest_power_of_two(N) - 1 == N>{});
}
template <std::size_t N, class Compare, class ForwardIt, class T>
constexpr bool binary_search(ForwardIt first, const T &value,
Compare const &compare) {
ForwardIt where = lower_bound<N>(first, value, compare);
return (!(where == first + N) && !(compare(value, *where)));
}
template<class InputIt1, class InputIt2>
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
{
for (; first1 != last1; ++first1, ++first2) {
if (!(*first1 == *first2)) {
return false;
}
}
return true;
}
template<class InputIt1, class InputIt2>
constexpr bool lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
{
for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) {
if (*first1 < *first2)
return true;
if (*first2 < *first1)
return false;
}
return (first1 == last1) && (first2 != last2);
}
} // namespace bits
} // namespace frozen
#endif

View File

@ -0,0 +1,198 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_BASIC_TYPES_H
#define FROZEN_LETITGO_BASIC_TYPES_H
#include "frozen/bits/exceptions.h"
#include <array>
#include <utility>
#include <string>
#include <type_traits>
namespace frozen {
namespace bits {
// used as a fake argument for frozen::make_set and frozen::make_map in the case of N=0
struct ignored_arg {};
template <class T, std::size_t N>
class cvector {
T data [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise
std::size_t dsize = 0;
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr cvector(void) = default;
constexpr cvector(size_type count, const T& value) : dsize(count) {
for (std::size_t i = 0; i < N; ++i)
data[i] = value;
}
// Iterators
constexpr iterator begin() noexcept { return data; }
constexpr iterator end() noexcept { return data + dsize; }
constexpr const_iterator begin() const noexcept { return data; }
constexpr const_iterator end() const noexcept { return data + dsize; }
// Capacity
constexpr size_type size() const { return dsize; }
// Element access
constexpr reference operator[](std::size_t index) { return data[index]; }
constexpr const_reference operator[](std::size_t index) const { return data[index]; }
constexpr reference back() { return data[dsize - 1]; }
constexpr const_reference back() const { return data[dsize - 1]; }
// Modifiers
constexpr void push_back(const T & a) { data[dsize++] = a; }
constexpr void push_back(T && a) { data[dsize++] = std::move(a); }
constexpr void pop_back() { --dsize; }
constexpr void clear() { dsize = 0; }
};
template <class T, std::size_t N>
class carray {
T data_ [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise
template <class Iter, std::size_t... I>
constexpr carray(Iter iter, std::index_sequence<I...>)
: data_{((void)I, *iter++)...} {}
template <std::size_t... I>
constexpr carray(const T& value, std::index_sequence<I...>)
: data_{((void)I, value)...} {}
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr carray() = default;
constexpr carray(const value_type& val)
: carray(val, std::make_index_sequence<N>()) {}
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value, std::size_t> M>
constexpr carray(U const (&init)[M])
: carray(init, std::make_index_sequence<N>())
{
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
}
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value, std::size_t> M>
constexpr carray(std::array<U, M> const &init)
: carray(init.begin(), std::make_index_sequence<N>())
{
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
}
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr>
constexpr carray(std::initializer_list<U> init)
: carray(init.begin(), std::make_index_sequence<N>())
{
// clang & gcc doesn't recognize init.size() as a constexpr
// static_assert(init.size() >= N, "Cannot initialize a carray with an smaller initializer list");
}
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr>
constexpr carray(const carray<U, N>& rhs)
: carray(rhs.begin(), std::make_index_sequence<N>())
{
}
// Iterators
constexpr iterator begin() noexcept { return data_; }
constexpr const_iterator begin() const noexcept { return data_; }
constexpr iterator end() noexcept { return data_ + N; }
constexpr const_iterator end() const noexcept { return data_ + N; }
// Capacity
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
// Element access
constexpr reference operator[](std::size_t index) { return data_[index]; }
constexpr const_reference operator[](std::size_t index) const { return data_[index]; }
constexpr reference at(std::size_t index) {
if (index > N)
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')'));
return data_[index];
}
constexpr const_reference at(std::size_t index) const {
if (index > N)
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')'));
return data_[index];
}
constexpr reference front() { return data_[0]; }
constexpr const_reference front() const { return data_[0]; }
constexpr reference back() { return data_[N - 1]; }
constexpr const_reference back() const { return data_[N - 1]; }
constexpr value_type* data() noexcept { return data_; }
constexpr const value_type* data() const noexcept { return data_; }
};
template <class T>
class carray<T, 0> {
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr carray(void) = default;
};
} // namespace bits
} // namespace frozen
#endif

View File

@ -0,0 +1,40 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_CONSTEXPR_ASSERT_H
#define FROZEN_LETITGO_CONSTEXPR_ASSERT_H
#include <cassert>
#ifdef _MSC_VER
// FIXME: find a way to implement that correctly for msvc
#define constexpr_assert(cond, msg)
#else
#define constexpr_assert(cond, msg)\
assert(cond && msg);
#endif
#endif

View File

@ -0,0 +1,66 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_DEFINES_H
#define FROZEN_LETITGO_DEFINES_H
#if defined(_MSVC_LANG) && !(defined(__EDG__) && defined(__clang__)) // TRANSITION, VSO#273681
#define FROZEN_LETITGO_IS_MSVC
#endif
// Code taken from https://stackoverflow.com/questions/43639122/which-values-can-msvc-lang-have
#if defined(FROZEN_LETITGO_IS_MSVC)
#if _MSVC_LANG > 201402
#define FROZEN_LETITGO_HAS_CXX17 1
#else /* _MSVC_LANG > 201402 */
#define FROZEN_LETITGO_HAS_CXX17 0
#endif /* _MSVC_LANG > 201402 */
#else /* _MSVC_LANG etc. */
#if __cplusplus > 201402
#define FROZEN_LETITGO_HAS_CXX17 1
#else /* __cplusplus > 201402 */
#define FROZEN_LETITGO_HAS_CXX17 0
#endif /* __cplusplus > 201402 */
#endif /* _MSVC_LANG etc. */
// End if taken code
#if FROZEN_LETITGO_HAS_CXX17 == 1 && defined(FROZEN_LETITGO_IS_MSVC)
#define FROZEN_LETITGO_HAS_STRING_VIEW // We assume Visual Studio always has string_view in C++17
#else
#if FROZEN_LETITGO_HAS_CXX17 == 1 && __has_include(<string_view>)
#define FROZEN_LETITGO_HAS_STRING_VIEW
#endif
#endif
#ifdef __cpp_char8_t
#define FROZEN_LETITGO_HAS_CHAR8T
#endif
#if __cpp_deduction_guides >= 201703L
#define FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
#endif
#if __cpp_lib_constexpr_string >= 201907L
#define FROZEN_LETITGO_HAS_CONSTEXPR_STRING
#endif
#endif // FROZEN_LETITGO_DEFINES_H

View File

@ -0,0 +1,57 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_ELSA_H
#define FROZEN_LETITGO_ELSA_H
#include <type_traits>
namespace frozen {
template <class T = void> struct elsa {
static_assert(std::is_integral<T>::value || std::is_enum<T>::value,
"only supports integral types, specialize for other types");
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
std::size_t key = seed ^ static_cast<std::size_t>(value);
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
};
template <> struct elsa<void> {
template<class T>
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
return elsa<T>{}(value, seed);
}
};
template <class T=void> using anna = elsa<T>;
} // namespace frozen
#endif

View File

@ -0,0 +1,41 @@
#ifndef FROZEN_LETITGO_BITS_ELSA_STD_H
#define FROZEN_LETITGO_BITS_ELSA_STD_H
#include "defines.h"
#include "elsa.h"
#include "hash_string.h"
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
#include <string_view>
#endif
#include <string>
namespace frozen {
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
template <typename CharT> struct elsa<std::basic_string_view<CharT>>
{
constexpr std::size_t operator()(const std::basic_string_view<CharT>& value) const {
return hash_string(value);
}
constexpr std::size_t operator()(const std::basic_string_view<CharT>& value, std::size_t seed) const {
return hash_string(value, seed);
}
};
#endif
template <typename CharT> struct elsa<std::basic_string<CharT>>
{
constexpr std::size_t operator()(const std::basic_string<CharT>& value) const {
return hash_string(value);
}
constexpr std::size_t operator()(const std::basic_string<CharT>& value, std::size_t seed) const {
return hash_string(value, seed);
}
};
} // namespace frozen
#endif // FROZEN_LETITGO_BITS_ELSA_STD_H

View File

@ -0,0 +1,39 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_EXCEPTIONS_H
#define FROZEN_LETITGO_EXCEPTIONS_H
#if defined(FROZEN_NO_EXCEPTIONS) || (defined(_MSC_VER) && !defined(_CPPUNWIND)) || (!defined(_MSC_VER) && !defined(__cpp_exceptions))
#include <cstdlib>
#define FROZEN_THROW_OR_ABORT(_) std::abort()
#else
#include <stdexcept>
#define FROZEN_THROW_OR_ABORT(err) throw err
#endif
#endif

View File

@ -0,0 +1,28 @@
#ifndef FROZEN_LETITGO_BITS_HASH_STRING_H
#define FROZEN_LETITGO_BITS_HASH_STRING_H
#include <cstddef>
namespace frozen {
template <typename String>
constexpr std::size_t hash_string(const String& value) {
std::size_t d = 5381;
for (const auto& c : value)
d = d * 33 + static_cast<std::size_t>(c);
return d;
}
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
// With the lowest bits removed, based on experimental setup.
template <typename String>
constexpr std::size_t hash_string(const String& value, std::size_t seed) {
std::size_t d = (0x811c9dc5 ^ seed) * static_cast<std::size_t>(0x01000193);
for (const auto& c : value)
d = (d ^ static_cast<std::size_t>(c)) * static_cast<std::size_t>(0x01000193);
return d >> 8 ;
}
} // namespace frozen
#endif // FROZEN_LETITGO_BITS_HASH_STRING_H

View File

@ -0,0 +1,56 @@
/*
* Frozen
* Copyright 2022 Giel van Schijndel
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_BITS_MPL_H
#define FROZEN_LETITGO_BITS_MPL_H
#include <utility>
namespace frozen {
namespace bits {
// Forward declarations
template <class, std::size_t>
class carray;
template <typename T>
struct remove_cv : std::remove_cv<T> {};
template <typename... T>
struct remove_cv<std::pair<T...>> {
using type = std::pair<typename remove_cv<T>::type...>;
};
template <typename T, std::size_t N>
struct remove_cv<carray<T, N>> {
using type = carray<typename remove_cv<T>::type, N>;
};
template <typename T>
using remove_cv_t = typename remove_cv<T>::type;
} // namespace bits
} // namespace frozen
#endif

View File

@ -0,0 +1,254 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
// inspired from http://stevehanov.ca/blog/index.php?id=119
#ifndef FROZEN_LETITGO_PMH_H
#define FROZEN_LETITGO_PMH_H
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <limits>
namespace frozen {
namespace bits {
// Function object for sorting buckets in decreasing order of size
struct bucket_size_compare {
template <typename B>
bool constexpr operator()(B const &b0,
B const &b1) const {
return b0.size() > b1.size();
}
};
// Step One in pmh routine is to take all items and hash them into buckets,
// with some collisions. Then process those buckets further to build a perfect
// hash function.
// pmh_buckets represents the initial placement into buckets.
template <std::size_t M>
struct pmh_buckets {
// Step 0: Bucket max is 2 * sqrt M
// TODO: Come up with justification for this, should it not be O(log M)?
static constexpr auto bucket_max = 2 * (1u << (log(M) / 2));
using bucket_t = cvector<std::size_t, bucket_max>;
carray<bucket_t, M> buckets;
std::uint64_t seed;
// Represents a reference to a bucket. This is used because the buckets
// have to be sorted, but buckets are big, making it slower than sorting refs
struct bucket_ref {
unsigned hash;
const bucket_t * ptr;
// Forward some interface of bucket
using value_type = typename bucket_t::value_type;
using const_iterator = typename bucket_t::const_iterator;
constexpr auto size() const { return ptr->size(); }
constexpr const auto & operator[](std::size_t idx) const { return (*ptr)[idx]; }
constexpr auto begin() const { return ptr->begin(); }
constexpr auto end() const { return ptr->end(); }
};
// Make a bucket_ref for each bucket
template <std::size_t... Is>
carray<bucket_ref, M> constexpr make_bucket_refs(std::index_sequence<Is...>) const {
return {{ bucket_ref{Is, &buckets[Is]}... }};
}
// Makes a bucket_ref for each bucket and sorts them by size
carray<bucket_ref, M> constexpr get_sorted_buckets() const {
carray<bucket_ref, M> result{this->make_bucket_refs(std::make_index_sequence<M>())};
bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{});
return result;
}
};
template <std::size_t M, class Item, std::size_t N, class Hash, class Key, class PRG>
pmh_buckets<M> constexpr make_pmh_buckets(const carray<Item, N> & items,
Hash const & hash,
Key const & key,
PRG & prg) {
using result_t = pmh_buckets<M>;
// Continue until all items are placed without exceeding bucket_max
while (1) {
result_t result{};
result.seed = prg();
bool rejected = false;
for (std::size_t i = 0; i < items.size(); ++i) {
auto & bucket = result.buckets[hash(key(items[i]), static_cast<std::size_t>(result.seed)) % M];
if (bucket.size() >= result_t::bucket_max) {
rejected = true;
break;
}
bucket.push_back(i);
}
if (!rejected) { return result; }
}
}
// Check if an item appears in a cvector
template<class T, std::size_t N>
constexpr bool all_different_from(cvector<T, N> & data, T & a) {
for (std::size_t i = 0; i < data.size(); ++i)
if (data[i] == a)
return false;
return true;
}
// Represents either an index to a data item array, or a seed to be used with
// a hasher. Seed must have high bit of 1, value has high bit of zero.
struct seed_or_index {
using value_type = std::uint64_t;
private:
static constexpr value_type MINUS_ONE = std::numeric_limits<value_type>::max();
static constexpr value_type HIGH_BIT = ~(MINUS_ONE >> 1);
value_type value_ = 0;
public:
constexpr value_type value() const { return value_; }
constexpr bool is_seed() const { return value_ & HIGH_BIT; }
constexpr seed_or_index(bool is_seed, value_type value)
: value_(is_seed ? (value | HIGH_BIT) : (value & ~HIGH_BIT)) {}
constexpr seed_or_index() = default;
constexpr seed_or_index(const seed_or_index &) = default;
constexpr seed_or_index & operator =(const seed_or_index &) = default;
};
// Represents the perfect hash function created by pmh algorithm
template <std::size_t M, class Hasher>
struct pmh_tables : private Hasher {
std::uint64_t first_seed_;
carray<seed_or_index, M> first_table_;
carray<std::size_t, M> second_table_;
constexpr pmh_tables(
std::uint64_t first_seed,
carray<seed_or_index, M> first_table,
carray<std::size_t, M> second_table,
Hasher hash) noexcept
: Hasher(hash)
, first_seed_(first_seed)
, first_table_(first_table)
, second_table_(second_table)
{}
constexpr Hasher const& hash_function() const noexcept {
return static_cast<Hasher const&>(*this);
}
template <typename KeyType>
constexpr std::size_t lookup(const KeyType & key) const {
return lookup(key, hash_function());
}
// Looks up a given key, to find its expected index in carray<Item, N>
// Always returns a valid index, must use KeyEqual test after to confirm.
template <typename KeyType, typename HasherType>
constexpr std::size_t lookup(const KeyType & key, const HasherType& hasher) const {
auto const d = first_table_[hasher(key, static_cast<std::size_t>(first_seed_)) % M];
if (!d.is_seed()) { return static_cast<std::size_t>(d.value()); } // this is narrowing std::uint64 -> std::size_t but should be fine
else { return second_table_[hasher(key, static_cast<std::size_t>(d.value())) % M]; }
}
};
// Make pmh tables for given items, hash function, prg, etc.
template <std::size_t M, class Item, std::size_t N, class Hash, class Key, class PRG>
pmh_tables<M, Hash> constexpr make_pmh_tables(const carray<Item, N> &
items,
Hash const &hash,
Key const &key,
PRG prg) {
// Step 1: Place all of the keys into buckets
auto step_one = make_pmh_buckets<M>(items, hash, key, prg);
// Step 2: Sort the buckets to process the ones with the most items first.
auto buckets = step_one.get_sorted_buckets();
// Special value for unused slots. This is purposefully the index
// one-past-the-end of 'items' to function as a sentinel value. Both to avoid
// the need to apply the KeyEqual predicate and to be easily convertible to
// end().
// Unused entries in both hash tables (G and H) have to contain this value.
const auto UNUSED = items.size();
// G becomes the first hash table in the resulting pmh function
carray<seed_or_index, M> G({false, UNUSED});
// H becomes the second hash table in the resulting pmh function
carray<std::size_t, M> H(UNUSED);
// Step 3: Map the items in buckets into hash tables.
for (const auto & bucket : buckets) {
auto const bsize = bucket.size();
if (bsize == 1) {
// Store index to the (single) item in G
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
G[bucket.hash] = {false, static_cast<std::uint64_t>(bucket[0])};
} else if (bsize > 1) {
// Repeatedly try different H of d until we find a hash function
// that places all items in the bucket into free slots
seed_or_index d{true, prg()};
cvector<std::size_t, decltype(step_one)::bucket_max> bucket_slots;
while (bucket_slots.size() < bsize) {
auto slot = hash(key(items[bucket[bucket_slots.size()]]), static_cast<std::size_t>(d.value())) % M;
if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) {
bucket_slots.clear();
d = {true, prg()};
continue;
}
bucket_slots.push_back(slot);
}
// Put successful seed in G, and put indices to items in their slots
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
G[bucket.hash] = d;
for (std::size_t i = 0; i < bsize; ++i)
H[bucket_slots[i]] = bucket[i];
}
}
return {step_one.seed, G, H, hash};
}
} // namespace bits
} // namespace frozen
#endif

View File

@ -0,0 +1,30 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_VERSION_H
#define FROZEN_LETITGO_VERSION_H
#define FROZEN_MAJOR_VERSION 1
#define FROZEN_MINOR_VERSION 1
#define FROZEN_PATCH_VERSION 1
#endif

357
lib/Frozen/frozen/map.h Normal file
View File

@ -0,0 +1,357 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_MAP_H
#define FROZEN_LETITGO_MAP_H
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/exceptions.h"
#include "frozen/bits/mpl.h"
#include "frozen/bits/version.h"
#include <iterator>
#include <utility>
namespace frozen {
namespace impl {
template <class Comparator> class CompareKey : private Comparator {
public:
constexpr Comparator const& key_comp() const noexcept {
return static_cast<Comparator const&>(*this);
}
constexpr CompareKey(Comparator const &comparator)
: Comparator(comparator) {}
template <class Key1, class Key2, class Value>
constexpr int operator()(std::pair<Key1, Value> const &self,
std::pair<Key2, Value> const &other) const {
return key_comp()(std::get<0>(self), std::get<0>(other));
}
template <class Key1, class Key2, class Value>
constexpr int operator()(Key1 const &self_key,
std::pair<Key2, Value> const &other) const {
return key_comp()(self_key, std::get<0>(other));
}
template <class Key1, class Key2, class Value>
constexpr int operator()(std::pair<Key1, Value> const &self,
Key2 const &other_key) const {
return key_comp()(std::get<0>(self), other_key);
}
template <class Key1, class Key2>
constexpr int operator()(Key1 const &self_key, Key2 const &other_key) const {
return key_comp()(self_key, other_key);
}
};
} // namespace impl
template <class Key, class Value, std::size_t N, class Compare = std::less<Key>>
class map : private impl::CompareKey<Compare> {
using container_type = bits::carray<std::pair<const Key, Value>, N>;
container_type items_;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using key_compare = Compare;
using value_compare = impl::CompareKey<Compare>;
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
public:
/* constructors */
constexpr map(container_type items, Compare const &compare)
: impl::CompareKey<Compare>{compare}
, items_{bits::quicksort(bits::remove_cv_t<container_type>(items), value_comp())} {}
explicit constexpr map(container_type items)
: map{items, Compare{}} {}
constexpr map(std::initializer_list<value_type> items, Compare const &compare)
: map{container_type {items}, compare} {
constexpr_assert(items.size() == N, "Inconsistent initializer_list size and type size argument");
}
constexpr map(std::initializer_list<value_type> items)
: map{items, Compare{}} {}
/* element access */
constexpr Value const& at(Key const &key) const {
return at_impl(*this, key);
}
constexpr Value& at(Key const &key) {
return at_impl(*this, key);
}
/* iterators */
constexpr iterator begin() { return items_.begin(); }
constexpr const_iterator begin() const { return items_.begin(); }
constexpr const_iterator cbegin() const { return items_.begin(); }
constexpr iterator end() { return items_.end(); }
constexpr const_iterator end() const { return items_.end(); }
constexpr const_iterator cend() const { return items_.end(); }
constexpr reverse_iterator rbegin() { return reverse_iterator{items_.end()}; }
constexpr const_reverse_iterator rbegin() const { return const_reverse_iterator{items_.end()}; }
constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{items_.end()}; }
constexpr reverse_iterator rend() { return reverse_iterator{items_.begin()}; }
constexpr const_reverse_iterator rend() const { return const_reverse_iterator{items_.begin()}; }
constexpr const_reverse_iterator crend() const { return const_reverse_iterator{items_.begin()}; }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return bits::binary_search<N>(items_.begin(), key, value_comp());
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return map::find_impl(*this, key);
}
template <class KeyType>
constexpr iterator find(KeyType const &key) {
return map::find_impl(*this, key);
}
template <class KeyType>
constexpr bool contains(KeyType const &key) const {
return this->find(key) != this->end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator>
equal_range(KeyType const &key) const {
return equal_range_impl(*this, key);
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
return equal_range_impl(*this, key);
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &key) const {
return lower_bound_impl(*this, key);
}
template <class KeyType>
constexpr iterator lower_bound(KeyType const &key) {
return lower_bound_impl(*this, key);
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &key) const {
return upper_bound_impl(*this, key);
}
template <class KeyType>
constexpr iterator upper_bound(KeyType const &key) {
return upper_bound_impl(*this, key);
}
/* observers */
constexpr const key_compare& key_comp() const { return value_comp().key_comp(); }
constexpr const value_compare& value_comp() const { return static_cast<impl::CompareKey<Compare> const&>(*this); }
private:
template <class This, class KeyType>
static inline constexpr auto& at_impl(This&& self, KeyType const &key) {
auto where = self.find(key);
if (where != self.end())
return where->second;
else
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
}
template <class This, class KeyType>
static inline constexpr auto find_impl(This&& self, KeyType const &key) {
auto where = self.lower_bound(key);
if (where != self.end() && !self.value_comp()(key, *where))
return where;
else
return self.end();
}
template <class This, class KeyType>
static inline constexpr auto equal_range_impl(This&& self, KeyType const &key) {
auto lower = self.lower_bound(key);
using lower_t = decltype(lower);
if (lower != self.end() && !self.value_comp()(key, *lower))
return std::pair<lower_t, lower_t>{lower, lower + 1};
else
return std::pair<lower_t, lower_t>{lower, lower};
}
template <class This, class KeyType>
static inline constexpr auto lower_bound_impl(This&& self, KeyType const &key) -> decltype(self.end()) {
return bits::lower_bound<N>(self.items_.begin(), key, self.value_comp());
}
template <class This, class KeyType>
static inline constexpr auto upper_bound_impl(This&& self, KeyType const &key) {
auto lower = self.lower_bound(key);
if (lower != self.end() && !self.value_comp()(key, *lower))
return lower + 1;
else
return lower;
}
};
template <class Key, class Value, class Compare>
class map<Key, Value, 0, Compare> : private impl::CompareKey<Compare> {
using container_type = bits::carray<std::pair<Key, Value>, 0>;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using key_compare = Compare;
using value_compare = impl::CompareKey<Compare>;
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = pointer;
using const_reverse_iterator = const_pointer;
public:
/* constructors */
constexpr map(const map &other) = default;
constexpr map(std::initializer_list<value_type>, Compare const &compare)
: impl::CompareKey<Compare>{compare} {}
constexpr map(std::initializer_list<value_type> items)
: map{items, Compare{}} {}
/* element access */
template <class KeyType>
constexpr mapped_type at(KeyType const &) const {
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
}
template <class KeyType>
constexpr mapped_type at(KeyType const &) {
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
}
/* iterators */
constexpr iterator begin() { return nullptr; }
constexpr const_iterator begin() const { return nullptr; }
constexpr const_iterator cbegin() const { return nullptr; }
constexpr iterator end() { return nullptr; }
constexpr const_iterator end() const { return nullptr; }
constexpr const_iterator cend() const { return nullptr; }
constexpr reverse_iterator rbegin() { return nullptr; }
constexpr const_reverse_iterator rbegin() const { return nullptr; }
constexpr const_reverse_iterator crbegin() const { return nullptr; }
constexpr reverse_iterator rend() { return nullptr; }
constexpr const_reverse_iterator rend() const { return nullptr; }
constexpr const_reverse_iterator crend() const { return nullptr; }
/* capacity */
constexpr bool empty() const { return true; }
constexpr size_type size() const { return 0; }
constexpr size_type max_size() const { return 0; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &) const { return 0; }
template <class KeyType>
constexpr const_iterator find(KeyType const &) const { return end(); }
template <class KeyType>
constexpr iterator find(KeyType const &) { return end(); }
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator>
equal_range(KeyType const &) const { return {end(), end()}; }
template <class KeyType>
constexpr std::pair<iterator, iterator>
equal_range(KeyType const &) { return {end(), end()}; }
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &) const { return end(); }
template <class KeyType>
constexpr iterator lower_bound(KeyType const &) { return end(); }
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &) const { return end(); }
template <class KeyType>
constexpr iterator upper_bound(KeyType const &) { return end(); }
/* observers */
constexpr key_compare const& key_comp() const { return value_comp().key_comp(); }
constexpr value_compare const& value_comp() const { return static_cast<impl::CompareKey<Compare> const&>(*this); }
};
template <typename T, typename U, typename Compare = std::less<T>>
constexpr auto make_map(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) {
return map<T, U, 0, Compare>{};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_map(std::pair<T, U> const (&items)[N]) {
return map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items) {
return map<T, U, N>{items};
}
template <typename T, typename U, typename Compare, std::size_t N>
constexpr auto make_map(std::pair<T, U> const (&items)[N], Compare const& compare = Compare{}) {
return map<T, U, N, Compare>{items, compare};
}
template <typename T, typename U, typename Compare, std::size_t N>
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items, Compare const& compare = Compare{}) {
return map<T, U, N, Compare>{items, compare};
}
} // namespace frozen
#endif

View File

@ -0,0 +1,97 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_RANDOM_H
#define FROZEN_LETITGO_RANDOM_H
#include "frozen/bits/algorithms.h"
#include "frozen/bits/version.h"
#include <cstdint>
#include <type_traits>
namespace frozen {
template <class UIntType, UIntType a, UIntType c, UIntType m>
class linear_congruential_engine {
static_assert(std::is_unsigned<UIntType>::value,
"UIntType must be an unsigned integral type");
template<class T>
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, 0>) {
return static_cast<UIntType>(val);
}
template<class T, UIntType M>
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, M>) {
// the static cast below may end up doing a truncation
return static_cast<UIntType>(val % M);
}
public:
using result_type = UIntType;
static constexpr result_type multiplier = a;
static constexpr result_type increment = c;
static constexpr result_type modulus = m;
static constexpr result_type default_seed = 1u;
linear_congruential_engine() = default;
constexpr linear_congruential_engine(result_type s) { seed(s); }
void seed(result_type s = default_seed) { state_ = s; }
constexpr result_type operator()() {
using uint_least_t = bits::select_uint_least_t<bits::log(a) + bits::log(m) + 4>;
uint_least_t tmp = static_cast<uint_least_t>(multiplier) * state_ + increment;
state_ = modulo(tmp, std::integral_constant<UIntType, modulus>());
return state_;
}
constexpr void discard(unsigned long long n) {
while (n--)
operator()();
}
static constexpr result_type min() { return increment == 0u ? 1u : 0u; }
static constexpr result_type max() { return modulus - 1u; }
friend constexpr bool operator==(linear_congruential_engine const &self,
linear_congruential_engine const &other) {
return self.state_ == other.state_;
}
friend constexpr bool operator!=(linear_congruential_engine const &self,
linear_congruential_engine const &other) {
return !(self == other);
}
private:
result_type state_ = default_seed;
};
using minstd_rand0 =
linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
using minstd_rand =
linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
// This generator is used by default in unordered frozen containers
using default_prg_t = minstd_rand;
} // namespace frozen
#endif

260
lib/Frozen/frozen/set.h Normal file
View File

@ -0,0 +1,260 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_SET_H
#define FROZEN_SET_H
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/version.h"
#include "frozen/bits/defines.h"
#include <iterator>
#include <utility>
namespace frozen {
template <class Key, std::size_t N, class Compare = std::less<Key>> class set : private Compare {
using container_type = bits::carray<Key, N>;
container_type keys_;
public:
/* container typedefs*/
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::size_type;
using key_compare = Compare;
using value_compare = Compare;
using reference = typename container_type::const_reference;
using const_reference = reference;
using pointer = typename container_type::const_pointer;
using const_pointer = pointer;
using iterator = typename container_type::const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_iterator = iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
public:
/* constructors */
constexpr set(const set &other) = default;
constexpr set(container_type keys, Compare const & comp)
: Compare{comp}
, keys_(bits::quicksort(keys, value_comp())) {
}
explicit constexpr set(container_type keys)
: set{keys, Compare{}} {}
constexpr set(std::initializer_list<Key> keys, Compare const & comp)
: set{container_type{keys}, comp} {
constexpr_assert(keys.size() == N, "Inconsistent initializer_list size and type size argument");
}
constexpr set(std::initializer_list<Key> keys)
: set{keys, Compare{}} {}
constexpr set& operator=(const set &other) = default;
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return bits::binary_search<N>(keys_.begin(), key, value_comp());
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
const_iterator where = lower_bound(key);
if ((where != end()) && !value_comp()(key, *where))
return where;
else
return end();
}
template <class KeyType>
constexpr bool contains(KeyType const &key) const {
return this->find(key) != keys_.end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(KeyType const &key) const {
auto const lower = lower_bound(key);
if (lower == end())
return {lower, lower};
else
return {lower, lower + 1};
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &key) const {
auto const where = bits::lower_bound<N>(keys_.begin(), key, value_comp());
if ((where != end()) && !value_comp()(key, *where))
return where;
else
return end();
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &key) const {
auto const where = bits::lower_bound<N>(keys_.begin(), key, value_comp());
if ((where != end()) && !value_comp()(key, *where))
return where + 1;
else
return end();
}
/* observers */
constexpr const key_compare& key_comp() const { return value_comp(); }
constexpr const key_compare& value_comp() const { return static_cast<const Compare&>(*this); }
/* iterators */
constexpr const_iterator begin() const { return keys_.begin(); }
constexpr const_iterator cbegin() const { return keys_.begin(); }
constexpr const_iterator end() const { return keys_.end(); }
constexpr const_iterator cend() const { return keys_.end(); }
constexpr const_reverse_iterator rbegin() const { return const_reverse_iterator{keys_.end()}; }
constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{keys_.end()}; }
constexpr const_reverse_iterator rend() const { return const_reverse_iterator{keys_.begin()}; }
constexpr const_reverse_iterator crend() const { return const_reverse_iterator{keys_.begin()}; }
/* comparison */
constexpr bool operator==(set const& rhs) const { return bits::equal(begin(), end(), rhs.begin()); }
constexpr bool operator!=(set const& rhs) const { return !(*this == rhs); }
constexpr bool operator<(set const& rhs) const { return bits::lexicographical_compare(begin(), end(), rhs.begin(), rhs.end()); }
constexpr bool operator<=(set const& rhs) const { return (*this < rhs) || (*this == rhs); }
constexpr bool operator>(set const& rhs) const { return bits::lexicographical_compare(rhs.begin(), rhs.end(), begin(), end()); }
constexpr bool operator>=(set const& rhs) const { return (*this > rhs) || (*this == rhs); }
};
template <class Key, class Compare> class set<Key, 0, Compare> : private Compare {
using container_type = bits::carray<Key, 0>; // just for the type definitions
public:
/* container typedefs*/
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::size_type;
using key_compare = Compare;
using value_compare = Compare;
using reference = typename container_type::const_reference;
using const_reference = reference;
using pointer = typename container_type::const_pointer;
using const_pointer = pointer;
using iterator = pointer;
using reverse_iterator = pointer;
using const_iterator = const_pointer;
using const_reverse_iterator = const_pointer;
public:
/* constructors */
constexpr set(const set &other) = default;
constexpr set(bits::carray<Key, 0>, Compare const &) {}
explicit constexpr set(bits::carray<Key, 0>) {}
constexpr set(std::initializer_list<Key>, Compare const &comp)
: Compare{comp} {}
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
constexpr set& operator=(const set &other) = default;
/* capacity */
constexpr bool empty() const { return true; }
constexpr size_type size() const { return 0; }
constexpr size_type max_size() const { return 0; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &) const { return 0; }
template <class KeyType>
constexpr const_iterator find(KeyType const &) const { return end(); }
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator>
equal_range(KeyType const &) const { return {end(), end()}; }
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &) const { return end(); }
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &) const { return end(); }
/* observers */
constexpr const key_compare& key_comp() const { return value_comp(); }
constexpr const key_compare& value_comp() const { return static_cast<Compare const&>(*this); }
/* iterators */
constexpr const_iterator begin() const { return nullptr; }
constexpr const_iterator cbegin() const { return nullptr; }
constexpr const_iterator end() const { return nullptr; }
constexpr const_iterator cend() const { return nullptr; }
constexpr const_reverse_iterator rbegin() const { return nullptr; }
constexpr const_reverse_iterator crbegin() const { return nullptr; }
constexpr const_reverse_iterator rend() const { return nullptr; }
constexpr const_reverse_iterator crend() const { return nullptr; }
};
template <typename T>
constexpr auto make_set(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) {
return set<T, 0>{};
}
template <typename T, std::size_t N>
constexpr auto make_set(const T (&args)[N]) {
return set<T, N>(args);
}
template <typename T, std::size_t N>
constexpr auto make_set(std::array<T, N> const &args) {
return set<T, N>(args);
}
template <typename T, typename Compare, std::size_t N>
constexpr auto make_set(const T (&args)[N], Compare const& compare = Compare{}) {
return set<T, N, Compare>(args, compare);
}
template <typename T, typename Compare, std::size_t N>
constexpr auto make_set(std::array<T, N> const &args, Compare const& compare = Compare{}) {
return set<T, N, Compare>(args, compare);
}
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
template<class T, class... Args>
set(T, Args...) -> set<T, sizeof...(Args) + 1>;
#endif // FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
} // namespace frozen
#endif

152
lib/Frozen/frozen/string.h Normal file
View File

@ -0,0 +1,152 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_STRING_H
#define FROZEN_LETITGO_STRING_H
#include "frozen/bits/elsa.h"
#include "frozen/bits/hash_string.h"
#include "frozen/bits/version.h"
#include "frozen/bits/defines.h"
#include <cstddef>
#include <functional>
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
#include <string_view>
#endif
namespace frozen {
template <typename _CharT>
class basic_string {
using chr_t = _CharT;
chr_t const *data_;
std::size_t size_;
public:
template <std::size_t N>
constexpr basic_string(chr_t const (&data)[N])
: data_(data), size_(N - 1) {}
constexpr basic_string(chr_t const *data, std::size_t size)
: data_(data), size_(size) {}
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
constexpr basic_string(std::basic_string_view<chr_t> data)
: data_(data.data()), size_(data.size()) {}
#endif
constexpr basic_string(const basic_string &) noexcept = default;
constexpr basic_string &operator=(const basic_string &) noexcept = default;
constexpr std::size_t size() const { return size_; }
constexpr chr_t operator[](std::size_t i) const { return data_[i]; }
constexpr bool operator==(basic_string other) const {
if (size_ != other.size_)
return false;
for (std::size_t i = 0; i < size_; ++i)
if (data_[i] != other.data_[i])
return false;
return true;
}
constexpr bool operator<(const basic_string &other) const {
unsigned i = 0;
while (i < size() && i < other.size()) {
if ((*this)[i] < other[i]) {
return true;
}
if ((*this)[i] > other[i]) {
return false;
}
++i;
}
return size() < other.size();
}
friend constexpr bool operator>(const basic_string& lhs, const basic_string& rhs) {
return rhs < lhs;
}
constexpr const chr_t *data() const { return data_; }
constexpr const chr_t *begin() const { return data(); }
constexpr const chr_t *end() const { return data() + size(); }
};
template <typename _CharT> struct elsa<basic_string<_CharT>> {
constexpr std::size_t operator()(basic_string<_CharT> value) const {
return hash_string(value);
}
constexpr std::size_t operator()(basic_string<_CharT> value, std::size_t seed) const {
return hash_string(value, seed);
}
};
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
using u16string = basic_string<char16_t>;
using u32string = basic_string<char32_t>;
#ifdef FROZEN_LETITGO_HAS_CHAR8T
using u8string = basic_string<char8_t>;
#endif
namespace string_literals {
constexpr string operator"" _s(const char *data, std::size_t size) {
return {data, size};
}
constexpr wstring operator"" _s(const wchar_t *data, std::size_t size) {
return {data, size};
}
constexpr u16string operator"" _s(const char16_t *data, std::size_t size) {
return {data, size};
}
constexpr u32string operator"" _s(const char32_t *data, std::size_t size) {
return {data, size};
}
#ifdef FROZEN_LETITGO_HAS_CHAR8T
constexpr u8string operator"" _s(const char8_t *data, std::size_t size) {
return {data, size};
}
#endif
} // namespace string_literals
} // namespace frozen
namespace std {
template <typename _CharT> struct hash<frozen::basic_string<_CharT>> {
std::size_t operator()(frozen::basic_string<_CharT> s) const {
return frozen::elsa<frozen::basic_string<_CharT>>{}(s);
}
};
} // namespace std
#endif

View File

@ -0,0 +1,217 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_UNORDERED_MAP_H
#define FROZEN_LETITGO_UNORDERED_MAP_H
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/exceptions.h"
#include "frozen/bits/pmh.h"
#include "frozen/bits/version.h"
#include "frozen/random.h"
#include <tuple>
#include <functional>
#include <utility>
namespace frozen {
namespace bits {
struct GetKey {
template <class KV> constexpr auto const &operator()(KV const &kv) const {
return kv.first;
}
};
} // namespace bits
template <class Key, class Value, std::size_t N, typename Hash = anna<Key>,
class KeyEqual = std::equal_to<Key>>
class unordered_map : private KeyEqual {
static constexpr std::size_t storage_size =
bits::next_highest_power_of_two(N) * (N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for small sets
using container_type = bits::carray<std::pair<const Key, Value>, N>;
using tables_type = bits::pmh_tables<storage_size, Hash>;
container_type items_;
tables_type tables_;
public:
/* typedefs */
using Self = unordered_map<Key, Value, N, Hash, KeyEqual>;
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using hasher = Hash;
using key_equal = KeyEqual;
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
public:
/* constructors */
unordered_map(unordered_map const &) = default;
constexpr unordered_map(container_type items,
Hash const &hash, KeyEqual const &equal)
: KeyEqual{equal}
, items_{items}
, tables_{
bits::make_pmh_tables<storage_size>(
items_, hash, bits::GetKey{}, default_prg_t{})} {}
explicit constexpr unordered_map(container_type items)
: unordered_map{items, Hash{}, KeyEqual{}} {}
constexpr unordered_map(std::initializer_list<value_type> items,
Hash const & hash, KeyEqual const & equal)
: unordered_map{container_type{items}, hash, equal} {
constexpr_assert(items.size() == N, "Inconsistent initializer_list size and type size argument");
}
constexpr unordered_map(std::initializer_list<value_type> items)
: unordered_map{items, Hash{}, KeyEqual{}} {}
/* iterators */
constexpr iterator begin() { return items_.begin(); }
constexpr iterator end() { return items_.end(); }
constexpr const_iterator begin() const { return items_.begin(); }
constexpr const_iterator end() const { return items_.end(); }
constexpr const_iterator cbegin() const { return items_.begin(); }
constexpr const_iterator cend() const { return items_.end(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return find(key) != end();
}
template <class KeyType>
constexpr Value const &at(KeyType const &key) const {
return at_impl(*this, key);
}
template <class KeyType>
constexpr Value &at(KeyType const &key) {
return at_impl(*this, key);
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return find_impl(*this, key, hash_function(), key_eq());
}
template <class KeyType>
constexpr iterator find(KeyType const &key) {
return find_impl(*this, key, hash_function(), key_eq());
}
template <class KeyType>
constexpr bool contains(KeyType const &key) const {
return this->find(key) != this->end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(KeyType const &key) const {
return equal_range_impl(*this, key);
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
return equal_range_impl(*this, key);
}
/* bucket interface */
constexpr std::size_t bucket_count() const { return storage_size; }
constexpr std::size_t max_bucket_count() const { return storage_size; }
/* observers*/
constexpr const hasher& hash_function() const { return tables_.hash_function(); }
constexpr const key_equal& key_eq() const { return static_cast<KeyEqual const&>(*this); }
private:
template <class This, class KeyType>
static inline constexpr auto& at_impl(This&& self, KeyType const &key) {
auto it = self.find(key);
if (it != self.end())
return it->second;
else
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
}
template <class This, class KeyType, class Hasher, class Equal>
static inline constexpr auto find_impl(This&& self, KeyType const &key, Hasher const &hash, Equal const &equal) {
auto const pos = self.tables_.lookup(key, hash);
auto it = self.items_.begin() + pos;
if (it != self.items_.end() && equal(it->first, key))
return it;
else
return self.items_.end();
}
template <class This, class KeyType>
static inline constexpr auto equal_range_impl(This&& self, KeyType const &key) {
auto const it = self.find(key);
if (it != self.end())
return std::make_pair(it, it + 1);
else
return std::make_pair(self.end(), self.end());
}
};
template <typename T, typename U, std::size_t N>
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N]) {
return unordered_map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_map(
std::pair<T, U> const (&items)[N],
Hasher const &hash = elsa<T>{},
Equal const &equal = std::equal_to<T>{}) {
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items) {
return unordered_map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_map(
std::array<std::pair<T, U>, N> const &items,
Hasher const &hash = elsa<T>{},
Equal const &equal = std::equal_to<T>{}) {
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
}
} // namespace frozen
#endif

View File

@ -0,0 +1,181 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License 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.
*/
#ifndef FROZEN_LETITGO_UNORDERED_SET_H
#define FROZEN_LETITGO_UNORDERED_SET_H
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/pmh.h"
#include "frozen/bits/version.h"
#include "frozen/random.h"
#include <utility>
namespace frozen {
namespace bits {
struct Get {
template <class T> constexpr T const &operator()(T const &key) const {
return key;
}
};
} // namespace bits
template <class Key, std::size_t N, typename Hash = elsa<Key>,
class KeyEqual = std::equal_to<Key>>
class unordered_set : private KeyEqual {
static constexpr std::size_t storage_size =
bits::next_highest_power_of_two(N) * (N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for small sets
using container_type = bits::carray<Key, N>;
using tables_type = bits::pmh_tables<storage_size, Hash>;
container_type keys_;
tables_type tables_;
public:
/* typedefs */
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using hasher = Hash;
using key_equal = KeyEqual;
using const_reference = typename container_type::const_reference;
using reference = const_reference;
using const_pointer = typename container_type::const_pointer;
using pointer = const_pointer;
using const_iterator = typename container_type::const_iterator;
using iterator = const_iterator;
public:
/* constructors */
unordered_set(unordered_set const &) = default;
constexpr unordered_set(container_type keys, Hash const &hash,
KeyEqual const &equal)
: KeyEqual{equal}
, keys_{keys}
, tables_{bits::make_pmh_tables<storage_size>(
keys_, hash, bits::Get{}, default_prg_t{})} {}
explicit constexpr unordered_set(container_type keys)
: unordered_set{keys, Hash{}, KeyEqual{}} {}
constexpr unordered_set(std::initializer_list<Key> keys)
: unordered_set{keys, Hash{}, KeyEqual{}} {}
constexpr unordered_set(std::initializer_list<Key> keys, Hash const & hash, KeyEqual const & equal)
: unordered_set{container_type{keys}, hash, equal} {
constexpr_assert(keys.size() == N, "Inconsistent initializer_list size and type size argument");
}
/* iterators */
constexpr const_iterator begin() const { return keys_.begin(); }
constexpr const_iterator end() const { return keys_.end(); }
constexpr const_iterator cbegin() const { return keys_.begin(); }
constexpr const_iterator cend() const { return keys_.end(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return find(key, hash_function(), key_eq()) != end();
}
template <class KeyType, class Hasher, class Equal>
constexpr const_iterator find(KeyType const &key, Hasher const &hash, Equal const &equal) const {
auto const pos = tables_.lookup(key, hash);
auto it = keys_.begin() + pos;
if (it != keys_.end() && equal(*it, key))
return it;
else
return keys_.end();
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
auto const pos = tables_.lookup(key, hash_function());
auto it = keys_.begin() + pos;
if (it != keys_.end() && key_eq()(*it, key))
return it;
else
return keys_.end();
}
template <class KeyType>
constexpr bool contains(KeyType const &key) const {
return this->find(key) != keys_.end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(KeyType const &key) const {
auto const it = find(key);
if (it != end())
return {it, it + 1};
else
return {keys_.end(), keys_.end()};
}
/* bucket interface */
constexpr std::size_t bucket_count() const { return storage_size; }
constexpr std::size_t max_bucket_count() const { return storage_size; }
/* observers*/
constexpr const hasher& hash_function() const { return tables_.hash_function(); }
constexpr const key_equal& key_eq() const { return static_cast<KeyEqual const&>(*this); }
};
template <typename T, std::size_t N>
constexpr auto make_unordered_set(T const (&keys)[N]) {
return unordered_set<T, N>{keys};
}
template <typename T, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_set(T const (&keys)[N], Hasher const& hash, Equal const& equal) {
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
}
template <typename T, std::size_t N>
constexpr auto make_unordered_set(std::array<T, N> const &keys) {
return unordered_set<T, N>{keys};
}
template <typename T, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_set(std::array<T, N> const &keys, Hasher const& hash, Equal const& equal) {
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
}
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
template <class T, class... Args>
unordered_set(T, Args...) -> unordered_set<T, sizeof...(Args) + 1>;
#endif
} // namespace frozen
#endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Thomas Basler and others
* Copyright (C) 2022-2023 Thomas Basler and others
*/
#include "Hoymiles.h"
#include "Utils.h"
@ -24,12 +24,12 @@ void HoymilesClass::init()
_radioCmt.reset(new HoymilesRadio_CMT());
}
void HoymilesClass::initNRF(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ)
void HoymilesClass::initNRF(SPIClass* initialisedSpiBus, const uint8_t pinCE, const uint8_t pinIRQ)
{
_radioNrf->init(initialisedSpiBus, pinCE, pinIRQ);
}
void HoymilesClass::initCMT(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, int8_t pin_gpio2, int8_t pin_gpio3)
void HoymilesClass::initCMT(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3)
{
_radioCmt->init(pin_sdio, pin_clk, pin_cs, pin_fcs, pin_gpio2, pin_gpio3);
}
@ -40,7 +40,10 @@ void HoymilesClass::loop()
_radioNrf->loop();
_radioCmt->loop();
if (getNumInverters() > 0) {
if (getNumInverters() == 0) {
return;
}
if (millis() - _lastPoll > (_pollInterval * 1000)) {
static uint8_t inverterPos = 0;
@ -54,7 +57,6 @@ void HoymilesClass::loop()
if (iv != nullptr && iv->getRadio()->isInitialized() && iv->getRadio()->isQueueEmpty()) {
if (iv->getZeroValuesIfUnreachable() && !iv->isReachable()) {
Hoymiles.getMessageOutput()->println("Set runtime data to zero");
iv->Statistics()->zeroRuntimeData();
}
@ -69,7 +71,7 @@ void HoymilesClass::loop()
iv->sendStatsRequest();
// Fetch event log
bool force = iv->EventLog()->getLastAlarmRequestSuccess() == CMD_NOK;
const bool force = iv->EventLog()->getLastAlarmRequestSuccess() == CMD_NOK;
iv->sendAlarmLogRequest(force);
// Fetch limit
@ -93,7 +95,7 @@ void HoymilesClass::loop()
// Fetch dev info (but first fetch stats)
if (iv->Statistics()->getLastUpdate() > 0) {
bool invalidDevInfo = !iv->DevInfo()->containsValidData()
const bool invalidDevInfo = !iv->DevInfo()->containsValidData()
&& iv->DevInfo()->getLastUpdateAll() > 0
&& iv->DevInfo()->getLastUpdateSimple() > 0;
@ -123,7 +125,7 @@ void HoymilesClass::loop()
}
// Perform housekeeping of all inverters on day change
int8_t currentWeekDay = Utils::getWeekDay();
const int8_t currentWeekDay = Utils::getWeekDay();
static int8_t lastWeekDay = -1;
if (lastWeekDay == -1) {
lastWeekDay = currentWeekDay;
@ -131,6 +133,9 @@ void HoymilesClass::loop()
if (currentWeekDay != lastWeekDay) {
for (auto& inv : _inverters) {
// Have to reset the offets first, otherwise it will
// Substract the offset from zero which leads to a high value
inv->Statistics()->resetYieldDayCorrection();
if (inv->getZeroYieldDayOnMidnight()) {
inv->Statistics()->zeroDailyData();
}
@ -141,9 +146,8 @@ void HoymilesClass::loop()
}
}
}
}
std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, uint64_t serial)
std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, const uint64_t serial)
{
std::shared_ptr<InverterAbstract> i = nullptr;
if (HMT_4CH::isValidSerial(serial)) {
@ -176,7 +180,7 @@ std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, u
return nullptr;
}
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByPos(uint8_t pos)
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByPos(const uint8_t pos)
{
if (pos >= _inverters.size()) {
return nullptr;
@ -185,7 +189,7 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByPos(uint8_t pos)
}
}
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(uint64_t serial)
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(const uint64_t serial)
{
for (uint8_t i = 0; i < _inverters.size(); i++) {
if (_inverters[i]->serial() == serial) {
@ -195,9 +199,9 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(uint64_t se
return nullptr;
}
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(fragment_t* fragment)
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(const fragment_t& fragment)
{
if (fragment->len <= 4) {
if (fragment.len <= 4) {
return nullptr;
}
@ -207,10 +211,10 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(fragment_
serial_u p;
p.u64 = inv->serial();
if ((p.b[3] == fragment->fragment[1])
&& (p.b[2] == fragment->fragment[2])
&& (p.b[1] == fragment->fragment[3])
&& (p.b[0] == fragment->fragment[4])) {
if ((p.b[3] == fragment.fragment[1])
&& (p.b[2] == fragment.fragment[2])
&& (p.b[1] == fragment.fragment[3])
&& (p.b[0] == fragment.fragment[4])) {
return inv;
}
@ -218,7 +222,7 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(fragment_
return nullptr;
}
void HoymilesClass::removeInverterBySerial(uint64_t serial)
void HoymilesClass::removeInverterBySerial(const uint64_t serial)
{
for (uint8_t i = 0; i < _inverters.size(); i++) {
if (_inverters[i]->serial() == serial) {
@ -229,7 +233,7 @@ void HoymilesClass::removeInverterBySerial(uint64_t serial)
}
}
size_t HoymilesClass::getNumInverters()
size_t HoymilesClass::getNumInverters() const
{
return _inverters.size();
}
@ -244,17 +248,17 @@ HoymilesRadio_CMT* HoymilesClass::getRadioCmt()
return _radioCmt.get();
}
bool HoymilesClass::isAllRadioIdle()
bool HoymilesClass::isAllRadioIdle() const
{
return _radioNrf.get()->isIdle() && _radioCmt.get()->isIdle();
}
uint32_t HoymilesClass::PollInterval()
uint32_t HoymilesClass::PollInterval() const
{
return _pollInterval;
}
void HoymilesClass::setPollInterval(uint32_t interval)
void HoymilesClass::setPollInterval(const uint32_t interval)
{
_pollInterval = interval;
}

View File

@ -16,29 +16,29 @@
class HoymilesClass {
public:
void init();
void initNRF(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ);
void initCMT(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, int8_t pin_gpio2, int8_t pin_gpio3);
void initNRF(SPIClass* initialisedSpiBus, const uint8_t pinCE, const uint8_t pinIRQ);
void initCMT(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3);
void loop();
void setMessageOutput(Print* output);
Print* getMessageOutput();
Print* getVerboseMessageOutput();
std::shared_ptr<InverterAbstract> addInverter(const char* name, uint64_t serial);
std::shared_ptr<InverterAbstract> getInverterByPos(uint8_t pos);
std::shared_ptr<InverterAbstract> getInverterBySerial(uint64_t serial);
std::shared_ptr<InverterAbstract> getInverterByFragment(fragment_t* fragment);
void removeInverterBySerial(uint64_t serial);
size_t getNumInverters();
std::shared_ptr<InverterAbstract> addInverter(const char* name, const uint64_t serial);
std::shared_ptr<InverterAbstract> getInverterByPos(const uint8_t pos);
std::shared_ptr<InverterAbstract> getInverterBySerial(const uint64_t serial);
std::shared_ptr<InverterAbstract> getInverterByFragment(const fragment_t& fragment);
void removeInverterBySerial(const uint64_t serial);
size_t getNumInverters() const;
HoymilesRadio_NRF* getRadioNrf();
HoymilesRadio_CMT* getRadioCmt();
uint32_t PollInterval();
void setPollInterval(uint32_t interval);
uint32_t PollInterval() const;
void setPollInterval(const uint32_t interval);
void setVerboseLogging(bool verboseLogging);
bool isAllRadioIdle();
bool isAllRadioIdle() const;
private:
std::vector<std::shared_ptr<InverterAbstract>> _inverters;

View File

@ -6,17 +6,17 @@
#include "Hoymiles.h"
#include "crc.h"
serial_u HoymilesRadio::DtuSerial()
serial_u HoymilesRadio::DtuSerial() const
{
return _dtuSerial;
}
void HoymilesRadio::setDtuSerial(uint64_t serial)
void HoymilesRadio::setDtuSerial(const uint64_t serial)
{
_dtuSerial.u64 = serial;
}
serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial)
serial_u HoymilesRadio::convertSerialToRadioId(const serial_u serial)
{
serial_u radioId;
radioId.u64 = 0;
@ -28,27 +28,27 @@ serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial)
return radioId;
}
bool HoymilesRadio::checkFragmentCrc(fragment_t* fragment)
bool HoymilesRadio::checkFragmentCrc(const fragment_t& fragment) const
{
uint8_t crc = crc8(fragment->fragment, fragment->len - 1);
return (crc == fragment->fragment[fragment->len - 1]);
const uint8_t crc = crc8(fragment.fragment, fragment.len - 1);
return (crc == fragment.fragment[fragment.len - 1]);
}
void HoymilesRadio::sendRetransmitPacket(uint8_t fragment_id)
void HoymilesRadio::sendRetransmitPacket(const uint8_t fragment_id)
{
CommandAbstract* cmd = _commandQueue.front().get();
CommandAbstract* requestCmd = cmd->getRequestFrameCommand(fragment_id);
if (requestCmd != nullptr) {
sendEsbPacket(requestCmd);
sendEsbPacket(*requestCmd);
}
}
void HoymilesRadio::sendLastPacketAgain()
{
CommandAbstract* cmd = _commandQueue.front().get();
sendEsbPacket(cmd);
sendEsbPacket(*cmd);
}
void HoymilesRadio::handleReceivedPackage()
@ -59,7 +59,7 @@ void HoymilesRadio::handleReceivedPackage()
if (nullptr != inv) {
CommandAbstract* cmd = _commandQueue.front().get();
uint8_t verifyResult = inv->verifyAllFragments(cmd);
uint8_t verifyResult = inv->verifyAllFragments(*cmd);
if (verifyResult == FRAGMENT_ALL_MISSING_RESEND) {
Hoymiles.getMessageOutput()->println("Nothing received, resend whole request");
sendLastPacketAgain();
@ -105,7 +105,7 @@ void HoymilesRadio::handleReceivedPackage()
auto inv = Hoymiles.getInverterBySerial(cmd->getTargetAddress());
if (nullptr != inv) {
inv->clearRxFragmentBuffer();
sendEsbPacket(cmd);
sendEsbPacket(*cmd);
} else {
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
_commandQueue.pop();
@ -114,7 +114,7 @@ void HoymilesRadio::handleReceivedPackage()
}
}
void HoymilesRadio::dumpBuf(const uint8_t buf[], uint8_t len, bool appendNewline)
void HoymilesRadio::dumpBuf(const uint8_t buf[], const uint8_t len, const bool appendNewline)
{
for (uint8_t i = 0; i < len; i++) {
Hoymiles.getVerboseMessageOutput()->printf("%02X ", buf[i]);
@ -124,17 +124,17 @@ void HoymilesRadio::dumpBuf(const uint8_t buf[], uint8_t len, bool appendNewline
}
}
bool HoymilesRadio::isInitialized()
bool HoymilesRadio::isInitialized() const
{
return _isInitialized;
}
bool HoymilesRadio::isIdle()
bool HoymilesRadio::isIdle() const
{
return !_busyFlag;
}
bool HoymilesRadio::isQueueEmpty()
bool HoymilesRadio::isQueueEmpty() const
{
return _commandQueue.size() == 0;
}

View File

@ -9,12 +9,12 @@
class HoymilesRadio {
public:
serial_u DtuSerial();
virtual void setDtuSerial(uint64_t serial);
serial_u DtuSerial() const;
virtual void setDtuSerial(const uint64_t serial);
bool isIdle();
bool isQueueEmpty();
bool isInitialized();
bool isIdle() const;
bool isQueueEmpty() const;
bool isInitialized() const;
void enqueCommand(std::shared_ptr<CommandAbstract> cmd)
{
@ -28,12 +28,12 @@ public:
}
protected:
static serial_u convertSerialToRadioId(serial_u serial);
void dumpBuf(const uint8_t buf[], uint8_t len, bool appendNewline = true);
static serial_u convertSerialToRadioId(const serial_u serial);
static void dumpBuf(const uint8_t buf[], const uint8_t len, const bool appendNewline = true);
bool checkFragmentCrc(fragment_t* fragment);
virtual void sendEsbPacket(CommandAbstract* cmd) = 0;
void sendRetransmitPacket(uint8_t fragment_id);
bool checkFragmentCrc(const fragment_t& fragment) const;
virtual void sendEsbPacket(CommandAbstract& cmd) = 0;
void sendRetransmitPacket(const uint8_t fragment_id);
void sendLastPacketAgain();
void handleReceivedPackage();

View File

@ -53,7 +53,7 @@ bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_freq_kHz)
return true;
}
void HoymilesRadio_CMT::init(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, int8_t pin_gpio2, int8_t pin_gpio3)
void HoymilesRadio_CMT::init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3)
{
_dtuSerial.u64 = 0;
@ -122,15 +122,15 @@ void HoymilesRadio_CMT::loop()
// Perform package parsing only if no packages are received
if (!_rxBuffer.empty()) {
fragment_t f = _rxBuffer.back();
if (checkFragmentCrc(&f)) {
if (checkFragmentCrc(f)) {
serial_u dtuId = convertSerialToRadioId(_dtuSerial);
const serial_u dtuId = convertSerialToRadioId(_dtuSerial);
// The CMT RF module does not filter foreign packages by itself.
// Has to be done manually here.
if (memcmp(&f.fragment[5], &dtuId.b[1], 4) == 0) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(&f);
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(f);
if (nullptr != inv) {
// Save packet in inverter rx buffer
@ -156,7 +156,7 @@ void HoymilesRadio_CMT::loop()
handleReceivedPackage();
}
void HoymilesRadio_CMT::setPALevel(int8_t paLevel)
void HoymilesRadio_CMT::setPALevel(const int8_t paLevel)
{
if (!_isInitialized) {
return;
@ -169,7 +169,7 @@ void HoymilesRadio_CMT::setPALevel(int8_t paLevel)
}
}
void HoymilesRadio_CMT::setInverterTargetFrequency(uint32_t frequency)
void HoymilesRadio_CMT::setInverterTargetFrequency(const uint32_t frequency)
{
_inverterTargetFrequency = frequency;
if (!_isInitialized) {
@ -178,12 +178,12 @@ void HoymilesRadio_CMT::setInverterTargetFrequency(uint32_t frequency)
cmtSwitchDtuFreq(_inverterTargetFrequency);
}
uint32_t HoymilesRadio_CMT::getInverterTargetFrequency()
uint32_t HoymilesRadio_CMT::getInverterTargetFrequency() const
{
return _inverterTargetFrequency;
}
bool HoymilesRadio_CMT::isConnected()
bool HoymilesRadio_CMT::isConnected() const
{
if (!_isInitialized) {
return false;
@ -211,27 +211,27 @@ void ARDUINO_ISR_ATTR HoymilesRadio_CMT::handleInt2()
_packetReceived = true;
}
void HoymilesRadio_CMT::sendEsbPacket(CommandAbstract* cmd)
void HoymilesRadio_CMT::sendEsbPacket(CommandAbstract& cmd)
{
cmd->incrementSendCount();
cmd.incrementSendCount();
cmd->setRouterAddress(DtuSerial().u64);
cmd.setRouterAddress(DtuSerial().u64);
_radio->stopListening();
if (cmd->getDataPayload()[0] == 0x56) { // @todo(tbnobody) Bad hack to identify ChannelChange Command
if (cmd.getDataPayload()[0] == 0x56) { // @todo(tbnobody) Bad hack to identify ChannelChange Command
cmtSwitchDtuFreq(HOY_BOOT_FREQ / 1000);
}
Hoymiles.getVerboseMessageOutput()->printf("TX %s %.2f MHz --> ",
cmd->getCommandName().c_str(), getFrequencyFromChannel(_radio->getChannel()));
cmd->dumpDataPayload(Hoymiles.getVerboseMessageOutput());
cmd.getCommandName().c_str(), getFrequencyFromChannel(_radio->getChannel()));
cmd.dumpDataPayload(Hoymiles.getVerboseMessageOutput());
if (!_radio->write(cmd->getDataPayload(), cmd->getDataSize())) {
if (!_radio->write(cmd.getDataPayload(), cmd.getDataSize())) {
Hoymiles.getMessageOutput()->println("TX SPI Timeout");
}
cmtSwitchDtuFreq(_inverterTargetFrequency);
_radio->startListening();
_busyFlag = true;
_rxTimeout.set(cmd->getTimeout());
_rxTimeout.set(cmd.getTimeout());
}

View File

@ -18,13 +18,13 @@
class HoymilesRadio_CMT : public HoymilesRadio {
public:
void init(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, int8_t pin_gpio2, int8_t pin_gpio3);
void init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3);
void loop();
void setPALevel(int8_t paLevel);
void setInverterTargetFrequency(uint32_t frequency);
uint32_t getInverterTargetFrequency();
void setPALevel(const int8_t paLevel);
void setInverterTargetFrequency(const uint32_t frequency);
uint32_t getInverterTargetFrequency() const;
bool isConnected();
bool isConnected() const;
static uint32_t getMinFrequency();
static uint32_t getMaxFrequency();
@ -36,7 +36,7 @@ private:
void ARDUINO_ISR_ATTR handleInt1();
void ARDUINO_ISR_ATTR handleInt2();
void sendEsbPacket(CommandAbstract* cmd);
void sendEsbPacket(CommandAbstract& cmd);
std::unique_ptr<CMT2300A> _radio;

View File

@ -8,7 +8,7 @@
#include <Every.h>
#include <FunctionalInterrupt.h>
void HoymilesRadio_NRF::init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ)
void HoymilesRadio_NRF::init(SPIClass* initialisedSpiBus, const uint8_t pinCE, const uint8_t pinIRQ)
{
_dtuSerial.u64 = 0;
@ -71,8 +71,8 @@ void HoymilesRadio_NRF::loop()
// Perform package parsing only if no packages are received
if (!_rxBuffer.empty()) {
fragment_t f = _rxBuffer.back();
if (checkFragmentCrc(&f)) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(&f);
if (checkFragmentCrc(f)) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(f);
if (nullptr != inv) {
// Save packet in inverter rx buffer
@ -97,7 +97,7 @@ void HoymilesRadio_NRF::loop()
handleReceivedPackage();
}
void HoymilesRadio_NRF::setPALevel(rf24_pa_dbm_e paLevel)
void HoymilesRadio_NRF::setPALevel(const rf24_pa_dbm_e paLevel)
{
if (!_isInitialized) {
return;
@ -105,7 +105,7 @@ void HoymilesRadio_NRF::setPALevel(rf24_pa_dbm_e paLevel)
_radio->setPALevel(paLevel);
}
void HoymilesRadio_NRF::setDtuSerial(uint64_t serial)
void HoymilesRadio_NRF::setDtuSerial(const uint64_t serial)
{
HoymilesRadio::setDtuSerial(serial);
@ -115,7 +115,7 @@ void HoymilesRadio_NRF::setDtuSerial(uint64_t serial)
openReadingPipe();
}
bool HoymilesRadio_NRF::isConnected()
bool HoymilesRadio_NRF::isConnected() const
{
if (!_isInitialized) {
return false;
@ -123,7 +123,7 @@ bool HoymilesRadio_NRF::isConnected()
return _radio->isChipConnected();
}
bool HoymilesRadio_NRF::isPVariant()
bool HoymilesRadio_NRF::isPVariant() const
{
if (!_isInitialized) {
return false;
@ -133,15 +133,13 @@ bool HoymilesRadio_NRF::isPVariant()
void HoymilesRadio_NRF::openReadingPipe()
{
serial_u s;
s = convertSerialToRadioId(_dtuSerial);
const serial_u s = convertSerialToRadioId(_dtuSerial);
_radio->openReadingPipe(1, s.u64);
}
void HoymilesRadio_NRF::openWritingPipe(serial_u serial)
void HoymilesRadio_NRF::openWritingPipe(const serial_u serial)
{
serial_u s;
s = convertSerialToRadioId(serial);
const serial_u s = convertSerialToRadioId(serial);
_radio->openWritingPipe(s.u64);
}
@ -171,29 +169,29 @@ void HoymilesRadio_NRF::switchRxCh()
_radio->startListening();
}
void HoymilesRadio_NRF::sendEsbPacket(CommandAbstract* cmd)
void HoymilesRadio_NRF::sendEsbPacket(CommandAbstract& cmd)
{
cmd->incrementSendCount();
cmd.incrementSendCount();
cmd->setRouterAddress(DtuSerial().u64);
cmd.setRouterAddress(DtuSerial().u64);
_radio->stopListening();
_radio->setChannel(getTxNxtChannel());
serial_u s;
s.u64 = cmd->getTargetAddress();
s.u64 = cmd.getTargetAddress();
openWritingPipe(s);
_radio->setRetries(3, 15);
Hoymiles.getVerboseMessageOutput()->printf("TX %s Channel: %d --> ",
cmd->getCommandName().c_str(), _radio->getChannel());
cmd->dumpDataPayload(Hoymiles.getVerboseMessageOutput());
_radio->write(cmd->getDataPayload(), cmd->getDataSize());
cmd.getCommandName().c_str(), _radio->getChannel());
cmd.dumpDataPayload(Hoymiles.getVerboseMessageOutput());
_radio->write(cmd.getDataPayload(), cmd.getDataSize());
_radio->setRetries(0, 0);
openReadingPipe();
_radio->setChannel(getRxNxtChannel());
_radio->startListening();
_busyFlag = true;
_rxTimeout.set(cmd->getTimeout());
_rxTimeout.set(cmd.getTimeout());
}

View File

@ -13,14 +13,14 @@
class HoymilesRadio_NRF : public HoymilesRadio {
public:
void init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ);
void init(SPIClass* initialisedSpiBus, const uint8_t pinCE, const uint8_t pinIRQ);
void loop();
void setPALevel(rf24_pa_dbm_e paLevel);
void setPALevel(const rf24_pa_dbm_e paLevel);
virtual void setDtuSerial(uint64_t serial);
virtual void setDtuSerial(const uint64_t serial);
bool isConnected();
bool isPVariant();
bool isConnected() const;
bool isPVariant() const;
private:
void ARDUINO_ISR_ATTR handleIntr();
@ -28,9 +28,9 @@ private:
uint8_t getTxNxtChannel();
void switchRxCh();
void openReadingPipe();
void openWritingPipe(serial_u serial);
void openWritingPipe(const serial_u serial);
void sendEsbPacket(CommandAbstract* cmd);
void sendEsbPacket(CommandAbstract& cmd);
std::unique_ptr<SPIClass> _spiPtr;
std::unique_ptr<RF24> _radio;

View File

@ -1,13 +1,31 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Thomas Basler and others
* Copyright (C) 2022-2023 Thomas Basler and others
*/
/*
This command is used to send a limit to the inverter.
Derives from DevControlCommand.
Command structure:
SCmd: Sub-Command ID. Is always 0x0b
Limit: limit to be set in the inverter
Type: absolute / relative and persistant/non-persistant
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
-------------------------------------------------------------------------------------------------------------------
|<------ CRC16 ------>|
51 71 60 35 46 80 12 23 04 81 0b 00 00 00 00 00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- --
^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^ ^^ ^^^^^ ^^^^^ ^^^^^ ^^
ID Target Addr Source Addr Cmd SCmd ? Limit Type CRC16 CRC8
*/
#include "ActivePowerControlCommand.h"
#include "inverters/InverterAbstract.h"
#define CRC_SIZE 6
ActivePowerControlCommand::ActivePowerControlCommand(uint64_t target_address, uint64_t router_address)
ActivePowerControlCommand::ActivePowerControlCommand(const uint64_t target_address, const uint64_t router_address)
: DevControlCommand(target_address, router_address)
{
_payload[10] = 0x0b;
@ -17,21 +35,21 @@ ActivePowerControlCommand::ActivePowerControlCommand(uint64_t target_address, ui
_payload[14] = 0x00;
_payload[15] = 0x00;
udpateCRC(CRC_SIZE); // 2 byte crc
udpateCRC(CRC_SIZE); // 6 byte crc
_payload_size = 18;
setTimeout(2000);
}
String ActivePowerControlCommand::getCommandName()
String ActivePowerControlCommand::getCommandName() const
{
return "ActivePowerControl";
}
void ActivePowerControlCommand::setActivePowerLimit(float limit, PowerLimitControlType type)
void ActivePowerControlCommand::setActivePowerLimit(const float limit, const PowerLimitControlType type)
{
uint16_t l = limit * 10;
const uint16_t l = limit * 10;
// limit
_payload[12] = (l >> 8) & 0xff;
@ -44,30 +62,30 @@ void ActivePowerControlCommand::setActivePowerLimit(float limit, PowerLimitContr
udpateCRC(CRC_SIZE);
}
bool ActivePowerControlCommand::handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id)
bool ActivePowerControlCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
{
if (!DevControlCommand::handleResponse(inverter, fragment, max_fragment_id)) {
return false;
}
if ((getType() == PowerLimitControlType::RelativNonPersistent) || (getType() == PowerLimitControlType::RelativPersistent)) {
inverter->SystemConfigPara()->setLimitPercent(getLimit());
inverter.SystemConfigPara()->setLimitPercent(getLimit());
} else {
uint16_t max_power = inverter->DevInfo()->getMaxPower();
const uint16_t max_power = inverter.DevInfo()->getMaxPower();
if (max_power > 0) {
inverter->SystemConfigPara()->setLimitPercent(static_cast<float>(getLimit()) / max_power * 100);
inverter.SystemConfigPara()->setLimitPercent(static_cast<float>(getLimit()) / max_power * 100);
} else {
// TODO(tbnobody): Not implemented yet because we only can publish the percentage value
}
}
inverter->SystemConfigPara()->setLastUpdateCommand(millis());
inverter->SystemConfigPara()->setLastLimitCommandSuccess(CMD_OK);
inverter.SystemConfigPara()->setLastUpdateCommand(millis());
inverter.SystemConfigPara()->setLastLimitCommandSuccess(CMD_OK);
return true;
}
float ActivePowerControlCommand::getLimit()
float ActivePowerControlCommand::getLimit() const
{
uint16_t l = (((uint16_t)_payload[12] << 8) | _payload[13]);
const uint16_t l = (((uint16_t)_payload[12] << 8) | _payload[13]);
return l / 10;
}
@ -76,7 +94,7 @@ PowerLimitControlType ActivePowerControlCommand::getType()
return (PowerLimitControlType)(((uint16_t)_payload[14] << 8) | _payload[15]);
}
void ActivePowerControlCommand::gotTimeout(InverterAbstract* inverter)
void ActivePowerControlCommand::gotTimeout(InverterAbstract& inverter)
{
inverter->SystemConfigPara()->setLastLimitCommandSuccess(CMD_NOK);
inverter.SystemConfigPara()->setLastLimitCommandSuccess(CMD_NOK);
}

View File

@ -12,14 +12,14 @@ typedef enum { // ToDo: to be verified by field tests
class ActivePowerControlCommand : public DevControlCommand {
public:
explicit ActivePowerControlCommand(uint64_t target_address = 0, uint64_t router_address = 0);
explicit ActivePowerControlCommand(const uint64_t target_address = 0, const uint64_t router_address = 0);
virtual String getCommandName();
virtual String getCommandName() const;
virtual bool handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id);
virtual void gotTimeout(InverterAbstract* inverter);
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
virtual void gotTimeout(InverterAbstract& inverter);
void setActivePowerLimit(float limit, PowerLimitControlType type = RelativNonPersistent);
float getLimit();
void setActivePowerLimit(const float limit, const PowerLimitControlType type = RelativNonPersistent);
float getLimit() const;
PowerLimitControlType getType();
};

View File

@ -1,11 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Thomas Basler and others
* Copyright (C) 2022-2023 Thomas Basler and others
*/
/*
This command is used to fetch the eventlog from the inverter.
Derives from MultiDataCommand
Command structure:
* DT: this specific command uses 0x11
* AlarmId: The last event id received from the inverter or zero in case that no events
has been received yet. --> Not Implemented yet
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
-----------------------------------------------------------------------------------------------------------------------
|<------------------- CRC16 --------------------->|
15 71 60 35 46 80 12 23 04 80 11 00 65 72 06 B8 00 00 00 00 00 00 00 00 00 00 00 -- -- -- -- --
^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^ ^^ ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^^^^^^^^^^ ^^^^^ ^^
ID Target Addr Source Addr Idx DT ? Time Gap AlarmId Password CRC16 CRC8
*/
#include "AlarmDataCommand.h"
#include "inverters/InverterAbstract.h"
AlarmDataCommand::AlarmDataCommand(uint64_t target_address, uint64_t router_address, time_t time)
AlarmDataCommand::AlarmDataCommand(const uint64_t target_address, const uint64_t router_address, const time_t time)
: MultiDataCommand(target_address, router_address)
{
setTime(time);
@ -13,12 +31,12 @@ AlarmDataCommand::AlarmDataCommand(uint64_t target_address, uint64_t router_addr
setTimeout(750);
}
String AlarmDataCommand::getCommandName()
String AlarmDataCommand::getCommandName() const
{
return "AlarmData";
}
bool AlarmDataCommand::handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id)
bool AlarmDataCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
{
// Check CRC of whole payload
if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) {
@ -27,19 +45,19 @@ bool AlarmDataCommand::handleResponse(InverterAbstract* inverter, fragment_t fra
// Move all fragments into target buffer
uint8_t offs = 0;
inverter->EventLog()->beginAppendFragment();
inverter->EventLog()->clearBuffer();
inverter.EventLog()->beginAppendFragment();
inverter.EventLog()->clearBuffer();
for (uint8_t i = 0; i < max_fragment_id; i++) {
inverter->EventLog()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
inverter.EventLog()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
offs += (fragment[i].len);
}
inverter->EventLog()->endAppendFragment();
inverter->EventLog()->setLastAlarmRequestSuccess(CMD_OK);
inverter->EventLog()->setLastUpdate(millis());
inverter.EventLog()->endAppendFragment();
inverter.EventLog()->setLastAlarmRequestSuccess(CMD_OK);
inverter.EventLog()->setLastUpdate(millis());
return true;
}
void AlarmDataCommand::gotTimeout(InverterAbstract* inverter)
void AlarmDataCommand::gotTimeout(InverterAbstract& inverter)
{
inverter->EventLog()->setLastAlarmRequestSuccess(CMD_NOK);
inverter.EventLog()->setLastAlarmRequestSuccess(CMD_NOK);
}

View File

@ -5,10 +5,10 @@
class AlarmDataCommand : public MultiDataCommand {
public:
explicit AlarmDataCommand(uint64_t target_address = 0, uint64_t router_address = 0, time_t time = 0);
explicit AlarmDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0);
virtual String getCommandName();
virtual String getCommandName() const;
virtual bool handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id);
virtual void gotTimeout(InverterAbstract* inverter);
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
virtual void gotTimeout(InverterAbstract& inverter);
};

Some files were not shown because too many files have changed in this diff Show More