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

Feature Page Phase 2 #1482

Merged
merged 37 commits into from
Nov 22, 2024
Merged

Feature Page Phase 2 #1482

merged 37 commits into from
Nov 22, 2024

Conversation

ethanzhouyc
Copy link
Collaborator

@ethanzhouyc ethanzhouyc commented Nov 13, 2024

New Features:

1. Enable toggle of device type features

  • automatically toggle elements (attributes, commands, and events) to correct conformance
  • update featureMap attribute for the cluster of toggled feature
  • add warning notification if a mandatory feature is disabled or a provisional one is enabled
  • disable feature updates and set warnings if conformance is too complex to manage

2. Add conformance warning to elements

  • display icon with warning messages and notifications for elements with incorrect conformance
  • sync warnings across locations and ensure compatibility with existing warnings from device type requirements
  • update warnings after feature and element toggles

3. Other improvements:

  • show Device Type Feature button and element conform warnings only if conformance data exist, ensuring compatibility with older Matter SDK
  • disable user from changing featureMap attribute
  • add unit tests for backend functions
  • update svg file

Example Use Cases:

1. Enable HS feature in an endpoint with Extended Color Light device type

image

2. After enabling HS, disabling attribute CurrentHue that conforms to HS will cause conformance warnings

image

Copy link
Collaborator

@paulr34 paulr34 left a comment

Choose a reason for hiding this comment

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

Great job @ethanzhouyc !!!!!

Copy link
Collaborator

@brdandu brdandu left a comment

Choose a reason for hiding this comment

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

Will need some more time to go through this because of its size.

src-electron/db/db-mapping.js Outdated Show resolved Hide resolved
src-electron/db/db-mapping.js Outdated Show resolved Hide resolved
src-electron/db/db-mapping.js Outdated Show resolved Hide resolved
@@ -2046,6 +2048,50 @@ async function selectNonManufacturerSpecificCommandDetailsFromAllEndpointTypesAn
)
}

/**
* Get all commands in an endpoint type cluster
* Non-required commands are not loaded into ENDPOINT_TYPE_COMMAND table,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are non-required attributes loaded into endpoint_type_attribute table? I don't see deviceTypeClusterId being used in selectAttributesByEndpointTypeClusterId

Copy link
Collaborator Author

@ethanzhouyc ethanzhouyc Nov 13, 2024

Choose a reason for hiding this comment

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

Yes, if you create an endpoint and check ENDPOINT_TYPE_ATTRIBUTE table, there will be attributes with INCLUDED = 0, so deviceTypeClusterId is not needed here

mandatoryFeaturesOnEndpointTypeAndCluster.map((f) => f.featureBit)
// only set featureMap bit to 1 for mandatory features
let featureMapBitsToBeEnabled = featuresOnEndpointTypeAndCluster
.filter((f) => f.conformance == 'M')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we filter for just Mandatory? Were we doing something wrong before?

Copy link
Collaborator Author

@ethanzhouyc ethanzhouyc Nov 13, 2024

Choose a reason for hiding this comment

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

Before, we only loaded mandatory features to the database so there is no need to filter. Now features with all types of conformance exist

f.BIT,
f.DEFAULT_VALUE,
f.DESCRIPTION
d.DESCRIPTION AS DEVICE_TYPE_NAME,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Fix the formatting here.

Copy link
Collaborator Author

@ethanzhouyc ethanzhouyc Nov 13, 2024

Choose a reason for hiding this comment

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

The format is in good shape if you view the actual file instead of PR diff. Github is displaying it weirdly

@ethanzhouyc
Copy link
Collaborator Author

ethanzhouyc commented Nov 14, 2024

New changes:

  1. Rebased with master to include changes from PR Restructure getEndpointIds function to pass ui.test.js #1483 and fix failed checks
  2. add extended db-mapping to endpoint type elements

Copy link
Collaborator

@tecimovic tecimovic left a comment

Choose a reason for hiding this comment

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

Lost of stuff..... I marked some obvious things, including some typos.

src-electron/db/query-config.js Outdated Show resolved Hide resolved
src-electron/db/query-session-notification.js Outdated Show resolved Hide resolved
src-electron/db/query-config.js Show resolved Hide resolved
src-electron/db/query-feature.js Show resolved Hide resolved
async function getFeaturesByDeviceTypeRefs(
db,
deviceTypeRefs,
endpointTypeRef
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why "get feature by device type" needs an "endpoint type" all of suddenly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ENDPOINT_TYPE_ATTRIBUTE and ATTRIBUTE table is joined to get info on:

  1. featureMap value related to the feature, deciding if the feature is enabled
  2. attribute id of featureMap, which is needed when updating the featureMap after toggling a feature

* @param {*} db
* @returns callback for the express uri registration
*/
function httpPostSetRequiredElements(db) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm confused with the name of this function..... Why do you need to POST the required elements? Aren't the required elements calculated on the back-end and at best sent over to the front-end as result of some "POST" that sets feature bits or something like that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are right. I changed it to a GET request as it only gets resources from backend and not change them

