Implement support for Get/SetParameterAttributes
This commit is contained in:
1 parent
6385f54888
commit
bf9517c652
10 files changed
+524
-56
No files matched your search
@@ -268,6 +268,19 @@ function sanitizeTask(task): void {
|
|||||||
)
|
)
|
||||||
throw new Error("Invalid 'targetFileName' property");
|
throw new Error("Invalid 'targetFileName' property");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "provisions":
|
||||||
|
if (
|
||||||
|
!Array.isArray(task.provisions) ||
|
||||||
|
!task.provisions.every((arr) =>
|
||||||
|
arr.every(
|
||||||
|
(s) =>
|
||||||
|
s == null || ["boolean", "number", "string"].includes(typeof s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
throw new Error("Invalid 'provisions' property");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
|
|||||||
@@ -715,6 +715,13 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
|
|||||||
["instances", task.objectName, 0],
|
["instances", task.objectName, 0],
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
|
case "provisions":
|
||||||
|
session.addProvisions(
|
||||||
|
sessionContext,
|
||||||
|
`task_${task._id}`,
|
||||||
|
task.provisions
|
||||||
|
);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("Task name not recognized");
|
throw new Error("Task name not recognized");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ export let tasksCollection: Collection,
|
|||||||
let clientPromise: Promise<MongoClient>;
|
let clientPromise: Promise<MongoClient>;
|
||||||
export let client: MongoClient;
|
export let client: MongoClient;
|
||||||
|
|
||||||
|
function compareAccessLists(list1: string[], list2: string[]): boolean {
|
||||||
|
if (list1.length !== list2.length) return false;
|
||||||
|
for (const [i, v] of list1.entries()) if (v !== list2[i]) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export async function connect(): Promise<void> {
|
export async function connect(): Promise<void> {
|
||||||
clientPromise = MongoClient.connect("" + get("MONGODB_CONNECTION_URL"), {
|
clientPromise = MongoClient.connect("" + get("MONGODB_CONNECTION_URL"), {
|
||||||
useNewUrlParser: true,
|
useNewUrlParser: true,
|
||||||
@@ -119,6 +125,8 @@ export async function fetchDevice(
|
|||||||
|
|
||||||
function storeParams(obj, path: string, pathLength: number, ts): void {
|
function storeParams(obj, path: string, pathLength: number, ts): void {
|
||||||
if (obj["_timestamp"]) obj["_timestamp"] = +obj["_timestamp"];
|
if (obj["_timestamp"]) obj["_timestamp"] = +obj["_timestamp"];
|
||||||
|
if (obj["_attributesTimestamp"])
|
||||||
|
obj["_attributesTimestamp"] = +obj["_attributesTimestamp"];
|
||||||
|
|
||||||
const attrs: Attributes = {};
|
const attrs: Attributes = {};
|
||||||
let t = obj["_timestamp"] || 1;
|
let t = obj["_timestamp"] || 1;
|
||||||
@@ -136,6 +144,16 @@ export async function fetchDevice(
|
|||||||
|
|
||||||
if (obj["_object"] != null) attrs.object = [t, obj["_object"] ? 1 : 0];
|
if (obj["_object"] != null) attrs.object = [t, obj["_object"] ? 1 : 0];
|
||||||
|
|
||||||
|
if (obj["_notification"] != null) {
|
||||||
|
attrs.notification = [
|
||||||
|
obj["_attributesTimestamp"] || 1,
|
||||||
|
obj["_notification"],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj["_accessList"] != null)
|
||||||
|
attrs.accessList = [obj["_attributesTimestamp"] || 1, obj["_accessList"]];
|
||||||
|
|
||||||
res.push([Path.parse(path.slice(0, -1)), t, attrs]);
|
res.push([Path.parse(path.slice(0, -1)), t, attrs]);
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(obj)) {
|
for (const [k, v] of Object.entries(obj)) {
|
||||||
@@ -332,6 +350,12 @@ export async function saveDevice(
|
|||||||
const object2 = ((diff[2] || {}).object || [])[1];
|
const object2 = ((diff[2] || {}).object || [])[1];
|
||||||
const writable2 = ((diff[2] || {}).writable || [])[1];
|
const writable2 = ((diff[2] || {}).writable || [])[1];
|
||||||
const writable1 = ((diff[1] || {}).writable || [])[1];
|
const writable1 = ((diff[1] || {}).writable || [])[1];
|
||||||
|
const attributesTimestamp1 = ((diff[1] || {}).notification || [])[0];
|
||||||
|
const attributesTimestamp2 = ((diff[2] || {}).notification || [])[0];
|
||||||
|
const notification1 = ((diff[1] || {}).notification || [])[1];
|
||||||
|
const notification2 = ((diff[2] || {}).notification || [])[1];
|
||||||
|
const accessList1 = ((diff[1] || {}).accessList || [])[1];
|
||||||
|
const accessList2 = ((diff[2] || {}).accessList || [])[1];
|
||||||
|
|
||||||
switch (path.segments[0]) {
|
switch (path.segments[0]) {
|
||||||
case "Events":
|
case "Events":
|
||||||
@@ -458,6 +482,46 @@ export async function saveDevice(
|
|||||||
path.length ? path.toString() + "._writable" : "_writable"
|
path.length ? path.toString() + "._writable" : "_writable"
|
||||||
] = !!writable2;
|
] = !!writable2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "notification":
|
||||||
|
if (
|
||||||
|
!diff[1] ||
|
||||||
|
!diff[1].notification ||
|
||||||
|
notification2 !== notification1
|
||||||
|
) {
|
||||||
|
update["$set"][
|
||||||
|
path.length
|
||||||
|
? path.toString() + "._notification"
|
||||||
|
: "_notification"
|
||||||
|
] = notification2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributesTimestamp2 !== attributesTimestamp1) {
|
||||||
|
update["$set"][
|
||||||
|
path.toString() + "._attributesTimestamp"
|
||||||
|
] = new Date(attributesTimestamp2);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "accessList":
|
||||||
|
if (
|
||||||
|
!diff[1] ||
|
||||||
|
!diff[1].accessList ||
|
||||||
|
!compareAccessLists(accessList2, accessList1)
|
||||||
|
) {
|
||||||
|
update["$set"][
|
||||||
|
path.length
|
||||||
|
? path.toString() + "._accessList"
|
||||||
|
: "_accessList"
|
||||||
|
] = accessList2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributesTimestamp2 !== attributesTimestamp1) {
|
||||||
|
update["$set"][
|
||||||
|
path.toString() + "._attributesTimestamp"
|
||||||
|
] = new Date(attributesTimestamp2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -473,6 +537,12 @@ export async function saveDevice(
|
|||||||
if (attrName === "value") {
|
if (attrName === "value") {
|
||||||
update["$unset"][p + "_type"] = 1;
|
update["$unset"][p + "_type"] = 1;
|
||||||
update["$unset"][p + "_timestamp"] = 1;
|
update["$unset"][p + "_timestamp"] = 1;
|
||||||
|
} else if (attrName === "notification") {
|
||||||
|
if (accessList2 == null)
|
||||||
|
update["$unset"][`${p}_attributesTimestamp`] = 1;
|
||||||
|
} else if (attrName === "accessList") {
|
||||||
|
if (notification2 == null)
|
||||||
|
update["$unset"][`${p}_attributesTimestamp`] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-11
@@ -34,25 +34,45 @@ export function refresh(
|
|||||||
(provision.length !== 2 || typeof provision[1] !== "string") &&
|
(provision.length !== 2 || typeof provision[1] !== "string") &&
|
||||||
(provision.length !== 3 ||
|
(provision.length !== 3 ||
|
||||||
typeof provision[1] !== "string" ||
|
typeof provision[1] !== "string" ||
|
||||||
typeof provision[2] !== "number")
|
typeof provision[2] !== "number") &&
|
||||||
|
(provision.length < 4 ||
|
||||||
|
typeof provision[1] !== "string" ||
|
||||||
|
typeof provision[2] !== "number" ||
|
||||||
|
typeof provision[3] !== "boolean")
|
||||||
)
|
)
|
||||||
throw new Error("Invalid arguments");
|
throw new Error("Invalid arguments");
|
||||||
|
|
||||||
const segments = Path.parse(provision[1]).segments.slice();
|
|
||||||
const l = segments.length;
|
|
||||||
segments.length = MAX_DEPTH;
|
|
||||||
segments.fill("*", l);
|
|
||||||
const path = Path.parse(segments.join("."));
|
|
||||||
const every = 1000 * ((provision[2] as number) || 1);
|
const every = 1000 * ((provision[2] as number) || 1);
|
||||||
const offset = scheduling.variance(sessionContext.deviceId, every);
|
const offset = scheduling.variance(sessionContext.deviceId, every);
|
||||||
const t = scheduling.interval(sessionContext.timestamp, every, offset);
|
const t = scheduling.interval(sessionContext.timestamp, every, offset);
|
||||||
|
|
||||||
for (let i = l; i < path.length; ++i) {
|
let attrGet;
|
||||||
|
let refreshChildren;
|
||||||
|
if (provision[3] == null) {
|
||||||
|
refreshChildren = true;
|
||||||
|
attrGet = { object: 1, writable: 1, value: t };
|
||||||
|
} else {
|
||||||
|
attrGet = {};
|
||||||
|
refreshChildren = !!provision[3];
|
||||||
|
for (const a of provision.slice(4)) attrGet[a as string] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = Path.parse(provision[1]);
|
||||||
|
let l = path.length;
|
||||||
|
if (refreshChildren) {
|
||||||
|
const segments = path.segments.slice();
|
||||||
|
l = segments.length;
|
||||||
|
segments.length = MAX_DEPTH;
|
||||||
|
segments.fill("*", l);
|
||||||
|
path = Path.parse(segments.join("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = l; i <= path.length; ++i) {
|
||||||
declarations.push({
|
declarations.push({
|
||||||
path: path.slice(0, i),
|
path: path.slice(0, i),
|
||||||
pathGet: t,
|
pathGet: t,
|
||||||
pathSet: null,
|
pathSet: null,
|
||||||
attrGet: { object: 1, writable: 1, value: t },
|
attrGet: attrGet,
|
||||||
attrSet: null,
|
attrSet: null,
|
||||||
defer: true,
|
defer: true,
|
||||||
});
|
});
|
||||||
@@ -66,15 +86,38 @@ export function value(
|
|||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[]
|
declarations: Declaration[]
|
||||||
): boolean {
|
): boolean {
|
||||||
if (provision.length !== 3 || typeof provision[1] !== "string")
|
if (
|
||||||
|
provision.length < 3 ||
|
||||||
|
provision.length > 4 ||
|
||||||
|
typeof provision[1] !== "string"
|
||||||
|
)
|
||||||
throw new Error("Invalid arguments");
|
throw new Error("Invalid arguments");
|
||||||
|
|
||||||
|
let attr: string, val: any;
|
||||||
|
|
||||||
|
if (provision.length === 3) {
|
||||||
|
attr = "value";
|
||||||
|
val = provision[2];
|
||||||
|
} else {
|
||||||
|
attr = (provision[2] as string) || "";
|
||||||
|
val = provision[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr === "accessList") {
|
||||||
|
val = (val || "")
|
||||||
|
.split(",")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => !!s);
|
||||||
|
} else if (attr === "value") {
|
||||||
|
val = [val];
|
||||||
|
}
|
||||||
|
|
||||||
declarations.push({
|
declarations.push({
|
||||||
path: Path.parse(provision[1]),
|
path: Path.parse(provision[1]),
|
||||||
pathGet: 1,
|
pathGet: 1,
|
||||||
pathSet: null,
|
pathSet: null,
|
||||||
attrGet: { value: 1 },
|
attrGet: { [attr]: 1 },
|
||||||
attrSet: { value: [provision[2]] },
|
attrSet: { [attr]: val },
|
||||||
defer: true,
|
defer: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ const CHANGE_FLAGS = {
|
|||||||
object: 2,
|
object: 2,
|
||||||
writable: 4,
|
writable: 4,
|
||||||
value: 8,
|
value: 8,
|
||||||
|
notification: 16,
|
||||||
|
accessList: 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseBool(v): boolean {
|
function parseBool(v): boolean {
|
||||||
|
|||||||
+42
-21
@@ -304,19 +304,30 @@ function parseDate(d: Date): number | string {
|
|||||||
return isNaN(n) ? "" + d : n;
|
return isNaN(n) ? "" + d : n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FlatAttributes {
|
||||||
|
object?: boolean;
|
||||||
|
objectTimestamp?: number;
|
||||||
|
writable?: boolean;
|
||||||
|
writableTimestamp?: number;
|
||||||
|
value?: [string | number | boolean, string];
|
||||||
|
valueTimestamp?: number;
|
||||||
|
notification?: number;
|
||||||
|
notificationTimestamp?: number;
|
||||||
|
accessList?: string[];
|
||||||
|
accessListTimestamp?: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface FlatDevice {
|
interface FlatDevice {
|
||||||
[param: string]: {
|
[param: string]: FlatAttributes;
|
||||||
object: boolean;
|
|
||||||
objectTimestamp: number;
|
|
||||||
writable: boolean;
|
|
||||||
writableTimestamp: number;
|
|
||||||
value: [string | number | boolean, string];
|
|
||||||
valueTimestamp: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flattenDevice(device: Record<string, unknown>): FlatDevice {
|
export function flattenDevice(device: Record<string, unknown>): FlatDevice {
|
||||||
function recursive(input, root, output, timestamp): void {
|
function recursive(
|
||||||
|
input,
|
||||||
|
root: string,
|
||||||
|
output: FlatDevice,
|
||||||
|
timestamp: number
|
||||||
|
): void {
|
||||||
for (const [name, tree] of Object.entries(input)) {
|
for (const [name, tree] of Object.entries(input)) {
|
||||||
if (!root) {
|
if (!root) {
|
||||||
if (name === "_lastInform") {
|
if (name === "_lastInform") {
|
||||||
@@ -357,7 +368,7 @@ export function flattenDevice(device: Record<string, unknown>): FlatDevice {
|
|||||||
};
|
};
|
||||||
} else if (name === "_id") {
|
} else if (name === "_id") {
|
||||||
output["DeviceID.ID"] = {
|
output["DeviceID.ID"] = {
|
||||||
value: [tree, "xsd:string"],
|
value: [tree as string, "xsd:string"],
|
||||||
valueTimestamp: timestamp,
|
valueTimestamp: timestamp,
|
||||||
writable: false,
|
writable: false,
|
||||||
writableTimestamp: timestamp,
|
writableTimestamp: timestamp,
|
||||||
@@ -426,34 +437,44 @@ export function flattenDevice(device: Record<string, unknown>): FlatDevice {
|
|||||||
else if (+input["_timestamp"] > timestamp)
|
else if (+input["_timestamp"] > timestamp)
|
||||||
childrenTimestamp = +input["_timestamp"];
|
childrenTimestamp = +input["_timestamp"];
|
||||||
|
|
||||||
const attrs = {};
|
const attrs: FlatAttributes = {};
|
||||||
if (tree["_value"] != null) {
|
if (tree["_value"] != null) {
|
||||||
attrs["value"] = [
|
attrs.value = [
|
||||||
tree["_value"] instanceof Date ? +tree["_value"] : tree["_value"],
|
tree["_value"] instanceof Date ? +tree["_value"] : tree["_value"],
|
||||||
tree["_type"],
|
tree["_type"],
|
||||||
];
|
];
|
||||||
attrs["valueTimestamp"] = +(tree["_timestamp"] || childrenTimestamp);
|
attrs.valueTimestamp = +(tree["_timestamp"] || childrenTimestamp);
|
||||||
attrs["object"] = false;
|
attrs.object = false;
|
||||||
attrs["objectTimestamp"] = childrenTimestamp;
|
attrs.objectTimestamp = childrenTimestamp;
|
||||||
} else if (tree["_object"] != null) {
|
} else if (tree["_object"] != null) {
|
||||||
attrs["object"] = tree["_object"];
|
attrs.object = tree["_object"];
|
||||||
attrs["objectTimestamp"] = childrenTimestamp;
|
attrs.objectTimestamp = childrenTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree["_writable"] != null) {
|
if (tree["_writable"] != null) {
|
||||||
attrs["writable"] = tree["_writable"];
|
attrs.writable = tree["_writable"];
|
||||||
attrs["writableTimestamp"] = childrenTimestamp;
|
attrs.writableTimestamp = childrenTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree["_notification"] != null) {
|
||||||
|
attrs.notification = tree["_notification"];
|
||||||
|
attrs.notificationTimestamp = +tree["_attributesTimestamp"] || 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree["_accessList"] != null) {
|
||||||
|
attrs.accessList = tree["_accessList"];
|
||||||
|
attrs.accessListTimestamp = +tree["_attributesTimestamp"] || 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const r = root ? `${root}.${name}` : name;
|
const r = root ? `${root}.${name}` : name;
|
||||||
output[r] = attrs;
|
output[r] = attrs;
|
||||||
|
|
||||||
if (attrs["object"] || tree["object"] == null)
|
if (attrs.object || tree["object"] == null)
|
||||||
recursive(tree, r, output, childrenTimestamp);
|
recursive(tree, r, output, childrenTimestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const newDevice = {};
|
const newDevice: FlatDevice = {};
|
||||||
const timestamp = new Date((device["_lastInform"] as Date) || 1).getTime();
|
const timestamp = new Date((device["_lastInform"] as Date) || 1).getTime();
|
||||||
recursive(device, "", newDevice, timestamp);
|
recursive(device, "", newDevice, timestamp);
|
||||||
return newDevice;
|
return newDevice;
|
||||||
|
|||||||
+185
-16
@@ -770,8 +770,11 @@ function runDeclarations(
|
|||||||
object: new Set(),
|
object: new Set(),
|
||||||
writable: new Set(),
|
writable: new Set(),
|
||||||
value: new Set(),
|
value: new Set(),
|
||||||
|
notification: new Set(),
|
||||||
|
accessList: new Set(),
|
||||||
},
|
},
|
||||||
spv: new Map(),
|
spv: new Map(),
|
||||||
|
spa: new Map(),
|
||||||
gpn: new Set<Path>(),
|
gpn: new Set<Path>(),
|
||||||
gpnPatterns: new Map(),
|
gpnPatterns: new Map(),
|
||||||
tags: new Map(),
|
tags: new Map(),
|
||||||
@@ -1508,6 +1511,8 @@ function generateGetRpcRequest(sessionContext: SessionContext): GetAcsRequest {
|
|||||||
syncState.refreshAttributes.value.has(p) ||
|
syncState.refreshAttributes.value.has(p) ||
|
||||||
syncState.refreshAttributes.object.has(p) ||
|
syncState.refreshAttributes.object.has(p) ||
|
||||||
syncState.refreshAttributes.writable.has(p) ||
|
syncState.refreshAttributes.writable.has(p) ||
|
||||||
|
syncState.refreshAttributes.notification.has(p) ||
|
||||||
|
syncState.refreshAttributes.accessList.has(p) ||
|
||||||
syncState.gpn.has(p)
|
syncState.gpn.has(p)
|
||||||
) {
|
) {
|
||||||
found = true;
|
found = true;
|
||||||
@@ -1536,7 +1541,9 @@ function generateGetRpcRequest(sessionContext: SessionContext): GetAcsRequest {
|
|||||||
syncState.refreshAttributes.value.has(p) ||
|
syncState.refreshAttributes.value.has(p) ||
|
||||||
(p.length > path.length &&
|
(p.length > path.length &&
|
||||||
(syncState.refreshAttributes.object.has(p) ||
|
(syncState.refreshAttributes.object.has(p) ||
|
||||||
syncState.refreshAttributes.writable.has(p)))
|
syncState.refreshAttributes.writable.has(p) ||
|
||||||
|
syncState.refreshAttributes.notification.has(p) ||
|
||||||
|
syncState.refreshAttributes.accessList.has(p)))
|
||||||
) {
|
) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@@ -1648,9 +1655,59 @@ function generateGetRpcRequest(sessionContext: SessionContext): GetAcsRequest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
syncState.refreshAttributes.notification.size ||
|
||||||
|
syncState.refreshAttributes.accessList.size
|
||||||
|
) {
|
||||||
|
const GPV_BATCH_SIZE = localCache.getConfig(
|
||||||
|
sessionContext.cacheSnapshot,
|
||||||
|
"cwmp.gpvBatchSize",
|
||||||
|
{},
|
||||||
|
sessionContext.timestamp,
|
||||||
|
(e) => configContextCallback(sessionContext, e)
|
||||||
|
);
|
||||||
|
|
||||||
|
const parameterNames: string[] = [];
|
||||||
|
for (const path of syncState.refreshAttributes.notification) {
|
||||||
|
syncState.refreshAttributes.notification.delete(path);
|
||||||
|
syncState.refreshAttributes.accessList.delete(path);
|
||||||
|
// Need to check in case param is deleted
|
||||||
|
const attrs = sessionContext.deviceData.attributes.get(path);
|
||||||
|
if (attrs) {
|
||||||
|
parameterNames.push(path.toString());
|
||||||
|
if (parameterNames.length >= GPV_BATCH_SIZE) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameterNames.length < GPV_BATCH_SIZE) {
|
||||||
|
for (const path of syncState.refreshAttributes.accessList) {
|
||||||
|
syncState.refreshAttributes.accessList.delete(path);
|
||||||
|
const attrs = sessionContext.deviceData.attributes.get(path);
|
||||||
|
if (attrs) {
|
||||||
|
parameterNames.push(path.toString());
|
||||||
|
if (parameterNames.length >= GPV_BATCH_SIZE) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameterNames.length) {
|
||||||
|
return {
|
||||||
|
name: "GetParameterAttributes",
|
||||||
|
parameterNames: parameterNames,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compareAccessLists(list1: string[], list2: string[]): boolean {
|
||||||
|
if (list1.length !== list2.length) return false;
|
||||||
|
for (const [i, v] of list1.entries()) if (v !== list2[i]) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest {
|
function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest {
|
||||||
const syncState = sessionContext.syncState;
|
const syncState = sessionContext.syncState;
|
||||||
if (!syncState) return null;
|
if (!syncState) return null;
|
||||||
@@ -1709,7 +1766,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest {
|
|||||||
(e) => configContextCallback(sessionContext, e)
|
(e) => configContextCallback(sessionContext, e)
|
||||||
);
|
);
|
||||||
|
|
||||||
const parameterValues: [Path, string | number | boolean, string][] = [];
|
const parameterValues: [string, string | number | boolean, string][] = [];
|
||||||
for (const [k, v] of syncState.spv) {
|
for (const [k, v] of syncState.spv) {
|
||||||
syncState.spv.delete(k);
|
syncState.spv.delete(k);
|
||||||
const attrs = sessionContext.deviceData.attributes.get(k);
|
const attrs = sessionContext.deviceData.attributes.get(k);
|
||||||
@@ -1728,7 +1785,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest {
|
|||||||
val[0] -= val[0] % 1000;
|
val[0] -= val[0] % 1000;
|
||||||
|
|
||||||
if (val[0] !== curVal[0] || val[1] !== curVal[1])
|
if (val[0] !== curVal[0] || val[1] !== curVal[1])
|
||||||
parameterValues.push([k, val[0], val[1]]);
|
parameterValues.push([k.toString(), val[0], val[1]]);
|
||||||
|
|
||||||
if (parameterValues.length >= GPV_BATCH_SIZE) break;
|
if (parameterValues.length >= GPV_BATCH_SIZE) break;
|
||||||
}
|
}
|
||||||
@@ -1737,19 +1794,44 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest {
|
|||||||
if (parameterValues.length) {
|
if (parameterValues.length) {
|
||||||
return {
|
return {
|
||||||
name: "SetParameterValues",
|
name: "SetParameterValues",
|
||||||
parameterList: parameterValues.map(
|
parameterList: parameterValues,
|
||||||
(p) =>
|
|
||||||
[p[0].toString(), p[1], p[2]] as [
|
|
||||||
string,
|
|
||||||
string | number | boolean,
|
|
||||||
string
|
|
||||||
]
|
|
||||||
),
|
|
||||||
DATETIME_MILLISECONDS: DATETIME_MILLISECONDS,
|
DATETIME_MILLISECONDS: DATETIME_MILLISECONDS,
|
||||||
BOOLEAN_LITERAL: BOOLEAN_LITERAL,
|
BOOLEAN_LITERAL: BOOLEAN_LITERAL,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set attributes
|
||||||
|
const parameterAttributes: [string, number, string[]][] = [];
|
||||||
|
for (const [k, v] of syncState.spa) {
|
||||||
|
syncState.spa.delete(k);
|
||||||
|
const attrs = sessionContext.deviceData.attributes.get(k);
|
||||||
|
|
||||||
|
if (
|
||||||
|
v.notification != null &&
|
||||||
|
(!attrs.notification || v.notification === attrs.notification[1])
|
||||||
|
)
|
||||||
|
v.notification = null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
v.accessList != null &&
|
||||||
|
(!attrs.accessList ||
|
||||||
|
compareAccessLists(v.accessList, attrs.accessList[1]))
|
||||||
|
)
|
||||||
|
v.accessList = null;
|
||||||
|
|
||||||
|
if (v.notification != null || v.accessList != null)
|
||||||
|
parameterAttributes.push([k.toString(), v.notification, v.accessList]);
|
||||||
|
|
||||||
|
if (parameterAttributes.length >= GPV_BATCH_SIZE) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameterAttributes.length) {
|
||||||
|
return {
|
||||||
|
name: "SetParameterAttributes",
|
||||||
|
parameterList: parameterAttributes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Downloads
|
// Downloads
|
||||||
for (const [p, t] of syncState.downloadsDownload) {
|
for (const [p, t] of syncState.downloadsDownload) {
|
||||||
const attrs = deviceData.attributes.get(p);
|
const attrs = deviceData.attributes.get(p);
|
||||||
@@ -2167,8 +2249,34 @@ function processDeclarations(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (declareAttributeValues && declareAttributeValues.value != null)
|
if (declareAttributeValues) {
|
||||||
syncState.spv.set(currentPath, declareAttributeValues.value);
|
if (declareAttributeValues.value != null)
|
||||||
|
syncState.spv.set(currentPath, declareAttributeValues.value);
|
||||||
|
|
||||||
|
if (declareAttributeValues.notification != null) {
|
||||||
|
const spa = syncState.spa.get(currentPath);
|
||||||
|
if (spa) {
|
||||||
|
spa.notification = declareAttributeValues.notification;
|
||||||
|
} else {
|
||||||
|
syncState.spa.set(currentPath, {
|
||||||
|
notification: declareAttributeValues.notification,
|
||||||
|
accessList: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declareAttributeValues.accessList != null) {
|
||||||
|
const spa = syncState.spa.get(currentPath);
|
||||||
|
if (spa) {
|
||||||
|
spa.accessList = declareAttributeValues.accessList;
|
||||||
|
} else {
|
||||||
|
syncState.spa.set(currentPath, {
|
||||||
|
notification: null,
|
||||||
|
accessList: declareAttributeValues.accessList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2305,7 +2413,7 @@ export async function rpcResponse(
|
|||||||
if (p[1] !== req.instanceValues[p[0]]) {
|
if (p[1] !== req.instanceValues[p[0]]) {
|
||||||
const v = device.sanitizeParameterValue([
|
const v = device.sanitizeParameterValue([
|
||||||
req.instanceValues[p[0]],
|
req.instanceValues[p[0]],
|
||||||
p[2],
|
p[2] as string,
|
||||||
]);
|
]);
|
||||||
parameterList.push([p[0], v[0], v[1]]);
|
parameterList.push([p[0], v[0], v[1]]);
|
||||||
}
|
}
|
||||||
@@ -2371,6 +2479,24 @@ export async function rpcResponse(
|
|||||||
toClear
|
toClear
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (rpcRes.name === "GetParameterAttributesResponse") {
|
||||||
|
const req = rpcReq as GetAcsRequest;
|
||||||
|
const res = rpcRes as CpeGetResponse;
|
||||||
|
if (req.name !== "GetParameterAttributes")
|
||||||
|
throw new Error("Response name does not match request name");
|
||||||
|
|
||||||
|
for (const p of res.parameterList) {
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
Path.parse(p[0]),
|
||||||
|
timestamp,
|
||||||
|
{
|
||||||
|
notification: [timestamp, p[1] as number],
|
||||||
|
accessList: [timestamp, p[2] as string[]],
|
||||||
|
},
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if (rpcRes.name === "GetParameterNamesResponse") {
|
} else if (rpcRes.name === "GetParameterNamesResponse") {
|
||||||
const req = rpcReq as GetAcsRequest;
|
const req = rpcReq as GetAcsRequest;
|
||||||
const res = rpcRes as CpeGetResponse;
|
const res = rpcRes as CpeGetResponse;
|
||||||
@@ -2485,6 +2611,37 @@ export async function rpcResponse(
|
|||||||
toClear
|
toClear
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (rpcRes.name === "SetParameterAttributesResponse") {
|
||||||
|
const req = rpcReq as SetAcsRequest;
|
||||||
|
if (req.name !== "SetParameterAttributes")
|
||||||
|
throw new Error("Response name does not match request name");
|
||||||
|
|
||||||
|
for (const p of req.parameterList) {
|
||||||
|
let attrs;
|
||||||
|
|
||||||
|
if (p[1] != null && p[2] != null) {
|
||||||
|
attrs = {
|
||||||
|
notification: [timestamp + 1, p[1]],
|
||||||
|
accessList: [timestamp + 1, p[2]],
|
||||||
|
};
|
||||||
|
} else if (p[1] != null) {
|
||||||
|
attrs = {
|
||||||
|
notification: [timestamp + 1, p[1]],
|
||||||
|
};
|
||||||
|
} else if (p[2] != null) {
|
||||||
|
attrs = {
|
||||||
|
accessList: [timestamp + 1, p[2]],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
Path.parse(p[0]),
|
||||||
|
timestamp + 1,
|
||||||
|
attrs,
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if (rpcRes.name === "AddObjectResponse") {
|
} else if (rpcRes.name === "AddObjectResponse") {
|
||||||
const req = rpcReq as SetAcsRequest;
|
const req = rpcReq as SetAcsRequest;
|
||||||
const res = rpcRes as CpeSetResponse;
|
const res = rpcRes as CpeSetResponse;
|
||||||
@@ -2628,7 +2785,7 @@ export async function rpcFault(
|
|||||||
|
|
||||||
// Recover from invalid parameter name faults
|
// Recover from invalid parameter name faults
|
||||||
if (faultResponse.detail.faultCode === "9005") {
|
if (faultResponse.detail.faultCode === "9005") {
|
||||||
const timestamp = sessionContext.timestamp + sessionContext.iteration;
|
const timestamp = sessionContext.timestamp + sessionContext.iteration + 1;
|
||||||
const revision =
|
const revision =
|
||||||
(sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1;
|
(sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1;
|
||||||
sessionContext.deviceData.timestamps.revision = revision;
|
sessionContext.deviceData.timestamps.revision = revision;
|
||||||
@@ -2646,13 +2803,25 @@ export async function rpcFault(
|
|||||||
(p) => [Path.parse(p.replace(/\.$/, "")), timestamp] as Clear
|
(p) => [Path.parse(p.replace(/\.$/, "")), timestamp] as Clear
|
||||||
);
|
);
|
||||||
} else if (rpcReq.name === "SetParameterValues") {
|
} else if (rpcReq.name === "SetParameterValues") {
|
||||||
toClear = rpcReq.parameterList.map(
|
toClear = (rpcReq.parameterList as [
|
||||||
|
string,
|
||||||
|
string | number | boolean,
|
||||||
|
string
|
||||||
|
][]).map(
|
||||||
(p) => [Path.parse(p[0].replace(/\.$/, "")), timestamp] as Clear
|
(p) => [Path.parse(p[0].replace(/\.$/, "")), timestamp] as Clear
|
||||||
);
|
);
|
||||||
} else if (rpcReq.name === "AddObject") {
|
} else if (rpcReq.name === "AddObject") {
|
||||||
toClear = [[Path.parse(rpcReq.objectName.replace(/\.$/, "")), timestamp]];
|
toClear = [[Path.parse(rpcReq.objectName.replace(/\.$/, "")), timestamp]];
|
||||||
} else if (rpcReq.name === "DeleteObject") {
|
} else if (rpcReq.name === "DeleteObject") {
|
||||||
toClear = [[Path.parse(rpcReq.objectName.replace(/\.$/, "")), timestamp]];
|
toClear = [[Path.parse(rpcReq.objectName.replace(/\.$/, "")), timestamp]];
|
||||||
|
} else if (rpcReq.name === "GetParameterAttributes") {
|
||||||
|
toClear = rpcReq.parameterNames.map(
|
||||||
|
(p) => [Path.parse(p.replace(/\.$/, "")), timestamp] as Clear
|
||||||
|
);
|
||||||
|
} else if (rpcReq.name === "SetParameterAttributes") {
|
||||||
|
toClear = (rpcReq.parameterList as [string, number, string[]][]).map(
|
||||||
|
(p) => [Path.parse(p[0].replace(/\.$/, "")), timestamp] as Clear
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toClear) {
|
if (toClear) {
|
||||||
|
|||||||
+98
@@ -191,6 +191,45 @@ function parameterValueList(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parameterAttributeList(xml: Element): [string, number, string[]][] {
|
||||||
|
return xml.children
|
||||||
|
.filter((e) => e.localName === "ParameterAttributeStruct")
|
||||||
|
.map<[string, number, string[]]>((e) => {
|
||||||
|
let notificationElement: Element,
|
||||||
|
accessListElement: Element,
|
||||||
|
param: string;
|
||||||
|
for (const c of e.children) {
|
||||||
|
switch (c.localName) {
|
||||||
|
case "Name":
|
||||||
|
param = c.text;
|
||||||
|
break;
|
||||||
|
case "Notification":
|
||||||
|
notificationElement = c;
|
||||||
|
break;
|
||||||
|
case "AccessList":
|
||||||
|
accessListElement = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notification = decodeEntities(notificationElement.text);
|
||||||
|
const notif = +notification;
|
||||||
|
|
||||||
|
if (notif !== parseInt(notification)) {
|
||||||
|
warnings.push({
|
||||||
|
message: "Invalid notification attribute",
|
||||||
|
parameter: param,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessList = accessListElement.children
|
||||||
|
.filter((c) => c.localName === "string")
|
||||||
|
.map((c) => decodeEntities(c.text));
|
||||||
|
|
||||||
|
return [param, notif, accessList];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function GetParameterNames(methodRequest): string {
|
function GetParameterNames(methodRequest): string {
|
||||||
return `<cwmp:GetParameterNames><ParameterPath>${
|
return `<cwmp:GetParameterNames><ParameterPath>${
|
||||||
methodRequest.parameterPath
|
methodRequest.parameterPath
|
||||||
@@ -223,6 +262,23 @@ function GetParameterValuesResponse(xml: Element): CpeGetResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GetParameterAttributes(methodRequest): string {
|
||||||
|
return `<cwmp:GetParameterAttributes><ParameterNames soap-enc:arrayType="xsd:string[${
|
||||||
|
methodRequest.parameterNames.length
|
||||||
|
}]">${methodRequest.parameterNames
|
||||||
|
.map((p) => `<string>${p}</string>`)
|
||||||
|
.join("")}</ParameterNames></cwmp:GetParameterAttributes>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetParameterAttributesResponse(xml: Element): CpeGetResponse {
|
||||||
|
return {
|
||||||
|
name: "GetParameterAttributesResponse",
|
||||||
|
parameterList: parameterAttributeList(
|
||||||
|
xml.children.find((n) => n.localName === "ParameterList")
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function SetParameterValues(methodRequest): string {
|
function SetParameterValues(methodRequest): string {
|
||||||
const params = methodRequest.parameterList.map((p) => {
|
const params = methodRequest.parameterList.map((p) => {
|
||||||
let val = p[1];
|
let val = p[1];
|
||||||
@@ -252,6 +308,36 @@ function SetParameterValuesResponse(xml: Element): CpeSetResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SetParameterAttributes(methodRequest): string {
|
||||||
|
const params = methodRequest.parameterList.map((p) => {
|
||||||
|
return `<SetParameterAttributesStruct><Name>${
|
||||||
|
p[0]
|
||||||
|
}</Name><NotificationChange>${
|
||||||
|
p[1] == null ? "false" : "true"
|
||||||
|
}</NotificationChange><Notification>${
|
||||||
|
p[1] == null ? "" : p[1]
|
||||||
|
}</Notification><AccessListChange>${
|
||||||
|
p[2] == null ? "false" : "true"
|
||||||
|
}</AccessListChange><AccessList soap-enc:arrayType="xsd:string[${
|
||||||
|
(p[2] || []).length
|
||||||
|
}]">${
|
||||||
|
p[2] == null
|
||||||
|
? ""
|
||||||
|
: p[2].map((s) => `<string>${encodeEntities(s)}</string>`).join("")
|
||||||
|
}</AccessList></SetParameterAttributesStruct>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return `<cwmp:SetParameterAttributes><ParameterList soap-enc:arrayType="cwmp:SetParameterAttributesStruct[${
|
||||||
|
methodRequest.parameterList.length
|
||||||
|
}]">${params.join("")}</ParameterList></cwmp:SetParameterAttributes>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetParameterAttributesResponse(): CpeSetResponse {
|
||||||
|
return {
|
||||||
|
name: "SetParameterAttributesResponse",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function AddObject(methodRequest): string {
|
function AddObject(methodRequest): string {
|
||||||
return `<cwmp:AddObject><ObjectName>${
|
return `<cwmp:AddObject><ObjectName>${
|
||||||
methodRequest.objectName
|
methodRequest.objectName
|
||||||
@@ -622,9 +708,15 @@ export function request(
|
|||||||
case "GetParameterValuesResponse":
|
case "GetParameterValuesResponse":
|
||||||
rpc.cpeResponse = GetParameterValuesResponse(methodElement);
|
rpc.cpeResponse = GetParameterValuesResponse(methodElement);
|
||||||
break;
|
break;
|
||||||
|
case "GetParameterAttributesResponse":
|
||||||
|
rpc.cpeResponse = GetParameterAttributesResponse(methodElement);
|
||||||
|
break;
|
||||||
case "SetParameterValuesResponse":
|
case "SetParameterValuesResponse":
|
||||||
rpc.cpeResponse = SetParameterValuesResponse(methodElement);
|
rpc.cpeResponse = SetParameterValuesResponse(methodElement);
|
||||||
break;
|
break;
|
||||||
|
case "SetParameterAttributesResponse":
|
||||||
|
rpc.cpeResponse = SetParameterAttributesResponse();
|
||||||
|
break;
|
||||||
case "AddObjectResponse":
|
case "AddObjectResponse":
|
||||||
rpc.cpeResponse = AddObjectResponse(methodElement);
|
rpc.cpeResponse = AddObjectResponse(methodElement);
|
||||||
break;
|
break;
|
||||||
@@ -707,9 +799,15 @@ export function response(rpc: {
|
|||||||
case "GetParameterValues":
|
case "GetParameterValues":
|
||||||
body = GetParameterValues(rpc.acsRequest);
|
body = GetParameterValues(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
|
case "GetParameterAttributes":
|
||||||
|
body = GetParameterAttributes(rpc.acsRequest);
|
||||||
|
break;
|
||||||
case "SetParameterValues":
|
case "SetParameterValues":
|
||||||
body = SetParameterValues(rpc.acsRequest);
|
body = SetParameterValues(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
|
case "SetParameterAttributes":
|
||||||
|
body = SetParameterAttributes(rpc.acsRequest);
|
||||||
|
break;
|
||||||
case "AddObject":
|
case "AddObject":
|
||||||
body = AddObject(rpc.acsRequest);
|
body = AddObject(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
|
|||||||
+48
-7
@@ -52,18 +52,24 @@ export interface Attributes {
|
|||||||
object?: [number, 1 | 0];
|
object?: [number, 1 | 0];
|
||||||
writable?: [number, 1 | 0];
|
writable?: [number, 1 | 0];
|
||||||
value?: [number, [string | number | boolean, string]];
|
value?: [number, [string | number | boolean, string]];
|
||||||
|
notification?: [number, number];
|
||||||
|
accessList?: [number, string[]];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttributeTimestamps {
|
export interface AttributeTimestamps {
|
||||||
object?: number;
|
object?: number;
|
||||||
writable?: number;
|
writable?: number;
|
||||||
value?: number;
|
value?: number;
|
||||||
|
notification?: number;
|
||||||
|
accessList?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttributeValues {
|
export interface AttributeValues {
|
||||||
object?: boolean;
|
object?: boolean;
|
||||||
writable?: boolean;
|
writable?: boolean;
|
||||||
value?: [string | number | boolean, string?];
|
value?: [string | number | boolean, string?];
|
||||||
|
notification?: number;
|
||||||
|
accessList?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceData {
|
export interface DeviceData {
|
||||||
@@ -76,12 +82,21 @@ export interface DeviceData {
|
|||||||
|
|
||||||
export type VirtualParameterDeclaration = [
|
export type VirtualParameterDeclaration = [
|
||||||
Path,
|
Path,
|
||||||
{ path?: number; object?: number; writable?: number; value?: number }?,
|
{
|
||||||
|
path?: number;
|
||||||
|
object?: number;
|
||||||
|
writable?: number;
|
||||||
|
value?: number;
|
||||||
|
notification?: number;
|
||||||
|
accessList?: number;
|
||||||
|
}?,
|
||||||
{
|
{
|
||||||
path?: [number, number];
|
path?: [number, number];
|
||||||
object?: boolean;
|
object?: boolean;
|
||||||
writable?: boolean;
|
writable?: boolean;
|
||||||
value?: [string | number | boolean, string?];
|
value?: [string | number | boolean, string?];
|
||||||
|
notification?: number;
|
||||||
|
accessList?: string[];
|
||||||
}?
|
}?
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -91,8 +106,11 @@ export interface SyncState {
|
|||||||
object: Set<Path>;
|
object: Set<Path>;
|
||||||
writable: Set<Path>;
|
writable: Set<Path>;
|
||||||
value: Set<Path>;
|
value: Set<Path>;
|
||||||
|
notification: Set<Path>;
|
||||||
|
accessList: Set<Path>;
|
||||||
};
|
};
|
||||||
spv: Map<Path, [string | number | boolean, string]>;
|
spv: Map<Path, [string | number | boolean, string]>;
|
||||||
|
spa: Map<Path, { notification: number; accessList: string[] }>;
|
||||||
gpn: Set<Path>;
|
gpn: Set<Path>;
|
||||||
gpnPatterns: Map<Path, number>;
|
gpnPatterns: Map<Path, number>;
|
||||||
tags: Map<Path, boolean>;
|
tags: Map<Path, boolean>;
|
||||||
@@ -161,6 +179,7 @@ export interface Task {
|
|||||||
fileName?: string;
|
fileName?: string;
|
||||||
targetFileName?: string;
|
targetFileName?: string;
|
||||||
expiry?: number;
|
expiry?: number;
|
||||||
|
provisions?: (string | number | boolean)[][];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Operation {
|
export interface Operation {
|
||||||
@@ -183,7 +202,7 @@ export interface AcsRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GetAcsRequest extends AcsRequest {
|
export interface GetAcsRequest extends AcsRequest {
|
||||||
name: "GetParameterNames" | "GetParameterValues";
|
name: "GetParameterNames" | "GetParameterValues" | "GetParameterAttributes";
|
||||||
objectName?: string;
|
objectName?: string;
|
||||||
parameterNames?: string[];
|
parameterNames?: string[];
|
||||||
parameterPath?: string;
|
parameterPath?: string;
|
||||||
@@ -194,12 +213,15 @@ export interface GetAcsRequest extends AcsRequest {
|
|||||||
export interface SetAcsRequest extends AcsRequest {
|
export interface SetAcsRequest extends AcsRequest {
|
||||||
name:
|
name:
|
||||||
| "SetParameterValues"
|
| "SetParameterValues"
|
||||||
|
| "SetParameterAttributes"
|
||||||
| "AddObject"
|
| "AddObject"
|
||||||
| "DeleteObject"
|
| "DeleteObject"
|
||||||
| "FactoryReset"
|
| "FactoryReset"
|
||||||
| "Reboot"
|
| "Reboot"
|
||||||
| "Download";
|
| "Download";
|
||||||
parameterList?: [string, string | number | boolean, string][];
|
parameterList?:
|
||||||
|
| [string, string | number | boolean, string][]
|
||||||
|
| [string, number, string[]][];
|
||||||
instanceValues?: { [param: string]: string | number | boolean };
|
instanceValues?: { [param: string]: string | number | boolean };
|
||||||
objectName?: string;
|
objectName?: string;
|
||||||
DATETIME_MILLISECONDS?: boolean;
|
DATETIME_MILLISECONDS?: boolean;
|
||||||
@@ -236,15 +258,20 @@ export interface CpeFault {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CpeGetResponse extends CpeResponse {
|
export interface CpeGetResponse extends CpeResponse {
|
||||||
name: "GetParameterNamesResponse" | "GetParameterValuesResponse";
|
name:
|
||||||
|
| "GetParameterNamesResponse"
|
||||||
|
| "GetParameterValuesResponse"
|
||||||
|
| "GetParameterAttributesResponse";
|
||||||
parameterList?:
|
parameterList?:
|
||||||
| [string, boolean][]
|
| [string, boolean][]
|
||||||
| [string, string | number | boolean, string][];
|
| [string, string | number | boolean, string][]
|
||||||
|
| [string, number, string[]][];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CpeSetResponse extends CpeResponse {
|
export interface CpeSetResponse extends CpeResponse {
|
||||||
name:
|
name:
|
||||||
| "SetParameterValuesResponse"
|
| "SetParameterValuesResponse"
|
||||||
|
| "SetParameterAttributesResponse"
|
||||||
| "AddObjectResponse"
|
| "AddObjectResponse"
|
||||||
| "DeleteObjectResponse"
|
| "DeleteObjectResponse"
|
||||||
| "RebootResponse"
|
| "RebootResponse"
|
||||||
@@ -301,11 +328,19 @@ export interface Declaration {
|
|||||||
path: Path;
|
path: Path;
|
||||||
pathGet: number;
|
pathGet: number;
|
||||||
pathSet?: number | [number, number];
|
pathSet?: number | [number, number];
|
||||||
attrGet?: { object?: number; writable?: number; value?: number };
|
attrGet?: {
|
||||||
|
object?: number;
|
||||||
|
writable?: number;
|
||||||
|
value?: number;
|
||||||
|
notification?: number;
|
||||||
|
accessList?: number;
|
||||||
|
};
|
||||||
attrSet?: {
|
attrSet?: {
|
||||||
object?: boolean;
|
object?: boolean;
|
||||||
writable?: boolean;
|
writable?: boolean;
|
||||||
value?: [string | number | boolean, string?];
|
value?: [string | number | boolean, string?];
|
||||||
|
notification?: number;
|
||||||
|
accessList?: string[];
|
||||||
};
|
};
|
||||||
defer: boolean;
|
defer: boolean;
|
||||||
}
|
}
|
||||||
@@ -313,7 +348,13 @@ export interface Declaration {
|
|||||||
export type Clear = [
|
export type Clear = [
|
||||||
Path,
|
Path,
|
||||||
number,
|
number,
|
||||||
{ object?: number; writable?: number; value?: number }?,
|
{
|
||||||
|
object?: number;
|
||||||
|
writable?: number;
|
||||||
|
value?: number;
|
||||||
|
notification?: number;
|
||||||
|
accessList?: number;
|
||||||
|
}?,
|
||||||
number?
|
number?
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -100,7 +100,7 @@ router.get(`/devices/:id.csv`, async (ctx) => {
|
|||||||
|
|
||||||
ctx.body = new stream.PassThrough();
|
ctx.body = new stream.PassThrough();
|
||||||
ctx.body.write(
|
ctx.body.write(
|
||||||
"Parameter,Object,Object timestamp,Writable,Writable timestamp,Value,Value type,Value timestamp\n"
|
"Parameter,Object,Object timestamp,Writable,Writable timestamp,Value,Value type,Value timestamp,Notification,Notification timestamp,Access list,Access list timestamp\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const k of Object.keys(res[0]).sort()) {
|
for (const k of Object.keys(res[0]).sort()) {
|
||||||
@@ -114,6 +114,10 @@ router.get(`/devices/:id.csv`, async (ctx) => {
|
|||||||
p.value != null ? `"${p.value[0].toString().replace(/"/g, '""')}"` : "",
|
p.value != null ? `"${p.value[0].toString().replace(/"/g, '""')}"` : "",
|
||||||
p.value != null ? p.value[1] : "",
|
p.value != null ? p.value[1] : "",
|
||||||
p.valueTimestamp,
|
p.valueTimestamp,
|
||||||
|
p.notification,
|
||||||
|
p.notificationTimestamp,
|
||||||
|
p.accessList ? p.accessList.join(", ") : "",
|
||||||
|
p.accessListTimestamp,
|
||||||
];
|
];
|
||||||
ctx.body.write(row.map((r) => (r != null ? r : "")).join(",") + "\n");
|
ctx.body.write(row.map((r) => (r != null ? r : "")).join(",") + "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user