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

"Unlimited" buses #4529

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft

"Unlimited" buses #4529

wants to merge 8 commits into from

Conversation

blazoncek
Copy link
Collaborator

@blazoncek blazoncek commented Jan 30, 2025

Add ability to have up to 36 (theoretical limit) buses, physical or virtual.

Stuff also done:

  • converted BusManager from class to namespace
  • modified BusConfig to use vector
  • modified ColorOrderMap::getPixelColorOrder() to use vector (and removed ColorOrderMap from BusDigital)
  • updated loading config into UI (LED settings, buttons, etc.)

WARNING web server may not support that many variables when creating buses so actual limit may be less.

Many thanks to @willmmiles @TripleWhy and @PaoloTK for helpful comments and suggestions.

Summary by CodeRabbit

• New Features
 - Expanded support for LED configurations on select hardware, enabling increased digital channel capacity and more flexible setups.

• Bug Fixes
 - Enhanced error checking and validation during hardware bus initialization, reducing the risk of improper configurations and runtime issues.

• Refactor
 - Streamlined configuration processing and resource management, leading to improved system stability, performance, and maintainability.

- use unique_ptr/make_unique for busses
- added config upload options
- number of buses it limited to 36 (0-9+A-Z identifiers)
- WRNING web server may not support that many variables
@blazoncek
Copy link
Collaborator Author

Apparently will require #4484 to be merged 1st.

@blazoncek blazoncek marked this pull request as draft January 30, 2025 19:40
wled00/bus_manager.h Outdated Show resolved Hide resolved
// Really simple C++11 shim for non-array case; implementation from cppreference.com
template<class T, class... Args>
std::unique_ptr<T>
make_unique(Args&&... args)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be moved to a different header file

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but I want to limit number of modified files for transparency sake.
Once toolchain is updated we may not need this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for transparency sake

The git diff won't look much different

Once toolchain is updated

Will that actually happen? ^^
Anyway it should be easier and cleaner to delete a polyfill.h than to delete a function here and check if other files included bus_manager.h just for the sake of make_unique

@@ -78,7 +93,7 @@ class Bus {
_autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY;
};

virtual ~Bus() {} //throw the bus under the bus
virtual ~Bus() {} //throw the bus under the bus (derived class needs to freeData())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way to make this safer would be to call freeData() here, and then override freeData() in the busses that don't need to do anything with an empty function

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate a bit more?
I was thinking if Bus does not allocate any data for itself (but provides non-virtual methods) a derived class should take care of deallocation if it allocates anything.
My first thoughts were to implement automatic deallocation upon destruction, but some derived classes assign _data pointer to class member variables to reduce heap fragmentation. In such case no deallocation should happen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about something like this:

//Bus
virtual Bus::~Bus() { freeData(); }
virtual void Bus::freeData() { if (_data != nullptr) free(_data); _data = nullptr; }

//Bus1 uses _data and doesn't override freeData()

//Bus2 doesn't use _data
virtual void Bus::freeData() {}

However:
freeData checks if _data is nullptr, so really I don't think you'd have to do anything like that:

//Bus
virtual Bus::~Bus() { freeData(); }
void Bus::freeData() { if (_data != nullptr) free(_data); _data = nullptr; }

//Bus1 uses _data and doesn't override freeData()
//Bus2 doesn't use _data and doesn't override freeData()

Should work just fine, shouldn't it?

Another option would be to move _data into the sub classes, so each class would be responsible for their own memory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option would be to move _data into the sub classes, so each class would be responsible for their own memory.

This would be my preferred option. It also lets each class use type-safe storage and descriptive names for their state data as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find that counterintuitive.

All derived classes may find use of _data pointer but some may allocate space on heap, some on stack (or on heap within its instance). For those that use heap, allocateData() and freeData() are provided.
If I understand virtual methods correctly if you override virtual base method in a derived class then you have to call base method explicitly if you want to use its implementation. I.e.

void Derived::freeData() {
  ... some stuff ...
  Base::freeData();
  ... more stuff ...
}

or implement functionality of Base::freeData() in derived (a nonsense). Having virtual Base::freeData() that does nothing (and is not pure virtual) and having pure virtual (that needs to be implemented in derived class) seems counterproductive in this particular instance as allocateData() does something.