src-electron/rest/user-data.js Outdated Show resolved Hide resolved
return async (request, response) => {
let resp = await querySessionNotification.setRequiredElementWarning(
db,
request.body,
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems unsafe and suspicious to post the whole request body straight into the query. This feels wrong somehow.

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 improved this by only getting relevant variables passed from frontend, creating a new object out of them, and passing the new object to backend

if (operand.mandatoryConform) {
let insideTerm = operand.mandatoryConform[0]
// Recurse further if insideTerm is not empty
if (insideTerm && Object.keys(insideTerm).toString() != '$') {
return parseFeatureConformance(operand.mandatoryConform[0])
return parseConformanceRecursively(operand.mandatoryConform[0])
Copy link
Collaborator

Choose a reason for hiding this comment

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

I generally like to pass a "depth" to any recursive function. You start with "depth = 0" and then whenever you call recursively, you call it with "depth+1". When depth gets to like 200, you know you're doing something wrong, you blow up with a clear exception, instead of some stupid stack overflow which is hard to track down.

But I'm possibly old school....

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's great advice for keeping recursive functions safe, but this function is already safeguarded against infinite recursion levels, as:

  1. each recursive call moves one layer deeper into the xml data object
  2. the if/else if/else structure ensures that all cases have a return value

Copy link
Collaborator

Choose a reason for hiding this comment

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

You are not guarding against yourself. You are guarding against a future developer, who might add things to this function, that would mistakenly cause this to end up in stack overflow.

Generally, be always aware who you're developing for:
1.) For you, TODAY.
2.) For you, TOMORROW.
3.) For a random guy that has no clue and shows up here TOMORROW.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not a terribly big deal though, not something to hold this up for.

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 added a max depth check on the recursive function as you said so someone in the future would get an error if they blow up

src/components/ZclDeviceTypeFeatureManager.vue Outdated Show resolved Hide resolved
@ethanzhouyc
Copy link
Collaborator Author

All typos and SQL related comments are addressed

// process andTerm and orTerm in the same logic
// when joining multiple orTerms inside andTerms, we need to
// surround them with '()', vice versa for andTerms inside orTerms
// e.g. A & (B | C) or A | (B & C)
Copy link
Collaborator

Choose a reason for hiding this comment

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

You should use antlr for these kid of things in the future.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes we could switch to ANTLR if conformance parsing became too complicated to be parsed one day

Copy link
Collaborator

@tecimovic tecimovic left a comment

Choose a reason for hiding this comment

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

Merge this in. There are things that are a bit weird, but we shouldn't hold this up.

You can clean up later.

Generally:
1.) Break up these large reworks into multiple PRs. There is nothing, for example, stopping you to do a smaller PR, that just updates the DB schema, then push that in. While we review that, you work on the next PR for middleware, and then up to the UI. Or whatever direction you want, but layer by layer.

2.) Large things like this are nicely developed if you have a toplevel preference option to turn these features on and off. That protects you, for example, in Zigbee case, where you can simply keep this off, and not even enable it, then Zigbee doesn't fall victim to your bugs.

@ethanzhouyc
Copy link
Collaborator Author

ethanzhouyc commented Nov 22, 2024

There will be future cleanup PR to separate feature related code from Zigbee

@paulr34 paulr34 merged commit c72231e into project-chip:master Nov 22, 2024
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants