@project-chip/matter.js 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/protocol/ChannelManager.d.ts.map +1 -1
- package/dist/cjs/protocol/ChannelManager.js +16 -15
- package/dist/cjs/protocol/ChannelManager.js.map +2 -2
- package/dist/cjs/protocol/interaction/SubscriptionHandler.d.ts.map +1 -1
- package/dist/cjs/protocol/interaction/SubscriptionHandler.js +8 -4
- package/dist/cjs/protocol/interaction/SubscriptionHandler.js.map +2 -2
- package/dist/esm/protocol/ChannelManager.d.ts.map +1 -1
- package/dist/esm/protocol/ChannelManager.js +16 -15
- package/dist/esm/protocol/ChannelManager.js.map +2 -2
- package/dist/esm/protocol/interaction/SubscriptionHandler.d.ts.map +1 -1
- package/dist/esm/protocol/interaction/SubscriptionHandler.js +8 -4
- package/dist/esm/protocol/interaction/SubscriptionHandler.js.map +2 -2
- package/package.json +3 -3
- package/src/protocol/ChannelManager.ts +17 -15
- package/src/protocol/interaction/SubscriptionHandler.ts +6 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelManager.d.ts","sourceRoot":"","sources":["../../../src/protocol/ChannelManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAItD,qBAAa,cAAe,SAAQ,WAAW;CAAG;AAElD,qBAAa,cAAc;;gBAMX,4BAA4B,SAAI;IAkBtC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"ChannelManager.d.ts","sourceRoot":"","sources":["../../../src/protocol/ChannelManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAItD,qBAAa,cAAe,SAAQ,WAAW;CAAG;AAElD,qBAAa,cAAc;;gBAMX,4BAA4B,SAAI;IAkBtC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC;IAqB7E,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC;IAYjE;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;IAapC,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAQpD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;IAkBzE,OAAO,CAAC,wBAAwB;IAU1B,kBAAkB,CAAC,gBAAgB,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;IA0B9E,KAAK;CAYd"}
|
|
@@ -57,23 +57,18 @@ class ChannelManager {
|
|
|
57
57
|
channel.closeCallback = async () => this.removeChannel(fabric, nodeId, channel.session);
|
|
58
58
|
const channelsKey = this.#getChannelKey(fabric, nodeId);
|
|
59
59
|
const currentChannels = this.#channels.get(channelsKey) ?? [];
|
|
60
|
-
|
|
60
|
+
currentChannels.push(channel);
|
|
61
|
+
this.#channels.set(channelsKey, currentChannels);
|
|
62
|
+
if (currentChannels.length > this.#caseSessionsPerFabricAndNode) {
|
|
61
63
|
const oldestChannel = this.#findLeastActiveChannel(currentChannels);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const { session } = oldestChannel;
|
|
66
|
-
if (session.id !== oldestChannel.session.id) {
|
|
67
|
-
logger.debug(
|
|
68
|
-
`Existing channel for fabricIndex ${fabric.fabricIndex} and node ${nodeId} with session ${session.name} gets replaced. Consider former session already inactive and so close it.`
|
|
69
|
-
);
|
|
70
|
-
await session.destroy(false, false);
|
|
64
|
+
const { session: oldSession } = oldestChannel;
|
|
65
|
+
if (channel.session.id !== oldSession.id) {
|
|
66
|
+
await oldSession.destroy(false, false);
|
|
71
67
|
}
|
|
72
|
-
logger.info(
|
|
68
|
+
logger.info(
|
|
69
|
+
`Close oldest channel for fabric ${fabric.fabricIndex} node ${nodeId} (from session ${oldSession.id})`
|
|
70
|
+
);
|
|
73
71
|
await oldestChannel.close();
|
|
74
|
-
} else {
|
|
75
|
-
currentChannels.push(channel);
|
|
76
|
-
this.#channels.set(channelsKey, currentChannels);
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
74
|
getChannel(fabric, nodeId, session) {
|
|
@@ -81,7 +76,10 @@ class ChannelManager {
|
|
|
81
76
|
if (session !== void 0) {
|
|
82
77
|
results = results.filter((channel) => channel.session.id === session.id);
|
|
83
78
|
}
|
|
84
|
-
if (results.length === 0)
|
|
79
|
+
if (results.length === 0)
|
|
80
|
+
throw new NoChannelError(
|
|
81
|
+
`Can't find a channel to node ${nodeId}${session !== void 0 ? ` and session ${session.id}` : ""}`
|
|
82
|
+
);
|
|
85
83
|
return results[results.length - 1];
|
|
86
84
|
}
|
|
87
85
|
/**
|
|
@@ -112,6 +110,9 @@ class ChannelManager {
|
|
|
112
110
|
const channelEntryIndex = fabricChannels.findIndex(
|
|
113
111
|
({ session: entrySession }) => entrySession.id === session.id
|
|
114
112
|
);
|
|
113
|
+
if (channelEntryIndex === -1) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
115
116
|
const channelEntry = fabricChannels.splice(channelEntryIndex, 1)[0];
|
|
116
117
|
if (channelEntry === void 0) {
|
|
117
118
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/protocol/ChannelManager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Channel } from \"../common/Channel.js\";\nimport { MatterError } from \"../common/MatterError.js\";\nimport { NodeId } from \"../datatype/NodeId.js\";\nimport { Fabric } from \"../fabric/Fabric.js\";\nimport { Logger } from \"../log/Logger.js\";\nimport { SecureSession } from \"../session/SecureSession.js\";\nimport { Session } from \"../session/Session.js\";\nimport { ByteArray } from \"../util/ByteArray.js\";\nimport { MessageChannel } from \"./ExchangeManager.js\";\n\nconst logger = Logger.get(\"ChannelManager\");\n\nexport class NoChannelError extends MatterError {}\n\nexport class ChannelManager {\n readonly #channels = new Map<string, MessageChannel<any>[]>();\n readonly #paseChannels = new Map<Session<any>, MessageChannel<any>>();\n readonly #caseSessionsPerFabricAndNode: number;\n\n // TODO evaluate with controller the effects of limiting the entries just for FabricIndex and not also NodeId\n constructor(caseSessionsPerFabricAndNode = 3) {\n this.#caseSessionsPerFabricAndNode = caseSessionsPerFabricAndNode;\n }\n\n #getChannelKey(fabric: Fabric, nodeId: NodeId) {\n return `${fabric.fabricIndex}/${nodeId}`;\n }\n\n #findLeastActiveChannel(channels: MessageChannel<any>[]) {\n let oldest = channels[0];\n for (const channel of channels) {\n if (channel.session.timestamp < oldest.session.timestamp) {\n oldest = channel;\n }\n }\n return oldest;\n }\n\n async setChannel(fabric: Fabric, nodeId: NodeId, channel: MessageChannel<any>) {\n channel.closeCallback = async () => this.removeChannel(fabric, nodeId, channel.session);\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const currentChannels = this.#channels.get(channelsKey) ?? [];\n if (currentChannels.length
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,yBAA4B;AAG5B,oBAAuB;AAIvB,6BAA+B;AAd/B;AAAA;AAAA;AAAA;AAAA;AAgBA,MAAM,SAAS,qBAAO,IAAI,gBAAgB;AAEnC,MAAM,uBAAuB,+BAAY;AAAC;AAE1C,MAAM,eAAe;AAAA,EACf,YAAY,oBAAI,IAAmC;AAAA,EACnD,gBAAgB,oBAAI,IAAuC;AAAA,EAC3D;AAAA;AAAA,EAGT,YAAY,+BAA+B,GAAG;AAC1C,SAAK,gCAAgC;AAAA,EACzC;AAAA,EAEA,eAAe,QAAgB,QAAgB;AAC3C,WAAO,GAAG,OAAO,WAAW,IAAI,MAAM;AAAA,EAC1C;AAAA,EAEA,wBAAwB,UAAiC;AACrD,QAAI,SAAS,SAAS,CAAC;AACvB,eAAW,WAAW,UAAU;AAC5B,UAAI,QAAQ,QAAQ,YAAY,OAAO,QAAQ,WAAW;AACtD,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,WAAW,QAAgB,QAAgB,SAA8B;AAC3E,YAAQ,gBAAgB,YAAY,KAAK,cAAc,QAAQ,QAAQ,QAAQ,OAAO;AACtF,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,kBAAkB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC5D,
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Channel } from \"../common/Channel.js\";\nimport { MatterError } from \"../common/MatterError.js\";\nimport { NodeId } from \"../datatype/NodeId.js\";\nimport { Fabric } from \"../fabric/Fabric.js\";\nimport { Logger } from \"../log/Logger.js\";\nimport { SecureSession } from \"../session/SecureSession.js\";\nimport { Session } from \"../session/Session.js\";\nimport { ByteArray } from \"../util/ByteArray.js\";\nimport { MessageChannel } from \"./ExchangeManager.js\";\n\nconst logger = Logger.get(\"ChannelManager\");\n\nexport class NoChannelError extends MatterError {}\n\nexport class ChannelManager {\n readonly #channels = new Map<string, MessageChannel<any>[]>();\n readonly #paseChannels = new Map<Session<any>, MessageChannel<any>>();\n readonly #caseSessionsPerFabricAndNode: number;\n\n // TODO evaluate with controller the effects of limiting the entries just for FabricIndex and not also NodeId\n constructor(caseSessionsPerFabricAndNode = 3) {\n this.#caseSessionsPerFabricAndNode = caseSessionsPerFabricAndNode;\n }\n\n #getChannelKey(fabric: Fabric, nodeId: NodeId) {\n return `${fabric.fabricIndex}/${nodeId}`;\n }\n\n #findLeastActiveChannel(channels: MessageChannel<any>[]) {\n let oldest = channels[0];\n for (const channel of channels) {\n if (channel.session.timestamp < oldest.session.timestamp) {\n oldest = channel;\n }\n }\n return oldest;\n }\n\n async setChannel(fabric: Fabric, nodeId: NodeId, channel: MessageChannel<any>) {\n channel.closeCallback = async () => this.removeChannel(fabric, nodeId, channel.session);\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const currentChannels = this.#channels.get(channelsKey) ?? [];\n currentChannels.push(channel);\n this.#channels.set(channelsKey, currentChannels);\n if (currentChannels.length > this.#caseSessionsPerFabricAndNode) {\n const oldestChannel = this.#findLeastActiveChannel(currentChannels);\n\n const { session: oldSession } = oldestChannel;\n // Should always be the case\n if (channel.session.id !== oldSession.id) {\n await oldSession.destroy(false, false);\n }\n logger.info(\n `Close oldest channel for fabric ${fabric.fabricIndex} node ${nodeId} (from session ${oldSession.id})`,\n );\n await oldestChannel.close();\n }\n }\n\n getChannel(fabric: Fabric, nodeId: NodeId, session?: Session<any>) {\n let results = this.#channels.get(this.#getChannelKey(fabric, nodeId)) ?? [];\n if (session !== undefined) {\n results = results.filter(channel => channel.session.id === session.id);\n }\n if (results.length === 0)\n throw new NoChannelError(\n `Can't find a channel to node ${nodeId}${session !== undefined ? ` and session ${session.id}` : \"\"}`,\n );\n return results[results.length - 1]; // Return the latest added channel (or the one belonging to the session requested)\n }\n\n /**\n * Returns the last established session for a Fabric and Node\n */\n getChannelForSession(session: Session<any>) {\n if (session.isSecure && !session.isPase) {\n const secureSession = session as SecureSession<any>;\n const fabric = secureSession.fabric;\n const nodeId = secureSession.peerNodeId;\n if (fabric === undefined) {\n return this.#paseChannels.get(session);\n }\n return this.getChannel(fabric, nodeId, session);\n }\n return this.#paseChannels.get(session);\n }\n\n async removeAllNodeChannels(fabric: Fabric, nodeId: NodeId) {\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const channelsToRemove = this.#channels.get(channelsKey) ?? [];\n for (const channel of channelsToRemove) {\n await channel.close();\n }\n }\n\n async removeChannel(fabric: Fabric, nodeId: NodeId, session: Session<any>) {\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const fabricChannels = this.#channels.get(channelsKey) ?? [];\n const channelEntryIndex = fabricChannels.findIndex(\n ({ session: entrySession }) => entrySession.id === session.id,\n );\n if (channelEntryIndex === -1) {\n // Seems already removed\n return;\n }\n const channelEntry = fabricChannels.splice(channelEntryIndex, 1)[0];\n if (channelEntry === undefined) {\n return;\n }\n await channelEntry.close();\n this.#channels.set(channelsKey, fabricChannels);\n }\n\n private getOrCreateAsPaseChannel(byteArrayChannel: Channel<ByteArray>, session: Session<any>) {\n const msgChannel = new MessageChannel(\n byteArrayChannel,\n session,\n async () => void this.#paseChannels.delete(session),\n );\n this.#paseChannels.set(session, msgChannel);\n return msgChannel;\n }\n\n async getOrCreateChannel(byteArrayChannel: Channel<ByteArray>, session: Session<any>) {\n if (!session.isSecure) {\n return this.getOrCreateAsPaseChannel(byteArrayChannel, session);\n }\n const secureSession = session as SecureSession<any>;\n const fabric = secureSession.fabric;\n const nodeId = secureSession.peerNodeId;\n if (fabric === undefined) {\n return this.getOrCreateAsPaseChannel(byteArrayChannel, session);\n }\n\n // Try to get\n try {\n return this.getChannel(fabric, nodeId, session);\n } catch (e) {\n NoChannelError.accept(e);\n }\n\n // Need to create\n const result = new MessageChannel(byteArrayChannel, session, async () =>\n this.removeChannel(fabric, nodeId, session),\n );\n await this.setChannel(fabric, nodeId, result);\n return result;\n }\n\n async close() {\n for (const channel of this.#paseChannels.values()) {\n await channel.close();\n }\n this.#paseChannels.clear();\n for (const channels of this.#channels.values()) {\n for (const channel of channels) {\n await channel.close();\n }\n }\n this.#channels.clear();\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,yBAA4B;AAG5B,oBAAuB;AAIvB,6BAA+B;AAd/B;AAAA;AAAA;AAAA;AAAA;AAgBA,MAAM,SAAS,qBAAO,IAAI,gBAAgB;AAEnC,MAAM,uBAAuB,+BAAY;AAAC;AAE1C,MAAM,eAAe;AAAA,EACf,YAAY,oBAAI,IAAmC;AAAA,EACnD,gBAAgB,oBAAI,IAAuC;AAAA,EAC3D;AAAA;AAAA,EAGT,YAAY,+BAA+B,GAAG;AAC1C,SAAK,gCAAgC;AAAA,EACzC;AAAA,EAEA,eAAe,QAAgB,QAAgB;AAC3C,WAAO,GAAG,OAAO,WAAW,IAAI,MAAM;AAAA,EAC1C;AAAA,EAEA,wBAAwB,UAAiC;AACrD,QAAI,SAAS,SAAS,CAAC;AACvB,eAAW,WAAW,UAAU;AAC5B,UAAI,QAAQ,QAAQ,YAAY,OAAO,QAAQ,WAAW;AACtD,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,WAAW,QAAgB,QAAgB,SAA8B;AAC3E,YAAQ,gBAAgB,YAAY,KAAK,cAAc,QAAQ,QAAQ,QAAQ,OAAO;AACtF,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,kBAAkB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC5D,oBAAgB,KAAK,OAAO;AAC5B,SAAK,UAAU,IAAI,aAAa,eAAe;AAC/C,QAAI,gBAAgB,SAAS,KAAK,+BAA+B;AAC7D,YAAM,gBAAgB,KAAK,wBAAwB,eAAe;AAElE,YAAM,EAAE,SAAS,WAAW,IAAI;AAEhC,UAAI,QAAQ,QAAQ,OAAO,WAAW,IAAI;AACtC,cAAM,WAAW,QAAQ,OAAO,KAAK;AAAA,MACzC;AACA,aAAO;AAAA,QACH,mCAAmC,OAAO,WAAW,SAAS,MAAM,kBAAkB,WAAW,EAAE;AAAA,MACvG;AACA,YAAM,cAAc,MAAM;AAAA,IAC9B;AAAA,EACJ;AAAA,EAEA,WAAW,QAAgB,QAAgB,SAAwB;AAC/D,QAAI,UAAU,KAAK,UAAU,IAAI,KAAK,eAAe,QAAQ,MAAM,CAAC,KAAK,CAAC;AAC1E,QAAI,YAAY,QAAW;AACvB,gBAAU,QAAQ,OAAO,aAAW,QAAQ,QAAQ,OAAO,QAAQ,EAAE;AAAA,IACzE;AACA,QAAI,QAAQ,WAAW;AACnB,YAAM,IAAI;AAAA,QACN,gCAAgC,MAAM,GAAG,YAAY,SAAY,gBAAgB,QAAQ,EAAE,KAAK,EAAE;AAAA,MACtG;AACJ,WAAO,QAAQ,QAAQ,SAAS,CAAC;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,SAAuB;AACxC,QAAI,QAAQ,YAAY,CAAC,QAAQ,QAAQ;AACrC,YAAM,gBAAgB;AACtB,YAAM,SAAS,cAAc;AAC7B,YAAM,SAAS,cAAc;AAC7B,UAAI,WAAW,QAAW;AACtB,eAAO,KAAK,cAAc,IAAI,OAAO;AAAA,MACzC;AACA,aAAO,KAAK,WAAW,QAAQ,QAAQ,OAAO;AAAA,IAClD;AACA,WAAO,KAAK,cAAc,IAAI,OAAO;AAAA,EACzC;AAAA,EAEA,MAAM,sBAAsB,QAAgB,QAAgB;AACxD,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,mBAAmB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC7D,eAAW,WAAW,kBAAkB;AACpC,YAAM,QAAQ,MAAM;AAAA,IACxB;AAAA,EACJ;AAAA,EAEA,MAAM,cAAc,QAAgB,QAAgB,SAAuB;AACvE,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,iBAAiB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC3D,UAAM,oBAAoB,eAAe;AAAA,MACrC,CAAC,EAAE,SAAS,aAAa,MAAM,aAAa,OAAO,QAAQ;AAAA,IAC/D;AACA,QAAI,sBAAsB,IAAI;AAE1B;AAAA,IACJ;AACA,UAAM,eAAe,eAAe,OAAO,mBAAmB,CAAC,EAAE,CAAC;AAClE,QAAI,iBAAiB,QAAW;AAC5B;AAAA,IACJ;AACA,UAAM,aAAa,MAAM;AACzB,SAAK,UAAU,IAAI,aAAa,cAAc;AAAA,EAClD;AAAA,EAEQ,yBAAyB,kBAAsC,SAAuB;AAC1F,UAAM,aAAa,IAAI;AAAA,MACnB;AAAA,MACA;AAAA,MACA,YAAY,KAAK,KAAK,cAAc,OAAO,OAAO;AAAA,IACtD;AACA,SAAK,cAAc,IAAI,SAAS,UAAU;AAC1C,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,mBAAmB,kBAAsC,SAAuB;AAClF,QAAI,CAAC,QAAQ,UAAU;AACnB,aAAO,KAAK,yBAAyB,kBAAkB,OAAO;AAAA,IAClE;AACA,UAAM,gBAAgB;AACtB,UAAM,SAAS,cAAc;AAC7B,UAAM,SAAS,cAAc;AAC7B,QAAI,WAAW,QAAW;AACtB,aAAO,KAAK,yBAAyB,kBAAkB,OAAO;AAAA,IAClE;AAGA,QAAI;AACA,aAAO,KAAK,WAAW,QAAQ,QAAQ,OAAO;AAAA,IAClD,SAAS,GAAG;AACR,qBAAe,OAAO,CAAC;AAAA,IAC3B;AAGA,UAAM,SAAS,IAAI;AAAA,MAAe;AAAA,MAAkB;AAAA,MAAS,YACzD,KAAK,cAAc,QAAQ,QAAQ,OAAO;AAAA,IAC9C;AACA,UAAM,KAAK,WAAW,QAAQ,QAAQ,MAAM;AAC5C,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,QAAQ;AACV,eAAW,WAAW,KAAK,cAAc,OAAO,GAAG;AAC/C,YAAM,QAAQ,MAAM;AAAA,IACxB;AACA,SAAK,cAAc,MAAM;AACzB,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC5C,iBAAW,WAAW,UAAU;AAC5B,cAAM,QAAQ,MAAM;AAAA,MACxB;AAAA,IACJ;AACA,SAAK,UAAU,MAAM;AAAA,EACzB;AACJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionHandler.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/SubscriptionHandler.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SubscriptionHandler.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/SubscriptionHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -111,7 +111,11 @@ class SubscriptionHandler {
|
|
|
111
111
|
this.#maxIntervalMs = maxInterval;
|
|
112
112
|
this.#sendIntervalMs = sendInterval;
|
|
113
113
|
this.updateTimer = import_Time.Time.getTimer("Subscription update", this.#sendIntervalMs, () => this.prepareDataUpdate());
|
|
114
|
-
this.sendDelayTimer = import_Time.Time.getTimer(
|
|
114
|
+
this.sendDelayTimer = import_Time.Time.getTimer(
|
|
115
|
+
"Subscription delay",
|
|
116
|
+
50,
|
|
117
|
+
() => this.sendUpdate().catch((error) => logger.warn("Sending subscription update failed:", error))
|
|
118
|
+
);
|
|
115
119
|
}
|
|
116
120
|
determineSendingIntervals(subscriptionMinIntervalMs, subscriptionMaxIntervalMs, subscriptionRandomizationWindowMs) {
|
|
117
121
|
const maxInterval = Math.min(
|
|
@@ -669,13 +673,13 @@ class SubscriptionHandler {
|
|
|
669
673
|
this.isFabricFiltered
|
|
670
674
|
);
|
|
671
675
|
}
|
|
672
|
-
} catch (
|
|
673
|
-
if (import_StatusCode.StatusResponseError.is(
|
|
676
|
+
} catch (error) {
|
|
677
|
+
if (import_StatusCode.StatusResponseError.is(error, import_StatusCode.StatusCode.InvalidSubscription, import_StatusCode.StatusCode.Failure)) {
|
|
674
678
|
logger.info(`Subscription ${this.subscriptionId} cancelled by peer.`);
|
|
675
679
|
await this.cancel(false, true);
|
|
676
680
|
} else {
|
|
681
|
+
import_StatusCode.StatusResponseError.accept(error);
|
|
677
682
|
await this.cancel(false);
|
|
678
|
-
throw e;
|
|
679
683
|
}
|
|
680
684
|
} finally {
|
|
681
685
|
await messenger.close();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/protocol/interaction/SubscriptionHandler.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { AnyAttributeServer, FabricScopedAttributeServer } from \"../../cluster/server/AttributeServer.js\";\nimport { AnyEventServer, FabricSensitiveEventServer } from \"../../cluster/server/EventServer.js\";\nimport { InternalError } from \"../../common/MatterError.js\";\nimport { EventNumber } from \"../../datatype/EventNumber.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { NetworkError } from \"../../net/Network.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { Time, Timer } from \"../../time/Time.js\";\nimport { TlvSchema, TypeFromSchema } from \"../../tlv/TlvSchema.js\";\nimport { MaybePromise } from \"../../util/Promises.js\";\nimport { isObject } from \"../../util/Type.js\";\nimport { RetransmissionLimitReachedError } from \"../MessageExchange.js\";\nimport { AttributeReportPayload, EventReportPayload } from \"./AttributeDataEncoder.js\";\nimport { EventStorageData } from \"./EventHandler.js\";\nimport { InteractionEndpointStructure } from \"./InteractionEndpointStructure.js\";\nimport { InteractionServerMessenger } from \"./InteractionMessenger.js\";\nimport {\n TlvAttributePath,\n TlvAttributeStatus,\n TlvDataVersionFilter,\n TlvEventFilter,\n TlvEventPath,\n TlvEventStatus,\n} from \"./InteractionProtocol.js\";\nimport {\n AttributePath,\n AttributeWithPath,\n EventPath,\n EventWithPath,\n INTERACTION_MODEL_REVISION,\n INTERACTION_PROTOCOL_ID,\n attributePathToId,\n clusterPathToId,\n eventPathToId,\n} from \"./InteractionServer.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\nimport { MAX_INTERVAL_PUBLISHER_LIMIT_S, SubscriptionOptions } from \"./SubscriptionOptions.js\";\n\nconst logger = Logger.get(\"SubscriptionHandler\");\n\ninterface AttributePathWithValueVersion<T> {\n path: TypeFromSchema<typeof TlvAttributePath>;\n attribute: AnyAttributeServer<T>;\n schema: TlvSchema<T>;\n value: T;\n version: number;\n}\n\ninterface EventPathWithEventData<T> {\n path: TypeFromSchema<typeof TlvEventPath>;\n event: AnyEventServer<any, any>;\n schema: TlvSchema<T>;\n data: EventStorageData<T>;\n}\n\nexport class SubscriptionHandler {\n readonly subscriptionId: number;\n private readonly session: SecureSession<any>;\n private readonly endpointStructure: InteractionEndpointStructure;\n private readonly attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n private readonly dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n private readonly eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n private readonly eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n private readonly isFabricFiltered: boolean;\n private readonly cancelCallback: () => void;\n private readonly readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n private readonly readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n\n private lastUpdateTimeMs = 0;\n private updateTimer: Timer;\n private readonly sendDelayTimer: Timer;\n private readonly outstandingAttributeUpdates = new Map<string, AttributePathWithValueVersion<any>>();\n private readonly outstandingEventUpdates = new Set<EventPathWithEventData<any>>();\n private readonly attributeListeners = new Map<\n string,\n {\n attribute: AnyAttributeServer<any>;\n listener?: (value: any, version: number) => void;\n }\n >();\n private readonly eventListeners = new Map<\n string,\n {\n event: AnyEventServer<any, any>;\n listener?: (newEvent: EventStorageData<any>) => void;\n }\n >();\n private sendUpdatesActivated = false;\n readonly #maxIntervalMs: number;\n readonly #sendIntervalMs: number;\n private readonly minIntervalFloorMs: number;\n private readonly maxIntervalCeilingMs: number;\n private readonly server: MatterDevice;\n private readonly fabric: Fabric;\n private readonly peerNodeId: NodeId;\n\n private sendingUpdateInProgress = false;\n private sendNextUpdateImmediately = false;\n private sendUpdateErrorCounter = 0;\n private attributeUpdatePromises = new Set<PromiseLike<void>>();\n\n constructor(options: {\n subscriptionId: number;\n session: SecureSession<any>;\n endpointStructure: InteractionEndpointStructure;\n attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n isFabricFiltered: boolean;\n minIntervalFloor: number;\n maxIntervalCeiling: number;\n cancelCallback: () => void;\n subscriptionOptions: SubscriptionOptions.Configuration;\n readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n }) {\n const {\n subscriptionId,\n session,\n endpointStructure,\n attributeRequests,\n dataVersionFilters,\n eventRequests,\n eventFilters,\n isFabricFiltered,\n minIntervalFloor,\n maxIntervalCeiling,\n cancelCallback,\n subscriptionOptions,\n } = options;\n this.subscriptionId = subscriptionId;\n this.session = session;\n this.endpointStructure = endpointStructure;\n this.attributeRequests = attributeRequests;\n this.dataVersionFilters = dataVersionFilters;\n this.eventRequests = eventRequests;\n this.eventFilters = eventFilters;\n this.isFabricFiltered = isFabricFiltered;\n this.cancelCallback = cancelCallback;\n this.readAttribute = options.readAttribute;\n this.readEvent = options.readEvent;\n\n this.server = this.session.context;\n this.fabric = this.session.associatedFabric;\n this.peerNodeId = this.session.peerNodeId;\n this.minIntervalFloorMs = minIntervalFloor * 1000;\n this.maxIntervalCeilingMs = maxIntervalCeiling * 1000;\n\n const { maxInterval, sendInterval } = this.determineSendingIntervals(\n subscriptionOptions.minIntervalSeconds * 1000,\n subscriptionOptions.maxIntervalSeconds * 1000,\n subscriptionOptions.randomizationWindowSeconds * 1000,\n );\n this.#maxIntervalMs = maxInterval;\n this.#sendIntervalMs = sendInterval;\n\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () => this.prepareDataUpdate()); // will be started later\n this.sendDelayTimer = Time.getTimer(\"Subscription delay\", 50, () => this.sendUpdate()); // will be started later\n }\n\n private determineSendingIntervals(\n subscriptionMinIntervalMs: number,\n subscriptionMaxIntervalMs: number,\n subscriptionRandomizationWindowMs: number,\n ): { maxInterval: number; sendInterval: number } {\n // Max Interval is the Max interval that the controller request, unless the configured one from the developer\n // is lower. In that case we use the configured one. But we make sure to not be smaller than the requested\n // controller minimum. But in general never faster than minimum interval configured or 2 seconds\n // (SUBSCRIPTION_MIN_INTERVAL_S). Additionally, we add a randomization window to the max interval to avoid all\n // devices sending at the same time. But we make sure not to exceed the global max interval.\n const maxInterval = Math.min(\n Math.max(\n subscriptionMinIntervalMs,\n Math.max(this.minIntervalFloorMs, Math.min(subscriptionMaxIntervalMs, this.maxIntervalCeilingMs)),\n ) + Math.floor(subscriptionRandomizationWindowMs * Math.random()),\n MAX_INTERVAL_PUBLISHER_LIMIT_S * 1000,\n );\n let sendInterval = Math.floor(maxInterval / 2); // Ideally we send at half the max interval\n if (sendInterval < 60_000) {\n // But if we have no chance of at least one full resubmission process we do like chip-tool.\n // One full resubmission process takes 33-45 seconds. So 60s means we reach at least first 2 retries of a\n // second subscription report after first failed.\n sendInterval = Math.max(this.minIntervalFloorMs, Math.floor(maxInterval * 0.8));\n }\n if (sendInterval < subscriptionMinIntervalMs) {\n // But not faster than once every 2s\n logger.warn(\n `Determined subscription send interval of ${sendInterval}ms is too low. Using maxInterval (${maxInterval}ms) instead.`,\n );\n sendInterval = subscriptionMinIntervalMs;\n }\n return { maxInterval, sendInterval };\n }\n\n private registerNewAttributes() {\n const newAttributes = new Array<AttributeWithPath>();\n const attributeErrors = new Array<TypeFromSchema<typeof TlvAttributeStatus>>();\n const formerAttributes = new Set<string>(this.attributeListeners.keys());\n\n if (this.attributeRequests !== undefined) {\n this.attributeRequests.forEach(path => {\n const attributes = this.endpointStructure.getAttributes([path]);\n\n if (attributes.length === 0) {\n // TODO: Also check nodeId\n const { endpointId, clusterId, attributeId } = path;\n if (endpointId === undefined || clusterId === undefined || attributeId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: ignore non-existing attribute`,\n );\n } else {\n // was a concrete path\n try {\n this.endpointStructure.validateConcreteAttributePath(endpointId, clusterId, attributeId);\n throw new InternalError(\n \"validateConcreteAttributePath check should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n attributeErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n attributes.forEach(({ path, attribute }) => {\n formerAttributes.delete(attributePathToId(path));\n\n const existingAttributeListener = this.attributeListeners.get(attributePathToId(path));\n if (existingAttributeListener !== undefined) {\n const { attribute: existingAttribute, listener: existingListener } = existingAttributeListener;\n if (existingAttribute !== attribute) {\n if (existingListener !== undefined) {\n existingAttribute.removeValueChangeListener(existingListener);\n }\n this.attributeListeners.delete(attributePathToId(path));\n } else {\n return; // Attribute is already registered and unchanged\n }\n }\n if (attribute.isSubscribable) {\n // If subscribable register listener\n // TODO: Move to state change listeners from behaviors to remove the dangling promise here\n const listener = (value: any, version: number) =>\n this.attributeChangeListener(path, attribute.schema, version, value);\n attribute.addValueChangeListener(listener);\n this.attributeListeners.set(attributePathToId(path), { attribute, listener });\n } else {\n this.attributeListeners.set(attributePathToId(path), { attribute });\n }\n newAttributes.push({ path, attribute });\n });\n });\n }\n\n // Remove all listeners to attributes that no longer match the subscription\n this.unregisterAttributeListeners(Array.from(formerAttributes.values()));\n return { newAttributes, attributeErrors };\n }\n\n unregisterAttributeListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingAttributeListener = this.attributeListeners.get(pathId);\n if (existingAttributeListener !== undefined) {\n const { attribute, listener } = existingAttributeListener;\n if (listener !== undefined) {\n attribute.removeValueChangeListener(listener);\n }\n this.attributeListeners.delete(pathId);\n }\n }\n }\n\n private registerNewEvents() {\n const newEvents = new Array<EventWithPath>();\n const eventErrors = new Array<TypeFromSchema<typeof TlvEventStatus>>();\n const formerEvents = new Set<string>(this.eventListeners.keys());\n\n if (this.eventRequests !== undefined) {\n this.eventRequests.forEach(path => {\n const events = this.endpointStructure.getEvents([path]);\n if (events.length === 0) {\n const { endpointId, clusterId, eventId } = path;\n if (endpointId === undefined || clusterId === undefined || eventId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: ignore non-existing event`,\n );\n } else {\n try {\n this.endpointStructure.validateConcreteEventPath(endpointId, clusterId, eventId);\n throw new InternalError(\n \"validateConcreteEventPath should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n eventErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n events.forEach(({ path, event }) => {\n formerEvents.delete(eventPathToId(path));\n\n const existingEventListener = this.eventListeners.get(eventPathToId(path));\n if (existingEventListener !== undefined) {\n const { event: existingEvent, listener: existingListener } = existingEventListener;\n if (existingEvent !== event) {\n if (existingListener !== undefined) {\n existingEvent.removeListener(existingListener);\n }\n this.eventListeners.delete(eventPathToId(path));\n } else {\n return; // Event is already registered and unchanged\n }\n }\n const listener = (newEvent: EventStorageData<any>) =>\n this.eventChangeListener(path, event.schema, newEvent);\n event.addListener(listener);\n newEvents.push({ path, event });\n this.eventListeners.set(eventPathToId(path), { event, listener });\n });\n });\n }\n\n // Remove all listeners to events that no longer match the subscription\n this.unregisterEventListeners(Array.from(formerEvents.values()));\n\n return { newEvents, eventErrors };\n }\n\n unregisterEventListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingEventListener = this.eventListeners.get(pathId);\n if (existingEventListener !== undefined) {\n const { event, listener } = existingEventListener;\n if (listener !== undefined) {\n event.removeListener(listener);\n }\n this.eventListeners.delete(pathId);\n }\n }\n }\n\n /**\n * Update the session after an endpoint structure change. The method will initialize all missing new attributes and\n * events and will remove listeners no longer needed.\n * Newly added attributes are then treated ad \"changed values\" and will be sent as subscription data update to the\n * controller. The data of newly added events are not sent automatically.\n */\n async updateSubscription() {\n const { newAttributes } = this.registerNewAttributes();\n\n for (const { path, attribute } of newAttributes) {\n const { version, value } = await this.readAttribute(path, attribute);\n\n // We do not do any version filtering for attributes that are newly added to make sure controller gets\n // most current state\n\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema: attribute.schema,\n version,\n value,\n });\n }\n\n const { newEvents } = this.registerNewEvents();\n newEvents\n .flatMap(({ path, event }): EventPathWithEventData<any>[] => {\n // But we use eventFilters because we do not want to send all events to the controller\n const { schema } = event;\n const matchingEvents = event.get(this.session, this.isFabricFiltered, undefined, this.eventFilters);\n return matchingEvents.map(data => ({\n event,\n schema,\n path,\n data,\n }));\n })\n .sort((a, b) => {\n const eventNumberA = a.data?.eventNumber ?? EventNumber(0);\n const eventNumberB = b.data?.eventNumber ?? EventNumber(0);\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n })\n .forEach(event => this.outstandingEventUpdates.add(event));\n\n this.prepareDataUpdate();\n }\n\n get maxInterval(): number {\n return Math.ceil(this.#maxIntervalMs / 1000);\n }\n\n get sendInterval(): number {\n return Math.ceil(this.#sendIntervalMs / 1000);\n }\n\n activateSendingUpdates() {\n // We do not need these data anymore, so we can free some memory\n if (this.eventFilters !== undefined) this.eventFilters.length = 0;\n if (this.dataVersionFilters !== undefined) this.dataVersionFilters.length = 0;\n\n this.sendUpdatesActivated = true;\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Check if data should be sent straight away or delayed because the minimum interval is not reached. Delay real\n * sending by 50ms in any case to mke sure to catch all updates.\n */\n prepareDataUpdate() {\n if (this.sendDelayTimer.isRunning) {\n // sending data is already scheduled, data updates go in there\n return;\n }\n\n if (!this.sendUpdatesActivated) {\n return;\n }\n\n this.updateTimer.stop();\n const now = Time.nowMs();\n const timeSinceLastUpdateMs = now - this.lastUpdateTimeMs;\n if (timeSinceLastUpdateMs < this.minIntervalFloorMs) {\n // Respect minimum delay time between updates\n this.updateTimer = Time.getTimer(\n \"Subscription update\",\n this.minIntervalFloorMs - timeSinceLastUpdateMs,\n () => this.prepareDataUpdate(),\n ).start();\n return;\n }\n\n this.sendDelayTimer.start();\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Determine all attributes that have changed since the last update and send them tout to the subscriber.\n */\n async sendUpdate() {\n if (this.sendingUpdateInProgress) {\n logger.debug(\"Sending update already in progress, delaying update ...\");\n this.sendNextUpdateImmediately = true;\n return;\n }\n\n // Get all outstanding updates, make sure the order is correct per endpoint and cluster\n const attributeUpdatesToSend = new Array<AttributePathWithValueVersion<any>>();\n const attributeUpdates: Record<string, AttributePathWithValueVersion<any>[]> = {};\n Array.from(this.outstandingAttributeUpdates.values()).forEach(entry => {\n const {\n path: { nodeId, endpointId, clusterId },\n } = entry;\n const pathId = `${nodeId}-${endpointId}-${clusterId}`;\n attributeUpdates[pathId] = attributeUpdates[pathId] ?? [];\n attributeUpdates[pathId].push(entry);\n });\n this.outstandingAttributeUpdates.clear();\n Object.values(attributeUpdates).forEach(data =>\n attributeUpdatesToSend.push(\n ...data.sort(({ version: versionA }, { version: versionB }) => versionA - versionB),\n ),\n );\n\n const eventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n this.lastUpdateTimeMs = Time.nowMs();\n\n this.sendingUpdateInProgress = true;\n try {\n await this.sendUpdateMessage(attributeUpdatesToSend, eventUpdatesToSend);\n this.sendUpdateErrorCounter = 0;\n } catch (error) {\n if (this.server.isClosing) {\n // No need to care about resubmissions when the server is closing\n return;\n }\n\n this.sendUpdateErrorCounter++;\n logger.error(\n `Error sending subscription update message (error count=${this.sendUpdateErrorCounter}):`,\n error,\n );\n if (this.sendUpdateErrorCounter <= 2) {\n // fill the data back in the queue to resend with next try\n const newAttributeUpdatesToSend = Array.from(this.outstandingAttributeUpdates.values());\n this.outstandingAttributeUpdates.clear();\n const newEventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n [...attributeUpdatesToSend, ...newAttributeUpdatesToSend].forEach(update =>\n this.outstandingAttributeUpdates.set(attributePathToId(update.path), update),\n );\n [...eventUpdatesToSend, ...newEventUpdatesToSend].forEach(update =>\n this.outstandingEventUpdates.add(update),\n );\n } else {\n logger.error(\n `Sending update failed 3 times in a row, canceling subscription ${this.subscriptionId} and let controller subscribe again.`,\n );\n this.sendNextUpdateImmediately = false;\n if (error instanceof RetransmissionLimitReachedError || error instanceof NetworkError) {\n // We could not send at all, consider session as dead\n await this.session.destroy(false);\n } else {\n throw error;\n }\n }\n }\n this.sendingUpdateInProgress = false;\n\n if (this.sendNextUpdateImmediately) {\n logger.debug(\"Sending delayed update immediately after last one was sent.\");\n this.sendNextUpdateImmediately = false;\n await this.sendUpdate();\n }\n }\n\n async sendInitialReport(messenger: InteractionServerMessenger) {\n this.updateTimer.stop();\n\n const { newAttributes, attributeErrors } = this.registerNewAttributes();\n\n const dataVersionFilterMap = new Map<string, number>(\n this.dataVersionFilters?.map(({ path, dataVersion }) => [clusterPathToId(path), dataVersion]) ?? [],\n );\n\n let attributesFilteredWithVersion = false;\n const attributes = new Array<{\n path: TypeFromSchema<typeof TlvAttributePath>;\n value: any;\n version: number;\n schema: TlvSchema<any>;\n attribute: AnyAttributeServer<any>;\n }>();\n for (const { path, attribute } of newAttributes) {\n try {\n const { value, version } = await this.readAttribute(path, attribute);\n if (value === undefined) continue;\n\n const { nodeId, endpointId, clusterId } = path;\n\n const versionFilterValue =\n endpointId !== undefined && clusterId !== undefined\n ? dataVersionFilterMap.get(clusterPathToId({ nodeId, endpointId, clusterId }))\n : undefined;\n if (versionFilterValue !== undefined && versionFilterValue === version) {\n attributesFilteredWithVersion = true;\n continue;\n }\n\n attributes.push({ path, value, version, schema: attribute.schema, attribute });\n } catch (error) {\n logger.error(`Error reading attribute ${this.endpointStructure.resolveAttributeName(path)}:`, error);\n }\n }\n const attributeReportsPayload: AttributeReportPayload[] = attributes.map(\n ({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n payload: value,\n schema,\n },\n }),\n );\n attributeErrors.forEach(attributeStatus =>\n attributeReportsPayload.push({\n hasFabricSensitiveData: false,\n attributeStatus,\n }),\n );\n\n const { newEvents, eventErrors } = this.registerNewEvents();\n\n let eventsFiltered = false;\n const eventReportsPayload = new Array<EventReportPayload>();\n for (const { path, event } of newEvents) {\n const { schema } = event;\n try {\n const matchingEvents = await this.readEvent(path, event, this.eventFilters);\n if (matchingEvents.length === 0) {\n eventsFiltered = true;\n } else {\n matchingEvents.forEach(({ eventNumber, priority, epochTimestamp, data }) => {\n eventReportsPayload.push({\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n payload: data,\n schema,\n },\n });\n });\n }\n } catch (error) {\n logger.error(`Error reading event ${this.endpointStructure.resolveEventName(path)}:`, error);\n }\n }\n eventReportsPayload.sort((a, b) => {\n const eventNumberA = a.eventData?.eventNumber ?? 0;\n const eventNumberB = b.eventData?.eventNumber ?? 0;\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n });\n\n if (\n attributes.length === 0 &&\n !attributesFilteredWithVersion &&\n eventReportsPayload.length === 0 &&\n !eventsFiltered\n ) {\n throw new StatusResponseError(\n \"Subscription failed because no attributes or events are matching the query\",\n StatusCode.InvalidAction,\n );\n }\n\n eventErrors.forEach(eventStatus =>\n eventReportsPayload.push({\n hasFabricSensitiveData: false,\n eventStatus,\n }),\n );\n\n logger.debug(\n `Initialize Subscription with ${attributes.length} attributes and ${eventReportsPayload.length} events.`,\n );\n this.lastUpdateTimeMs = Time.nowMs();\n\n await messenger.sendDataReport(\n {\n suppressResponse: false, // we always need proper response for initial report\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload,\n eventReportsPayload,\n },\n this.isFabricFiltered,\n );\n }\n\n attributeChangeListener<T>(path: AttributePath, schema: TlvSchema<T>, version: number, value: T) {\n const changeResult = this.attributeChangeHandler(path, schema, version, value);\n if (MaybePromise.is(changeResult)) {\n const resolver = Promise.resolve(changeResult)\n .catch(error => logger.error(`Error handling attribute change:`, error))\n .finally(() => this.attributeUpdatePromises.delete(resolver));\n this.attributeUpdatePromises.add(resolver);\n }\n }\n\n attributeChangeHandler<T>(\n path: AttributePath,\n schema: TlvSchema<T>,\n version: number,\n value: T,\n ): MaybePromise<void> {\n const attributeListenerData = this.attributeListeners.get(attributePathToId(path));\n if (attributeListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { attribute } = attributeListenerData;\n if (attribute instanceof FabricScopedAttributeServer) {\n // We cannot be sure what value we got for fabric filtered attributes (and from which fabric),\n // so get it again for this relevant fabric. This also makes sure that fabric sensitive fields are filtered\n // TODO: Maybe add try/catch when we add ACL handling and ignore the update if we cannot get the value?\n return this.readAttribute(path, attribute).then(({ value }) => {\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema,\n version,\n value,\n });\n this.prepareDataUpdate();\n });\n }\n this.outstandingAttributeUpdates.set(attributePathToId(path), { attribute, path, schema, version, value });\n this.prepareDataUpdate();\n }\n\n eventChangeListener<T>(path: EventPath, schema: TlvSchema<T>, newEvent: EventStorageData<T>) {\n const eventListenerData = this.eventListeners.get(eventPathToId(path));\n if (eventListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { event } = eventListenerData;\n if (event instanceof FabricSensitiveEventServer) {\n const { data } = newEvent;\n if (isObject(data) && \"fabricIndex\" in data && data.fabricIndex !== this.session.fabric?.fabricIndex) {\n // Ignore events from different fabrics because events are kind of always fabric filtered\n return;\n }\n }\n this.outstandingEventUpdates.add({ event, path, schema, data: newEvent });\n if (path.isUrgent) {\n this.prepareDataUpdate();\n }\n }\n\n async flush() {\n this.sendDelayTimer.stop();\n logger.debug(\n `Flushing subscription ${this.subscriptionId} with ${this.outstandingAttributeUpdates.size} attributes and ${this.outstandingEventUpdates.size} events`,\n );\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n }\n\n async cancel(flush = false, cancelledByPeer = false) {\n this.sendUpdatesActivated = false;\n if (this.attributeUpdatePromises.size) {\n const resolvers = [...this.attributeUpdatePromises.values()];\n this.attributeUpdatePromises.clear();\n await Promise.all(resolvers);\n }\n this.updateTimer.stop();\n this.sendDelayTimer.stop();\n this.unregisterAttributeListeners(Array.from(this.attributeListeners.keys()));\n this.unregisterEventListeners(Array.from(this.eventListeners.keys()));\n if (flush) {\n await this.flush();\n }\n this.session.removeSubscription(this.subscriptionId);\n this.cancelCallback();\n if (cancelledByPeer) {\n await this.session.context.startAnnouncement();\n }\n }\n\n private async sendUpdateMessage(\n attributes: AttributePathWithValueVersion<any>[],\n events: EventPathWithEventData<any>[],\n ) {\n logger.debug(\n `Sending subscription update message for ID ${this.subscriptionId} with ${attributes.length} attributes and ${events.length} events`,\n );\n const exchange = this.server.initiateExchange(this.fabric, this.peerNodeId, INTERACTION_PROTOCOL_ID);\n if (exchange === undefined) return;\n logger.debug(\n `Sending subscription changes for ID ${this.subscriptionId}: ${attributes\n .map(\n ({ path, value, version }) =>\n `${this.endpointStructure.resolveAttributeName(path)}=${Logger.toJSON(value)} (${version})`,\n )\n .join(\", \")}`,\n ); // TODO Format path better using endpoint structure\n const messenger = new InteractionServerMessenger(exchange);\n\n try {\n if (attributes.length === 0 && events.length === 0) {\n await messenger.sendDataReport(\n {\n suppressResponse: true, // suppressResponse true for empty DataReports\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n },\n this.isFabricFiltered,\n );\n } else {\n await messenger.sendDataReport(\n {\n suppressResponse: false, // Non empty data reports always need to send response\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload: attributes.map(({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n schema,\n payload: value,\n },\n })),\n eventReportsPayload: events.map(({ path, schema, event, data }) => {\n const { eventNumber, priority, epochTimestamp, data: payload } = data;\n return {\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n schema,\n payload,\n },\n };\n }),\n },\n this.isFabricFiltered,\n );\n }\n } catch (e) {\n if (StatusResponseError.is(e, StatusCode.InvalidSubscription, StatusCode.Failure)) {\n logger.info(`Subscription ${this.subscriptionId} cancelled by peer.`);\n await this.cancel(false, true);\n } else {\n await this.cancel(false);\n throw e;\n }\n } finally {\n await messenger.close();\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { AnyAttributeServer, FabricScopedAttributeServer } from \"../../cluster/server/AttributeServer.js\";\nimport { AnyEventServer, FabricSensitiveEventServer } from \"../../cluster/server/EventServer.js\";\nimport { InternalError } from \"../../common/MatterError.js\";\nimport { EventNumber } from \"../../datatype/EventNumber.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { NetworkError } from \"../../net/Network.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { Time, Timer } from \"../../time/Time.js\";\nimport { TlvSchema, TypeFromSchema } from \"../../tlv/TlvSchema.js\";\nimport { MaybePromise } from \"../../util/Promises.js\";\nimport { isObject } from \"../../util/Type.js\";\nimport { RetransmissionLimitReachedError } from \"../MessageExchange.js\";\nimport { AttributeReportPayload, EventReportPayload } from \"./AttributeDataEncoder.js\";\nimport { EventStorageData } from \"./EventHandler.js\";\nimport { InteractionEndpointStructure } from \"./InteractionEndpointStructure.js\";\nimport { InteractionServerMessenger } from \"./InteractionMessenger.js\";\nimport {\n TlvAttributePath,\n TlvAttributeStatus,\n TlvDataVersionFilter,\n TlvEventFilter,\n TlvEventPath,\n TlvEventStatus,\n} from \"./InteractionProtocol.js\";\nimport {\n AttributePath,\n AttributeWithPath,\n EventPath,\n EventWithPath,\n INTERACTION_MODEL_REVISION,\n INTERACTION_PROTOCOL_ID,\n attributePathToId,\n clusterPathToId,\n eventPathToId,\n} from \"./InteractionServer.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\nimport { MAX_INTERVAL_PUBLISHER_LIMIT_S, SubscriptionOptions } from \"./SubscriptionOptions.js\";\n\nconst logger = Logger.get(\"SubscriptionHandler\");\n\ninterface AttributePathWithValueVersion<T> {\n path: TypeFromSchema<typeof TlvAttributePath>;\n attribute: AnyAttributeServer<T>;\n schema: TlvSchema<T>;\n value: T;\n version: number;\n}\n\ninterface EventPathWithEventData<T> {\n path: TypeFromSchema<typeof TlvEventPath>;\n event: AnyEventServer<any, any>;\n schema: TlvSchema<T>;\n data: EventStorageData<T>;\n}\n\nexport class SubscriptionHandler {\n readonly subscriptionId: number;\n private readonly session: SecureSession<any>;\n private readonly endpointStructure: InteractionEndpointStructure;\n private readonly attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n private readonly dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n private readonly eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n private readonly eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n private readonly isFabricFiltered: boolean;\n private readonly cancelCallback: () => void;\n private readonly readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n private readonly readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n\n private lastUpdateTimeMs = 0;\n private updateTimer: Timer;\n private readonly sendDelayTimer: Timer;\n private readonly outstandingAttributeUpdates = new Map<string, AttributePathWithValueVersion<any>>();\n private readonly outstandingEventUpdates = new Set<EventPathWithEventData<any>>();\n private readonly attributeListeners = new Map<\n string,\n {\n attribute: AnyAttributeServer<any>;\n listener?: (value: any, version: number) => void;\n }\n >();\n private readonly eventListeners = new Map<\n string,\n {\n event: AnyEventServer<any, any>;\n listener?: (newEvent: EventStorageData<any>) => void;\n }\n >();\n private sendUpdatesActivated = false;\n readonly #maxIntervalMs: number;\n readonly #sendIntervalMs: number;\n private readonly minIntervalFloorMs: number;\n private readonly maxIntervalCeilingMs: number;\n private readonly server: MatterDevice;\n private readonly fabric: Fabric;\n private readonly peerNodeId: NodeId;\n\n private sendingUpdateInProgress = false;\n private sendNextUpdateImmediately = false;\n private sendUpdateErrorCounter = 0;\n private attributeUpdatePromises = new Set<PromiseLike<void>>();\n\n constructor(options: {\n subscriptionId: number;\n session: SecureSession<any>;\n endpointStructure: InteractionEndpointStructure;\n attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n isFabricFiltered: boolean;\n minIntervalFloor: number;\n maxIntervalCeiling: number;\n cancelCallback: () => void;\n subscriptionOptions: SubscriptionOptions.Configuration;\n readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n }) {\n const {\n subscriptionId,\n session,\n endpointStructure,\n attributeRequests,\n dataVersionFilters,\n eventRequests,\n eventFilters,\n isFabricFiltered,\n minIntervalFloor,\n maxIntervalCeiling,\n cancelCallback,\n subscriptionOptions,\n } = options;\n this.subscriptionId = subscriptionId;\n this.session = session;\n this.endpointStructure = endpointStructure;\n this.attributeRequests = attributeRequests;\n this.dataVersionFilters = dataVersionFilters;\n this.eventRequests = eventRequests;\n this.eventFilters = eventFilters;\n this.isFabricFiltered = isFabricFiltered;\n this.cancelCallback = cancelCallback;\n this.readAttribute = options.readAttribute;\n this.readEvent = options.readEvent;\n\n this.server = this.session.context;\n this.fabric = this.session.associatedFabric;\n this.peerNodeId = this.session.peerNodeId;\n this.minIntervalFloorMs = minIntervalFloor * 1000;\n this.maxIntervalCeilingMs = maxIntervalCeiling * 1000;\n\n const { maxInterval, sendInterval } = this.determineSendingIntervals(\n subscriptionOptions.minIntervalSeconds * 1000,\n subscriptionOptions.maxIntervalSeconds * 1000,\n subscriptionOptions.randomizationWindowSeconds * 1000,\n );\n this.#maxIntervalMs = maxInterval;\n this.#sendIntervalMs = sendInterval;\n\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () => this.prepareDataUpdate()); // will be started later\n this.sendDelayTimer = Time.getTimer(\"Subscription delay\", 50, () =>\n this.sendUpdate().catch(error => logger.warn(\"Sending subscription update failed:\", error)),\n ); // will be started later\n }\n\n private determineSendingIntervals(\n subscriptionMinIntervalMs: number,\n subscriptionMaxIntervalMs: number,\n subscriptionRandomizationWindowMs: number,\n ): { maxInterval: number; sendInterval: number } {\n // Max Interval is the Max interval that the controller request, unless the configured one from the developer\n // is lower. In that case we use the configured one. But we make sure to not be smaller than the requested\n // controller minimum. But in general never faster than minimum interval configured or 2 seconds\n // (SUBSCRIPTION_MIN_INTERVAL_S). Additionally, we add a randomization window to the max interval to avoid all\n // devices sending at the same time. But we make sure not to exceed the global max interval.\n const maxInterval = Math.min(\n Math.max(\n subscriptionMinIntervalMs,\n Math.max(this.minIntervalFloorMs, Math.min(subscriptionMaxIntervalMs, this.maxIntervalCeilingMs)),\n ) + Math.floor(subscriptionRandomizationWindowMs * Math.random()),\n MAX_INTERVAL_PUBLISHER_LIMIT_S * 1000,\n );\n let sendInterval = Math.floor(maxInterval / 2); // Ideally we send at half the max interval\n if (sendInterval < 60_000) {\n // But if we have no chance of at least one full resubmission process we do like chip-tool.\n // One full resubmission process takes 33-45 seconds. So 60s means we reach at least first 2 retries of a\n // second subscription report after first failed.\n sendInterval = Math.max(this.minIntervalFloorMs, Math.floor(maxInterval * 0.8));\n }\n if (sendInterval < subscriptionMinIntervalMs) {\n // But not faster than once every 2s\n logger.warn(\n `Determined subscription send interval of ${sendInterval}ms is too low. Using maxInterval (${maxInterval}ms) instead.`,\n );\n sendInterval = subscriptionMinIntervalMs;\n }\n return { maxInterval, sendInterval };\n }\n\n private registerNewAttributes() {\n const newAttributes = new Array<AttributeWithPath>();\n const attributeErrors = new Array<TypeFromSchema<typeof TlvAttributeStatus>>();\n const formerAttributes = new Set<string>(this.attributeListeners.keys());\n\n if (this.attributeRequests !== undefined) {\n this.attributeRequests.forEach(path => {\n const attributes = this.endpointStructure.getAttributes([path]);\n\n if (attributes.length === 0) {\n // TODO: Also check nodeId\n const { endpointId, clusterId, attributeId } = path;\n if (endpointId === undefined || clusterId === undefined || attributeId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: ignore non-existing attribute`,\n );\n } else {\n // was a concrete path\n try {\n this.endpointStructure.validateConcreteAttributePath(endpointId, clusterId, attributeId);\n throw new InternalError(\n \"validateConcreteAttributePath check should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n attributeErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n attributes.forEach(({ path, attribute }) => {\n formerAttributes.delete(attributePathToId(path));\n\n const existingAttributeListener = this.attributeListeners.get(attributePathToId(path));\n if (existingAttributeListener !== undefined) {\n const { attribute: existingAttribute, listener: existingListener } = existingAttributeListener;\n if (existingAttribute !== attribute) {\n if (existingListener !== undefined) {\n existingAttribute.removeValueChangeListener(existingListener);\n }\n this.attributeListeners.delete(attributePathToId(path));\n } else {\n return; // Attribute is already registered and unchanged\n }\n }\n if (attribute.isSubscribable) {\n // If subscribable register listener\n // TODO: Move to state change listeners from behaviors to remove the dangling promise here\n const listener = (value: any, version: number) =>\n this.attributeChangeListener(path, attribute.schema, version, value);\n attribute.addValueChangeListener(listener);\n this.attributeListeners.set(attributePathToId(path), { attribute, listener });\n } else {\n this.attributeListeners.set(attributePathToId(path), { attribute });\n }\n newAttributes.push({ path, attribute });\n });\n });\n }\n\n // Remove all listeners to attributes that no longer match the subscription\n this.unregisterAttributeListeners(Array.from(formerAttributes.values()));\n return { newAttributes, attributeErrors };\n }\n\n unregisterAttributeListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingAttributeListener = this.attributeListeners.get(pathId);\n if (existingAttributeListener !== undefined) {\n const { attribute, listener } = existingAttributeListener;\n if (listener !== undefined) {\n attribute.removeValueChangeListener(listener);\n }\n this.attributeListeners.delete(pathId);\n }\n }\n }\n\n private registerNewEvents() {\n const newEvents = new Array<EventWithPath>();\n const eventErrors = new Array<TypeFromSchema<typeof TlvEventStatus>>();\n const formerEvents = new Set<string>(this.eventListeners.keys());\n\n if (this.eventRequests !== undefined) {\n this.eventRequests.forEach(path => {\n const events = this.endpointStructure.getEvents([path]);\n if (events.length === 0) {\n const { endpointId, clusterId, eventId } = path;\n if (endpointId === undefined || clusterId === undefined || eventId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: ignore non-existing event`,\n );\n } else {\n try {\n this.endpointStructure.validateConcreteEventPath(endpointId, clusterId, eventId);\n throw new InternalError(\n \"validateConcreteEventPath should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n eventErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n events.forEach(({ path, event }) => {\n formerEvents.delete(eventPathToId(path));\n\n const existingEventListener = this.eventListeners.get(eventPathToId(path));\n if (existingEventListener !== undefined) {\n const { event: existingEvent, listener: existingListener } = existingEventListener;\n if (existingEvent !== event) {\n if (existingListener !== undefined) {\n existingEvent.removeListener(existingListener);\n }\n this.eventListeners.delete(eventPathToId(path));\n } else {\n return; // Event is already registered and unchanged\n }\n }\n const listener = (newEvent: EventStorageData<any>) =>\n this.eventChangeListener(path, event.schema, newEvent);\n event.addListener(listener);\n newEvents.push({ path, event });\n this.eventListeners.set(eventPathToId(path), { event, listener });\n });\n });\n }\n\n // Remove all listeners to events that no longer match the subscription\n this.unregisterEventListeners(Array.from(formerEvents.values()));\n\n return { newEvents, eventErrors };\n }\n\n unregisterEventListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingEventListener = this.eventListeners.get(pathId);\n if (existingEventListener !== undefined) {\n const { event, listener } = existingEventListener;\n if (listener !== undefined) {\n event.removeListener(listener);\n }\n this.eventListeners.delete(pathId);\n }\n }\n }\n\n /**\n * Update the session after an endpoint structure change. The method will initialize all missing new attributes and\n * events and will remove listeners no longer needed.\n * Newly added attributes are then treated ad \"changed values\" and will be sent as subscription data update to the\n * controller. The data of newly added events are not sent automatically.\n */\n async updateSubscription() {\n const { newAttributes } = this.registerNewAttributes();\n\n for (const { path, attribute } of newAttributes) {\n const { version, value } = await this.readAttribute(path, attribute);\n\n // We do not do any version filtering for attributes that are newly added to make sure controller gets\n // most current state\n\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema: attribute.schema,\n version,\n value,\n });\n }\n\n const { newEvents } = this.registerNewEvents();\n newEvents\n .flatMap(({ path, event }): EventPathWithEventData<any>[] => {\n // But we use eventFilters because we do not want to send all events to the controller\n const { schema } = event;\n const matchingEvents = event.get(this.session, this.isFabricFiltered, undefined, this.eventFilters);\n return matchingEvents.map(data => ({\n event,\n schema,\n path,\n data,\n }));\n })\n .sort((a, b) => {\n const eventNumberA = a.data?.eventNumber ?? EventNumber(0);\n const eventNumberB = b.data?.eventNumber ?? EventNumber(0);\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n })\n .forEach(event => this.outstandingEventUpdates.add(event));\n\n this.prepareDataUpdate();\n }\n\n get maxInterval(): number {\n return Math.ceil(this.#maxIntervalMs / 1000);\n }\n\n get sendInterval(): number {\n return Math.ceil(this.#sendIntervalMs / 1000);\n }\n\n activateSendingUpdates() {\n // We do not need these data anymore, so we can free some memory\n if (this.eventFilters !== undefined) this.eventFilters.length = 0;\n if (this.dataVersionFilters !== undefined) this.dataVersionFilters.length = 0;\n\n this.sendUpdatesActivated = true;\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Check if data should be sent straight away or delayed because the minimum interval is not reached. Delay real\n * sending by 50ms in any case to mke sure to catch all updates.\n */\n prepareDataUpdate() {\n if (this.sendDelayTimer.isRunning) {\n // sending data is already scheduled, data updates go in there\n return;\n }\n\n if (!this.sendUpdatesActivated) {\n return;\n }\n\n this.updateTimer.stop();\n const now = Time.nowMs();\n const timeSinceLastUpdateMs = now - this.lastUpdateTimeMs;\n if (timeSinceLastUpdateMs < this.minIntervalFloorMs) {\n // Respect minimum delay time between updates\n this.updateTimer = Time.getTimer(\n \"Subscription update\",\n this.minIntervalFloorMs - timeSinceLastUpdateMs,\n () => this.prepareDataUpdate(),\n ).start();\n return;\n }\n\n this.sendDelayTimer.start();\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Determine all attributes that have changed since the last update and send them tout to the subscriber.\n */\n async sendUpdate() {\n if (this.sendingUpdateInProgress) {\n logger.debug(\"Sending update already in progress, delaying update ...\");\n this.sendNextUpdateImmediately = true;\n return;\n }\n\n // Get all outstanding updates, make sure the order is correct per endpoint and cluster\n const attributeUpdatesToSend = new Array<AttributePathWithValueVersion<any>>();\n const attributeUpdates: Record<string, AttributePathWithValueVersion<any>[]> = {};\n Array.from(this.outstandingAttributeUpdates.values()).forEach(entry => {\n const {\n path: { nodeId, endpointId, clusterId },\n } = entry;\n const pathId = `${nodeId}-${endpointId}-${clusterId}`;\n attributeUpdates[pathId] = attributeUpdates[pathId] ?? [];\n attributeUpdates[pathId].push(entry);\n });\n this.outstandingAttributeUpdates.clear();\n Object.values(attributeUpdates).forEach(data =>\n attributeUpdatesToSend.push(\n ...data.sort(({ version: versionA }, { version: versionB }) => versionA - versionB),\n ),\n );\n\n const eventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n this.lastUpdateTimeMs = Time.nowMs();\n\n this.sendingUpdateInProgress = true;\n try {\n await this.sendUpdateMessage(attributeUpdatesToSend, eventUpdatesToSend);\n this.sendUpdateErrorCounter = 0;\n } catch (error) {\n if (this.server.isClosing) {\n // No need to care about resubmissions when the server is closing\n return;\n }\n\n this.sendUpdateErrorCounter++;\n logger.error(\n `Error sending subscription update message (error count=${this.sendUpdateErrorCounter}):`,\n error,\n );\n if (this.sendUpdateErrorCounter <= 2) {\n // fill the data back in the queue to resend with next try\n const newAttributeUpdatesToSend = Array.from(this.outstandingAttributeUpdates.values());\n this.outstandingAttributeUpdates.clear();\n const newEventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n [...attributeUpdatesToSend, ...newAttributeUpdatesToSend].forEach(update =>\n this.outstandingAttributeUpdates.set(attributePathToId(update.path), update),\n );\n [...eventUpdatesToSend, ...newEventUpdatesToSend].forEach(update =>\n this.outstandingEventUpdates.add(update),\n );\n } else {\n logger.error(\n `Sending update failed 3 times in a row, canceling subscription ${this.subscriptionId} and let controller subscribe again.`,\n );\n this.sendNextUpdateImmediately = false;\n if (error instanceof RetransmissionLimitReachedError || error instanceof NetworkError) {\n // We could not send at all, consider session as dead\n await this.session.destroy(false);\n } else {\n throw error;\n }\n }\n }\n this.sendingUpdateInProgress = false;\n\n if (this.sendNextUpdateImmediately) {\n logger.debug(\"Sending delayed update immediately after last one was sent.\");\n this.sendNextUpdateImmediately = false;\n await this.sendUpdate();\n }\n }\n\n async sendInitialReport(messenger: InteractionServerMessenger) {\n this.updateTimer.stop();\n\n const { newAttributes, attributeErrors } = this.registerNewAttributes();\n\n const dataVersionFilterMap = new Map<string, number>(\n this.dataVersionFilters?.map(({ path, dataVersion }) => [clusterPathToId(path), dataVersion]) ?? [],\n );\n\n let attributesFilteredWithVersion = false;\n const attributes = new Array<{\n path: TypeFromSchema<typeof TlvAttributePath>;\n value: any;\n version: number;\n schema: TlvSchema<any>;\n attribute: AnyAttributeServer<any>;\n }>();\n for (const { path, attribute } of newAttributes) {\n try {\n const { value, version } = await this.readAttribute(path, attribute);\n if (value === undefined) continue;\n\n const { nodeId, endpointId, clusterId } = path;\n\n const versionFilterValue =\n endpointId !== undefined && clusterId !== undefined\n ? dataVersionFilterMap.get(clusterPathToId({ nodeId, endpointId, clusterId }))\n : undefined;\n if (versionFilterValue !== undefined && versionFilterValue === version) {\n attributesFilteredWithVersion = true;\n continue;\n }\n\n attributes.push({ path, value, version, schema: attribute.schema, attribute });\n } catch (error) {\n logger.error(`Error reading attribute ${this.endpointStructure.resolveAttributeName(path)}:`, error);\n }\n }\n const attributeReportsPayload: AttributeReportPayload[] = attributes.map(\n ({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n payload: value,\n schema,\n },\n }),\n );\n attributeErrors.forEach(attributeStatus =>\n attributeReportsPayload.push({\n hasFabricSensitiveData: false,\n attributeStatus,\n }),\n );\n\n const { newEvents, eventErrors } = this.registerNewEvents();\n\n let eventsFiltered = false;\n const eventReportsPayload = new Array<EventReportPayload>();\n for (const { path, event } of newEvents) {\n const { schema } = event;\n try {\n const matchingEvents = await this.readEvent(path, event, this.eventFilters);\n if (matchingEvents.length === 0) {\n eventsFiltered = true;\n } else {\n matchingEvents.forEach(({ eventNumber, priority, epochTimestamp, data }) => {\n eventReportsPayload.push({\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n payload: data,\n schema,\n },\n });\n });\n }\n } catch (error) {\n logger.error(`Error reading event ${this.endpointStructure.resolveEventName(path)}:`, error);\n }\n }\n eventReportsPayload.sort((a, b) => {\n const eventNumberA = a.eventData?.eventNumber ?? 0;\n const eventNumberB = b.eventData?.eventNumber ?? 0;\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n });\n\n if (\n attributes.length === 0 &&\n !attributesFilteredWithVersion &&\n eventReportsPayload.length === 0 &&\n !eventsFiltered\n ) {\n throw new StatusResponseError(\n \"Subscription failed because no attributes or events are matching the query\",\n StatusCode.InvalidAction,\n );\n }\n\n eventErrors.forEach(eventStatus =>\n eventReportsPayload.push({\n hasFabricSensitiveData: false,\n eventStatus,\n }),\n );\n\n logger.debug(\n `Initialize Subscription with ${attributes.length} attributes and ${eventReportsPayload.length} events.`,\n );\n this.lastUpdateTimeMs = Time.nowMs();\n\n await messenger.sendDataReport(\n {\n suppressResponse: false, // we always need proper response for initial report\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload,\n eventReportsPayload,\n },\n this.isFabricFiltered,\n );\n }\n\n attributeChangeListener<T>(path: AttributePath, schema: TlvSchema<T>, version: number, value: T) {\n const changeResult = this.attributeChangeHandler(path, schema, version, value);\n if (MaybePromise.is(changeResult)) {\n const resolver = Promise.resolve(changeResult)\n .catch(error => logger.error(`Error handling attribute change:`, error))\n .finally(() => this.attributeUpdatePromises.delete(resolver));\n this.attributeUpdatePromises.add(resolver);\n }\n }\n\n attributeChangeHandler<T>(\n path: AttributePath,\n schema: TlvSchema<T>,\n version: number,\n value: T,\n ): MaybePromise<void> {\n const attributeListenerData = this.attributeListeners.get(attributePathToId(path));\n if (attributeListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { attribute } = attributeListenerData;\n if (attribute instanceof FabricScopedAttributeServer) {\n // We cannot be sure what value we got for fabric filtered attributes (and from which fabric),\n // so get it again for this relevant fabric. This also makes sure that fabric sensitive fields are filtered\n // TODO: Maybe add try/catch when we add ACL handling and ignore the update if we cannot get the value?\n return this.readAttribute(path, attribute).then(({ value }) => {\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema,\n version,\n value,\n });\n this.prepareDataUpdate();\n });\n }\n this.outstandingAttributeUpdates.set(attributePathToId(path), { attribute, path, schema, version, value });\n this.prepareDataUpdate();\n }\n\n eventChangeListener<T>(path: EventPath, schema: TlvSchema<T>, newEvent: EventStorageData<T>) {\n const eventListenerData = this.eventListeners.get(eventPathToId(path));\n if (eventListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { event } = eventListenerData;\n if (event instanceof FabricSensitiveEventServer) {\n const { data } = newEvent;\n if (isObject(data) && \"fabricIndex\" in data && data.fabricIndex !== this.session.fabric?.fabricIndex) {\n // Ignore events from different fabrics because events are kind of always fabric filtered\n return;\n }\n }\n this.outstandingEventUpdates.add({ event, path, schema, data: newEvent });\n if (path.isUrgent) {\n this.prepareDataUpdate();\n }\n }\n\n async flush() {\n this.sendDelayTimer.stop();\n logger.debug(\n `Flushing subscription ${this.subscriptionId} with ${this.outstandingAttributeUpdates.size} attributes and ${this.outstandingEventUpdates.size} events`,\n );\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n }\n\n async cancel(flush = false, cancelledByPeer = false) {\n this.sendUpdatesActivated = false;\n if (this.attributeUpdatePromises.size) {\n const resolvers = [...this.attributeUpdatePromises.values()];\n this.attributeUpdatePromises.clear();\n await Promise.all(resolvers);\n }\n this.updateTimer.stop();\n this.sendDelayTimer.stop();\n this.unregisterAttributeListeners(Array.from(this.attributeListeners.keys()));\n this.unregisterEventListeners(Array.from(this.eventListeners.keys()));\n if (flush) {\n await this.flush();\n }\n this.session.removeSubscription(this.subscriptionId);\n this.cancelCallback();\n if (cancelledByPeer) {\n await this.session.context.startAnnouncement();\n }\n }\n\n private async sendUpdateMessage(\n attributes: AttributePathWithValueVersion<any>[],\n events: EventPathWithEventData<any>[],\n ) {\n logger.debug(\n `Sending subscription update message for ID ${this.subscriptionId} with ${attributes.length} attributes and ${events.length} events`,\n );\n const exchange = this.server.initiateExchange(this.fabric, this.peerNodeId, INTERACTION_PROTOCOL_ID);\n if (exchange === undefined) return;\n logger.debug(\n `Sending subscription changes for ID ${this.subscriptionId}: ${attributes\n .map(\n ({ path, value, version }) =>\n `${this.endpointStructure.resolveAttributeName(path)}=${Logger.toJSON(value)} (${version})`,\n )\n .join(\", \")}`,\n ); // TODO Format path better using endpoint structure\n const messenger = new InteractionServerMessenger(exchange);\n\n try {\n if (attributes.length === 0 && events.length === 0) {\n await messenger.sendDataReport(\n {\n suppressResponse: true, // suppressResponse true for empty DataReports\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n },\n this.isFabricFiltered,\n );\n } else {\n await messenger.sendDataReport(\n {\n suppressResponse: false, // Non empty data reports always need to send response\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload: attributes.map(({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n schema,\n payload: value,\n },\n })),\n eventReportsPayload: events.map(({ path, schema, event, data }) => {\n const { eventNumber, priority, epochTimestamp, data: payload } = data;\n return {\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n schema,\n payload,\n },\n };\n }),\n },\n this.isFabricFiltered,\n );\n }\n } catch (error) {\n if (StatusResponseError.is(error, StatusCode.InvalidSubscription, StatusCode.Failure)) {\n logger.info(`Subscription ${this.subscriptionId} cancelled by peer.`);\n await this.cancel(false, true);\n } else {\n StatusResponseError.accept(error);\n await this.cancel(false);\n }\n } finally {\n await messenger.close();\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path", "value"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelManager.d.ts","sourceRoot":"","sources":["../../../src/protocol/ChannelManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAItD,qBAAa,cAAe,SAAQ,WAAW;CAAG;AAElD,qBAAa,cAAc;;gBAMX,4BAA4B,SAAI;IAkBtC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"ChannelManager.d.ts","sourceRoot":"","sources":["../../../src/protocol/ChannelManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAItD,qBAAa,cAAe,SAAQ,WAAW;CAAG;AAElD,qBAAa,cAAc;;gBAMX,4BAA4B,SAAI;IAkBtC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC;IAqB7E,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC;IAYjE;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;IAapC,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAQpD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;IAkBzE,OAAO,CAAC,wBAAwB;IAU1B,kBAAkB,CAAC,gBAAgB,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;IA0B9E,KAAK;CAYd"}
|
|
@@ -33,23 +33,18 @@ class ChannelManager {
|
|
|
33
33
|
channel.closeCallback = async () => this.removeChannel(fabric, nodeId, channel.session);
|
|
34
34
|
const channelsKey = this.#getChannelKey(fabric, nodeId);
|
|
35
35
|
const currentChannels = this.#channels.get(channelsKey) ?? [];
|
|
36
|
-
|
|
36
|
+
currentChannels.push(channel);
|
|
37
|
+
this.#channels.set(channelsKey, currentChannels);
|
|
38
|
+
if (currentChannels.length > this.#caseSessionsPerFabricAndNode) {
|
|
37
39
|
const oldestChannel = this.#findLeastActiveChannel(currentChannels);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const { session } = oldestChannel;
|
|
42
|
-
if (session.id !== oldestChannel.session.id) {
|
|
43
|
-
logger.debug(
|
|
44
|
-
`Existing channel for fabricIndex ${fabric.fabricIndex} and node ${nodeId} with session ${session.name} gets replaced. Consider former session already inactive and so close it.`
|
|
45
|
-
);
|
|
46
|
-
await session.destroy(false, false);
|
|
40
|
+
const { session: oldSession } = oldestChannel;
|
|
41
|
+
if (channel.session.id !== oldSession.id) {
|
|
42
|
+
await oldSession.destroy(false, false);
|
|
47
43
|
}
|
|
48
|
-
logger.info(
|
|
44
|
+
logger.info(
|
|
45
|
+
`Close oldest channel for fabric ${fabric.fabricIndex} node ${nodeId} (from session ${oldSession.id})`
|
|
46
|
+
);
|
|
49
47
|
await oldestChannel.close();
|
|
50
|
-
} else {
|
|
51
|
-
currentChannels.push(channel);
|
|
52
|
-
this.#channels.set(channelsKey, currentChannels);
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
50
|
getChannel(fabric, nodeId, session) {
|
|
@@ -57,7 +52,10 @@ class ChannelManager {
|
|
|
57
52
|
if (session !== void 0) {
|
|
58
53
|
results = results.filter((channel) => channel.session.id === session.id);
|
|
59
54
|
}
|
|
60
|
-
if (results.length === 0)
|
|
55
|
+
if (results.length === 0)
|
|
56
|
+
throw new NoChannelError(
|
|
57
|
+
`Can't find a channel to node ${nodeId}${session !== void 0 ? ` and session ${session.id}` : ""}`
|
|
58
|
+
);
|
|
61
59
|
return results[results.length - 1];
|
|
62
60
|
}
|
|
63
61
|
/**
|
|
@@ -88,6 +86,9 @@ class ChannelManager {
|
|
|
88
86
|
const channelEntryIndex = fabricChannels.findIndex(
|
|
89
87
|
({ session: entrySession }) => entrySession.id === session.id
|
|
90
88
|
);
|
|
89
|
+
if (channelEntryIndex === -1) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
91
92
|
const channelEntry = fabricChannels.splice(channelEntryIndex, 1)[0];
|
|
92
93
|
if (channelEntry === void 0) {
|
|
93
94
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/protocol/ChannelManager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Channel } from \"../common/Channel.js\";\nimport { MatterError } from \"../common/MatterError.js\";\nimport { NodeId } from \"../datatype/NodeId.js\";\nimport { Fabric } from \"../fabric/Fabric.js\";\nimport { Logger } from \"../log/Logger.js\";\nimport { SecureSession } from \"../session/SecureSession.js\";\nimport { Session } from \"../session/Session.js\";\nimport { ByteArray } from \"../util/ByteArray.js\";\nimport { MessageChannel } from \"./ExchangeManager.js\";\n\nconst logger = Logger.get(\"ChannelManager\");\n\nexport class NoChannelError extends MatterError {}\n\nexport class ChannelManager {\n readonly #channels = new Map<string, MessageChannel<any>[]>();\n readonly #paseChannels = new Map<Session<any>, MessageChannel<any>>();\n readonly #caseSessionsPerFabricAndNode: number;\n\n // TODO evaluate with controller the effects of limiting the entries just for FabricIndex and not also NodeId\n constructor(caseSessionsPerFabricAndNode = 3) {\n this.#caseSessionsPerFabricAndNode = caseSessionsPerFabricAndNode;\n }\n\n #getChannelKey(fabric: Fabric, nodeId: NodeId) {\n return `${fabric.fabricIndex}/${nodeId}`;\n }\n\n #findLeastActiveChannel(channels: MessageChannel<any>[]) {\n let oldest = channels[0];\n for (const channel of channels) {\n if (channel.session.timestamp < oldest.session.timestamp) {\n oldest = channel;\n }\n }\n return oldest;\n }\n\n async setChannel(fabric: Fabric, nodeId: NodeId, channel: MessageChannel<any>) {\n channel.closeCallback = async () => this.removeChannel(fabric, nodeId, channel.session);\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const currentChannels = this.#channels.get(channelsKey) ?? [];\n if (currentChannels.length
|
|
5
|
-
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAS,mBAAmB;AAG5B,SAAS,cAAc;AAIvB,SAAS,sBAAsB;AAE/B,MAAM,SAAS,OAAO,IAAI,gBAAgB;AAEnC,MAAM,uBAAuB,YAAY;AAAC;AAE1C,MAAM,eAAe;AAAA,EACf,YAAY,oBAAI,IAAmC;AAAA,EACnD,gBAAgB,oBAAI,IAAuC;AAAA,EAC3D;AAAA;AAAA,EAGT,YAAY,+BAA+B,GAAG;AAC1C,SAAK,gCAAgC;AAAA,EACzC;AAAA,EAEA,eAAe,QAAgB,QAAgB;AAC3C,WAAO,GAAG,OAAO,WAAW,IAAI,MAAM;AAAA,EAC1C;AAAA,EAEA,wBAAwB,UAAiC;AACrD,QAAI,SAAS,SAAS,CAAC;AACvB,eAAW,WAAW,UAAU;AAC5B,UAAI,QAAQ,QAAQ,YAAY,OAAO,QAAQ,WAAW;AACtD,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,WAAW,QAAgB,QAAgB,SAA8B;AAC3E,YAAQ,gBAAgB,YAAY,KAAK,cAAc,QAAQ,QAAQ,QAAQ,OAAO;AACtF,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,kBAAkB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC5D,
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Channel } from \"../common/Channel.js\";\nimport { MatterError } from \"../common/MatterError.js\";\nimport { NodeId } from \"../datatype/NodeId.js\";\nimport { Fabric } from \"../fabric/Fabric.js\";\nimport { Logger } from \"../log/Logger.js\";\nimport { SecureSession } from \"../session/SecureSession.js\";\nimport { Session } from \"../session/Session.js\";\nimport { ByteArray } from \"../util/ByteArray.js\";\nimport { MessageChannel } from \"./ExchangeManager.js\";\n\nconst logger = Logger.get(\"ChannelManager\");\n\nexport class NoChannelError extends MatterError {}\n\nexport class ChannelManager {\n readonly #channels = new Map<string, MessageChannel<any>[]>();\n readonly #paseChannels = new Map<Session<any>, MessageChannel<any>>();\n readonly #caseSessionsPerFabricAndNode: number;\n\n // TODO evaluate with controller the effects of limiting the entries just for FabricIndex and not also NodeId\n constructor(caseSessionsPerFabricAndNode = 3) {\n this.#caseSessionsPerFabricAndNode = caseSessionsPerFabricAndNode;\n }\n\n #getChannelKey(fabric: Fabric, nodeId: NodeId) {\n return `${fabric.fabricIndex}/${nodeId}`;\n }\n\n #findLeastActiveChannel(channels: MessageChannel<any>[]) {\n let oldest = channels[0];\n for (const channel of channels) {\n if (channel.session.timestamp < oldest.session.timestamp) {\n oldest = channel;\n }\n }\n return oldest;\n }\n\n async setChannel(fabric: Fabric, nodeId: NodeId, channel: MessageChannel<any>) {\n channel.closeCallback = async () => this.removeChannel(fabric, nodeId, channel.session);\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const currentChannels = this.#channels.get(channelsKey) ?? [];\n currentChannels.push(channel);\n this.#channels.set(channelsKey, currentChannels);\n if (currentChannels.length > this.#caseSessionsPerFabricAndNode) {\n const oldestChannel = this.#findLeastActiveChannel(currentChannels);\n\n const { session: oldSession } = oldestChannel;\n // Should always be the case\n if (channel.session.id !== oldSession.id) {\n await oldSession.destroy(false, false);\n }\n logger.info(\n `Close oldest channel for fabric ${fabric.fabricIndex} node ${nodeId} (from session ${oldSession.id})`,\n );\n await oldestChannel.close();\n }\n }\n\n getChannel(fabric: Fabric, nodeId: NodeId, session?: Session<any>) {\n let results = this.#channels.get(this.#getChannelKey(fabric, nodeId)) ?? [];\n if (session !== undefined) {\n results = results.filter(channel => channel.session.id === session.id);\n }\n if (results.length === 0)\n throw new NoChannelError(\n `Can't find a channel to node ${nodeId}${session !== undefined ? ` and session ${session.id}` : \"\"}`,\n );\n return results[results.length - 1]; // Return the latest added channel (or the one belonging to the session requested)\n }\n\n /**\n * Returns the last established session for a Fabric and Node\n */\n getChannelForSession(session: Session<any>) {\n if (session.isSecure && !session.isPase) {\n const secureSession = session as SecureSession<any>;\n const fabric = secureSession.fabric;\n const nodeId = secureSession.peerNodeId;\n if (fabric === undefined) {\n return this.#paseChannels.get(session);\n }\n return this.getChannel(fabric, nodeId, session);\n }\n return this.#paseChannels.get(session);\n }\n\n async removeAllNodeChannels(fabric: Fabric, nodeId: NodeId) {\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const channelsToRemove = this.#channels.get(channelsKey) ?? [];\n for (const channel of channelsToRemove) {\n await channel.close();\n }\n }\n\n async removeChannel(fabric: Fabric, nodeId: NodeId, session: Session<any>) {\n const channelsKey = this.#getChannelKey(fabric, nodeId);\n const fabricChannels = this.#channels.get(channelsKey) ?? [];\n const channelEntryIndex = fabricChannels.findIndex(\n ({ session: entrySession }) => entrySession.id === session.id,\n );\n if (channelEntryIndex === -1) {\n // Seems already removed\n return;\n }\n const channelEntry = fabricChannels.splice(channelEntryIndex, 1)[0];\n if (channelEntry === undefined) {\n return;\n }\n await channelEntry.close();\n this.#channels.set(channelsKey, fabricChannels);\n }\n\n private getOrCreateAsPaseChannel(byteArrayChannel: Channel<ByteArray>, session: Session<any>) {\n const msgChannel = new MessageChannel(\n byteArrayChannel,\n session,\n async () => void this.#paseChannels.delete(session),\n );\n this.#paseChannels.set(session, msgChannel);\n return msgChannel;\n }\n\n async getOrCreateChannel(byteArrayChannel: Channel<ByteArray>, session: Session<any>) {\n if (!session.isSecure) {\n return this.getOrCreateAsPaseChannel(byteArrayChannel, session);\n }\n const secureSession = session as SecureSession<any>;\n const fabric = secureSession.fabric;\n const nodeId = secureSession.peerNodeId;\n if (fabric === undefined) {\n return this.getOrCreateAsPaseChannel(byteArrayChannel, session);\n }\n\n // Try to get\n try {\n return this.getChannel(fabric, nodeId, session);\n } catch (e) {\n NoChannelError.accept(e);\n }\n\n // Need to create\n const result = new MessageChannel(byteArrayChannel, session, async () =>\n this.removeChannel(fabric, nodeId, session),\n );\n await this.setChannel(fabric, nodeId, result);\n return result;\n }\n\n async close() {\n for (const channel of this.#paseChannels.values()) {\n await channel.close();\n }\n this.#paseChannels.clear();\n for (const channels of this.#channels.values()) {\n for (const channel of channels) {\n await channel.close();\n }\n }\n this.#channels.clear();\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAS,mBAAmB;AAG5B,SAAS,cAAc;AAIvB,SAAS,sBAAsB;AAE/B,MAAM,SAAS,OAAO,IAAI,gBAAgB;AAEnC,MAAM,uBAAuB,YAAY;AAAC;AAE1C,MAAM,eAAe;AAAA,EACf,YAAY,oBAAI,IAAmC;AAAA,EACnD,gBAAgB,oBAAI,IAAuC;AAAA,EAC3D;AAAA;AAAA,EAGT,YAAY,+BAA+B,GAAG;AAC1C,SAAK,gCAAgC;AAAA,EACzC;AAAA,EAEA,eAAe,QAAgB,QAAgB;AAC3C,WAAO,GAAG,OAAO,WAAW,IAAI,MAAM;AAAA,EAC1C;AAAA,EAEA,wBAAwB,UAAiC;AACrD,QAAI,SAAS,SAAS,CAAC;AACvB,eAAW,WAAW,UAAU;AAC5B,UAAI,QAAQ,QAAQ,YAAY,OAAO,QAAQ,WAAW;AACtD,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,WAAW,QAAgB,QAAgB,SAA8B;AAC3E,YAAQ,gBAAgB,YAAY,KAAK,cAAc,QAAQ,QAAQ,QAAQ,OAAO;AACtF,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,kBAAkB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC5D,oBAAgB,KAAK,OAAO;AAC5B,SAAK,UAAU,IAAI,aAAa,eAAe;AAC/C,QAAI,gBAAgB,SAAS,KAAK,+BAA+B;AAC7D,YAAM,gBAAgB,KAAK,wBAAwB,eAAe;AAElE,YAAM,EAAE,SAAS,WAAW,IAAI;AAEhC,UAAI,QAAQ,QAAQ,OAAO,WAAW,IAAI;AACtC,cAAM,WAAW,QAAQ,OAAO,KAAK;AAAA,MACzC;AACA,aAAO;AAAA,QACH,mCAAmC,OAAO,WAAW,SAAS,MAAM,kBAAkB,WAAW,EAAE;AAAA,MACvG;AACA,YAAM,cAAc,MAAM;AAAA,IAC9B;AAAA,EACJ;AAAA,EAEA,WAAW,QAAgB,QAAgB,SAAwB;AAC/D,QAAI,UAAU,KAAK,UAAU,IAAI,KAAK,eAAe,QAAQ,MAAM,CAAC,KAAK,CAAC;AAC1E,QAAI,YAAY,QAAW;AACvB,gBAAU,QAAQ,OAAO,aAAW,QAAQ,QAAQ,OAAO,QAAQ,EAAE;AAAA,IACzE;AACA,QAAI,QAAQ,WAAW;AACnB,YAAM,IAAI;AAAA,QACN,gCAAgC,MAAM,GAAG,YAAY,SAAY,gBAAgB,QAAQ,EAAE,KAAK,EAAE;AAAA,MACtG;AACJ,WAAO,QAAQ,QAAQ,SAAS,CAAC;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,SAAuB;AACxC,QAAI,QAAQ,YAAY,CAAC,QAAQ,QAAQ;AACrC,YAAM,gBAAgB;AACtB,YAAM,SAAS,cAAc;AAC7B,YAAM,SAAS,cAAc;AAC7B,UAAI,WAAW,QAAW;AACtB,eAAO,KAAK,cAAc,IAAI,OAAO;AAAA,MACzC;AACA,aAAO,KAAK,WAAW,QAAQ,QAAQ,OAAO;AAAA,IAClD;AACA,WAAO,KAAK,cAAc,IAAI,OAAO;AAAA,EACzC;AAAA,EAEA,MAAM,sBAAsB,QAAgB,QAAgB;AACxD,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,mBAAmB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC7D,eAAW,WAAW,kBAAkB;AACpC,YAAM,QAAQ,MAAM;AAAA,IACxB;AAAA,EACJ;AAAA,EAEA,MAAM,cAAc,QAAgB,QAAgB,SAAuB;AACvE,UAAM,cAAc,KAAK,eAAe,QAAQ,MAAM;AACtD,UAAM,iBAAiB,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;AAC3D,UAAM,oBAAoB,eAAe;AAAA,MACrC,CAAC,EAAE,SAAS,aAAa,MAAM,aAAa,OAAO,QAAQ;AAAA,IAC/D;AACA,QAAI,sBAAsB,IAAI;AAE1B;AAAA,IACJ;AACA,UAAM,eAAe,eAAe,OAAO,mBAAmB,CAAC,EAAE,CAAC;AAClE,QAAI,iBAAiB,QAAW;AAC5B;AAAA,IACJ;AACA,UAAM,aAAa,MAAM;AACzB,SAAK,UAAU,IAAI,aAAa,cAAc;AAAA,EAClD;AAAA,EAEQ,yBAAyB,kBAAsC,SAAuB;AAC1F,UAAM,aAAa,IAAI;AAAA,MACnB;AAAA,MACA;AAAA,MACA,YAAY,KAAK,KAAK,cAAc,OAAO,OAAO;AAAA,IACtD;AACA,SAAK,cAAc,IAAI,SAAS,UAAU;AAC1C,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,mBAAmB,kBAAsC,SAAuB;AAClF,QAAI,CAAC,QAAQ,UAAU;AACnB,aAAO,KAAK,yBAAyB,kBAAkB,OAAO;AAAA,IAClE;AACA,UAAM,gBAAgB;AACtB,UAAM,SAAS,cAAc;AAC7B,UAAM,SAAS,cAAc;AAC7B,QAAI,WAAW,QAAW;AACtB,aAAO,KAAK,yBAAyB,kBAAkB,OAAO;AAAA,IAClE;AAGA,QAAI;AACA,aAAO,KAAK,WAAW,QAAQ,QAAQ,OAAO;AAAA,IAClD,SAAS,GAAG;AACR,qBAAe,OAAO,CAAC;AAAA,IAC3B;AAGA,UAAM,SAAS,IAAI;AAAA,MAAe;AAAA,MAAkB;AAAA,MAAS,YACzD,KAAK,cAAc,QAAQ,QAAQ,OAAO;AAAA,IAC9C;AACA,UAAM,KAAK,WAAW,QAAQ,QAAQ,MAAM;AAC5C,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,QAAQ;AACV,eAAW,WAAW,KAAK,cAAc,OAAO,GAAG;AAC/C,YAAM,QAAQ,MAAM;AAAA,IACxB;AACA,SAAK,cAAc,MAAM;AACzB,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC5C,iBAAW,WAAW,UAAU;AAC5B,cAAM,QAAQ,MAAM;AAAA,MACxB;AAAA,IACJ;AACA,SAAK,UAAU,MAAM;AAAA,EACzB;AACJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionHandler.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/SubscriptionHandler.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SubscriptionHandler.d.ts","sourceRoot":"","sources":["../../../../src/protocol/interaction/SubscriptionHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -94,7 +94,11 @@ class SubscriptionHandler {
|
|
|
94
94
|
this.#maxIntervalMs = maxInterval;
|
|
95
95
|
this.#sendIntervalMs = sendInterval;
|
|
96
96
|
this.updateTimer = Time.getTimer("Subscription update", this.#sendIntervalMs, () => this.prepareDataUpdate());
|
|
97
|
-
this.sendDelayTimer = Time.getTimer(
|
|
97
|
+
this.sendDelayTimer = Time.getTimer(
|
|
98
|
+
"Subscription delay",
|
|
99
|
+
50,
|
|
100
|
+
() => this.sendUpdate().catch((error) => logger.warn("Sending subscription update failed:", error))
|
|
101
|
+
);
|
|
98
102
|
}
|
|
99
103
|
determineSendingIntervals(subscriptionMinIntervalMs, subscriptionMaxIntervalMs, subscriptionRandomizationWindowMs) {
|
|
100
104
|
const maxInterval = Math.min(
|
|
@@ -652,13 +656,13 @@ class SubscriptionHandler {
|
|
|
652
656
|
this.isFabricFiltered
|
|
653
657
|
);
|
|
654
658
|
}
|
|
655
|
-
} catch (
|
|
656
|
-
if (StatusResponseError.is(
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (StatusResponseError.is(error, StatusCode.InvalidSubscription, StatusCode.Failure)) {
|
|
657
661
|
logger.info(`Subscription ${this.subscriptionId} cancelled by peer.`);
|
|
658
662
|
await this.cancel(false, true);
|
|
659
663
|
} else {
|
|
664
|
+
StatusResponseError.accept(error);
|
|
660
665
|
await this.cancel(false);
|
|
661
|
-
throw e;
|
|
662
666
|
}
|
|
663
667
|
} finally {
|
|
664
668
|
await messenger.close();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/protocol/interaction/SubscriptionHandler.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { AnyAttributeServer, FabricScopedAttributeServer } from \"../../cluster/server/AttributeServer.js\";\nimport { AnyEventServer, FabricSensitiveEventServer } from \"../../cluster/server/EventServer.js\";\nimport { InternalError } from \"../../common/MatterError.js\";\nimport { EventNumber } from \"../../datatype/EventNumber.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { NetworkError } from \"../../net/Network.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { Time, Timer } from \"../../time/Time.js\";\nimport { TlvSchema, TypeFromSchema } from \"../../tlv/TlvSchema.js\";\nimport { MaybePromise } from \"../../util/Promises.js\";\nimport { isObject } from \"../../util/Type.js\";\nimport { RetransmissionLimitReachedError } from \"../MessageExchange.js\";\nimport { AttributeReportPayload, EventReportPayload } from \"./AttributeDataEncoder.js\";\nimport { EventStorageData } from \"./EventHandler.js\";\nimport { InteractionEndpointStructure } from \"./InteractionEndpointStructure.js\";\nimport { InteractionServerMessenger } from \"./InteractionMessenger.js\";\nimport {\n TlvAttributePath,\n TlvAttributeStatus,\n TlvDataVersionFilter,\n TlvEventFilter,\n TlvEventPath,\n TlvEventStatus,\n} from \"./InteractionProtocol.js\";\nimport {\n AttributePath,\n AttributeWithPath,\n EventPath,\n EventWithPath,\n INTERACTION_MODEL_REVISION,\n INTERACTION_PROTOCOL_ID,\n attributePathToId,\n clusterPathToId,\n eventPathToId,\n} from \"./InteractionServer.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\nimport { MAX_INTERVAL_PUBLISHER_LIMIT_S, SubscriptionOptions } from \"./SubscriptionOptions.js\";\n\nconst logger = Logger.get(\"SubscriptionHandler\");\n\ninterface AttributePathWithValueVersion<T> {\n path: TypeFromSchema<typeof TlvAttributePath>;\n attribute: AnyAttributeServer<T>;\n schema: TlvSchema<T>;\n value: T;\n version: number;\n}\n\ninterface EventPathWithEventData<T> {\n path: TypeFromSchema<typeof TlvEventPath>;\n event: AnyEventServer<any, any>;\n schema: TlvSchema<T>;\n data: EventStorageData<T>;\n}\n\nexport class SubscriptionHandler {\n readonly subscriptionId: number;\n private readonly session: SecureSession<any>;\n private readonly endpointStructure: InteractionEndpointStructure;\n private readonly attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n private readonly dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n private readonly eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n private readonly eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n private readonly isFabricFiltered: boolean;\n private readonly cancelCallback: () => void;\n private readonly readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n private readonly readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n\n private lastUpdateTimeMs = 0;\n private updateTimer: Timer;\n private readonly sendDelayTimer: Timer;\n private readonly outstandingAttributeUpdates = new Map<string, AttributePathWithValueVersion<any>>();\n private readonly outstandingEventUpdates = new Set<EventPathWithEventData<any>>();\n private readonly attributeListeners = new Map<\n string,\n {\n attribute: AnyAttributeServer<any>;\n listener?: (value: any, version: number) => void;\n }\n >();\n private readonly eventListeners = new Map<\n string,\n {\n event: AnyEventServer<any, any>;\n listener?: (newEvent: EventStorageData<any>) => void;\n }\n >();\n private sendUpdatesActivated = false;\n readonly #maxIntervalMs: number;\n readonly #sendIntervalMs: number;\n private readonly minIntervalFloorMs: number;\n private readonly maxIntervalCeilingMs: number;\n private readonly server: MatterDevice;\n private readonly fabric: Fabric;\n private readonly peerNodeId: NodeId;\n\n private sendingUpdateInProgress = false;\n private sendNextUpdateImmediately = false;\n private sendUpdateErrorCounter = 0;\n private attributeUpdatePromises = new Set<PromiseLike<void>>();\n\n constructor(options: {\n subscriptionId: number;\n session: SecureSession<any>;\n endpointStructure: InteractionEndpointStructure;\n attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n isFabricFiltered: boolean;\n minIntervalFloor: number;\n maxIntervalCeiling: number;\n cancelCallback: () => void;\n subscriptionOptions: SubscriptionOptions.Configuration;\n readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n }) {\n const {\n subscriptionId,\n session,\n endpointStructure,\n attributeRequests,\n dataVersionFilters,\n eventRequests,\n eventFilters,\n isFabricFiltered,\n minIntervalFloor,\n maxIntervalCeiling,\n cancelCallback,\n subscriptionOptions,\n } = options;\n this.subscriptionId = subscriptionId;\n this.session = session;\n this.endpointStructure = endpointStructure;\n this.attributeRequests = attributeRequests;\n this.dataVersionFilters = dataVersionFilters;\n this.eventRequests = eventRequests;\n this.eventFilters = eventFilters;\n this.isFabricFiltered = isFabricFiltered;\n this.cancelCallback = cancelCallback;\n this.readAttribute = options.readAttribute;\n this.readEvent = options.readEvent;\n\n this.server = this.session.context;\n this.fabric = this.session.associatedFabric;\n this.peerNodeId = this.session.peerNodeId;\n this.minIntervalFloorMs = minIntervalFloor * 1000;\n this.maxIntervalCeilingMs = maxIntervalCeiling * 1000;\n\n const { maxInterval, sendInterval } = this.determineSendingIntervals(\n subscriptionOptions.minIntervalSeconds * 1000,\n subscriptionOptions.maxIntervalSeconds * 1000,\n subscriptionOptions.randomizationWindowSeconds * 1000,\n );\n this.#maxIntervalMs = maxInterval;\n this.#sendIntervalMs = sendInterval;\n\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () => this.prepareDataUpdate()); // will be started later\n this.sendDelayTimer = Time.getTimer(\"Subscription delay\", 50, () => this.sendUpdate()); // will be started later\n }\n\n private determineSendingIntervals(\n subscriptionMinIntervalMs: number,\n subscriptionMaxIntervalMs: number,\n subscriptionRandomizationWindowMs: number,\n ): { maxInterval: number; sendInterval: number } {\n // Max Interval is the Max interval that the controller request, unless the configured one from the developer\n // is lower. In that case we use the configured one. But we make sure to not be smaller than the requested\n // controller minimum. But in general never faster than minimum interval configured or 2 seconds\n // (SUBSCRIPTION_MIN_INTERVAL_S). Additionally, we add a randomization window to the max interval to avoid all\n // devices sending at the same time. But we make sure not to exceed the global max interval.\n const maxInterval = Math.min(\n Math.max(\n subscriptionMinIntervalMs,\n Math.max(this.minIntervalFloorMs, Math.min(subscriptionMaxIntervalMs, this.maxIntervalCeilingMs)),\n ) + Math.floor(subscriptionRandomizationWindowMs * Math.random()),\n MAX_INTERVAL_PUBLISHER_LIMIT_S * 1000,\n );\n let sendInterval = Math.floor(maxInterval / 2); // Ideally we send at half the max interval\n if (sendInterval < 60_000) {\n // But if we have no chance of at least one full resubmission process we do like chip-tool.\n // One full resubmission process takes 33-45 seconds. So 60s means we reach at least first 2 retries of a\n // second subscription report after first failed.\n sendInterval = Math.max(this.minIntervalFloorMs, Math.floor(maxInterval * 0.8));\n }\n if (sendInterval < subscriptionMinIntervalMs) {\n // But not faster than once every 2s\n logger.warn(\n `Determined subscription send interval of ${sendInterval}ms is too low. Using maxInterval (${maxInterval}ms) instead.`,\n );\n sendInterval = subscriptionMinIntervalMs;\n }\n return { maxInterval, sendInterval };\n }\n\n private registerNewAttributes() {\n const newAttributes = new Array<AttributeWithPath>();\n const attributeErrors = new Array<TypeFromSchema<typeof TlvAttributeStatus>>();\n const formerAttributes = new Set<string>(this.attributeListeners.keys());\n\n if (this.attributeRequests !== undefined) {\n this.attributeRequests.forEach(path => {\n const attributes = this.endpointStructure.getAttributes([path]);\n\n if (attributes.length === 0) {\n // TODO: Also check nodeId\n const { endpointId, clusterId, attributeId } = path;\n if (endpointId === undefined || clusterId === undefined || attributeId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: ignore non-existing attribute`,\n );\n } else {\n // was a concrete path\n try {\n this.endpointStructure.validateConcreteAttributePath(endpointId, clusterId, attributeId);\n throw new InternalError(\n \"validateConcreteAttributePath check should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n attributeErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n attributes.forEach(({ path, attribute }) => {\n formerAttributes.delete(attributePathToId(path));\n\n const existingAttributeListener = this.attributeListeners.get(attributePathToId(path));\n if (existingAttributeListener !== undefined) {\n const { attribute: existingAttribute, listener: existingListener } = existingAttributeListener;\n if (existingAttribute !== attribute) {\n if (existingListener !== undefined) {\n existingAttribute.removeValueChangeListener(existingListener);\n }\n this.attributeListeners.delete(attributePathToId(path));\n } else {\n return; // Attribute is already registered and unchanged\n }\n }\n if (attribute.isSubscribable) {\n // If subscribable register listener\n // TODO: Move to state change listeners from behaviors to remove the dangling promise here\n const listener = (value: any, version: number) =>\n this.attributeChangeListener(path, attribute.schema, version, value);\n attribute.addValueChangeListener(listener);\n this.attributeListeners.set(attributePathToId(path), { attribute, listener });\n } else {\n this.attributeListeners.set(attributePathToId(path), { attribute });\n }\n newAttributes.push({ path, attribute });\n });\n });\n }\n\n // Remove all listeners to attributes that no longer match the subscription\n this.unregisterAttributeListeners(Array.from(formerAttributes.values()));\n return { newAttributes, attributeErrors };\n }\n\n unregisterAttributeListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingAttributeListener = this.attributeListeners.get(pathId);\n if (existingAttributeListener !== undefined) {\n const { attribute, listener } = existingAttributeListener;\n if (listener !== undefined) {\n attribute.removeValueChangeListener(listener);\n }\n this.attributeListeners.delete(pathId);\n }\n }\n }\n\n private registerNewEvents() {\n const newEvents = new Array<EventWithPath>();\n const eventErrors = new Array<TypeFromSchema<typeof TlvEventStatus>>();\n const formerEvents = new Set<string>(this.eventListeners.keys());\n\n if (this.eventRequests !== undefined) {\n this.eventRequests.forEach(path => {\n const events = this.endpointStructure.getEvents([path]);\n if (events.length === 0) {\n const { endpointId, clusterId, eventId } = path;\n if (endpointId === undefined || clusterId === undefined || eventId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: ignore non-existing event`,\n );\n } else {\n try {\n this.endpointStructure.validateConcreteEventPath(endpointId, clusterId, eventId);\n throw new InternalError(\n \"validateConcreteEventPath should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n eventErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n events.forEach(({ path, event }) => {\n formerEvents.delete(eventPathToId(path));\n\n const existingEventListener = this.eventListeners.get(eventPathToId(path));\n if (existingEventListener !== undefined) {\n const { event: existingEvent, listener: existingListener } = existingEventListener;\n if (existingEvent !== event) {\n if (existingListener !== undefined) {\n existingEvent.removeListener(existingListener);\n }\n this.eventListeners.delete(eventPathToId(path));\n } else {\n return; // Event is already registered and unchanged\n }\n }\n const listener = (newEvent: EventStorageData<any>) =>\n this.eventChangeListener(path, event.schema, newEvent);\n event.addListener(listener);\n newEvents.push({ path, event });\n this.eventListeners.set(eventPathToId(path), { event, listener });\n });\n });\n }\n\n // Remove all listeners to events that no longer match the subscription\n this.unregisterEventListeners(Array.from(formerEvents.values()));\n\n return { newEvents, eventErrors };\n }\n\n unregisterEventListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingEventListener = this.eventListeners.get(pathId);\n if (existingEventListener !== undefined) {\n const { event, listener } = existingEventListener;\n if (listener !== undefined) {\n event.removeListener(listener);\n }\n this.eventListeners.delete(pathId);\n }\n }\n }\n\n /**\n * Update the session after an endpoint structure change. The method will initialize all missing new attributes and\n * events and will remove listeners no longer needed.\n * Newly added attributes are then treated ad \"changed values\" and will be sent as subscription data update to the\n * controller. The data of newly added events are not sent automatically.\n */\n async updateSubscription() {\n const { newAttributes } = this.registerNewAttributes();\n\n for (const { path, attribute } of newAttributes) {\n const { version, value } = await this.readAttribute(path, attribute);\n\n // We do not do any version filtering for attributes that are newly added to make sure controller gets\n // most current state\n\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema: attribute.schema,\n version,\n value,\n });\n }\n\n const { newEvents } = this.registerNewEvents();\n newEvents\n .flatMap(({ path, event }): EventPathWithEventData<any>[] => {\n // But we use eventFilters because we do not want to send all events to the controller\n const { schema } = event;\n const matchingEvents = event.get(this.session, this.isFabricFiltered, undefined, this.eventFilters);\n return matchingEvents.map(data => ({\n event,\n schema,\n path,\n data,\n }));\n })\n .sort((a, b) => {\n const eventNumberA = a.data?.eventNumber ?? EventNumber(0);\n const eventNumberB = b.data?.eventNumber ?? EventNumber(0);\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n })\n .forEach(event => this.outstandingEventUpdates.add(event));\n\n this.prepareDataUpdate();\n }\n\n get maxInterval(): number {\n return Math.ceil(this.#maxIntervalMs / 1000);\n }\n\n get sendInterval(): number {\n return Math.ceil(this.#sendIntervalMs / 1000);\n }\n\n activateSendingUpdates() {\n // We do not need these data anymore, so we can free some memory\n if (this.eventFilters !== undefined) this.eventFilters.length = 0;\n if (this.dataVersionFilters !== undefined) this.dataVersionFilters.length = 0;\n\n this.sendUpdatesActivated = true;\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Check if data should be sent straight away or delayed because the minimum interval is not reached. Delay real\n * sending by 50ms in any case to mke sure to catch all updates.\n */\n prepareDataUpdate() {\n if (this.sendDelayTimer.isRunning) {\n // sending data is already scheduled, data updates go in there\n return;\n }\n\n if (!this.sendUpdatesActivated) {\n return;\n }\n\n this.updateTimer.stop();\n const now = Time.nowMs();\n const timeSinceLastUpdateMs = now - this.lastUpdateTimeMs;\n if (timeSinceLastUpdateMs < this.minIntervalFloorMs) {\n // Respect minimum delay time between updates\n this.updateTimer = Time.getTimer(\n \"Subscription update\",\n this.minIntervalFloorMs - timeSinceLastUpdateMs,\n () => this.prepareDataUpdate(),\n ).start();\n return;\n }\n\n this.sendDelayTimer.start();\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Determine all attributes that have changed since the last update and send them tout to the subscriber.\n */\n async sendUpdate() {\n if (this.sendingUpdateInProgress) {\n logger.debug(\"Sending update already in progress, delaying update ...\");\n this.sendNextUpdateImmediately = true;\n return;\n }\n\n // Get all outstanding updates, make sure the order is correct per endpoint and cluster\n const attributeUpdatesToSend = new Array<AttributePathWithValueVersion<any>>();\n const attributeUpdates: Record<string, AttributePathWithValueVersion<any>[]> = {};\n Array.from(this.outstandingAttributeUpdates.values()).forEach(entry => {\n const {\n path: { nodeId, endpointId, clusterId },\n } = entry;\n const pathId = `${nodeId}-${endpointId}-${clusterId}`;\n attributeUpdates[pathId] = attributeUpdates[pathId] ?? [];\n attributeUpdates[pathId].push(entry);\n });\n this.outstandingAttributeUpdates.clear();\n Object.values(attributeUpdates).forEach(data =>\n attributeUpdatesToSend.push(\n ...data.sort(({ version: versionA }, { version: versionB }) => versionA - versionB),\n ),\n );\n\n const eventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n this.lastUpdateTimeMs = Time.nowMs();\n\n this.sendingUpdateInProgress = true;\n try {\n await this.sendUpdateMessage(attributeUpdatesToSend, eventUpdatesToSend);\n this.sendUpdateErrorCounter = 0;\n } catch (error) {\n if (this.server.isClosing) {\n // No need to care about resubmissions when the server is closing\n return;\n }\n\n this.sendUpdateErrorCounter++;\n logger.error(\n `Error sending subscription update message (error count=${this.sendUpdateErrorCounter}):`,\n error,\n );\n if (this.sendUpdateErrorCounter <= 2) {\n // fill the data back in the queue to resend with next try\n const newAttributeUpdatesToSend = Array.from(this.outstandingAttributeUpdates.values());\n this.outstandingAttributeUpdates.clear();\n const newEventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n [...attributeUpdatesToSend, ...newAttributeUpdatesToSend].forEach(update =>\n this.outstandingAttributeUpdates.set(attributePathToId(update.path), update),\n );\n [...eventUpdatesToSend, ...newEventUpdatesToSend].forEach(update =>\n this.outstandingEventUpdates.add(update),\n );\n } else {\n logger.error(\n `Sending update failed 3 times in a row, canceling subscription ${this.subscriptionId} and let controller subscribe again.`,\n );\n this.sendNextUpdateImmediately = false;\n if (error instanceof RetransmissionLimitReachedError || error instanceof NetworkError) {\n // We could not send at all, consider session as dead\n await this.session.destroy(false);\n } else {\n throw error;\n }\n }\n }\n this.sendingUpdateInProgress = false;\n\n if (this.sendNextUpdateImmediately) {\n logger.debug(\"Sending delayed update immediately after last one was sent.\");\n this.sendNextUpdateImmediately = false;\n await this.sendUpdate();\n }\n }\n\n async sendInitialReport(messenger: InteractionServerMessenger) {\n this.updateTimer.stop();\n\n const { newAttributes, attributeErrors } = this.registerNewAttributes();\n\n const dataVersionFilterMap = new Map<string, number>(\n this.dataVersionFilters?.map(({ path, dataVersion }) => [clusterPathToId(path), dataVersion]) ?? [],\n );\n\n let attributesFilteredWithVersion = false;\n const attributes = new Array<{\n path: TypeFromSchema<typeof TlvAttributePath>;\n value: any;\n version: number;\n schema: TlvSchema<any>;\n attribute: AnyAttributeServer<any>;\n }>();\n for (const { path, attribute } of newAttributes) {\n try {\n const { value, version } = await this.readAttribute(path, attribute);\n if (value === undefined) continue;\n\n const { nodeId, endpointId, clusterId } = path;\n\n const versionFilterValue =\n endpointId !== undefined && clusterId !== undefined\n ? dataVersionFilterMap.get(clusterPathToId({ nodeId, endpointId, clusterId }))\n : undefined;\n if (versionFilterValue !== undefined && versionFilterValue === version) {\n attributesFilteredWithVersion = true;\n continue;\n }\n\n attributes.push({ path, value, version, schema: attribute.schema, attribute });\n } catch (error) {\n logger.error(`Error reading attribute ${this.endpointStructure.resolveAttributeName(path)}:`, error);\n }\n }\n const attributeReportsPayload: AttributeReportPayload[] = attributes.map(\n ({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n payload: value,\n schema,\n },\n }),\n );\n attributeErrors.forEach(attributeStatus =>\n attributeReportsPayload.push({\n hasFabricSensitiveData: false,\n attributeStatus,\n }),\n );\n\n const { newEvents, eventErrors } = this.registerNewEvents();\n\n let eventsFiltered = false;\n const eventReportsPayload = new Array<EventReportPayload>();\n for (const { path, event } of newEvents) {\n const { schema } = event;\n try {\n const matchingEvents = await this.readEvent(path, event, this.eventFilters);\n if (matchingEvents.length === 0) {\n eventsFiltered = true;\n } else {\n matchingEvents.forEach(({ eventNumber, priority, epochTimestamp, data }) => {\n eventReportsPayload.push({\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n payload: data,\n schema,\n },\n });\n });\n }\n } catch (error) {\n logger.error(`Error reading event ${this.endpointStructure.resolveEventName(path)}:`, error);\n }\n }\n eventReportsPayload.sort((a, b) => {\n const eventNumberA = a.eventData?.eventNumber ?? 0;\n const eventNumberB = b.eventData?.eventNumber ?? 0;\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n });\n\n if (\n attributes.length === 0 &&\n !attributesFilteredWithVersion &&\n eventReportsPayload.length === 0 &&\n !eventsFiltered\n ) {\n throw new StatusResponseError(\n \"Subscription failed because no attributes or events are matching the query\",\n StatusCode.InvalidAction,\n );\n }\n\n eventErrors.forEach(eventStatus =>\n eventReportsPayload.push({\n hasFabricSensitiveData: false,\n eventStatus,\n }),\n );\n\n logger.debug(\n `Initialize Subscription with ${attributes.length} attributes and ${eventReportsPayload.length} events.`,\n );\n this.lastUpdateTimeMs = Time.nowMs();\n\n await messenger.sendDataReport(\n {\n suppressResponse: false, // we always need proper response for initial report\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload,\n eventReportsPayload,\n },\n this.isFabricFiltered,\n );\n }\n\n attributeChangeListener<T>(path: AttributePath, schema: TlvSchema<T>, version: number, value: T) {\n const changeResult = this.attributeChangeHandler(path, schema, version, value);\n if (MaybePromise.is(changeResult)) {\n const resolver = Promise.resolve(changeResult)\n .catch(error => logger.error(`Error handling attribute change:`, error))\n .finally(() => this.attributeUpdatePromises.delete(resolver));\n this.attributeUpdatePromises.add(resolver);\n }\n }\n\n attributeChangeHandler<T>(\n path: AttributePath,\n schema: TlvSchema<T>,\n version: number,\n value: T,\n ): MaybePromise<void> {\n const attributeListenerData = this.attributeListeners.get(attributePathToId(path));\n if (attributeListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { attribute } = attributeListenerData;\n if (attribute instanceof FabricScopedAttributeServer) {\n // We cannot be sure what value we got for fabric filtered attributes (and from which fabric),\n // so get it again for this relevant fabric. This also makes sure that fabric sensitive fields are filtered\n // TODO: Maybe add try/catch when we add ACL handling and ignore the update if we cannot get the value?\n return this.readAttribute(path, attribute).then(({ value }) => {\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema,\n version,\n value,\n });\n this.prepareDataUpdate();\n });\n }\n this.outstandingAttributeUpdates.set(attributePathToId(path), { attribute, path, schema, version, value });\n this.prepareDataUpdate();\n }\n\n eventChangeListener<T>(path: EventPath, schema: TlvSchema<T>, newEvent: EventStorageData<T>) {\n const eventListenerData = this.eventListeners.get(eventPathToId(path));\n if (eventListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { event } = eventListenerData;\n if (event instanceof FabricSensitiveEventServer) {\n const { data } = newEvent;\n if (isObject(data) && \"fabricIndex\" in data && data.fabricIndex !== this.session.fabric?.fabricIndex) {\n // Ignore events from different fabrics because events are kind of always fabric filtered\n return;\n }\n }\n this.outstandingEventUpdates.add({ event, path, schema, data: newEvent });\n if (path.isUrgent) {\n this.prepareDataUpdate();\n }\n }\n\n async flush() {\n this.sendDelayTimer.stop();\n logger.debug(\n `Flushing subscription ${this.subscriptionId} with ${this.outstandingAttributeUpdates.size} attributes and ${this.outstandingEventUpdates.size} events`,\n );\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n }\n\n async cancel(flush = false, cancelledByPeer = false) {\n this.sendUpdatesActivated = false;\n if (this.attributeUpdatePromises.size) {\n const resolvers = [...this.attributeUpdatePromises.values()];\n this.attributeUpdatePromises.clear();\n await Promise.all(resolvers);\n }\n this.updateTimer.stop();\n this.sendDelayTimer.stop();\n this.unregisterAttributeListeners(Array.from(this.attributeListeners.keys()));\n this.unregisterEventListeners(Array.from(this.eventListeners.keys()));\n if (flush) {\n await this.flush();\n }\n this.session.removeSubscription(this.subscriptionId);\n this.cancelCallback();\n if (cancelledByPeer) {\n await this.session.context.startAnnouncement();\n }\n }\n\n private async sendUpdateMessage(\n attributes: AttributePathWithValueVersion<any>[],\n events: EventPathWithEventData<any>[],\n ) {\n logger.debug(\n `Sending subscription update message for ID ${this.subscriptionId} with ${attributes.length} attributes and ${events.length} events`,\n );\n const exchange = this.server.initiateExchange(this.fabric, this.peerNodeId, INTERACTION_PROTOCOL_ID);\n if (exchange === undefined) return;\n logger.debug(\n `Sending subscription changes for ID ${this.subscriptionId}: ${attributes\n .map(\n ({ path, value, version }) =>\n `${this.endpointStructure.resolveAttributeName(path)}=${Logger.toJSON(value)} (${version})`,\n )\n .join(\", \")}`,\n ); // TODO Format path better using endpoint structure\n const messenger = new InteractionServerMessenger(exchange);\n\n try {\n if (attributes.length === 0 && events.length === 0) {\n await messenger.sendDataReport(\n {\n suppressResponse: true, // suppressResponse true for empty DataReports\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n },\n this.isFabricFiltered,\n );\n } else {\n await messenger.sendDataReport(\n {\n suppressResponse: false, // Non empty data reports always need to send response\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload: attributes.map(({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n schema,\n payload: value,\n },\n })),\n eventReportsPayload: events.map(({ path, schema, event, data }) => {\n const { eventNumber, priority, epochTimestamp, data: payload } = data;\n return {\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n schema,\n payload,\n },\n };\n }),\n },\n this.isFabricFiltered,\n );\n }\n } catch (e) {\n if (StatusResponseError.is(e, StatusCode.InvalidSubscription, StatusCode.Failure)) {\n logger.info(`Subscription ${this.subscriptionId} cancelled by peer.`);\n await this.cancel(false, true);\n } else {\n await this.cancel(false);\n throw e;\n }\n } finally {\n await messenger.close();\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { MatterDevice } from \"../../MatterDevice.js\";\nimport { AnyAttributeServer, FabricScopedAttributeServer } from \"../../cluster/server/AttributeServer.js\";\nimport { AnyEventServer, FabricSensitiveEventServer } from \"../../cluster/server/EventServer.js\";\nimport { InternalError } from \"../../common/MatterError.js\";\nimport { EventNumber } from \"../../datatype/EventNumber.js\";\nimport { NodeId } from \"../../datatype/NodeId.js\";\nimport { Fabric } from \"../../fabric/Fabric.js\";\nimport { Logger } from \"../../log/Logger.js\";\nimport { NetworkError } from \"../../net/Network.js\";\nimport { SecureSession } from \"../../session/SecureSession.js\";\nimport { Time, Timer } from \"../../time/Time.js\";\nimport { TlvSchema, TypeFromSchema } from \"../../tlv/TlvSchema.js\";\nimport { MaybePromise } from \"../../util/Promises.js\";\nimport { isObject } from \"../../util/Type.js\";\nimport { RetransmissionLimitReachedError } from \"../MessageExchange.js\";\nimport { AttributeReportPayload, EventReportPayload } from \"./AttributeDataEncoder.js\";\nimport { EventStorageData } from \"./EventHandler.js\";\nimport { InteractionEndpointStructure } from \"./InteractionEndpointStructure.js\";\nimport { InteractionServerMessenger } from \"./InteractionMessenger.js\";\nimport {\n TlvAttributePath,\n TlvAttributeStatus,\n TlvDataVersionFilter,\n TlvEventFilter,\n TlvEventPath,\n TlvEventStatus,\n} from \"./InteractionProtocol.js\";\nimport {\n AttributePath,\n AttributeWithPath,\n EventPath,\n EventWithPath,\n INTERACTION_MODEL_REVISION,\n INTERACTION_PROTOCOL_ID,\n attributePathToId,\n clusterPathToId,\n eventPathToId,\n} from \"./InteractionServer.js\";\nimport { StatusCode, StatusResponseError } from \"./StatusCode.js\";\nimport { MAX_INTERVAL_PUBLISHER_LIMIT_S, SubscriptionOptions } from \"./SubscriptionOptions.js\";\n\nconst logger = Logger.get(\"SubscriptionHandler\");\n\ninterface AttributePathWithValueVersion<T> {\n path: TypeFromSchema<typeof TlvAttributePath>;\n attribute: AnyAttributeServer<T>;\n schema: TlvSchema<T>;\n value: T;\n version: number;\n}\n\ninterface EventPathWithEventData<T> {\n path: TypeFromSchema<typeof TlvEventPath>;\n event: AnyEventServer<any, any>;\n schema: TlvSchema<T>;\n data: EventStorageData<T>;\n}\n\nexport class SubscriptionHandler {\n readonly subscriptionId: number;\n private readonly session: SecureSession<any>;\n private readonly endpointStructure: InteractionEndpointStructure;\n private readonly attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n private readonly dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n private readonly eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n private readonly eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n private readonly isFabricFiltered: boolean;\n private readonly cancelCallback: () => void;\n private readonly readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n private readonly readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n\n private lastUpdateTimeMs = 0;\n private updateTimer: Timer;\n private readonly sendDelayTimer: Timer;\n private readonly outstandingAttributeUpdates = new Map<string, AttributePathWithValueVersion<any>>();\n private readonly outstandingEventUpdates = new Set<EventPathWithEventData<any>>();\n private readonly attributeListeners = new Map<\n string,\n {\n attribute: AnyAttributeServer<any>;\n listener?: (value: any, version: number) => void;\n }\n >();\n private readonly eventListeners = new Map<\n string,\n {\n event: AnyEventServer<any, any>;\n listener?: (newEvent: EventStorageData<any>) => void;\n }\n >();\n private sendUpdatesActivated = false;\n readonly #maxIntervalMs: number;\n readonly #sendIntervalMs: number;\n private readonly minIntervalFloorMs: number;\n private readonly maxIntervalCeilingMs: number;\n private readonly server: MatterDevice;\n private readonly fabric: Fabric;\n private readonly peerNodeId: NodeId;\n\n private sendingUpdateInProgress = false;\n private sendNextUpdateImmediately = false;\n private sendUpdateErrorCounter = 0;\n private attributeUpdatePromises = new Set<PromiseLike<void>>();\n\n constructor(options: {\n subscriptionId: number;\n session: SecureSession<any>;\n endpointStructure: InteractionEndpointStructure;\n attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];\n dataVersionFilters?: TypeFromSchema<typeof TlvDataVersionFilter>[];\n eventRequests?: TypeFromSchema<typeof TlvEventPath>[];\n eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];\n isFabricFiltered: boolean;\n minIntervalFloor: number;\n maxIntervalCeiling: number;\n cancelCallback: () => void;\n subscriptionOptions: SubscriptionOptions.Configuration;\n readAttribute: (path: AttributePath, attribute: AnyAttributeServer<any>) => Promise<any>;\n readEvent: (\n path: EventPath,\n event: AnyEventServer<any, any>,\n eventFilters: TypeFromSchema<typeof TlvEventFilter>[] | undefined,\n ) => Promise<EventStorageData<any>[]>;\n }) {\n const {\n subscriptionId,\n session,\n endpointStructure,\n attributeRequests,\n dataVersionFilters,\n eventRequests,\n eventFilters,\n isFabricFiltered,\n minIntervalFloor,\n maxIntervalCeiling,\n cancelCallback,\n subscriptionOptions,\n } = options;\n this.subscriptionId = subscriptionId;\n this.session = session;\n this.endpointStructure = endpointStructure;\n this.attributeRequests = attributeRequests;\n this.dataVersionFilters = dataVersionFilters;\n this.eventRequests = eventRequests;\n this.eventFilters = eventFilters;\n this.isFabricFiltered = isFabricFiltered;\n this.cancelCallback = cancelCallback;\n this.readAttribute = options.readAttribute;\n this.readEvent = options.readEvent;\n\n this.server = this.session.context;\n this.fabric = this.session.associatedFabric;\n this.peerNodeId = this.session.peerNodeId;\n this.minIntervalFloorMs = minIntervalFloor * 1000;\n this.maxIntervalCeilingMs = maxIntervalCeiling * 1000;\n\n const { maxInterval, sendInterval } = this.determineSendingIntervals(\n subscriptionOptions.minIntervalSeconds * 1000,\n subscriptionOptions.maxIntervalSeconds * 1000,\n subscriptionOptions.randomizationWindowSeconds * 1000,\n );\n this.#maxIntervalMs = maxInterval;\n this.#sendIntervalMs = sendInterval;\n\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () => this.prepareDataUpdate()); // will be started later\n this.sendDelayTimer = Time.getTimer(\"Subscription delay\", 50, () =>\n this.sendUpdate().catch(error => logger.warn(\"Sending subscription update failed:\", error)),\n ); // will be started later\n }\n\n private determineSendingIntervals(\n subscriptionMinIntervalMs: number,\n subscriptionMaxIntervalMs: number,\n subscriptionRandomizationWindowMs: number,\n ): { maxInterval: number; sendInterval: number } {\n // Max Interval is the Max interval that the controller request, unless the configured one from the developer\n // is lower. In that case we use the configured one. But we make sure to not be smaller than the requested\n // controller minimum. But in general never faster than minimum interval configured or 2 seconds\n // (SUBSCRIPTION_MIN_INTERVAL_S). Additionally, we add a randomization window to the max interval to avoid all\n // devices sending at the same time. But we make sure not to exceed the global max interval.\n const maxInterval = Math.min(\n Math.max(\n subscriptionMinIntervalMs,\n Math.max(this.minIntervalFloorMs, Math.min(subscriptionMaxIntervalMs, this.maxIntervalCeilingMs)),\n ) + Math.floor(subscriptionRandomizationWindowMs * Math.random()),\n MAX_INTERVAL_PUBLISHER_LIMIT_S * 1000,\n );\n let sendInterval = Math.floor(maxInterval / 2); // Ideally we send at half the max interval\n if (sendInterval < 60_000) {\n // But if we have no chance of at least one full resubmission process we do like chip-tool.\n // One full resubmission process takes 33-45 seconds. So 60s means we reach at least first 2 retries of a\n // second subscription report after first failed.\n sendInterval = Math.max(this.minIntervalFloorMs, Math.floor(maxInterval * 0.8));\n }\n if (sendInterval < subscriptionMinIntervalMs) {\n // But not faster than once every 2s\n logger.warn(\n `Determined subscription send interval of ${sendInterval}ms is too low. Using maxInterval (${maxInterval}ms) instead.`,\n );\n sendInterval = subscriptionMinIntervalMs;\n }\n return { maxInterval, sendInterval };\n }\n\n private registerNewAttributes() {\n const newAttributes = new Array<AttributeWithPath>();\n const attributeErrors = new Array<TypeFromSchema<typeof TlvAttributeStatus>>();\n const formerAttributes = new Set<string>(this.attributeListeners.keys());\n\n if (this.attributeRequests !== undefined) {\n this.attributeRequests.forEach(path => {\n const attributes = this.endpointStructure.getAttributes([path]);\n\n if (attributes.length === 0) {\n // TODO: Also check nodeId\n const { endpointId, clusterId, attributeId } = path;\n if (endpointId === undefined || clusterId === undefined || attributeId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: ignore non-existing attribute`,\n );\n } else {\n // was a concrete path\n try {\n this.endpointStructure.validateConcreteAttributePath(endpointId, clusterId, attributeId);\n throw new InternalError(\n \"validateConcreteAttributePath check should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription attribute ${this.endpointStructure.resolveAttributeName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n attributeErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n attributes.forEach(({ path, attribute }) => {\n formerAttributes.delete(attributePathToId(path));\n\n const existingAttributeListener = this.attributeListeners.get(attributePathToId(path));\n if (existingAttributeListener !== undefined) {\n const { attribute: existingAttribute, listener: existingListener } = existingAttributeListener;\n if (existingAttribute !== attribute) {\n if (existingListener !== undefined) {\n existingAttribute.removeValueChangeListener(existingListener);\n }\n this.attributeListeners.delete(attributePathToId(path));\n } else {\n return; // Attribute is already registered and unchanged\n }\n }\n if (attribute.isSubscribable) {\n // If subscribable register listener\n // TODO: Move to state change listeners from behaviors to remove the dangling promise here\n const listener = (value: any, version: number) =>\n this.attributeChangeListener(path, attribute.schema, version, value);\n attribute.addValueChangeListener(listener);\n this.attributeListeners.set(attributePathToId(path), { attribute, listener });\n } else {\n this.attributeListeners.set(attributePathToId(path), { attribute });\n }\n newAttributes.push({ path, attribute });\n });\n });\n }\n\n // Remove all listeners to attributes that no longer match the subscription\n this.unregisterAttributeListeners(Array.from(formerAttributes.values()));\n return { newAttributes, attributeErrors };\n }\n\n unregisterAttributeListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingAttributeListener = this.attributeListeners.get(pathId);\n if (existingAttributeListener !== undefined) {\n const { attribute, listener } = existingAttributeListener;\n if (listener !== undefined) {\n attribute.removeValueChangeListener(listener);\n }\n this.attributeListeners.delete(pathId);\n }\n }\n }\n\n private registerNewEvents() {\n const newEvents = new Array<EventWithPath>();\n const eventErrors = new Array<TypeFromSchema<typeof TlvEventStatus>>();\n const formerEvents = new Set<string>(this.eventListeners.keys());\n\n if (this.eventRequests !== undefined) {\n this.eventRequests.forEach(path => {\n const events = this.endpointStructure.getEvents([path]);\n if (events.length === 0) {\n const { endpointId, clusterId, eventId } = path;\n if (endpointId === undefined || clusterId === undefined || eventId === undefined) {\n // Wildcard path: Just leave out values\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: ignore non-existing event`,\n );\n } else {\n try {\n this.endpointStructure.validateConcreteEventPath(endpointId, clusterId, eventId);\n throw new InternalError(\n \"validateConcreteEventPath should throw StatusResponseError but did not.\",\n );\n } catch (e) {\n StatusResponseError.accept(e);\n\n logger.debug(\n `Subscription event ${this.endpointStructure.resolveEventName(\n path,\n )}: unsupported path: Status=${e.code}`,\n );\n\n eventErrors.push({ path, status: { status: e.code } });\n }\n }\n return;\n }\n\n events.forEach(({ path, event }) => {\n formerEvents.delete(eventPathToId(path));\n\n const existingEventListener = this.eventListeners.get(eventPathToId(path));\n if (existingEventListener !== undefined) {\n const { event: existingEvent, listener: existingListener } = existingEventListener;\n if (existingEvent !== event) {\n if (existingListener !== undefined) {\n existingEvent.removeListener(existingListener);\n }\n this.eventListeners.delete(eventPathToId(path));\n } else {\n return; // Event is already registered and unchanged\n }\n }\n const listener = (newEvent: EventStorageData<any>) =>\n this.eventChangeListener(path, event.schema, newEvent);\n event.addListener(listener);\n newEvents.push({ path, event });\n this.eventListeners.set(eventPathToId(path), { event, listener });\n });\n });\n }\n\n // Remove all listeners to events that no longer match the subscription\n this.unregisterEventListeners(Array.from(formerEvents.values()));\n\n return { newEvents, eventErrors };\n }\n\n unregisterEventListeners(list: Array<string>) {\n for (const pathId of list) {\n const existingEventListener = this.eventListeners.get(pathId);\n if (existingEventListener !== undefined) {\n const { event, listener } = existingEventListener;\n if (listener !== undefined) {\n event.removeListener(listener);\n }\n this.eventListeners.delete(pathId);\n }\n }\n }\n\n /**\n * Update the session after an endpoint structure change. The method will initialize all missing new attributes and\n * events and will remove listeners no longer needed.\n * Newly added attributes are then treated ad \"changed values\" and will be sent as subscription data update to the\n * controller. The data of newly added events are not sent automatically.\n */\n async updateSubscription() {\n const { newAttributes } = this.registerNewAttributes();\n\n for (const { path, attribute } of newAttributes) {\n const { version, value } = await this.readAttribute(path, attribute);\n\n // We do not do any version filtering for attributes that are newly added to make sure controller gets\n // most current state\n\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema: attribute.schema,\n version,\n value,\n });\n }\n\n const { newEvents } = this.registerNewEvents();\n newEvents\n .flatMap(({ path, event }): EventPathWithEventData<any>[] => {\n // But we use eventFilters because we do not want to send all events to the controller\n const { schema } = event;\n const matchingEvents = event.get(this.session, this.isFabricFiltered, undefined, this.eventFilters);\n return matchingEvents.map(data => ({\n event,\n schema,\n path,\n data,\n }));\n })\n .sort((a, b) => {\n const eventNumberA = a.data?.eventNumber ?? EventNumber(0);\n const eventNumberB = b.data?.eventNumber ?? EventNumber(0);\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n })\n .forEach(event => this.outstandingEventUpdates.add(event));\n\n this.prepareDataUpdate();\n }\n\n get maxInterval(): number {\n return Math.ceil(this.#maxIntervalMs / 1000);\n }\n\n get sendInterval(): number {\n return Math.ceil(this.#sendIntervalMs / 1000);\n }\n\n activateSendingUpdates() {\n // We do not need these data anymore, so we can free some memory\n if (this.eventFilters !== undefined) this.eventFilters.length = 0;\n if (this.dataVersionFilters !== undefined) this.dataVersionFilters.length = 0;\n\n this.sendUpdatesActivated = true;\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Check if data should be sent straight away or delayed because the minimum interval is not reached. Delay real\n * sending by 50ms in any case to mke sure to catch all updates.\n */\n prepareDataUpdate() {\n if (this.sendDelayTimer.isRunning) {\n // sending data is already scheduled, data updates go in there\n return;\n }\n\n if (!this.sendUpdatesActivated) {\n return;\n }\n\n this.updateTimer.stop();\n const now = Time.nowMs();\n const timeSinceLastUpdateMs = now - this.lastUpdateTimeMs;\n if (timeSinceLastUpdateMs < this.minIntervalFloorMs) {\n // Respect minimum delay time between updates\n this.updateTimer = Time.getTimer(\n \"Subscription update\",\n this.minIntervalFloorMs - timeSinceLastUpdateMs,\n () => this.prepareDataUpdate(),\n ).start();\n return;\n }\n\n this.sendDelayTimer.start();\n this.updateTimer = Time.getTimer(\"Subscription update\", this.#sendIntervalMs, () =>\n this.prepareDataUpdate(),\n ).start();\n }\n\n /**\n * Determine all attributes that have changed since the last update and send them tout to the subscriber.\n */\n async sendUpdate() {\n if (this.sendingUpdateInProgress) {\n logger.debug(\"Sending update already in progress, delaying update ...\");\n this.sendNextUpdateImmediately = true;\n return;\n }\n\n // Get all outstanding updates, make sure the order is correct per endpoint and cluster\n const attributeUpdatesToSend = new Array<AttributePathWithValueVersion<any>>();\n const attributeUpdates: Record<string, AttributePathWithValueVersion<any>[]> = {};\n Array.from(this.outstandingAttributeUpdates.values()).forEach(entry => {\n const {\n path: { nodeId, endpointId, clusterId },\n } = entry;\n const pathId = `${nodeId}-${endpointId}-${clusterId}`;\n attributeUpdates[pathId] = attributeUpdates[pathId] ?? [];\n attributeUpdates[pathId].push(entry);\n });\n this.outstandingAttributeUpdates.clear();\n Object.values(attributeUpdates).forEach(data =>\n attributeUpdatesToSend.push(\n ...data.sort(({ version: versionA }, { version: versionB }) => versionA - versionB),\n ),\n );\n\n const eventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n this.lastUpdateTimeMs = Time.nowMs();\n\n this.sendingUpdateInProgress = true;\n try {\n await this.sendUpdateMessage(attributeUpdatesToSend, eventUpdatesToSend);\n this.sendUpdateErrorCounter = 0;\n } catch (error) {\n if (this.server.isClosing) {\n // No need to care about resubmissions when the server is closing\n return;\n }\n\n this.sendUpdateErrorCounter++;\n logger.error(\n `Error sending subscription update message (error count=${this.sendUpdateErrorCounter}):`,\n error,\n );\n if (this.sendUpdateErrorCounter <= 2) {\n // fill the data back in the queue to resend with next try\n const newAttributeUpdatesToSend = Array.from(this.outstandingAttributeUpdates.values());\n this.outstandingAttributeUpdates.clear();\n const newEventUpdatesToSend = Array.from(this.outstandingEventUpdates.values());\n this.outstandingEventUpdates.clear();\n [...attributeUpdatesToSend, ...newAttributeUpdatesToSend].forEach(update =>\n this.outstandingAttributeUpdates.set(attributePathToId(update.path), update),\n );\n [...eventUpdatesToSend, ...newEventUpdatesToSend].forEach(update =>\n this.outstandingEventUpdates.add(update),\n );\n } else {\n logger.error(\n `Sending update failed 3 times in a row, canceling subscription ${this.subscriptionId} and let controller subscribe again.`,\n );\n this.sendNextUpdateImmediately = false;\n if (error instanceof RetransmissionLimitReachedError || error instanceof NetworkError) {\n // We could not send at all, consider session as dead\n await this.session.destroy(false);\n } else {\n throw error;\n }\n }\n }\n this.sendingUpdateInProgress = false;\n\n if (this.sendNextUpdateImmediately) {\n logger.debug(\"Sending delayed update immediately after last one was sent.\");\n this.sendNextUpdateImmediately = false;\n await this.sendUpdate();\n }\n }\n\n async sendInitialReport(messenger: InteractionServerMessenger) {\n this.updateTimer.stop();\n\n const { newAttributes, attributeErrors } = this.registerNewAttributes();\n\n const dataVersionFilterMap = new Map<string, number>(\n this.dataVersionFilters?.map(({ path, dataVersion }) => [clusterPathToId(path), dataVersion]) ?? [],\n );\n\n let attributesFilteredWithVersion = false;\n const attributes = new Array<{\n path: TypeFromSchema<typeof TlvAttributePath>;\n value: any;\n version: number;\n schema: TlvSchema<any>;\n attribute: AnyAttributeServer<any>;\n }>();\n for (const { path, attribute } of newAttributes) {\n try {\n const { value, version } = await this.readAttribute(path, attribute);\n if (value === undefined) continue;\n\n const { nodeId, endpointId, clusterId } = path;\n\n const versionFilterValue =\n endpointId !== undefined && clusterId !== undefined\n ? dataVersionFilterMap.get(clusterPathToId({ nodeId, endpointId, clusterId }))\n : undefined;\n if (versionFilterValue !== undefined && versionFilterValue === version) {\n attributesFilteredWithVersion = true;\n continue;\n }\n\n attributes.push({ path, value, version, schema: attribute.schema, attribute });\n } catch (error) {\n logger.error(`Error reading attribute ${this.endpointStructure.resolveAttributeName(path)}:`, error);\n }\n }\n const attributeReportsPayload: AttributeReportPayload[] = attributes.map(\n ({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n payload: value,\n schema,\n },\n }),\n );\n attributeErrors.forEach(attributeStatus =>\n attributeReportsPayload.push({\n hasFabricSensitiveData: false,\n attributeStatus,\n }),\n );\n\n const { newEvents, eventErrors } = this.registerNewEvents();\n\n let eventsFiltered = false;\n const eventReportsPayload = new Array<EventReportPayload>();\n for (const { path, event } of newEvents) {\n const { schema } = event;\n try {\n const matchingEvents = await this.readEvent(path, event, this.eventFilters);\n if (matchingEvents.length === 0) {\n eventsFiltered = true;\n } else {\n matchingEvents.forEach(({ eventNumber, priority, epochTimestamp, data }) => {\n eventReportsPayload.push({\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n payload: data,\n schema,\n },\n });\n });\n }\n } catch (error) {\n logger.error(`Error reading event ${this.endpointStructure.resolveEventName(path)}:`, error);\n }\n }\n eventReportsPayload.sort((a, b) => {\n const eventNumberA = a.eventData?.eventNumber ?? 0;\n const eventNumberB = b.eventData?.eventNumber ?? 0;\n if (eventNumberA > eventNumberB) {\n return 1;\n } else if (eventNumberA < eventNumberB) {\n return -1;\n } else {\n return 0;\n }\n });\n\n if (\n attributes.length === 0 &&\n !attributesFilteredWithVersion &&\n eventReportsPayload.length === 0 &&\n !eventsFiltered\n ) {\n throw new StatusResponseError(\n \"Subscription failed because no attributes or events are matching the query\",\n StatusCode.InvalidAction,\n );\n }\n\n eventErrors.forEach(eventStatus =>\n eventReportsPayload.push({\n hasFabricSensitiveData: false,\n eventStatus,\n }),\n );\n\n logger.debug(\n `Initialize Subscription with ${attributes.length} attributes and ${eventReportsPayload.length} events.`,\n );\n this.lastUpdateTimeMs = Time.nowMs();\n\n await messenger.sendDataReport(\n {\n suppressResponse: false, // we always need proper response for initial report\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload,\n eventReportsPayload,\n },\n this.isFabricFiltered,\n );\n }\n\n attributeChangeListener<T>(path: AttributePath, schema: TlvSchema<T>, version: number, value: T) {\n const changeResult = this.attributeChangeHandler(path, schema, version, value);\n if (MaybePromise.is(changeResult)) {\n const resolver = Promise.resolve(changeResult)\n .catch(error => logger.error(`Error handling attribute change:`, error))\n .finally(() => this.attributeUpdatePromises.delete(resolver));\n this.attributeUpdatePromises.add(resolver);\n }\n }\n\n attributeChangeHandler<T>(\n path: AttributePath,\n schema: TlvSchema<T>,\n version: number,\n value: T,\n ): MaybePromise<void> {\n const attributeListenerData = this.attributeListeners.get(attributePathToId(path));\n if (attributeListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { attribute } = attributeListenerData;\n if (attribute instanceof FabricScopedAttributeServer) {\n // We cannot be sure what value we got for fabric filtered attributes (and from which fabric),\n // so get it again for this relevant fabric. This also makes sure that fabric sensitive fields are filtered\n // TODO: Maybe add try/catch when we add ACL handling and ignore the update if we cannot get the value?\n return this.readAttribute(path, attribute).then(({ value }) => {\n this.outstandingAttributeUpdates.set(attributePathToId(path), {\n attribute,\n path,\n schema,\n version,\n value,\n });\n this.prepareDataUpdate();\n });\n }\n this.outstandingAttributeUpdates.set(attributePathToId(path), { attribute, path, schema, version, value });\n this.prepareDataUpdate();\n }\n\n eventChangeListener<T>(path: EventPath, schema: TlvSchema<T>, newEvent: EventStorageData<T>) {\n const eventListenerData = this.eventListeners.get(eventPathToId(path));\n if (eventListenerData === undefined) return; // Ignore changes to attributes that are not subscribed to\n\n const { event } = eventListenerData;\n if (event instanceof FabricSensitiveEventServer) {\n const { data } = newEvent;\n if (isObject(data) && \"fabricIndex\" in data && data.fabricIndex !== this.session.fabric?.fabricIndex) {\n // Ignore events from different fabrics because events are kind of always fabric filtered\n return;\n }\n }\n this.outstandingEventUpdates.add({ event, path, schema, data: newEvent });\n if (path.isUrgent) {\n this.prepareDataUpdate();\n }\n }\n\n async flush() {\n this.sendDelayTimer.stop();\n logger.debug(\n `Flushing subscription ${this.subscriptionId} with ${this.outstandingAttributeUpdates.size} attributes and ${this.outstandingEventUpdates.size} events`,\n );\n if (this.outstandingAttributeUpdates.size > 0 || this.outstandingEventUpdates.size > 0) {\n void this.sendUpdate();\n }\n }\n\n async cancel(flush = false, cancelledByPeer = false) {\n this.sendUpdatesActivated = false;\n if (this.attributeUpdatePromises.size) {\n const resolvers = [...this.attributeUpdatePromises.values()];\n this.attributeUpdatePromises.clear();\n await Promise.all(resolvers);\n }\n this.updateTimer.stop();\n this.sendDelayTimer.stop();\n this.unregisterAttributeListeners(Array.from(this.attributeListeners.keys()));\n this.unregisterEventListeners(Array.from(this.eventListeners.keys()));\n if (flush) {\n await this.flush();\n }\n this.session.removeSubscription(this.subscriptionId);\n this.cancelCallback();\n if (cancelledByPeer) {\n await this.session.context.startAnnouncement();\n }\n }\n\n private async sendUpdateMessage(\n attributes: AttributePathWithValueVersion<any>[],\n events: EventPathWithEventData<any>[],\n ) {\n logger.debug(\n `Sending subscription update message for ID ${this.subscriptionId} with ${attributes.length} attributes and ${events.length} events`,\n );\n const exchange = this.server.initiateExchange(this.fabric, this.peerNodeId, INTERACTION_PROTOCOL_ID);\n if (exchange === undefined) return;\n logger.debug(\n `Sending subscription changes for ID ${this.subscriptionId}: ${attributes\n .map(\n ({ path, value, version }) =>\n `${this.endpointStructure.resolveAttributeName(path)}=${Logger.toJSON(value)} (${version})`,\n )\n .join(\", \")}`,\n ); // TODO Format path better using endpoint structure\n const messenger = new InteractionServerMessenger(exchange);\n\n try {\n if (attributes.length === 0 && events.length === 0) {\n await messenger.sendDataReport(\n {\n suppressResponse: true, // suppressResponse true for empty DataReports\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n },\n this.isFabricFiltered,\n );\n } else {\n await messenger.sendDataReport(\n {\n suppressResponse: false, // Non empty data reports always need to send response\n subscriptionId: this.subscriptionId,\n interactionModelRevision: INTERACTION_MODEL_REVISION,\n attributeReportsPayload: attributes.map(({ path, schema, value, version, attribute }) => ({\n hasFabricSensitiveData: attribute.hasFabricSensitiveData,\n attributeData: {\n path,\n dataVersion: version,\n schema,\n payload: value,\n },\n })),\n eventReportsPayload: events.map(({ path, schema, event, data }) => {\n const { eventNumber, priority, epochTimestamp, data: payload } = data;\n return {\n hasFabricSensitiveData: event.hasFabricSensitiveData,\n eventData: {\n path,\n eventNumber,\n priority,\n epochTimestamp,\n schema,\n payload,\n },\n };\n }),\n },\n this.isFabricFiltered,\n );\n }\n } catch (error) {\n if (StatusResponseError.is(error, StatusCode.InvalidSubscription, StatusCode.Failure)) {\n logger.info(`Subscription ${this.subscriptionId} cancelled by peer.`);\n await this.cancel(false, true);\n } else {\n StatusResponseError.accept(error);\n await this.cancel(false);\n }\n } finally {\n await messenger.close();\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path", "value"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@project-chip/matter.js",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Matter protocol in pure js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iot",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@noble/curves": "^1.5.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@project-chip/matter.js-tools": "0.10.
|
|
39
|
+
"@project-chip/matter.js-tools": "0.10.3",
|
|
40
40
|
"@types/chai": "^4.3.16",
|
|
41
41
|
"@types/mocha": "^10.0.7",
|
|
42
42
|
"@types/wtfnode": "^0.7.3",
|
|
@@ -199,5 +199,5 @@
|
|
|
199
199
|
"publishConfig": {
|
|
200
200
|
"access": "public"
|
|
201
201
|
},
|
|
202
|
-
"gitHead": "
|
|
202
|
+
"gitHead": "ccda21285cc5dedc1f362e289654729333018662"
|
|
203
203
|
}
|
|
@@ -46,25 +46,20 @@ export class ChannelManager {
|
|
|
46
46
|
channel.closeCallback = async () => this.removeChannel(fabric, nodeId, channel.session);
|
|
47
47
|
const channelsKey = this.#getChannelKey(fabric, nodeId);
|
|
48
48
|
const currentChannels = this.#channels.get(channelsKey) ?? [];
|
|
49
|
-
|
|
49
|
+
currentChannels.push(channel);
|
|
50
|
+
this.#channels.set(channelsKey, currentChannels);
|
|
51
|
+
if (currentChannels.length > this.#caseSessionsPerFabricAndNode) {
|
|
50
52
|
const oldestChannel = this.#findLeastActiveChannel(currentChannels);
|
|
51
|
-
currentChannels.splice(currentChannels.indexOf(oldestChannel), 1);
|
|
52
|
-
currentChannels.push(channel);
|
|
53
|
-
this.#channels.set(channelsKey, currentChannels);
|
|
54
53
|
|
|
54
|
+
const { session: oldSession } = oldestChannel;
|
|
55
55
|
// Should always be the case
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
logger.debug(
|
|
59
|
-
`Existing channel for fabricIndex ${fabric.fabricIndex} and node ${nodeId} with session ${session.name} gets replaced. Consider former session already inactive and so close it.`,
|
|
60
|
-
);
|
|
61
|
-
await session.destroy(false, false);
|
|
56
|
+
if (channel.session.id !== oldSession.id) {
|
|
57
|
+
await oldSession.destroy(false, false);
|
|
62
58
|
}
|
|
63
|
-
logger.info(
|
|
59
|
+
logger.info(
|
|
60
|
+
`Close oldest channel for fabric ${fabric.fabricIndex} node ${nodeId} (from session ${oldSession.id})`,
|
|
61
|
+
);
|
|
64
62
|
await oldestChannel.close();
|
|
65
|
-
} else {
|
|
66
|
-
currentChannels.push(channel);
|
|
67
|
-
this.#channels.set(channelsKey, currentChannels);
|
|
68
63
|
}
|
|
69
64
|
}
|
|
70
65
|
|
|
@@ -73,7 +68,10 @@ export class ChannelManager {
|
|
|
73
68
|
if (session !== undefined) {
|
|
74
69
|
results = results.filter(channel => channel.session.id === session.id);
|
|
75
70
|
}
|
|
76
|
-
if (results.length === 0)
|
|
71
|
+
if (results.length === 0)
|
|
72
|
+
throw new NoChannelError(
|
|
73
|
+
`Can't find a channel to node ${nodeId}${session !== undefined ? ` and session ${session.id}` : ""}`,
|
|
74
|
+
);
|
|
77
75
|
return results[results.length - 1]; // Return the latest added channel (or the one belonging to the session requested)
|
|
78
76
|
}
|
|
79
77
|
|
|
@@ -107,6 +105,10 @@ export class ChannelManager {
|
|
|
107
105
|
const channelEntryIndex = fabricChannels.findIndex(
|
|
108
106
|
({ session: entrySession }) => entrySession.id === session.id,
|
|
109
107
|
);
|
|
108
|
+
if (channelEntryIndex === -1) {
|
|
109
|
+
// Seems already removed
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
110
112
|
const channelEntry = fabricChannels.splice(channelEntryIndex, 1)[0];
|
|
111
113
|
if (channelEntry === undefined) {
|
|
112
114
|
return;
|
|
@@ -173,7 +173,9 @@ export class SubscriptionHandler {
|
|
|
173
173
|
this.#sendIntervalMs = sendInterval;
|
|
174
174
|
|
|
175
175
|
this.updateTimer = Time.getTimer("Subscription update", this.#sendIntervalMs, () => this.prepareDataUpdate()); // will be started later
|
|
176
|
-
this.sendDelayTimer = Time.getTimer("Subscription delay", 50, () =>
|
|
176
|
+
this.sendDelayTimer = Time.getTimer("Subscription delay", 50, () =>
|
|
177
|
+
this.sendUpdate().catch(error => logger.warn("Sending subscription update failed:", error)),
|
|
178
|
+
); // will be started later
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
private determineSendingIntervals(
|
|
@@ -852,13 +854,13 @@ export class SubscriptionHandler {
|
|
|
852
854
|
this.isFabricFiltered,
|
|
853
855
|
);
|
|
854
856
|
}
|
|
855
|
-
} catch (
|
|
856
|
-
if (StatusResponseError.is(
|
|
857
|
+
} catch (error) {
|
|
858
|
+
if (StatusResponseError.is(error, StatusCode.InvalidSubscription, StatusCode.Failure)) {
|
|
857
859
|
logger.info(`Subscription ${this.subscriptionId} cancelled by peer.`);
|
|
858
860
|
await this.cancel(false, true);
|
|
859
861
|
} else {
|
|
862
|
+
StatusResponseError.accept(error);
|
|
860
863
|
await this.cancel(false);
|
|
861
|
-
throw e;
|
|
862
864
|
}
|
|
863
865
|
} finally {
|
|
864
866
|
await messenger.close();
|