As allocateData() and freeData() are not virtual, two less pointers need to be allocated for derived class' vtables (which is good in our case). Of course that means that when derived class uses heap allocated data, it needs to call allocateData() at some point and then (at least in destructor) call freeData(). All is well and also well defined.
One could add heap/stack detection into virtual Base::freeData() (if that approach is taken) so that correct de-allocation may happen, but I think that is a hack best avoided as it is architecture dependant.

BTW: All of the derived classes use _data pointer in one way or another, but only BusDigital and BusNetwork use heap allocated buffers. When I first implemented buffers, _data was defined in those two derived classes (as suggested), but that meant to also implement 2 allocateData() and 2 freeData() methods. I found that redundant and prone to error more than forgetting to call base class freeData().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need allocateData or freeData?

allocateData is called exactly once, during construction (in 2 classes). Which means there is no need for gating or freeing _data before allocation. The zero length check is probably also unnecessary, leaving only one effective call: calloc. Does it make sense to abstract that? Not really. Does it improve anything to have allocateData that can be used in two classes instead of calloc (which can also be used in both classes)? Well, no.

What about freeData? Once again, it is called exactly once, during destruction (in 2 classes). Oh but it performs a nullptr check, that has to be useful, right? well, no; free does nothing when called with nullptr. Ok but it also sets _data to nullptr when it's done. Which sounds like a good safety move, but remember, this happens during destruction, so even reading the value _data would be unsafe. So really this function can be replaced with a single free call as well.

About the error-proneness of this all: using calloc and free is error prone. If you are worried about that, consider unique_ptr or other type safe per-class solutions instead :)

Copy link
Collaborator Author

@blazoncek blazoncek Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about Bus::_data then? It should also be eliminated and moved into those 2 classes that use it. Right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, in an ideal world, yes. I'm not sure how other functions that currently use _data would work then though.

wled00/bus_manager.h Outdated Show resolved Hide resolved
extern std::vector<std::unique_ptr<Bus>> busses;
//extern std::vector<Bus*> busses;
extern uint16_t _gMilliAmpsUsed;
extern uint16_t _gMilliAmpsMax;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think adding public and extern gloabals is a really bad idea

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if enclosed in namespace?
I could do without but then functions could no longer be inline (not that this is good or bad).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • They are public now. Anyone can access and modify them. Functional safety isn't about not doing the wrong thing because you know what you are doing, its about preventing the wrong thing even if not everyone knows what they are doing.
  • You now have to separate definition and initialization.
  • extern can prevent optimizations

Avoiding this would work by keeping the class or moving the variables to the cpp. I don't know how the latter affects embedded code though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoiding this would work by keeping the class or moving the variables to the cpp. I don't know how the latter affects embedded code though.

Moving the declarations in to the cpp would give up any inlined access -- everything would have to be a function call. We don't have link-time optimization to help us here. :(

The previous code (a non-instantiated class, where everything is static) is the only way I know of to get both access control and still support inlining.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WLED is full of globals, accessible/accessed throughout the code.
If you'll look many may be shadowed in certain functions which is prone to error (forget or accidentally remove definition and you are using global).
Using namespaced variable reduces the possibility to inadvertently use such global. But yes, I agree that having globals is poor programming practice.

As we discuss these issues it may indeed be better to forego namespace and keep using instanceless class.

- as suggested by @TripleWhy
- minimum length guard

Conflicts:
	wled00/bus_manager.cpp
	wled00/bus_manager.h
@netmindz
Copy link
Member

netmindz commented Feb 9, 2025

@coderabbitai review

Copy link

coderabbitai bot commented Feb 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

coderabbitai bot commented Feb 9, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes refactor several modules to improve pointer safety, memory management, and control flow. Bus pointers are now declared as const and their validity checks are simplified. The bus management system has transitioned from fixed-size raw pointer arrays to using std::vector with unique pointers. In addition, loop limits in configuration and LED settings have been standardized, method return types have been updated from uint8_t to size_t where appropriate, and several functions have been refactored for clearer, more concise control flow.

Changes

