Compare commits
2
Commits
v1.2.15
...
ChangeDUState
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84de431872 | ||
|
|
716f2c6df3 |
No files matched your search
+43
@@ -361,6 +361,39 @@ async function transferComplete(sessionContext, rpc): Promise<void> {
|
|||||||
return writeResponse(sessionContext, res);
|
return writeResponse(sessionContext, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function duStateChangeComplete(sessionContext, rpc): Promise<void> {
|
||||||
|
const { acsResponse, operation, fault } = await session.duStateChangeComplete(
|
||||||
|
sessionContext,
|
||||||
|
rpc.cpeRequest
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!operation) {
|
||||||
|
logger.accessWarn({
|
||||||
|
sessionContext: sessionContext,
|
||||||
|
message: "Unrecognized command key",
|
||||||
|
rpc: rpc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fault) {
|
||||||
|
Object.assign(sessionContext.retries, operation.retries);
|
||||||
|
recordFault(
|
||||||
|
sessionContext,
|
||||||
|
fault,
|
||||||
|
operation.provisions,
|
||||||
|
operation.channels
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = soap.response({
|
||||||
|
id: rpc.id,
|
||||||
|
acsResponse: acsResponse,
|
||||||
|
cwmpVersion: sessionContext.cwmpVersion,
|
||||||
|
});
|
||||||
|
|
||||||
|
return writeResponse(sessionContext, res);
|
||||||
|
}
|
||||||
|
|
||||||
// Append provisions and remove duplicates
|
// Append provisions and remove duplicates
|
||||||
function appendProvisions(original, toAppend): boolean {
|
function appendProvisions(original, toAppend): boolean {
|
||||||
let modified = false;
|
let modified = false;
|
||||||
@@ -1239,6 +1272,16 @@ async function processRequest(
|
|||||||
cwmpVersion: sessionContext.cwmpVersion,
|
cwmpVersion: sessionContext.cwmpVersion,
|
||||||
});
|
});
|
||||||
return writeResponse(sessionContext, res);
|
return writeResponse(sessionContext, res);
|
||||||
|
} else if (rpc.cpeRequest.name === "DUStateChangeComplete") {
|
||||||
|
if (sessionContext.state !== 1) return reportBadState(sessionContext);
|
||||||
|
|
||||||
|
logger.accessInfo({
|
||||||
|
sessionContext: sessionContext,
|
||||||
|
message: "CPE request",
|
||||||
|
rpc: rpc,
|
||||||
|
});
|
||||||
|
|
||||||
|
return duStateChangeComplete(sessionContext, rpc);
|
||||||
} else {
|
} else {
|
||||||
if (sessionContext.state !== 1 || rpc.cpeRequest.name === "Inform")
|
if (sessionContext.state !== 1 || rpc.cpeRequest.name === "Inform")
|
||||||
return void reportBadState(sessionContext);
|
return void reportBadState(sessionContext);
|
||||||
|
|||||||
+418
-1
@@ -57,6 +57,10 @@ import {
|
|||||||
FactoryReset,
|
FactoryReset,
|
||||||
AddObjectResponse,
|
AddObjectResponse,
|
||||||
GetParameterValuesResponse,
|
GetParameterValuesResponse,
|
||||||
|
ChangeDUStateOperation,
|
||||||
|
DownloadOperation,
|
||||||
|
DUStateChangeCompleteRequest,
|
||||||
|
ChangeDUState,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { getRequestOrigin } from "./forwarded";
|
import { getRequestOrigin } from "./forwarded";
|
||||||
import * as logger from "./logger";
|
import * as logger from "./logger";
|
||||||
@@ -284,6 +288,8 @@ export async function transferComplete(
|
|||||||
sessionContext.deviceData.attributes.revision = revision;
|
sessionContext.deviceData.attributes.revision = revision;
|
||||||
const commandKey = rpcReq.commandKey;
|
const commandKey = rpcReq.commandKey;
|
||||||
const operation = sessionContext.operations[commandKey];
|
const operation = sessionContext.operations[commandKey];
|
||||||
|
if (operation.name !== "Download")
|
||||||
|
throw new Error("Unexpected operation name");
|
||||||
|
|
||||||
if (!operation) {
|
if (!operation) {
|
||||||
return {
|
return {
|
||||||
@@ -433,6 +439,142 @@ function revertDownloadParameters(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function duStateChangeComplete(
|
||||||
|
sessionContext: SessionContext,
|
||||||
|
rpcReq: DUStateChangeCompleteRequest
|
||||||
|
): Promise<{
|
||||||
|
acsResponse: AcsResponse;
|
||||||
|
operation: ChangeDUStateOperation;
|
||||||
|
fault: Fault;
|
||||||
|
}> {
|
||||||
|
const revision =
|
||||||
|
(sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1;
|
||||||
|
sessionContext.deviceData.timestamps.revision = revision;
|
||||||
|
sessionContext.deviceData.attributes.revision = revision;
|
||||||
|
const commandKey = rpcReq.commandKey;
|
||||||
|
const operation = sessionContext.operations[commandKey];
|
||||||
|
|
||||||
|
if (!operation) {
|
||||||
|
return {
|
||||||
|
acsResponse: { name: "DUStateChangeCompleteResponse" },
|
||||||
|
operation: null,
|
||||||
|
fault: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.name !== "ChangeDUState")
|
||||||
|
throw new Error("Unexpected operation name");
|
||||||
|
|
||||||
|
if (operation.args.operations.length !== rpcReq.results.length) {
|
||||||
|
return {
|
||||||
|
acsResponse: { name: "DUStateChangeCompleteResponse" },
|
||||||
|
operation: operation,
|
||||||
|
fault: {
|
||||||
|
code: "invalid_response",
|
||||||
|
message: "Unexpected number of results in DUStateChangeComplete",
|
||||||
|
timestamp: operation.timestamp,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
delete sessionContext.operations[commandKey];
|
||||||
|
if (!sessionContext.operationsTouched) sessionContext.operationsTouched = {};
|
||||||
|
sessionContext.operationsTouched[commandKey] = 1;
|
||||||
|
|
||||||
|
let toClear = null;
|
||||||
|
const timestamp = sessionContext.timestamp + sessionContext.iteration + 1;
|
||||||
|
|
||||||
|
let fault: Fault;
|
||||||
|
for (const [i, res] of rpcReq.results.entries()) {
|
||||||
|
const op = operation.args.operations[i];
|
||||||
|
if (res.fault && res.fault.faultCode !== "0") {
|
||||||
|
revertChangeDuStateParameters(sessionContext, op.instance);
|
||||||
|
fault = {
|
||||||
|
code: `cwmp.${res.fault.faultCode}`,
|
||||||
|
message: res.fault.faultString,
|
||||||
|
timestamp: operation.timestamp,
|
||||||
|
detail: res.fault,
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toSet: Record<string, [string | number | boolean, string]> = {
|
||||||
|
LastChangeDUState: [operation.timestamp, "xsd:dateTime"],
|
||||||
|
LastOperationType: [op.operationType, "xsd:string"],
|
||||||
|
LastURL: [op.url, "xsd:string"],
|
||||||
|
LastUUID: [op.uuid, "xsd:string"],
|
||||||
|
LastExecutionEnvRef: [op.executionEnvRef, "xsd:string"],
|
||||||
|
StartTime: [+res.startTime, "xsd:dateTime"],
|
||||||
|
CompleteTime: [+res.completeTime, "xsd:dateTime"],
|
||||||
|
CurrentState: [res.currentState, "xsd:string"],
|
||||||
|
DeploymentUnitRef: [res.deploymentUnitRef, "xsd:string"],
|
||||||
|
Version: [res.version, "xsd:string"],
|
||||||
|
Resolved: [res.resolved, "xsd:string"],
|
||||||
|
ExecutionUnitRefList: [res.executionUnitRefList, "xsd:string"],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(toSet)) {
|
||||||
|
const p = sessionContext.deviceData.paths.add(
|
||||||
|
Path.parse(`DUStates.${op.instance}.${k}`)
|
||||||
|
);
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
p,
|
||||||
|
timestamp,
|
||||||
|
{ value: [timestamp, v] },
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toClear) {
|
||||||
|
for (const c of toClear)
|
||||||
|
device.clear(sessionContext.deviceData, c[0], c[1], c[2], c[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
acsResponse: { name: "DUStateChangeCompleteResponse" },
|
||||||
|
operation: operation,
|
||||||
|
fault: fault,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function revertChangeDuStateParameters(
|
||||||
|
sessionContext: SessionContext,
|
||||||
|
instance
|
||||||
|
): void {
|
||||||
|
const timestamp = sessionContext.timestamp + sessionContext.iteration + 1;
|
||||||
|
|
||||||
|
let p;
|
||||||
|
|
||||||
|
p = sessionContext.deviceData.paths.add(
|
||||||
|
Path.parse(`DUStates.${instance}.LastChangeDUState`)
|
||||||
|
);
|
||||||
|
|
||||||
|
const lastChangeDuState = sessionContext.deviceData.attributes.get(p);
|
||||||
|
|
||||||
|
p = sessionContext.deviceData.paths.add(
|
||||||
|
Path.parse(`DUStates.${instance}.ChangeDUState`)
|
||||||
|
);
|
||||||
|
|
||||||
|
const toClear = device.set(sessionContext.deviceData, p, timestamp, {
|
||||||
|
value: [
|
||||||
|
timestamp,
|
||||||
|
[
|
||||||
|
lastChangeDuState && lastChangeDuState.value[1]
|
||||||
|
? lastChangeDuState.value[1][0]
|
||||||
|
: 0,
|
||||||
|
"xsd:dateTime",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (toClear) {
|
||||||
|
for (const c of toClear)
|
||||||
|
device.clear(sessionContext.deviceData, c[0], c[1], c[2], c[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function timeoutOperations(
|
export async function timeoutOperations(
|
||||||
sessionContext: SessionContext
|
sessionContext: SessionContext
|
||||||
): Promise<{ faults: Fault[]; operations: Operation[] }> {
|
): Promise<{ faults: Fault[]; operations: Operation[] }> {
|
||||||
@@ -794,6 +936,10 @@ function runDeclarations(
|
|||||||
downloadsToCreate: new InstanceSet(),
|
downloadsToCreate: new InstanceSet(),
|
||||||
downloadsValues: new Map(),
|
downloadsValues: new Map(),
|
||||||
downloadsDownload: new Map(),
|
downloadsDownload: new Map(),
|
||||||
|
duStatesToDelete: new Set(),
|
||||||
|
duStatesToCreate: new InstanceSet(),
|
||||||
|
duStatesValues: new Map(),
|
||||||
|
duStatesChangeDUState: new Map(),
|
||||||
reboot: 0,
|
reboot: 0,
|
||||||
factoryReset: 0,
|
factoryReset: 0,
|
||||||
};
|
};
|
||||||
@@ -1439,6 +1585,147 @@ export async function rpcRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DUState
|
||||||
|
index = null;
|
||||||
|
for (const instance of sessionContext.syncState.duStatesToCreate) {
|
||||||
|
if (index == null) {
|
||||||
|
index = 0;
|
||||||
|
for (const p of sessionContext.deviceData.paths.find(
|
||||||
|
Path.parse("DUStates.*"),
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
)) {
|
||||||
|
if (
|
||||||
|
+p.segments[1] > index &&
|
||||||
|
sessionContext.deviceData.attributes.has(p)
|
||||||
|
)
|
||||||
|
index = +p.segments[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
Path.parse("DUStates"),
|
||||||
|
timestamp,
|
||||||
|
{ object: [timestamp, 1], writable: [timestamp, 1] },
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
Path.parse(`DUStates.${index}`),
|
||||||
|
timestamp,
|
||||||
|
{ object: [timestamp, 1], writable: [timestamp, 1] },
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
URL: {
|
||||||
|
writable: 1,
|
||||||
|
value: [instance.URL || "", "xsd:string"],
|
||||||
|
},
|
||||||
|
UUID: {
|
||||||
|
writable: 1,
|
||||||
|
value: [instance.UUID || "", "xsd:string"],
|
||||||
|
},
|
||||||
|
ExecutionEnvRef: {
|
||||||
|
writable: 1,
|
||||||
|
value: [instance.ExecutionEnvRef || "", "xsd:string"],
|
||||||
|
},
|
||||||
|
Username: {
|
||||||
|
writable: 1,
|
||||||
|
value: [instance.Username || "", "xsd:string"],
|
||||||
|
},
|
||||||
|
Password: {
|
||||||
|
writable: 1,
|
||||||
|
value: [instance.Password || "", "xsd:string"],
|
||||||
|
},
|
||||||
|
ChangeDUState: {
|
||||||
|
writable: 1,
|
||||||
|
value: [instance.ChangeDUState || "", "xsd:dateTime"],
|
||||||
|
},
|
||||||
|
OperationType: {
|
||||||
|
writable: 1,
|
||||||
|
value: [instance.OperationType || "", "xsd:string"],
|
||||||
|
},
|
||||||
|
LastURL: { writable: 0, value: ["", "xsd:string"] },
|
||||||
|
LastUUID: { writable: 0, value: ["", "xsd:string"] },
|
||||||
|
LastExecutionEnvRef: { writable: 0, value: ["", "xsd:string"] },
|
||||||
|
LastChangeDUState: { writable: 0, value: [0, "xsd:dateTime"] },
|
||||||
|
LastOperationType: { writable: 0, value: [0, "xsd:string"] },
|
||||||
|
Version: { writable: 0, value: [0, "xsd:string"] },
|
||||||
|
CurrentState: { writable: 0, value: [0, "xsd:string"] },
|
||||||
|
ExecutionUnitRefList: { writable: 0, value: [0, "xsd:string"] },
|
||||||
|
DeploymentUnitRef: { writable: 0, value: [0, "xsd:string"] },
|
||||||
|
Resolved: { writable: 0, value: [0, "xsd:boolean"] },
|
||||||
|
StartTime: { writable: 0, value: [0, "xsd:dateTime"] },
|
||||||
|
CompleteTime: { writable: 0, value: [0, "xsd:dateTime"] },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(params)) {
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
Path.parse(`DUStates.${index}.${k}`),
|
||||||
|
timestamp,
|
||||||
|
{
|
||||||
|
object: [timestamp, 0],
|
||||||
|
writable: [timestamp, v.writable as 0 | 1],
|
||||||
|
value: [
|
||||||
|
timestamp,
|
||||||
|
v.value as [string | number | boolean, string],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
Path.parse(`DUStates.${index}.*`),
|
||||||
|
timestamp,
|
||||||
|
null,
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionContext.syncState.duStatesToCreate.clear();
|
||||||
|
|
||||||
|
for (const instance of sessionContext.syncState.duStatesToDelete) {
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
instance,
|
||||||
|
timestamp,
|
||||||
|
null,
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
for (const p of sessionContext.syncState.duStatesValues.keys()) {
|
||||||
|
if (p.segments[1] === instance.segments[1])
|
||||||
|
sessionContext.syncState.duStatesValues.delete(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionContext.syncState.duStatesToDelete.clear();
|
||||||
|
|
||||||
|
for (const [p, v] of sessionContext.syncState.duStatesValues) {
|
||||||
|
const attrs = sessionContext.deviceData.attributes.get(p);
|
||||||
|
if (attrs) {
|
||||||
|
if (attrs.writable && attrs.writable[1] && attrs.value) {
|
||||||
|
const val = device.sanitizeParameterValue([v, attrs.value[1][1]]);
|
||||||
|
if (val[0] !== attrs.value[1][0]) {
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
p,
|
||||||
|
timestamp,
|
||||||
|
{ value: [timestamp, val] },
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (toClear || sessionContext.deviceData.changes.has("prerequisite")) {
|
if (toClear || sessionContext.deviceData.changes.has("prerequisite")) {
|
||||||
if (toClear) {
|
if (toClear) {
|
||||||
for (const c of toClear)
|
for (const c of toClear)
|
||||||
@@ -1741,6 +2028,7 @@ function generateSetRpcRequest(
|
|||||||
| FactoryReset
|
| FactoryReset
|
||||||
| Reboot
|
| Reboot
|
||||||
| Download
|
| Download
|
||||||
|
| ChangeDUState
|
||||||
) & { next?: string } {
|
) & { next?: string } {
|
||||||
const syncState = sessionContext.syncState;
|
const syncState = sessionContext.syncState;
|
||||||
if (!syncState) return null;
|
if (!syncState) return null;
|
||||||
@@ -1919,6 +2207,76 @@ function generateSetRpcRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeDUState
|
||||||
|
const duStateOps: ChangeDUState["operations"] = [];
|
||||||
|
for (const [p, t] of syncState.duStatesChangeDUState) {
|
||||||
|
const attrs = deviceData.attributes.get(p);
|
||||||
|
if (attrs && attrs.value && t <= attrs.value[1][0]) continue;
|
||||||
|
const urlAttrs = deviceData.attributes.get(
|
||||||
|
deviceData.paths.get(p.slice(0, -1).concat(Path.parse("URL")))
|
||||||
|
);
|
||||||
|
const operationTypeAttrs = deviceData.attributes.get(
|
||||||
|
deviceData.paths.get(p.slice(0, -1).concat(Path.parse("OperationType")))
|
||||||
|
);
|
||||||
|
const uuidAttrs = deviceData.attributes.get(
|
||||||
|
deviceData.paths.get(p.slice(0, -1).concat(Path.parse("UUID")))
|
||||||
|
);
|
||||||
|
const executionEnvRefAttrs = deviceData.attributes.get(
|
||||||
|
deviceData.paths.get(p.slice(0, -1).concat(Path.parse("ExecutionEnvRef")))
|
||||||
|
);
|
||||||
|
const versionAttrs = deviceData.attributes.get(
|
||||||
|
deviceData.paths.get(p.slice(0, -1).concat(Path.parse("Version")))
|
||||||
|
);
|
||||||
|
const usernameAttrs = deviceData.attributes.get(
|
||||||
|
deviceData.paths.get(p.slice(0, -1).concat(Path.parse("Username")))
|
||||||
|
);
|
||||||
|
const passwordAttrs = deviceData.attributes.get(
|
||||||
|
deviceData.paths.get(p.slice(0, -1).concat(Path.parse("Password")))
|
||||||
|
);
|
||||||
|
|
||||||
|
const operationType = operationTypeAttrs
|
||||||
|
? (operationTypeAttrs.value[1][0] as string)
|
||||||
|
: null;
|
||||||
|
const url = urlAttrs ? (urlAttrs.value[1][0] as string) : null;
|
||||||
|
const uuid = uuidAttrs ? (uuidAttrs.value[1][0] as string) : null;
|
||||||
|
const executionEnvRef = executionEnvRefAttrs
|
||||||
|
? (executionEnvRefAttrs.value[1][0] as string)
|
||||||
|
: null;
|
||||||
|
const version = versionAttrs ? (versionAttrs.value[1][0] as string) : null;
|
||||||
|
const username = usernameAttrs
|
||||||
|
? (usernameAttrs.value[1][0] as string)
|
||||||
|
: null;
|
||||||
|
const password = passwordAttrs
|
||||||
|
? (passwordAttrs.value[1][0] as string)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
operationType !== "install" &&
|
||||||
|
operationType !== "update" &&
|
||||||
|
operationType !== "uninstall"
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
duStateOps.push({
|
||||||
|
operationType: operationType,
|
||||||
|
instance: p.segments[1] as string,
|
||||||
|
url: url,
|
||||||
|
uuid: uuid,
|
||||||
|
executionEnvRef: executionEnvRef,
|
||||||
|
version: version,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duStateOps.length) {
|
||||||
|
return {
|
||||||
|
name: "ChangeDUState",
|
||||||
|
commandKey: generateRpcId(sessionContext),
|
||||||
|
operations: duStateOps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Reboot
|
// Reboot
|
||||||
if (syncState.reboot) {
|
if (syncState.reboot) {
|
||||||
const p = sessionContext.deviceData.paths.get(Path.parse("Reboot"));
|
const p = sessionContext.deviceData.paths.get(Path.parse("Reboot"));
|
||||||
@@ -2200,6 +2558,26 @@ function processDeclarations(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "DUStates":
|
||||||
|
if (
|
||||||
|
currentPath.length === 3 &&
|
||||||
|
currentPath.wildcard === 0 &&
|
||||||
|
declareAttributeValues &&
|
||||||
|
declareAttributeValues.value
|
||||||
|
) {
|
||||||
|
if (currentPath.segments[2] === "ChangeDUState") {
|
||||||
|
syncState.duStatesChangeDUState.set(
|
||||||
|
currentPath,
|
||||||
|
declareAttributeValues.value[0]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
syncState.duStatesValues.set(
|
||||||
|
currentPath,
|
||||||
|
declareAttributeValues.value[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "VirtualParameters":
|
case "VirtualParameters":
|
||||||
if (currentPath.length <= 2) {
|
if (currentPath.length <= 2) {
|
||||||
let d;
|
let d;
|
||||||
@@ -2370,6 +2748,10 @@ function processInstances(
|
|||||||
if (parent.length !== 1) return;
|
if (parent.length !== 1) return;
|
||||||
instancesToDelete = sessionContext.syncState.downloadsToDelete;
|
instancesToDelete = sessionContext.syncState.downloadsToDelete;
|
||||||
instancesToCreate = sessionContext.syncState.downloadsToCreate;
|
instancesToCreate = sessionContext.syncState.downloadsToCreate;
|
||||||
|
} else if (parent.segments[0] === "DUStates") {
|
||||||
|
if (parent.length !== 1) return;
|
||||||
|
instancesToDelete = sessionContext.syncState.duStatesToDelete;
|
||||||
|
instancesToCreate = sessionContext.syncState.duStatesToCreate;
|
||||||
} else {
|
} else {
|
||||||
instancesToDelete = sessionContext.syncState.instancesToDelete.get(parent);
|
instancesToDelete = sessionContext.syncState.instancesToDelete.get(parent);
|
||||||
if (instancesToDelete == null) {
|
if (instancesToDelete == null) {
|
||||||
@@ -2658,6 +3040,7 @@ export async function rpcResponse(
|
|||||||
"FactoryReset",
|
"FactoryReset",
|
||||||
"VirtualParameters",
|
"VirtualParameters",
|
||||||
"Downloads",
|
"Downloads",
|
||||||
|
"DUStates",
|
||||||
]) {
|
]) {
|
||||||
const p = sessionContext.deviceData.paths.get(Path.parse(n));
|
const p = sessionContext.deviceData.paths.get(Path.parse(n));
|
||||||
if (p && sessionContext.deviceData.attributes.has(p))
|
if (p && sessionContext.deviceData.attributes.has(p))
|
||||||
@@ -2857,7 +3240,7 @@ export async function rpcResponse(
|
|||||||
toClear
|
toClear
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const operation = {
|
const operation: DownloadOperation = {
|
||||||
name: "Download",
|
name: "Download",
|
||||||
timestamp: sessionContext.timestamp,
|
timestamp: sessionContext.timestamp,
|
||||||
provisions: sessionContext.provisions,
|
provisions: sessionContext.provisions,
|
||||||
@@ -2881,6 +3264,40 @@ export async function rpcResponse(
|
|||||||
sessionContext.operationsTouched = {};
|
sessionContext.operationsTouched = {};
|
||||||
sessionContext.operationsTouched[rpcReq.commandKey] = 1;
|
sessionContext.operationsTouched[rpcReq.commandKey] = 1;
|
||||||
}
|
}
|
||||||
|
} else if (rpcRes.name === "ChangeDUStateResponse") {
|
||||||
|
if (rpcReq.name !== "ChangeDUState")
|
||||||
|
return invalidResponse("Response name does not match request name");
|
||||||
|
|
||||||
|
for (const op of rpcReq.operations) {
|
||||||
|
toClear = device.set(
|
||||||
|
sessionContext.deviceData,
|
||||||
|
Path.parse(`DUStates.${op.instance}.ChangeDUState`),
|
||||||
|
timestamp + 1,
|
||||||
|
{ value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] },
|
||||||
|
toClear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const operation = {
|
||||||
|
name: "ChangeDUState",
|
||||||
|
timestamp: sessionContext.timestamp,
|
||||||
|
provisions: sessionContext.provisions,
|
||||||
|
channels: sessionContext.channels,
|
||||||
|
retries: {},
|
||||||
|
args: {
|
||||||
|
operations: rpcReq.operations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const channel of Object.keys(sessionContext.channels)) {
|
||||||
|
if (sessionContext.retries[channel] != null)
|
||||||
|
operation.retries[channel] = sessionContext.retries[channel];
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionContext.operations[rpcReq.commandKey] = operation as Operation;
|
||||||
|
if (!sessionContext.operationsTouched)
|
||||||
|
sessionContext.operationsTouched = {};
|
||||||
|
sessionContext.operationsTouched[rpcReq.commandKey] = 1;
|
||||||
} else {
|
} else {
|
||||||
return invalidResponse("Response name not recognized");
|
return invalidResponse("Response name not recognized");
|
||||||
}
|
}
|
||||||
|
|||||||
+129
@@ -48,6 +48,9 @@ import {
|
|||||||
GetRPCMethodsRequest,
|
GetRPCMethodsRequest,
|
||||||
RequestDownloadRequest,
|
RequestDownloadRequest,
|
||||||
AcsResponse,
|
AcsResponse,
|
||||||
|
ChangeDUState,
|
||||||
|
ChangeDUStateResponse,
|
||||||
|
DUStateChangeCompleteRequest,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import Path from "./common/path";
|
import Path from "./common/path";
|
||||||
|
|
||||||
@@ -573,6 +576,29 @@ function DownloadResponse(xml: Element): DownloadResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ChangeDUState(methodRequest: ChangeDUState): string {
|
||||||
|
const operations = methodRequest.operations.map((op) => {
|
||||||
|
if (op.operationType === "install")
|
||||||
|
return `<Operations xsi:type="cwmp:InstallOpStruct"><UUID>${op.uuid}</UUID><URL>${op.url}</URL><ExecutionEnvRef>${op.executionEnvRef}</ExecutionEnvRef><Username>${op.username}</Username><Password>${op.password}</Password></Operations>`;
|
||||||
|
else if (op.operationType === "update")
|
||||||
|
return `<Operations xsi:type="cwmp:UpdateOpStruct"><UUID>${op.uuid}</UUID><URL>${op.url}</URL><Version>${op.version}</Version><Username>${op.username}</Username><Password>${op.password}</Password></Operations>`;
|
||||||
|
else if (op.operationType === "uninstall")
|
||||||
|
return `<Operations xsi:type="cwmp:UninstallOpStruct"><UUID>${op.uuid}</UUID><Version>${op.version}</Version><ExecutionEnvRef>${op.executionEnvRef}</ExecutionEnvRef></Operations>`;
|
||||||
|
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
|
||||||
|
return `<cwmp:ChangeDUState><CommandKey>${
|
||||||
|
methodRequest.commandKey || ""
|
||||||
|
}</CommandKey>${operations.join("")}</cwmp:ChangeDUState>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChangeDUStateResponse(): ChangeDUStateResponse {
|
||||||
|
return {
|
||||||
|
name: "ChangeDUStateResponse",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function Inform(xml: Element): InformRequest {
|
function Inform(xml: Element): InformRequest {
|
||||||
let retryCount: number, evnt: string[];
|
let retryCount: number, evnt: string[];
|
||||||
let parameterList: [Path, string | number | boolean, string][];
|
let parameterList: [Path, string | number | boolean, string][];
|
||||||
@@ -719,6 +745,97 @@ function TransferCompleteResponse(): string {
|
|||||||
return "<cwmp:TransferCompleteResponse></cwmp:TransferCompleteResponse>";
|
return "<cwmp:TransferCompleteResponse></cwmp:TransferCompleteResponse>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DUStateChangeComplete(xml: Element): DUStateChangeCompleteRequest {
|
||||||
|
let commandKey: string,
|
||||||
|
results: DUStateChangeCompleteRequest["results"],
|
||||||
|
res: DUStateChangeCompleteRequest["results"][0];
|
||||||
|
for (const c of xml.children) {
|
||||||
|
switch (c.localName) {
|
||||||
|
case "CommandKey":
|
||||||
|
commandKey = c.text;
|
||||||
|
break;
|
||||||
|
case "Results":
|
||||||
|
if (!results) results = [];
|
||||||
|
res = {
|
||||||
|
uuid: null,
|
||||||
|
deploymentUnitRef: null,
|
||||||
|
version: null,
|
||||||
|
currentState: null,
|
||||||
|
resolved: true,
|
||||||
|
executionUnitRefList: null,
|
||||||
|
startTime: null,
|
||||||
|
completeTime: null,
|
||||||
|
fault: null,
|
||||||
|
};
|
||||||
|
for (const cc of c.children) {
|
||||||
|
switch (cc.localName) {
|
||||||
|
case "UUID":
|
||||||
|
res.uuid = cc.text;
|
||||||
|
break;
|
||||||
|
case "DeploymentUnitRef":
|
||||||
|
res.deploymentUnitRef = cc.text;
|
||||||
|
break;
|
||||||
|
case "Version":
|
||||||
|
res.version = cc.text;
|
||||||
|
break;
|
||||||
|
case "CurrentState":
|
||||||
|
res.currentState = cc.text;
|
||||||
|
break;
|
||||||
|
case "Resolved":
|
||||||
|
res.resolved = parseBool(cc.text);
|
||||||
|
break;
|
||||||
|
case "ExecutionUnitRefList":
|
||||||
|
res.executionUnitRefList = cc.text;
|
||||||
|
break;
|
||||||
|
case "StartTime":
|
||||||
|
if (c.text) res.startTime = Date.parse(c.text);
|
||||||
|
break;
|
||||||
|
case "CompleteTime":
|
||||||
|
if (c.text) res.completeTime = Date.parse(c.text);
|
||||||
|
break;
|
||||||
|
case "Fault":
|
||||||
|
res.fault = {
|
||||||
|
faultCode: cc.children.find((n) => n.localName === "FaultCode")
|
||||||
|
.text,
|
||||||
|
faultString: cc.children.find(
|
||||||
|
(n) => n.localName === "FaultString"
|
||||||
|
).text,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results.push(res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandKey == null) {
|
||||||
|
warnings.push({
|
||||||
|
message: "Missing or invalid XML node",
|
||||||
|
element: "CommandKey",
|
||||||
|
});
|
||||||
|
commandKey = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!results) {
|
||||||
|
warnings.push({
|
||||||
|
message: "Missing or invalid XML node",
|
||||||
|
element: "Results",
|
||||||
|
});
|
||||||
|
results = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "DUStateChangeComplete",
|
||||||
|
commandKey: commandKey,
|
||||||
|
results: results,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function DUStateChangeCompleteResponse(): string {
|
||||||
|
return "<cwmp:DUStateChangeCompleteResponse></cwmp:DUStateChangeCompleteResponse>";
|
||||||
|
}
|
||||||
|
|
||||||
function RequestDownload(xml: Element): RequestDownloadRequest {
|
function RequestDownload(xml: Element): RequestDownloadRequest {
|
||||||
return {
|
return {
|
||||||
name: "RequestDownload",
|
name: "RequestDownload",
|
||||||
@@ -923,6 +1040,9 @@ export function request(
|
|||||||
case "TransferComplete":
|
case "TransferComplete":
|
||||||
rpc.cpeRequest = TransferComplete(methodElement);
|
rpc.cpeRequest = TransferComplete(methodElement);
|
||||||
break;
|
break;
|
||||||
|
case "DUStateChangeComplete":
|
||||||
|
rpc.cpeRequest = DUStateChangeComplete(methodElement);
|
||||||
|
break;
|
||||||
case "RequestDownload":
|
case "RequestDownload":
|
||||||
rpc.cpeRequest = RequestDownload(methodElement);
|
rpc.cpeRequest = RequestDownload(methodElement);
|
||||||
break;
|
break;
|
||||||
@@ -956,6 +1076,9 @@ export function request(
|
|||||||
case "DownloadResponse":
|
case "DownloadResponse":
|
||||||
rpc.cpeResponse = DownloadResponse(methodElement);
|
rpc.cpeResponse = DownloadResponse(methodElement);
|
||||||
break;
|
break;
|
||||||
|
case "ChangeDUStateResponse":
|
||||||
|
rpc.cpeResponse = ChangeDUStateResponse();
|
||||||
|
break;
|
||||||
case "Fault":
|
case "Fault":
|
||||||
rpc.cpeFault = fault(methodElement);
|
rpc.cpeFault = fault(methodElement);
|
||||||
break;
|
break;
|
||||||
@@ -1009,6 +1132,9 @@ export function response(rpc: {
|
|||||||
case "TransferCompleteResponse":
|
case "TransferCompleteResponse":
|
||||||
body = TransferCompleteResponse();
|
body = TransferCompleteResponse();
|
||||||
break;
|
break;
|
||||||
|
case "DUStateChangeCompleteResponse":
|
||||||
|
body = DUStateChangeCompleteResponse();
|
||||||
|
break;
|
||||||
case "RequestDownloadResponse":
|
case "RequestDownloadResponse":
|
||||||
body = RequestDownloadResponse();
|
body = RequestDownloadResponse();
|
||||||
break;
|
break;
|
||||||
@@ -1051,6 +1177,9 @@ export function response(rpc: {
|
|||||||
case "Download":
|
case "Download":
|
||||||
body = Download(rpc.acsRequest);
|
body = Download(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
|
case "ChangeDUState":
|
||||||
|
body = ChangeDUState(rpc.acsRequest);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown method request ${(rpc.acsRequest as AcsRequest).name}`
|
`Unknown method request ${(rpc.acsRequest as AcsRequest).name}`
|
||||||
|
|||||||
+65
-5
@@ -121,6 +121,10 @@ export interface SyncState {
|
|||||||
downloadsToCreate: InstanceSet;
|
downloadsToCreate: InstanceSet;
|
||||||
downloadsValues: Map<Path, string | number>;
|
downloadsValues: Map<Path, string | number>;
|
||||||
downloadsDownload: Map<Path, number>;
|
downloadsDownload: Map<Path, number>;
|
||||||
|
duStatesToDelete: Set<Path>;
|
||||||
|
duStatesToCreate: InstanceSet;
|
||||||
|
duStatesValues: Map<Path, string | number>;
|
||||||
|
duStatesChangeDUState: Map<Path, number>;
|
||||||
reboot: number;
|
reboot: number;
|
||||||
factoryReset: number;
|
factoryReset: number;
|
||||||
}
|
}
|
||||||
@@ -183,12 +187,17 @@ export interface Task {
|
|||||||
provisions?: (string | number | boolean)[][];
|
provisions?: (string | number | boolean)[][];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Operation {
|
interface OperationBase {
|
||||||
name: string;
|
name: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
provisions: string[][];
|
provisions: string[][];
|
||||||
channels: { [channel: string]: number };
|
channels: { [channel: string]: number };
|
||||||
retries: { [channel: string]: number };
|
retries: { [channel: string]: number };
|
||||||
|
args: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DownloadOperation extends OperationBase {
|
||||||
|
name: "Download";
|
||||||
args: {
|
args: {
|
||||||
instance: string;
|
instance: string;
|
||||||
fileType: string;
|
fileType: string;
|
||||||
@@ -197,6 +206,15 @@ export interface Operation {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChangeDUStateOperation extends OperationBase {
|
||||||
|
name: "ChangeDUState";
|
||||||
|
args: {
|
||||||
|
operations: ChangeDUState["operations"];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Operation = DownloadOperation | ChangeDUStateOperation;
|
||||||
|
|
||||||
export type AcsRequest =
|
export type AcsRequest =
|
||||||
| GetParameterNames
|
| GetParameterNames
|
||||||
| GetParameterValues
|
| GetParameterValues
|
||||||
@@ -207,7 +225,8 @@ export type AcsRequest =
|
|||||||
| DeleteObject
|
| DeleteObject
|
||||||
| FactoryReset
|
| FactoryReset
|
||||||
| Reboot
|
| Reboot
|
||||||
| Download;
|
| Download
|
||||||
|
| ChangeDUState;
|
||||||
|
|
||||||
export interface GetParameterNames {
|
export interface GetParameterNames {
|
||||||
name: "GetParameterNames";
|
name: "GetParameterNames";
|
||||||
@@ -277,6 +296,21 @@ export interface Download {
|
|||||||
failureUrl?: string;
|
failureUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChangeDUState {
|
||||||
|
name: "ChangeDUState";
|
||||||
|
commandKey: string;
|
||||||
|
operations: {
|
||||||
|
operationType: "install" | "update" | "uninstall";
|
||||||
|
instance: string;
|
||||||
|
url: string;
|
||||||
|
uuid: string;
|
||||||
|
version: string;
|
||||||
|
executionEnvRef: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface SpvFault {
|
export interface SpvFault {
|
||||||
parameterName: string;
|
parameterName: string;
|
||||||
faultCode: string;
|
faultCode: string;
|
||||||
@@ -305,7 +339,8 @@ export type CpeResponse =
|
|||||||
| DeleteObjectResponse
|
| DeleteObjectResponse
|
||||||
| RebootResponse
|
| RebootResponse
|
||||||
| FactoryResetResponse
|
| FactoryResetResponse
|
||||||
| DownloadResponse;
|
| DownloadResponse
|
||||||
|
| ChangeDUStateResponse;
|
||||||
|
|
||||||
export interface GetParameterNamesResponse {
|
export interface GetParameterNamesResponse {
|
||||||
name: "GetParameterNamesResponse";
|
name: "GetParameterNamesResponse";
|
||||||
@@ -342,6 +377,10 @@ export interface DeleteObjectResponse {
|
|||||||
status: number;
|
status: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChangeDUStateResponse {
|
||||||
|
name: "ChangeDUStateResponse";
|
||||||
|
}
|
||||||
|
|
||||||
export interface RebootResponse {
|
export interface RebootResponse {
|
||||||
name: "RebootResponse";
|
name: "RebootResponse";
|
||||||
}
|
}
|
||||||
@@ -361,7 +400,8 @@ export type CpeRequest =
|
|||||||
| InformRequest
|
| InformRequest
|
||||||
| TransferCompleteRequest
|
| TransferCompleteRequest
|
||||||
| GetRPCMethodsRequest
|
| GetRPCMethodsRequest
|
||||||
| RequestDownloadRequest;
|
| RequestDownloadRequest
|
||||||
|
| DUStateChangeCompleteRequest;
|
||||||
|
|
||||||
export interface InformRequest {
|
export interface InformRequest {
|
||||||
name: "Inform";
|
name: "Inform";
|
||||||
@@ -384,6 +424,22 @@ export interface TransferCompleteRequest {
|
|||||||
completeTime?: number;
|
completeTime?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DUStateChangeCompleteRequest {
|
||||||
|
name: "DUStateChangeComplete";
|
||||||
|
commandKey: string;
|
||||||
|
results: {
|
||||||
|
uuid: string;
|
||||||
|
deploymentUnitRef: string;
|
||||||
|
version: string;
|
||||||
|
currentState: string;
|
||||||
|
resolved: boolean;
|
||||||
|
executionUnitRefList: string;
|
||||||
|
fault?: { faultCode: string; faultString: string };
|
||||||
|
startTime?: number;
|
||||||
|
completeTime?: number;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface GetRPCMethodsRequest {
|
export interface GetRPCMethodsRequest {
|
||||||
name: "GetRPCMethods";
|
name: "GetRPCMethods";
|
||||||
}
|
}
|
||||||
@@ -397,7 +453,8 @@ export type AcsResponse =
|
|||||||
| InformResponse
|
| InformResponse
|
||||||
| GetRPCMethodsResponse
|
| GetRPCMethodsResponse
|
||||||
| TransferCompleteResponse
|
| TransferCompleteResponse
|
||||||
| RequestDownloadResponse;
|
| RequestDownloadResponse
|
||||||
|
| DUStateChangeCompleteResponse;
|
||||||
|
|
||||||
export interface InformResponse {
|
export interface InformResponse {
|
||||||
name: "InformResponse";
|
name: "InformResponse";
|
||||||
@@ -415,6 +472,9 @@ export interface TransferCompleteResponse {
|
|||||||
export interface RequestDownloadResponse {
|
export interface RequestDownloadResponse {
|
||||||
name: "RequestDownloadResponse";
|
name: "RequestDownloadResponse";
|
||||||
}
|
}
|
||||||
|
export interface DUStateChangeCompleteResponse {
|
||||||
|
name: "DUStateChangeCompleteResponse";
|
||||||
|
}
|
||||||
|
|
||||||
export interface QueryOptions {
|
export interface QueryOptions {
|
||||||
projection?: any;
|
projection?: any;
|
||||||
|
|||||||
Reference in new issue
Block a user