Skip to content

Commit

Permalink
Endpoint Composition Tracking (#1364)
Browse files Browse the repository at this point in the history
endpoint composition

implementing loader with endpoint composition

adding helper function

add unit test 

adding an ENDPOINT_COMPOSITION table to enable other endpoint composition features like conformance

adding DEVICE_COMPOSITION table  for child devices

PR review

updating schema diagram

adding mandatory device type enum 

getting device type ref after the device types are inserted to maintain db integrity
  • Loading branch information
paulr34 authored Aug 2, 2024
1 parent f018fd2 commit 9a12110
Show file tree
Hide file tree
Showing 14 changed files with 3,435 additions and 2,143 deletions.
4,708 changes: 2,607 additions & 2,101 deletions docs/zap-schema.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src-electron/db/db-mapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ exports.map = {
featureName: x.FEATURE_NAME,
featureBit: x.FEATURE_BIT,
clusterId: x.CLUSTER_REF,
composition: x.TYPE,
}
},
endpointTypeCluster: (x) => {
Expand Down
40 changes: 40 additions & 0 deletions src-electron/db/query-device-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,44 @@ async function updateDeviceTypeEntityReferences(db, packageId) {
return updateFeatureReferencesForDeviceTypeReferences(db, packageId)
}

/**
* Asynchronously selects device types with their compositions by a specific endpoint type ID.
*
* This function queries the database for device types associated with a given endpoint type ID,
* including details about the device type and any endpoint compositions linked to it.
*
* @param {Object} db - The database connection object.
* @param {number} endpointTypeId - The ID of the endpoint type used to filter the device types.
* @returns {Promise<Array>} A promise that resolves with an array of device types and their compositions.
*/
async function selectDeviceTypesWithCompositionByEndpointTypeId(
db,
endpointTypeId
) {
let rows = await dbApi.dbAll(
db,
`
SELECT
ETD.ENDPOINT_TYPE_DEVICE_ID,
ETD.DEVICE_TYPE_REF,
ETD.ENDPOINT_TYPE_REF,
ETD.DEVICE_TYPE_ORDER,
ETD.DEVICE_IDENTIFIER,
ETD.DEVICE_VERSION,
EC.TYPE
FROM
ENDPOINT_TYPE_DEVICE AS ETD
JOIN
DEVICE_TYPE AS DT ON ETD.DEVICE_TYPE_REF = DT.DEVICE_TYPE_ID
LEFT JOIN
ENDPOINT_COMPOSITION AS EC ON DT.CODE = EC.CODE
WHERE
ETD.ENDPOINT_TYPE_REF = ?`,
[endpointTypeId]
)
return rows.map(dbMapping.map.endpointTypeDeviceExtended)
}

/**
* Retrieves the zcl device type information based on an endpoint type id
* @param {*} db
Expand Down Expand Up @@ -467,3 +505,5 @@ exports.updateDeviceTypeEntityReferences = updateDeviceTypeEntityReferences
exports.selectDeviceTypesByEndpointTypeId = selectDeviceTypesByEndpointTypeId
exports.selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId =
selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId
exports.selectDeviceTypesWithCompositionByEndpointTypeId =
selectDeviceTypesWithCompositionByEndpointTypeId
106 changes: 99 additions & 7 deletions src-electron/db/query-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
const env = require('../util/env')
const dbApi = require('./db-api.js')
const queryNotification = require('../db/query-package-notification')
const dbEnum = require('../../src-shared/db-enum.js')

// Some loading queries that are reused few times.

Expand Down Expand Up @@ -926,6 +927,67 @@ async function insertAtomics(db, packageId, data) {
)
}

/**
* Inserts endpoint composition data into the database based on the context's mandatory device type.
* This function checks if the context's mandatory device type matches the composition code.
* If they match, it performs an insert operation with a specific type from `dbEnum.mandatoryDeviceType`.
* If they do not match, it performs an insert with the composition's type.
*
* @param {*} db - The database connection object.
* @param {*} composition - The composition data to be inserted.
* @param {*} context - The context containing the mandatory device type to check against.
* @returns A promise resolved with the result of the database insert operation.
*/
function insertEndpointComposition(db, composition, context) {
if (parseInt(context.mandatoryDeviceTypes, 16) === composition.code) {
return dbApi.dbInsert(
db,
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
[dbEnum.composition.mandatoryEndpoint, composition.code]
)
} else {
return dbApi.dbInsert(
db,
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
[composition.compositionType, composition.code]
)
}
}

/**
* Asynchronously retrieves the ID of an endpoint composition based on its code.
*
* @param {Object} db - The database connection object.
* @param {Object} deviceType - An object representing the device type, which contains the 'code' property.
* @returns {Promise<number|null>} A promise that resolves with the ID of the endpoint composition if found, or null otherwise.
*/
async function getEndpointCompositionIdByCode(db, deviceType) {
const query =
'SELECT ENDPOINT_COMPOSITION_ID FROM ENDPOINT_COMPOSITION WHERE CODE = ?'
const result = await dbApi.dbGet(db, query, [deviceType.code])
return result ? result.ENDPOINT_COMPOSITION_ID : null
}

/**
* Inserts a new device composition record into the database.
*
* @param {Object} db - The database connection object.
* @param {Object} deviceType - An object representing the device type, which contains the 'childDeviceId' property.
* @param {number} endpointCompositionId - The ID of the endpoint composition associated with this device composition.
* @returns {Promise} A promise that resolves with the result of the database insertion operation.
*/

function insertDeviceComposition(db, deviceType, endpointCompositionId) {
const insertQuery = `
INSERT INTO DEVICE_COMPOSITION (CODE, ENDPOINT_COMPOSITION_REF)
VALUES (?, ?)
`
return dbApi.dbInsert(db, insertQuery, [
parseInt(deviceType.childDeviceId, 16),
endpointCompositionId,
])
}

/**
* Inserts device types into the database.
*
Expand Down Expand Up @@ -984,13 +1046,40 @@ async function insertDeviceTypes(db, packageId, data) {
}
})
)
).then((dtClusterRefDataPairs) => {
let promises = []
promises.push(insertDeviceTypeAttributes(db, dtClusterRefDataPairs))
promises.push(insertDeviceTypeCommands(db, dtClusterRefDataPairs))
promises.push(insertDeviceTypeFeatures(db, dtClusterRefDataPairs))
return Promise.all(promises)
})
)
.then((dtClusterRefDataPairs) => {
let promises = []
promises.push(
insertDeviceTypeAttributes(db, dtClusterRefDataPairs)
)
promises.push(insertDeviceTypeCommands(db, dtClusterRefDataPairs))
promises.push(insertDeviceTypeFeatures(db, dtClusterRefDataPairs))
return Promise.all(promises)
})
.then(() => {
// Update ENDPOINT_COMPOSITION with DEVICE_TYPE_REF
const updateEndpointComposition = `
UPDATE ENDPOINT_COMPOSITION
SET DEVICE_TYPE_REF = (
SELECT DEVICE_TYPE_ID
FROM DEVICE_TYPE
WHERE DEVICE_TYPE.CODE = ENDPOINT_COMPOSITION.CODE
)
`
return dbApi.dbAll(db, updateEndpointComposition)
})
.then(() => {
// Update DEVICE_COMPOSITION with DEVICE_TYPE_REF
const updateDeviceComposition = `
UPDATE DEVICE_COMPOSITION
SET DEVICE_TYPE_REF = (
SELECT DEVICE_TYPE_ID
FROM DEVICE_TYPE
WHERE DEVICE_TYPE.CODE = DEVICE_COMPOSITION.CODE
)
`
return dbApi.dbAll(db, updateDeviceComposition)
})
}
}
return zclIdsPromises
Expand Down Expand Up @@ -1997,3 +2086,6 @@ exports.insertStruct = insertStruct
exports.insertStructItems = insertStructItems
exports.updateDataTypeClusterReferences = updateDataTypeClusterReferences
exports.insertAttributeMappings = insertAttributeMappings
exports.insertEndpointComposition = insertEndpointComposition
exports.insertDeviceComposition = insertDeviceComposition
exports.getEndpointCompositionIdByCode = getEndpointCompositionIdByCode
47 changes: 47 additions & 0 deletions src-electron/db/zap-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,53 @@ CREATE TABLE IF NOT EXISTS "DEVICE_TYPE" (
"SUPERSET" text,
foreign key (PACKAGE_REF) references PACKAGE(PACKAGE_ID) ON DELETE CASCADE ON UPDATE CASCADE
);
/*
This table stores information about endpoint compositions.
Each record represents a composition associated with a specific device type.
Columns:
ENDPOINT_COMPOSITION_ID: The primary key of the table, auto-incremented for each new record.
DEVICE_TYPE_REF: A foreign key linking to the DEVICE_TYPE table, indicating the device type associated with this composition.
TYPE: A text field describing the type of the endpoint composition.
CODE: An integer representing a unique code for the endpoint composition.
Foreign Key Constraints:
The DEVICE_TYPE_REF column references the DEVICE_TYPE_ID column of the DEVICE_TYPE table.
On deletion of a referenced device type, corresponding records in this table are deleted (CASCADE).
*/
CREATE TABLE IF NOT EXISTS "ENDPOINT_COMPOSITION" (
"ENDPOINT_COMPOSITION_ID" integer PRIMARY KEY AUTOINCREMENT,
"DEVICE_TYPE_REF" integer,
"TYPE" text,
"CODE" integer,
FOREIGN KEY ("DEVICE_TYPE_REF") REFERENCES "DEVICE_TYPE"("DEVICE_TYPE_ID") ON DELETE CASCADE
);
/*
This table defines the composition of devices within the system.
It links devices to their types and endpoint compositions, specifying their conformance and constraints.
Columns:
DEVICE_COMPOSITION_ID: The primary key of the table, auto-incremented for each new record.
DEVICE_TYPE_REF: An integer that acts as a foreign key to reference a specific device type.
ENDPOINT_COMPOSITION_REF: A foreign key linking to the ENDPOINT_COMPOSITION table to specify the endpoint composition associated with this device.
CONFORMANCE: A text field describing the conformance level of the device composition.
CONSTRAINT: An integer representing any constraints applied to the device composition.
Foreign Key Constraints:
The DEVICE_TYPE_REF column references the DEVICE_TYPE_ID column of the DEVICE_TYPE table. On deletion of a device type, corresponding records in this table are deleted (CASCADE).
The ENDPOINT_COMPOSITION_REF column references the ENDPOINT_COMPOSITION_ID column of the ENDPOINT_COMPOSITION table. On deletion of an endpoint composition, corresponding records in this table are deleted (CASCADE).
*/
CREATE TABLE IF NOT EXISTS "DEVICE_COMPOSITION" (
"DEVICE_COMPOSITION_ID" integer PRIMARY KEY AUTOINCREMENT,
"CODE" integer,
"DEVICE_TYPE_REF" integer,
"ENDPOINT_COMPOSITION_REF" integer,
"CONFORMANCE" text,
"CONSTRAINT" integer,
FOREIGN KEY ("ENDPOINT_COMPOSITION_REF") REFERENCES "ENDPOINT_COMPOSITION"("ENDPOINT_COMPOSITION_ID") ON DELETE CASCADE
FOREIGN KEY ("DEVICE_TYPE_REF") REFERENCES "DEVICE_TYPE"("DEVICE_TYPE_ID") ON DELETE CASCADE
);

/*
DEVICE_TYPE_CLUSTER contains clusters that belong to the device type.
*/
Expand Down
6 changes: 4 additions & 2 deletions src-electron/generator/helper-session.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,13 @@ function user_endpoints(options) {
*/
async function user_device_types(options) {
let promise = queryDeviceType
.selectDeviceTypesByEndpointTypeId(this.global.db, this.endpointTypeId)
.selectDeviceTypesWithCompositionByEndpointTypeId(
this.global.db,
this.endpointTypeId
)
.then((deviceTypes) =>
templateUtil.collectBlocks(deviceTypes, options, this)
)

return templateUtil.templatePromise(this.global, promise)
}

Expand Down
Loading

0 comments on commit 9a12110

Please sign in to comment.