File(s) Change Summary
wled00/FX_fcn.cpp Updated bus pointer types from non-const to const in WS2812FX and Segment methods; consolidated and simplified validity checks in methods such as refreshLightCapabilities, finalizeInit, hasRGBWBus, and hasCCTBus.
wled00/bus_manager.cpp
wled00/bus_manager.h
Refactored bus classes (BusDigital, BusOnOff, BusNetwork, etc.) to use dynamic memory management via std::vector with unique_ptr; updated several method signatures (e.g. getPins, memUsage) from uint8_t/uint32_t to size_t; replaced traditional loops with range-based loops; removed unused parameters.
wled00/cfg.cpp
wled00/set.cpp
Streamlined bus configuration and initialization logic; adjusted loop limits to a fixed maximum (36); replaced pointer‐based memory handling with vector::emplace_back; added and refined debugging outputs and bus initialization checks.
wled00/const.h Updated constant definitions for virtual and maximum bus counts (WLED_MIN_VIRTUAL_BUSSES and WLED_MAX_BUSSES) to new values and adjusted accompanying comments.
wled00/data/settings_leds.htm Modified JavaScript functions for LED settings; updated loop conditions and bus limits; integrated a new helper (chrID) for LED ID generation; adjusted conditions in addLEDs and loadCfg to better reflect hardware limits.
wled00/wled.h Changed global variable declarations: removed the global BusManager instance, updated the WS2812FX instantiation syntax, and replaced the busConfigs pointer array with a std::vector for dynamic sizing.
wled00/xml.cpp Changed the bus pointer declaration to const; improved conditional checks in getSettingsJS for bus validity and color order mappings, and modified output formatting for bus index values.

Sequence Diagram(s)

sequenceDiagram
    participant Config as Configuration Loader
    participant BM as Bus Manager
    participant Bus as Bus Object

    Config->>BM: deserializeConfig()
    BM->>BM: Iterate bus configurations (fixed limit)
    BM->>Bus: Validate bus (using const pointer check)
    Bus-->>BM: Return bus status
    BM->>BM: Add bus config via vector emplace_back
    BM-->>Config: Return updated configuration
Loading
sequenceDiagram
    participant JS as JS UI
    participant LCM as LED Config Manager
    participant LED as LED Bus

    JS->>LCM: loadCfg()
    LCM->>LED: Remove existing LED outputs
    LCM->>LCM: Loop through LED configurations (fixed limit of 36)
    LCM->>LED: Generate LED ID using chrID and update properties
    LED-->>LCM: LED settings applied
    LCM-->>JS: Return updated LED configuration
Loading

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
wled00/cfg.cpp (1)

190-190: Too many arguments to BusManager::memUsage().
The current function signature of memUsage() takes no arguments, causing a build error. Implement an overload or remove extra parameters.

-    mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8);
+    // Either implement an overloaded memUsage(...) or remove arguments:
+    // mem = BusManager::memUsage();
🧰 Tools
🪛 GitHub Actions: WLED CI

[error] 190-190: Too many arguments to function 'size_t BusManager::memUsage()'.

🧹 Nitpick comments (3)
wled00/data/settings_leds.htm (1)

353-364: Consider using constants for hardware-specific values.

While the logic correctly handles different ESP variants, using magic numbers makes the code harder to maintain. Consider defining constants for these values.

+// Hardware-specific constants
+const S2_MAX_BUSES = 14;
+const S2_MAX_VIRTUAL = 4;
+const S3_MAX_VIRTUAL = 6;
+const RMT_CHANNELS_S2_S3 = 4;
+const RMT_CHANNELS_ESP32 = 8;
+const PARALLEL_I2S_CHANNELS = 8;
+
-			const S2 = (oMaxB == 14) && (maxV == 4);
-			const S3 = (oMaxB == 14) && (maxV == 6);
+			const S2 = (oMaxB == S2_MAX_BUSES) && (maxV == S2_MAX_VIRTUAL);
+			const S3 = (oMaxB == S2_MAX_BUSES) && (maxV == S3_MAX_VIRTUAL);
wled00/bus_manager.h (1)

14-24: Consider moving this C++11 polyfill to a dedicated header or shared utility.
Currently, the inline implementation of make_unique might be duplicated elsewhere or may cause clutter. Creating a separate "cpp11_compat.h" or similar makes the code more modular.

wled00/FX_fcn.cpp (1)

1395-1395: Fix integer signedness comparison warning.

The comparison between i (int) and BusManager::getNumBusses() (size_t) triggers a warning. Consider changing the loop variable type to size_t to match the return type of getNumBusses().

-  for (int i=0; i<BusManager::getNumBusses(); i++) {
+  for (size_t i=0; i<BusManager::getNumBusses(); i++) {
🧰 Tools
🪛 GitHub Actions: WLED CI

[warning] 1395-1395: Comparison of integer expressions of different signedness: 'int' and 'size_t'.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32a75c1 and ed91c54.

📒 Files selected for processing (9)
  • wled00/FX_fcn.cpp (7 hunks)
  • wled00/bus_manager.cpp (29 hunks)
  • wled00/bus_manager.h (16 hunks)
  • wled00/cfg.cpp (3 hunks)
  • wled00/const.h (3 hunks)
  • wled00/data/settings_leds.htm (5 hunks)
  • wled00/set.cpp (3 hunks)
  • wled00/wled.h (1 hunks)
  • wled00/xml.cpp (4 hunks)
🧰 Additional context used
🪛 GitHub Actions: WLED CI
wled00/FX_fcn.cpp

[warning] 1395-1395: Comparison of integer expressions of different signedness: 'int' and 'size_t'.

wled00/cfg.cpp

[error] 190-190: Too many arguments to function 'size_t BusManager::memUsage()'.

wled00/bus_manager.cpp

[error] 148-148: 'd_calloc' was not declared in this scope. Suggested alternative: 'ps_calloc'.


[error] 387-387: 'getDataSize' is not a member of 'PolyBus'.


[error] 747-747: 'd_calloc' was not declared in this scope. Suggested alternative: 'ps_calloc'.


[error] 810-810: 'memUsage' is not a member of 'PolyBus'.


[error] 810-810: Invalid conversion from 'const uint8_t*' to 'uint8_t*'.

🔇 Additional comments (21)
wled00/data/settings_leds.htm (2)

44-53: Great documentation of hardware-specific limits!

The detailed comments for each parameter clearly explain the maximum values for different ESP models, which improves code maintainability and helps prevent configuration errors.


424-425: 🏁 Script executed:

#!/bin/bash
# Search for memory-related constants and calculations
rg -A 2 'maxM|bquot|memu'

Length of output: 2834

wled00/bus_manager.h (2)

86-86: Good safeguard for minimum length.
Enforcing at least length 1 avoids edge cases with zero-length buses.


98-98: Verify the default return of canShow().
Returning true by default may mask bus-specific readiness checks. If a derived bus must hold off on updates, ensure it properly overrides this method.

wled00/cfg.cpp (1)

225-226: Use of emplace_back is a good improvement.
Emplace construction simplifies memory management for BusConfig.

wled00/const.h (2)

52-52: LGTM! Updated virtual bus purpose for ESP8266.

The comment clarifies that virtual buses are now used for UI distinction rather than bus creation, which aligns with the architectural changes.


66-70: Verify ESP32S3 bus configuration changes.

The significant increase in maximum buses (from 6 to 14) and digital channels (to 12) for ESP32S3 needs validation:

  1. Max buses increased from 6 to 14
  2. Digital channels increased to support x4 RMT + x8 I2S-LCD
✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Verify ESP32S3 bus configuration changes
# Check for any existing bus configuration that might be affected

# Search for bus configurations in the codebase
rg -A 5 "WLED_MAX_BUSSES|WLED_MAX_DIGITAL_CHANNELS" --type cpp

Length of output: 7707


ESP32S3 bus configuration verified

Based on the grep output, the changes for CONFIG_IDF_TARGET_ESP32S3 are properly applied in wled00/const.h with:
• WLED_MAX_BUSSES set to 14 (supporting 12 digital and 2 analog bus channels)
• WLED_MAX_DIGITAL_CHANNELS set to 12 (documented as x4 RMT + x8 I2S‐LCD)

Furthermore, the updated macros are referenced consistently in files like wled.cpp, bus_manager.cpp, and others. There’s no indication of further conflicts or legacy checks affecting the new configuration for ESP32S3.

wled00/xml.cpp (6)

295-296: LGTM! Improved bus pointer safety.

The changes enhance safety by:

  1. Using const pointer for bus object
  2. Adding comprehensive validation with !bus || !bus->isOk()

315-315: LGTM! Fixed potential integer overflow.

Changed from offset-based to direct ASCII calculation, preventing potential integer overflow issues.


360-360: LGTM! Enhanced entry validation.

Added length check !entry || !entry->len to prevent processing invalid entries.


275-275: LGTM! Helpful comment added.

The added comment clarifies that quotes must be included when defining WLED_RELEASE_NAME, which helps prevent initialization errors.


295-296: Improved bus pointer safety and validation.

The changes enhance robustness through:

  1. Using const to prevent unintended modifications to the bus.
  2. Adding isOk() check to ensure bus validity.
  3. Using break to stop processing when an invalid bus is encountered.

315-316: Enhanced pin handling for both physical and virtual buses.

The changes improve pin configuration by:

  1. Using direct ASCII conversion for pin indexing.
  2. Adding support for virtual bus configurations through the isVirtual() check.
wled00/wled.h (3)

894-898: LGTM! Improved bus configuration management.

The changes enhance memory management and control:

  1. Using std::vector<BusConfig> for dynamic sizing
  2. Added control variables for bus initialization and LED mapping

Note: These changes align with the PR objective to support up to 36 buses by removing fixed-size limitations.


894-895: Improved memory management using std::vector.

The changes enhance safety and flexibility by:

  1. Using std::vector<BusConfig> for dynamic bus configuration storage.
  2. Eliminating manual memory management of raw pointer arrays.

896-898: Added support for bus initialization and LED mapping.

The changes add necessary state tracking for:

  1. Bus initialization through the doInitBusses flag.
  2. LED map loading and current map tracking.
wled00/set.cpp (1)

144-144: LGTM! The loop limit change aligns with the PR objective.

The change from using constants to a fixed value of 36 aligns with the PR objective of supporting up to 36 buses. The debug output has been improved to show the number of buses processed.

Also applies to: 160-160

wled00/FX_fcn.cpp (4)

1120-1122: Improved const correctness and simplified bus validation.

The bus pointer is now correctly marked as const since the function doesn't modify the bus. The bus validity check has been simplified to a more concise form.


1397-1397: Simplified bus validity check.

The bus validity check has been simplified while maintaining the same functionality.


1691-1692: Improved const correctness in hasRGBWBus().

The bus pointer is now correctly marked as const since the function only reads from the bus. The bus validity check has been simplified.


1701-1702: Improved const correctness in hasCCTBus().

The bus pointer is now correctly marked as const since the function only reads from the bus. The bus validity check has been simplified.

Comment on lines +618 to 669
let c = JSON.parse(lines);
if (c.hw) {
if (c.hw.led) {
for (var i=0; i<10; i++) addLEDs(-1);
var l = c.hw.led;
// remove all existing outputs
for (const i=0; i<36; i++) addLEDs(-1); // was i<maxb+maxV when number of virtual buses was limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let l = c.hw.led;
l.ins.forEach((v,i,a)=>{
addLEDs(1);
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
d.getElementsByName("LT"+i)[0].value = v.type;
d.getElementsByName("LS"+i)[0].value = v.start;
d.getElementsByName("LC"+i)[0].value = v.len;
d.getElementsByName("CO"+i)[0].value = v.order;
d.getElementsByName("SL"+i)[0].value = v.skip;
d.getElementsByName("LT"+i)[0].value = v.type;
d.getElementsByName("LS"+i)[0].value = v.start;
d.getElementsByName("LC"+i)[0].value = v.len;
d.getElementsByName("CO"+i)[0].value = v.order & 0x0F;
d.getElementsByName("SL"+i)[0].value = v.skip;
d.getElementsByName("RF"+i)[0].checked = v.ref;
d.getElementsByName("CV"+i)[0].checked = v.rev;
d.getElementsByName("AW"+i)[0].value = v.rgbwm;
d.getElementsByName("WO"+i)[0].value = (v.order>>4) & 0x0F;
d.getElementsByName("SP"+i)[0].value = v.freq;
d.getElementsByName("LA"+i)[0].value = v.ledma;
d.getElementsByName("MA"+i)[0].value = v.maxpwr;
});
d.getElementsByName("PR")[0].checked = l.prl | 0;
d.getElementsByName("LD")[0].checked = l.ld;
d.getElementsByName("MA")[0].value = l.maxpwr;
d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
}
if(c.hw.com) {
resetCOM();
c.hw.com.forEach(e => {
addCOM(e.start, e.len, e.order);
});
}
if (c.hw.btn) {
var b = c.hw.btn;
let b = c.hw.btn;
if (b) {
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
b.ins.forEach((v,i,a)=>{
addBtn(i,v.pin[0],v.type);
});
d.getElementsByName("TT")[0].value = b.tt;
}
if (c.hw.ir) {
d.getElementsByName("IR")[0].value = c.hw.ir.pin;
d.getElementsByName("IT")[0].value = c.hw.ir.type;
let ir = c.hw.ir;
if (ir) {
d.getElementsByName("IR")[0].value = ir.pin;
d.getElementsByName("IT")[0].value = ir.type;
}
let rl = c.hw.relay;
if (rl) {
d.getElementsByName("RL")[0].value = rl.pin;
d.getElementsByName("RM")[0].checked = rl.rev;
d.getElementsByName("RO")[0].checked = rl.odrain;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for loaded configurations.

While the configuration loading handles the new bus limit, it lacks validation for:

  • Maximum number of buses per ESP variant
  • Memory constraints
  • Valid pin configurations

Add validation before applying the configuration:

 let c = JSON.parse(lines); 
+// Validate configuration
+function validateConfig(config) {
+  if (!config.hw?.led?.ins) return false;
+  const totalBuses = config.hw.led.ins.length;
+  if (totalBuses > 36) return false;
+  // Add more validation as needed
+  return true;
+}
+
 if (c.hw) {
+  if (!validateConfig(c)) {
+    alert("Invalid configuration!");
+    return;
+  }
   if (c.hw.led) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let c = JSON.parse(lines);
if (c.hw) {
if (c.hw.led) {
for (var i=0; i<10; i++) addLEDs(-1);
var l = c.hw.led;
// remove all existing outputs
for (const i=0; i<36; i++) addLEDs(-1); // was i<maxb+maxV when number of virtual buses was limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let l = c.hw.led;
l.ins.forEach((v,i,a)=>{
addLEDs(1);
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
d.getElementsByName("LT"+i)[0].value = v.type;
d.getElementsByName("LS"+i)[0].value = v.start;
d.getElementsByName("LC"+i)[0].value = v.len;
d.getElementsByName("CO"+i)[0].value = v.order;
d.getElementsByName("SL"+i)[0].value = v.skip;
d.getElementsByName("LT"+i)[0].value = v.type;
d.getElementsByName("LS"+i)[0].value = v.start;
d.getElementsByName("LC"+i)[0].value = v.len;
d.getElementsByName("CO"+i)[0].value = v.order & 0x0F;
d.getElementsByName("SL"+i)[0].value = v.skip;
d.getElementsByName("RF"+i)[0].checked = v.ref;
d.getElementsByName("CV"+i)[0].checked = v.rev;
d.getElementsByName("AW"+i)[0].value = v.rgbwm;
d.getElementsByName("WO"+i)[0].value = (v.order>>4) & 0x0F;
d.getElementsByName("SP"+i)[0].value = v.freq;
d.getElementsByName("LA"+i)[0].value = v.ledma;
d.getElementsByName("MA"+i)[0].value = v.maxpwr;
});
d.getElementsByName("PR")[0].checked = l.prl | 0;
d.getElementsByName("LD")[0].checked = l.ld;
d.getElementsByName("MA")[0].value = l.maxpwr;
d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
}
if(c.hw.com) {
resetCOM();
c.hw.com.forEach(e => {
addCOM(e.start, e.len, e.order);
});
}
if (c.hw.btn) {
var b = c.hw.btn;
let b = c.hw.btn;
if (b) {
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
b.ins.forEach((v,i,a)=>{
addBtn(i,v.pin[0],v.type);
});
d.getElementsByName("TT")[0].value = b.tt;
}
if (c.hw.ir) {
d.getElementsByName("IR")[0].value = c.hw.ir.pin;
d.getElementsByName("IT")[0].value = c.hw.ir.type;
let ir = c.hw.ir;
if (ir) {
d.getElementsByName("IR")[0].value = ir.pin;
d.getElementsByName("IT")[0].value = ir.type;
}
let rl = c.hw.relay;
if (rl) {
d.getElementsByName("RL")[0].value = rl.pin;
d.getElementsByName("RM")[0].checked = rl.rev;
d.getElementsByName("RO")[0].checked = rl.odrain;
}
let c = JSON.parse(lines);
// Validate configuration
function validateConfig(config) {
if (!config.hw?.led?.ins) return false;
const totalBuses = config.hw.led.ins.length;
if (totalBuses > 36) return false;
// Add more validation as needed
return true;
}
if (c.hw) {
if (!validateConfig(c)) {
alert("Invalid configuration!");
return;
}
if (c.hw.led) {
// remove all existing outputs
for (const i=0; i<36; i++) addLEDs(-1); // was i<maxb+maxV when number of virtual buses was limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let l = c.hw.led;
l.ins.forEach((v,i,a)=>{
addLEDs(1);
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
d.getElementsByName("LT"+i)[0].value = v.type;
d.getElementsByName("LS"+i)[0].value = v.start;
d.getElementsByName("LC"+i)[0].value = v.len;
d.getElementsByName("CO"+i)[0].value = v.order & 0x0F;
d.getElementsByName("SL"+i)[0].value = v.skip;
d.getElementsByName("RF"+i)[0].checked = v.ref;
d.getElementsByName("CV"+i)[0].checked = v.rev;
d.getElementsByName("AW"+i)[0].value = v.rgbwm;
d.getElementsByName("WO"+i)[0].value = (v.order>>4) & 0x0F;
d.getElementsByName("SP"+i)[0].value = v.freq;
d.getElementsByName("LA"+i)[0].value = v.ledma;
d.getElementsByName("MA"+i)[0].value = v.maxpwr;
});
d.getElementsByName("PR")[0].checked = l.prl | 0;
d.getElementsByName("LD")[0].checked = l.ld;
d.getElementsByName("MA")[0].value = l.maxpwr;
d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
}
if(c.hw.com) {
resetCOM();
c.hw.com.forEach(e => {
addCOM(e.start, e.len, e.order);
});
}
let b = c.hw.btn;
if (b) {
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
b.ins.forEach((v,i,a)=>{
addBtn(i,v.pin[0],v.type);
});
d.getElementsByName("TT")[0].value = b.tt;
}
let ir = c.hw.ir;
if (ir) {
d.getElementsByName("IR")[0].value = ir.pin;
d.getElementsByName("IT")[0].value = ir.type;
}
let rl = c.hw.relay;
if (rl) {
d.getElementsByName("RL")[0].value = rl.pin;
d.getElementsByName("RM")[0].checked = rl.rev;
d.getElementsByName("RO")[0].checked = rl.odrain;
}

wled00/bus_manager.cpp Outdated Show resolved Hide resolved
wled00/bus_manager.cpp Outdated Show resolved Hide resolved
unsigned numPins = is2Pin(_type) + 1;
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}

size_t BusDigital::getBusSize() const {
return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

PolyBus::getDataSize() not found.
The build fails because 'getDataSize' is not a member of 'PolyBus'. Confirm if there's a replacement method or remove this call.

Possible fix:

-return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0);
+return sizeof(BusDigital) + (isOk() ? (_data ? _len * getNumberOfChannels() : 0) : 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0);
return sizeof(BusDigital) + (isOk() ? (_data ? _len * getNumberOfChannels() : 0) : 0);
🧰 Tools
🪛 GitHub Actions: WLED CI

[error] 387-387: 'getDataSize' is not a member of 'PolyBus'.

if (Bus::isVirtual(type)) {
return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type));
} else if (Bus::isDigital(type)) {
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) + doubleBuffer * (count + skipAmount) * Bus::getNumberOfChannels(type);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

'memUsage' is not a member of 'PolyBus', invalid conversion.
The function call to PolyBus::memUsage(...) doesn't exist, and "pins" might be read-only. Confirm the correct approach or if a different method is needed.

-  return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) ...
+  // Possibly remove or replace with a valid memory usage function
+  return sizeof(BusDigital) + (doubleBuffer ? (count + skipAmount) * Bus::getNumberOfChannels(type) : 0);

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Actions: WLED CI

[error] 810-810: 'memUsage' is not a member of 'PolyBus'.


[error] 810-810: Invalid conversion from 'const uint8_t*' to 'uint8_t*'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants