Skip to content

Commit 9a12110

Browse files
authored
Endpoint Composition Tracking (#1364)
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
1 parent f018fd2 commit 9a12110

File tree

14 files changed

+3435
-2143
lines changed

14 files changed

+3435
-2143
lines changed

docs/zap-schema.svg

Lines changed: 2607 additions & 2101 deletions
Loading

src-electron/db/db-mapping.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ exports.map = {
562562
featureName: x.FEATURE_NAME,
563563
featureBit: x.FEATURE_BIT,
564564
clusterId: x.CLUSTER_REF,
565+
composition: x.TYPE,
565566
}
566567
},
567568
endpointTypeCluster: (x) => {

src-electron/db/query-device-type.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,44 @@ async function updateDeviceTypeEntityReferences(db, packageId) {
369369
return updateFeatureReferencesForDeviceTypeReferences(db, packageId)
370370
}
371371

372+
/**
373+
* Asynchronously selects device types with their compositions by a specific endpoint type ID.
374+
*
375+
* This function queries the database for device types associated with a given endpoint type ID,
376+
* including details about the device type and any endpoint compositions linked to it.
377+
*
378+
* @param {Object} db - The database connection object.
379+
* @param {number} endpointTypeId - The ID of the endpoint type used to filter the device types.
380+
* @returns {Promise<Array>} A promise that resolves with an array of device types and their compositions.
381+
*/
382+
async function selectDeviceTypesWithCompositionByEndpointTypeId(
383+
db,
384+
endpointTypeId
385+
) {
386+
let rows = await dbApi.dbAll(
387+
db,
388+
`
389+
SELECT
390+
ETD.ENDPOINT_TYPE_DEVICE_ID,
391+
ETD.DEVICE_TYPE_REF,
392+
ETD.ENDPOINT_TYPE_REF,
393+
ETD.DEVICE_TYPE_ORDER,
394+
ETD.DEVICE_IDENTIFIER,
395+
ETD.DEVICE_VERSION,
396+
EC.TYPE
397+
FROM
398+
ENDPOINT_TYPE_DEVICE AS ETD
399+
JOIN
400+
DEVICE_TYPE AS DT ON ETD.DEVICE_TYPE_REF = DT.DEVICE_TYPE_ID
401+
LEFT JOIN
402+
ENDPOINT_COMPOSITION AS EC ON DT.CODE = EC.CODE
403+
WHERE
404+
ETD.ENDPOINT_TYPE_REF = ?`,
405+
[endpointTypeId]
406+
)
407+
return rows.map(dbMapping.map.endpointTypeDeviceExtended)
408+
}
409+
372410
/**
373411
* Retrieves the zcl device type information based on an endpoint type id
374412
* @param {*} db
@@ -467,3 +505,5 @@ exports.updateDeviceTypeEntityReferences = updateDeviceTypeEntityReferences
467505
exports.selectDeviceTypesByEndpointTypeId = selectDeviceTypesByEndpointTypeId
468506
exports.selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId =
469507
selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId
508+
exports.selectDeviceTypesWithCompositionByEndpointTypeId =
509+
selectDeviceTypesWithCompositionByEndpointTypeId

src-electron/db/query-loader.js

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
const env = require('../util/env')
2424
const dbApi = require('./db-api.js')
2525
const queryNotification = require('../db/query-package-notification')
26+
const dbEnum = require('../../src-shared/db-enum.js')
2627

2728
// Some loading queries that are reused few times.
2829

@@ -926,6 +927,67 @@ async function insertAtomics(db, packageId, data) {
926927
)
927928
}
928929

930+
/**
931+
* Inserts endpoint composition data into the database based on the context's mandatory device type.
932+
* This function checks if the context's mandatory device type matches the composition code.
933+
* If they match, it performs an insert operation with a specific type from `dbEnum.mandatoryDeviceType`.
934+
* If they do not match, it performs an insert with the composition's type.
935+
*
936+
* @param {*} db - The database connection object.
937+
* @param {*} composition - The composition data to be inserted.
938+
* @param {*} context - The context containing the mandatory device type to check against.
939+
* @returns A promise resolved with the result of the database insert operation.
940+
*/
941+
function insertEndpointComposition(db, composition, context) {
942+
if (parseInt(context.mandatoryDeviceTypes, 16) === composition.code) {
943+
return dbApi.dbInsert(
944+
db,
945+
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
946+
[dbEnum.composition.mandatoryEndpoint, composition.code]
947+
)
948+
} else {
949+
return dbApi.dbInsert(
950+
db,
951+
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
952+
[composition.compositionType, composition.code]
953+
)
954+
}
955+
}
956+
957+
/**
958+
* Asynchronously retrieves the ID of an endpoint composition based on its code.
959+
*
960+
* @param {Object} db - The database connection object.
961+
* @param {Object} deviceType - An object representing the device type, which contains the 'code' property.
962+
* @returns {Promise<number|null>} A promise that resolves with the ID of the endpoint composition if found, or null otherwise.
963+
*/
964+
async function getEndpointCompositionIdByCode(db, deviceType) {
965+
const query =
966+
'SELECT ENDPOINT_COMPOSITION_ID FROM ENDPOINT_COMPOSITION WHERE CODE = ?'
967+
const result = await dbApi.dbGet(db, query, [deviceType.code])
968+
return result ? result.ENDPOINT_COMPOSITION_ID : null
969+
}
970+
971+
/**
972+
* Inserts a new device composition record into the database.
973+
*
974+
* @param {Object} db - The database connection object.
975+
* @param {Object} deviceType - An object representing the device type, which contains the 'childDeviceId' property.
976+
* @param {number} endpointCompositionId - The ID of the endpoint composition associated with this device composition.
977+
* @returns {Promise} A promise that resolves with the result of the database insertion operation.
978+
*/
979+
980+
function insertDeviceComposition(db, deviceType, endpointCompositionId) {
981+
const insertQuery = `
982+
INSERT INTO DEVICE_COMPOSITION (CODE, ENDPOINT_COMPOSITION_REF)
983+
VALUES (?, ?)
984+
`
985+
return dbApi.dbInsert(db, insertQuery, [
986+
parseInt(deviceType.childDeviceId, 16),
987+
endpointCompositionId,
988+
])
989+
}
990+
929991
/**
930992
* Inserts device types into the database.
931993
*
@@ -984,13 +1046,40 @@ async function insertDeviceTypes(db, packageId, data) {
9841046
}
9851047
})
9861048
)
987-
).then((dtClusterRefDataPairs) => {
988-
let promises = []
989-
promises.push(insertDeviceTypeAttributes(db, dtClusterRefDataPairs))
990-
promises.push(insertDeviceTypeCommands(db, dtClusterRefDataPairs))
991-
promises.push(insertDeviceTypeFeatures(db, dtClusterRefDataPairs))
992-
return Promise.all(promises)
993-
})
1049+
)
1050+
.then((dtClusterRefDataPairs) => {
1051+
let promises = []
1052+
promises.push(
1053+
insertDeviceTypeAttributes(db, dtClusterRefDataPairs)
1054+
)
1055+
promises.push(insertDeviceTypeCommands(db, dtClusterRefDataPairs))
1056+
promises.push(insertDeviceTypeFeatures(db, dtClusterRefDataPairs))
1057+
return Promise.all(promises)
1058+
})
1059+
.then(() => {
1060+
// Update ENDPOINT_COMPOSITION with DEVICE_TYPE_REF
1061+
const updateEndpointComposition = `
1062+
UPDATE ENDPOINT_COMPOSITION
1063+
SET DEVICE_TYPE_REF = (
1064+
SELECT DEVICE_TYPE_ID
1065+
FROM DEVICE_TYPE
1066+
WHERE DEVICE_TYPE.CODE = ENDPOINT_COMPOSITION.CODE
1067+
)
1068+
`
1069+
return dbApi.dbAll(db, updateEndpointComposition)
1070+
})
1071+
.then(() => {
1072+
// Update DEVICE_COMPOSITION with DEVICE_TYPE_REF
1073+
const updateDeviceComposition = `
1074+
UPDATE DEVICE_COMPOSITION
1075+
SET DEVICE_TYPE_REF = (
1076+
SELECT DEVICE_TYPE_ID
1077+
FROM DEVICE_TYPE
1078+
WHERE DEVICE_TYPE.CODE = DEVICE_COMPOSITION.CODE
1079+
)
1080+
`
1081+
return dbApi.dbAll(db, updateDeviceComposition)
1082+
})
9941083
}
9951084
}
9961085
return zclIdsPromises
@@ -1997,3 +2086,6 @@ exports.insertStruct = insertStruct
19972086
exports.insertStructItems = insertStructItems
19982087
exports.updateDataTypeClusterReferences = updateDataTypeClusterReferences
19992088
exports.insertAttributeMappings = insertAttributeMappings
2089+
exports.insertEndpointComposition = insertEndpointComposition
2090+
exports.insertDeviceComposition = insertDeviceComposition
2091+
exports.getEndpointCompositionIdByCode = getEndpointCompositionIdByCode

src-electron/db/zap-schema.sql

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,53 @@ CREATE TABLE IF NOT EXISTS "DEVICE_TYPE" (
355355
"SUPERSET" text,
356356
foreign key (PACKAGE_REF) references PACKAGE(PACKAGE_ID) ON DELETE CASCADE ON UPDATE CASCADE
357357
);
358+
/*
359+
This table stores information about endpoint compositions.
360+
Each record represents a composition associated with a specific device type.
361+
362+
Columns:
363+
ENDPOINT_COMPOSITION_ID: The primary key of the table, auto-incremented for each new record.
364+
DEVICE_TYPE_REF: A foreign key linking to the DEVICE_TYPE table, indicating the device type associated with this composition.
365+
TYPE: A text field describing the type of the endpoint composition.
366+
CODE: An integer representing a unique code for the endpoint composition.
367+
368+
Foreign Key Constraints:
369+
The DEVICE_TYPE_REF column references the DEVICE_TYPE_ID column of the DEVICE_TYPE table.
370+
On deletion of a referenced device type, corresponding records in this table are deleted (CASCADE).
371+
*/
372+
CREATE TABLE IF NOT EXISTS "ENDPOINT_COMPOSITION" (
373+
"ENDPOINT_COMPOSITION_ID" integer PRIMARY KEY AUTOINCREMENT,
374+
"DEVICE_TYPE_REF" integer,
375+
"TYPE" text,
376+
"CODE" integer,
377+
FOREIGN KEY ("DEVICE_TYPE_REF") REFERENCES "DEVICE_TYPE"("DEVICE_TYPE_ID") ON DELETE CASCADE
378+
);
379+
/*
380+
This table defines the composition of devices within the system.
381+
It links devices to their types and endpoint compositions, specifying their conformance and constraints.
382+
383+
Columns:
384+
DEVICE_COMPOSITION_ID: The primary key of the table, auto-incremented for each new record.
385+
DEVICE_TYPE_REF: An integer that acts as a foreign key to reference a specific device type.
386+
ENDPOINT_COMPOSITION_REF: A foreign key linking to the ENDPOINT_COMPOSITION table to specify the endpoint composition associated with this device.
387+
CONFORMANCE: A text field describing the conformance level of the device composition.
388+
CONSTRAINT: An integer representing any constraints applied to the device composition.
389+
390+
Foreign Key Constraints:
391+
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).
392+
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).
393+
*/
394+
CREATE TABLE IF NOT EXISTS "DEVICE_COMPOSITION" (
395+
"DEVICE_COMPOSITION_ID" integer PRIMARY KEY AUTOINCREMENT,
396+
"CODE" integer,
397+
"DEVICE_TYPE_REF" integer,
398+
"ENDPOINT_COMPOSITION_REF" integer,
399+
"CONFORMANCE" text,
400+
"CONSTRAINT" integer,
401+
FOREIGN KEY ("ENDPOINT_COMPOSITION_REF") REFERENCES "ENDPOINT_COMPOSITION"("ENDPOINT_COMPOSITION_ID") ON DELETE CASCADE
402+
FOREIGN KEY ("DEVICE_TYPE_REF") REFERENCES "DEVICE_TYPE"("DEVICE_TYPE_ID") ON DELETE CASCADE
403+
);
404+
358405
/*
359406
DEVICE_TYPE_CLUSTER contains clusters that belong to the device type.
360407
*/

src-electron/generator/helper-session.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,13 @@ function user_endpoints(options) {
115115
*/
116116
async function user_device_types(options) {
117117
let promise = queryDeviceType
118-
.selectDeviceTypesByEndpointTypeId(this.global.db, this.endpointTypeId)
118+
.selectDeviceTypesWithCompositionByEndpointTypeId(
119+
this.global.db,
120+
this.endpointTypeId
121+
)
119122
.then((deviceTypes) =>
120123
templateUtil.collectBlocks(deviceTypes, options, this)
121124
)
122-
123125
return templateUtil.templatePromise(this.global, promise)
124126
}
125127

0 commit comments

Comments
 (0)