Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for stable effects / palettes configuration #230

Open
henrygab opened this issue Dec 14, 2021 · 3 comments · May be fixed by #234
Open

Support for stable effects / palettes configuration #230

henrygab opened this issue Dec 14, 2021 · 3 comments · May be fixed by #234

Comments

@henrygab
Copy link
Collaborator

henrygab commented Dec 14, 2021

Currently, when the list of palettes or effects changes, any saved configuration is implicitly invalidated. This is because, currently, the value is stored as an integer index into the array. However, it is not currently detected as an invalid saved index. This is also true for the definition of the per-product default effect.

Goals:

  • Store selected default pattern in a manner that detects the implicit invalidation
  • Allow declaration of default pattern using a name (not an array index)

Work-in-Progress

Solution is now written (but not yet tested on real hardware). Feedback on design is welcome on PR #234.


complex thoughts using constexpr, now moot

Note

Here, I use the term string to refer to const char *, typically stored in ROM. The term String is an Arduino-specific construct, that unfortunately is not currently constexpr compliant. The term F-string refers to the Arduino-specific flash string helper, which is also not constexpr compliant.


Expected functionality required:

  • A constexpr class that can be constructed from F-String or const char * as constexpr, and supports the below functionalities
    • constexpr case-sensitive comparison
    • constexpr case-sensitive equality
    • constexpr case-insensitive (US-EN, ASCII only) comparison
    • constexpr case-insensitive (US-EN, ASCII only) equality
    • constexpr case-sensitive uint32_t result hash function
    • constexpr conversion to lowercase (US-EN, ASCII only) version of string

Then, given constant arrays of const POD structures, where one member is a const char * null-terminated string...

  • A constexpr function that, given that array and a hash of a target string, returns a first index to the matching string.

  • A constexpr function that, given that array and a target string or F-String, returns an array index that contains a matching string.

    Psuedocode strawman

    // ignoring the C++11 requirement that a constexpr be a single return statement ... this is just psuedocode
    template<typename T, size_t INDEX>
    size_t IndexOfMatchingMemberImpl(T type, const char * stringToFind) {
        return ( constexpr_compliant_string_comparison( T[INDEX].Name, stringToFind ) == 0 ) ?
            INDEX :
            IndexOfMatchingMemberImpl<T, INDEX-1>(type, stringToFind);
    }
    template<typename T>
    size_t IndexOfMatchingMemberImpl<T,0>(T type, const char * stringToFind) {
        static_assert( constexpr_compliant_string_comparison( T[0].Name, stringToFind ) == 0, "Unable to find matching string" );
        return ( constexpr_compliant_string_comparison( T[0].Name, stringToFind ) == 0 ) ? 0 : -1;
    }
    template<typename T, size_t N>
    size_t IndexOfMatchingMember(T type[N], const char * stringToFind) {
        return IndexOfMatchingMemberImpl<T, N-1>(type, stringToFind);
    }

What the above could enable

  • Products can store default patterns using friendly name, rather than opaque (and unstable) array index
  • Configuration can store array index + hash of the string, to detect invalidation

Other considerations

Maybe there's already a library to do this?

See also:

@henrygab henrygab changed the title Better support for changed list of effects / palettes Support for stable effects / palettes configuration Dec 14, 2021
@tobi01001
Copy link
Contributor

Isn't that a bit too much for just that purpose?

Just wondering if one could just use an enumeration (with incrementing values) where the enumeration entry is the friendly name. So why would one need to calculate hashes and store strings?
OK, the friendly name is there anyway but it sounds rather complicated to me...

Cleaning the #ifdefs and simplifications in the number of patterns seems to be more useful?

on the otherr hand, I would of course be interested in what you come up with ;-)

@jasoncoon
Copy link
Owner

I'm interested in the discussion, but I would be fine with just changing the "magic number" when new patterns are added (or any other change that would invalidate stored settings) and reverting to the defaults. Especially given that settings can be imported/exported, saved, etc (although that whole UI could use some improvement).

https://github.com/jasoncoon/esp8266-fastled-webserver/blob/main/esp8266-fastled-webserver/esp8266-fastled-webserver.ino#L786-L791

@henrygab
Copy link
Collaborator Author

henrygab commented Dec 14, 2021

@jasoncoon ... Two questions:

  1. Can you help me understand how settings are currently imported / exported? Are you referring to the EEPROM settings? Or, is there code that generates JSON with the friendly names of the current palette / current effect, and HTML/Javascript that converts that back to an index? If so, that would be a reasonably good solution.

  2. What do you think about the simpler solutions listed below?

Of course, this issue is marked an enhancement. It was created to track my thoughts on how to avoid accidental bugs in future, which could be caught at compile-time. In particular, the following two cases were of particular interest:

A. Adding a new pattern, shifting a product's default pattern to a new index, without updating the default pattern in that product's configuration. This is currently not detectable, and is a "surprise" when noticed.
B. Adding a new pattern, causing a user's existing selection to break on upgrade. This is currently not detected, and results in a user getting a surprise new default effect.

My current thinking, which I will now call Option3, avoids the complexities of the constexpr string templates altogether, and solves the problems above:

  • Solve case A by adding static_assert() that certain patterns exist at specific offsets in the table. The text would indicate that the index must match the products' default index. Then, if the effect index changes, the static_assert() would fire, raising attention to the implicit reliance in the configuration files, so they can be updated at the same time. Indices change due to #if blocks.
  • Solve case A by defining the default effect using the function name
  • Solve case B by storing a validator (e.g., hash of the friendly name) for those two settings that implicitly depend upon array layout. One validator for default effect, one for default palette ... and if done right, having the EEPROM settings store this hash means that, when the tables are changed, the EEPROM validator can still find the matching value in the table, which reduces user frustration in lost settings.

@tobi01001 - YES, I will absolutely share results of static string header, if I get it working as a std-free, C++11 header. As you may know, I contribute to the SimpleHacks UtilHeader depot, whose files are MIT-licensed, and try to target Arduino compatibility (no reliance on std namespace, and limit to C++11 features). That is where the results would be added, even if not used in this project.

@henrygab henrygab linked a pull request Dec 15, 2021 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants