75c46f7a0e
* Very incomplete work toward sorting. * Sort modes and palettes upon startup so I don't have to maintain the static index array. * Remove cpp test file I used for development * Added ModeSortUsermod, modified the other two usermods to use it. * Update platformio_override.ini.sample and readme for ModeSortUsermod * restore methods accidentally removed.
249 lines
7.6 KiB
C++
249 lines
7.6 KiB
C++
#pragma once
|
|
|
|
#include "wled.h"
|
|
|
|
//
|
|
// v2 usermod that provides data about modes and
|
|
// palettes to other usermods. Notably it provides:
|
|
// * A direct method for a mode or palette name
|
|
// * Ability to retrieve mode and palette names in
|
|
// alphabetical order
|
|
//
|
|
// char **getModesQStrings()
|
|
// Provides an array of char* (pointers) to the names of the
|
|
// palettes within JSON_mode_names, in the same order as
|
|
// JSON_mode_names. These strings end in double quote (")
|
|
// (or \0 if there is a problem).
|
|
//
|
|
// byte *getModesAlphaIndexes()
|
|
// An array of byte designating the indexes of names of the
|
|
// modes in alphabetical order. "Solid" will always remain
|
|
// at the front of the list.
|
|
//
|
|
// char **getPalettesQStrings()
|
|
// Provides an array of char* (pointers) to the names of the
|
|
// palettes within JSON_palette_names, in the same order as
|
|
// JSON_palette_names. These strings end in double quote (")
|
|
// (or \0 if there is a problem).
|
|
//
|
|
// byte *getPalettesAlphaIndexes()
|
|
// An array of byte designating the indexes of names of the
|
|
// palettes in alphabetical order. "Default" and those
|
|
// starting with "(" will always remain at the front of the list.
|
|
//
|
|
|
|
// Number of modes at the start of the list to not sort
|
|
#define MODE_SORT_SKIP_COUNT 1
|
|
|
|
// Which list is being sorted
|
|
char **listBeingSorted = nullptr;
|
|
|
|
/**
|
|
* Modes and palettes are stored as strings that
|
|
* end in a quote character. Compare two of them.
|
|
* We are comparing directly within either
|
|
* JSON_mode_names or JSON_palette_names.
|
|
*/
|
|
int re_qstringCmp(const void *ap, const void *bp) {
|
|
char *a = listBeingSorted[*((byte *)ap)];
|
|
char *b = listBeingSorted[*((byte *)bp)];
|
|
int i = 0;
|
|
do {
|
|
char aVal = pgm_read_byte_near(a + i);
|
|
if (aVal >= 97 && aVal <= 122) {
|
|
// Lowercase
|
|
aVal -= 32;
|
|
}
|
|
char bVal = pgm_read_byte_near(b + i);
|
|
if (bVal >= 97 && bVal <= 122) {
|
|
// Lowercase
|
|
bVal -= 32;
|
|
}
|
|
// Relly we shouldn't ever get to '\0'
|
|
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
|
|
// We're done. one is a substring of the other
|
|
// or something happenend and the quote didn't stop us.
|
|
if (aVal == bVal) {
|
|
// Same value, probably shouldn't happen
|
|
// with this dataset
|
|
return 0;
|
|
}
|
|
else if (aVal == '"' || aVal == '\0') {
|
|
return -1;
|
|
}
|
|
else {
|
|
return 1;
|
|
}
|
|
}
|
|
if (aVal == bVal) {
|
|
// Same characters. Move to the next.
|
|
i++;
|
|
continue;
|
|
}
|
|
// We're done
|
|
if (aVal < bVal) {
|
|
return -1;
|
|
}
|
|
else {
|
|
return 1;
|
|
}
|
|
} while (true);
|
|
// We shouldn't get here.
|
|
return 0;
|
|
}
|
|
|
|
class ModeSortUsermod : public Usermod {
|
|
private:
|
|
|
|
// Pointers the start of the mode names within JSON_mode_names
|
|
char **modes_qstrings = nullptr;
|
|
|
|
// Array of mode indexes in alphabetical order.
|
|
byte *modes_alpha_indexes = nullptr;
|
|
|
|
// Pointers the start of the palette names within JSON_palette_names
|
|
char **palettes_qstrings = nullptr;
|
|
|
|
// Array of palette indexes in alphabetical order.
|
|
byte *palettes_alpha_indexes = nullptr;
|
|
|
|
public:
|
|
/**
|
|
* setup() is called once at boot. WiFi is not yet connected at this point.
|
|
* You can use it to initialize variables, sensors or similar.
|
|
*/
|
|
void setup() {
|
|
// Sort the modes and palettes on startup
|
|
// as they are guarantted to change.
|
|
sortModesAndPalettes();
|
|
}
|
|
|
|
char **getModesQStrings() {
|
|
return modes_qstrings;
|
|
}
|
|
|
|
byte *getModesAlphaIndexes() {
|
|
return modes_alpha_indexes;
|
|
}
|
|
|
|
char **getPalettesQStrings() {
|
|
return palettes_qstrings;
|
|
}
|
|
|
|
byte *getPalettesAlphaIndexes() {
|
|
return palettes_alpha_indexes;
|
|
}
|
|
|
|
/**
|
|
* This Usermod doesn't have anything for loop.
|
|
*/
|
|
void loop() {}
|
|
|
|
/**
|
|
* Sort the modes and palettes to the index arrays
|
|
* modes_alpha_indexes and palettes_alpha_indexes.
|
|
*/
|
|
void sortModesAndPalettes() {
|
|
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
|
|
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
|
|
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
|
|
|
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
|
|
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
|
|
|
|
int skipPaletteCount = 1;
|
|
while (true) {
|
|
// How many palette names start with '*' and should not be sorted?
|
|
// (Also skipping the first one, 'Default').
|
|
if (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') {
|
|
skipPaletteCount++;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
|
|
}
|
|
|
|
byte *re_initIndexArray(int numModes) {
|
|
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
|
|
for (byte i = 0; i < numModes; i++) {
|
|
indexes[i] = i;
|
|
}
|
|
return indexes;
|
|
}
|
|
|
|
/**
|
|
* Return an array of mode or palette names from the JSON string.
|
|
* They don't end in '\0', they end in '"'.
|
|
*/
|
|
char **re_findModeStrings(const char json[], int numModes) {
|
|
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
|
|
uint8_t modeIndex = 0;
|
|
bool insideQuotes = false;
|
|
// advance past the mark for markLineNum that may exist.
|
|
char singleJsonSymbol;
|
|
|
|
// Find the mode name in JSON
|
|
bool complete = false;
|
|
for (size_t i = 0; i < strlen_P(json); i++) {
|
|
singleJsonSymbol = pgm_read_byte_near(json + i);
|
|
switch (singleJsonSymbol) {
|
|
case '"':
|
|
insideQuotes = !insideQuotes;
|
|
if (insideQuotes) {
|
|
// We have a new mode or palette
|
|
modeStrings[modeIndex] = (char *)(json + i + 1);
|
|
}
|
|
break;
|
|
case '[':
|
|
break;
|
|
case ']':
|
|
complete = true;
|
|
break;
|
|
case ',':
|
|
modeIndex++;
|
|
default:
|
|
if (!insideQuotes) {
|
|
break;
|
|
}
|
|
}
|
|
if (complete) {
|
|
break;
|
|
}
|
|
}
|
|
return modeStrings;
|
|
}
|
|
|
|
/**
|
|
* Sort either the modes or the palettes using quicksort.
|
|
*/
|
|
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
|
|
listBeingSorted = modeNames;
|
|
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
|
|
listBeingSorted = nullptr;
|
|
}
|
|
|
|
/*
|
|
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
|
* Values in the state object may be modified by connected clients
|
|
*/
|
|
void addToJsonState(JsonObject &root) {}
|
|
|
|
/*
|
|
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
|
* Values in the state object may be modified by connected clients
|
|
*/
|
|
void readFromJsonState(JsonObject &root) {}
|
|
|
|
/*
|
|
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
|
* This could be used in the future for the system to determine whether your usermod is installed.
|
|
*/
|
|
uint16_t getId()
|
|
{
|
|
return USERMOD_ID_MODE_SORT;
|
|
}
|
|
};
|