Compare commits
2
Commits
v1.2.6
...
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);
|
||||
}
|
||||
|
||||
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
|
||||
function appendProvisions(original, toAppend): boolean {
|
||||
let modified = false;
|
||||
@@ -1239,6 +1272,16 @@ async function processRequest(
|
||||
cwmpVersion: sessionContext.cwmpVersion,
|
||||
});
|
||||
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 {
|
||||
if (sessionContext.state !== 1 || rpc.cpeRequest.name === "Inform")
|
||||
return void reportBadState(sessionContext);
|
||||
|
||||
+418
-1
@@ -57,6 +57,10 @@ import {
|
||||
FactoryReset,
|
||||
AddObjectResponse,
|
||||
GetParameterValuesResponse,
|
||||
ChangeDUStateOperation,
|
||||
DownloadOperation,
|
||||
DUStateChangeCompleteRequest,
|
||||
ChangeDUState,
|
||||
} from "./types";
|
||||
import { getRequestOrigin } from "./forwarded";
|
||||
import * as logger from "./logger";
|
||||
@@ -284,6 +288,8 @@ export async function transferComplete(
|
||||
sessionContext.deviceData.attributes.revision = revision;
|
||||
const commandKey = rpcReq.commandKey;
|
||||
const operation = sessionContext.operations[commandKey];
|
||||
if (operation.name !== "Download")
|
||||
throw new Error("Unexpected operation name");
|
||||
|
||||
if (!operation) {
|
||||
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(
|
||||
sessionContext: SessionContext
|
||||
): Promise<{ faults: Fault[]; operations: Operation[] }> {
|
||||
@@ -794,6 +936,10 @@ function runDeclarations(
|
||||
downloadsToCreate: new InstanceSet(),
|
||||
downloadsValues: new Map(),
|
||||
downloadsDownload: new Map(),
|
||||
duStatesToDelete: new Set(),
|
||||
duStatesToCreate: new InstanceSet(),
|
||||
duStatesValues: new Map(),
|
||||
duStatesChangeDUState: new Map(),
|
||||
reboot: 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) {
|
||||
for (const c of toClear)
|
||||
@@ -1741,6 +2028,7 @@ function generateSetRpcRequest(
|
||||
| FactoryReset
|
||||
| Reboot
|
||||
| Download
|
||||
| ChangeDUState
|
||||
) & { next?: string } {
|
||||
const syncState = sessionContext.syncState;
|
||||
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
|
||||
if (syncState.reboot) {
|
||||
const p = sessionContext.deviceData.paths.get(Path.parse("Reboot"));
|
||||
@@ -2200,6 +2558,26 @@ function processDeclarations(
|
||||
}
|
||||
}
|
||||
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":
|
||||
if (currentPath.length <= 2) {
|
||||
let d;
|
||||
@@ -2370,6 +2748,10 @@ function processInstances(
|
||||
if (parent.length !== 1) return;
|
||||
instancesToDelete = sessionContext.syncState.downloadsToDelete;
|
||||
instancesToCreate = sessionContext.syncState.downloadsToCreate;
|
||||
} else if (parent.segments[0] === "DUStates") {
|
||||
if (parent.length !== 1) return;
|
||||
instancesToDelete = sessionContext.syncState.duStatesToDelete;
|
||||
instancesToCreate = sessionContext.syncState.duStatesToCreate;
|
||||
} else {
|
||||
instancesToDelete = sessionContext.syncState.instancesToDelete.get(parent);
|
||||
if (instancesToDelete == null) {
|
||||
@@ -2658,6 +3040,7 @@ export async function rpcResponse(
|
||||
"FactoryReset",
|
||||
"VirtualParameters",
|
||||
"Downloads",
|
||||
"DUStates",
|
||||
]) {
|
||||
const p = sessionContext.deviceData.paths.get(Path.parse(n));
|
||||
if (p && sessionContext.deviceData.attributes.has(p))
|
||||
@@ -2857,7 +3240,7 @@ export async function rpcResponse(
|
||||
toClear
|
||||
);
|
||||
} else {
|
||||
const operation = {
|
||||
const operation: DownloadOperation = {
|
||||
name: "Download",
|
||||
timestamp: sessionContext.timestamp,
|
||||
provisions: sessionContext.provisions,
|
||||
@@ -2881,6 +3264,40 @@ export async function rpcResponse(
|
||||
sessionContext.operationsTouched = {};
|
||||
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 {
|
||||
return invalidResponse("Response name not recognized");
|
||||
}
|
||||
|
||||
+129
@@ -48,6 +48,9 @@ import {
|
||||
GetRPCMethodsRequest,
|
||||
RequestDownloadRequest,
|
||||
AcsResponse,
|
||||
ChangeDUState,
|
||||
ChangeDUStateResponse,
|
||||
DUStateChangeCompleteRequest,
|
||||
} from "./types";
|
||||
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 {
|
||||
let retryCount: number, evnt: string[];
|
||||
let parameterList: [Path, string | number | boolean, string][];
|
||||
@@ -719,6 +745,97 @@ function TransferCompleteResponse(): string {
|
||||
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 {
|
||||
return {
|
||||
name: "RequestDownload",
|
||||
@@ -923,6 +1040,9 @@ export function request(
|
||||
case "TransferComplete":
|
||||
rpc.cpeRequest = TransferComplete(methodElement);
|
||||
break;
|
||||
case "DUStateChangeComplete":
|
||||
rpc.cpeRequest = DUStateChangeComplete(methodElement);
|
||||
break;
|
||||
case "RequestDownload":
|
||||
rpc.cpeRequest = RequestDownload(methodElement);
|
||||
break;
|
||||
@@ -956,6 +1076,9 @@ export function request(
|
||||
case "DownloadResponse":
|
||||
rpc.cpeResponse = DownloadResponse(methodElement);
|
||||
break;
|
||||
case "ChangeDUStateResponse":
|
||||
rpc.cpeResponse = ChangeDUStateResponse();
|
||||
break;
|
||||
case "Fault":
|
||||
rpc.cpeFault = fault(methodElement);
|
||||
break;
|
||||
@@ -1009,6 +1132,9 @@ export function response(rpc: {
|
||||
case "TransferCompleteResponse":
|
||||
body = TransferCompleteResponse();
|
||||
break;
|
||||
case "DUStateChangeCompleteResponse":
|
||||
body = DUStateChangeCompleteResponse();
|
||||
break;
|
||||
case "RequestDownloadResponse":
|
||||
body = RequestDownloadResponse();
|
||||
break;
|
||||
@@ -1051,6 +1177,9 @@ export function response(rpc: {
|
||||
case "Download":
|
||||
body = Download(rpc.acsRequest);
|
||||
break;
|
||||
case "ChangeDUState":
|
||||
body = ChangeDUState(rpc.acsRequest);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown method request ${(rpc.acsRequest as AcsRequest).name}`
|
||||
|
||||
+65
-5
@@ -121,6 +121,10 @@ export interface SyncState {
|
||||
downloadsToCreate: InstanceSet;
|
||||
downloadsValues: Map<Path, string | number>;
|
||||
downloadsDownload: Map<Path, number>;
|
||||
duStatesToDelete: Set<Path>;
|
||||
duStatesToCreate: InstanceSet;
|
||||
duStatesValues: Map<Path, string | number>;
|
||||
duStatesChangeDUState: Map<Path, number>;
|
||||
reboot: number;
|
||||
factoryReset: number;
|
||||
}
|
||||
@@ -183,12 +187,17 @@ export interface Task {
|
||||
provisions?: (string | number | boolean)[][];
|
||||
}
|
||||
|
||||
export interface Operation {
|
||||
interface OperationBase {
|
||||
name: string;
|
||||
timestamp: number;
|
||||
provisions: string[][];
|
||||
channels: { [channel: string]: number };
|
||||
retries: { [channel: string]: number };
|
||||
args: any;
|
||||
}
|
||||
|
||||
export interface DownloadOperation extends OperationBase {
|
||||
name: "Download";
|
||||
args: {
|
||||
instance: 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 =
|
||||
| GetParameterNames
|
||||
| GetParameterValues
|
||||
@@ -207,7 +225,8 @@ export type AcsRequest =
|
||||
| DeleteObject
|
||||
| FactoryReset
|
||||
| Reboot
|
||||
| Download;
|
||||
| Download
|
||||
| ChangeDUState;
|
||||
|
||||
export interface GetParameterNames {
|
||||
name: "GetParameterNames";
|
||||
@@ -277,6 +296,21 @@ export interface Download {
|
||||
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 {
|
||||
parameterName: string;
|
||||
faultCode: string;
|
||||
@@ -305,7 +339,8 @@ export type CpeResponse =
|
||||
| DeleteObjectResponse
|
||||
| RebootResponse
|
||||
| FactoryResetResponse
|
||||
| DownloadResponse;
|
||||
| DownloadResponse
|
||||
| ChangeDUStateResponse;
|
||||
|
||||
export interface GetParameterNamesResponse {
|
||||
name: "GetParameterNamesResponse";
|
||||
@@ -342,6 +377,10 @@ export interface DeleteObjectResponse {
|
||||
status: number;
|
||||
}
|
||||
|
||||
export interface ChangeDUStateResponse {
|
||||
name: "ChangeDUStateResponse";
|
||||
}
|
||||
|
||||
export interface RebootResponse {
|
||||
name: "RebootResponse";
|
||||
}
|
||||
@@ -361,7 +400,8 @@ export type CpeRequest =
|
||||
| InformRequest
|
||||
| TransferCompleteRequest
|
||||
| GetRPCMethodsRequest
|
||||
| RequestDownloadRequest;
|
||||
| RequestDownloadRequest
|
||||
| DUStateChangeCompleteRequest;
|
||||
|
||||
export interface InformRequest {
|
||||
name: "Inform";
|
||||
@@ -384,6 +424,22 @@ export interface TransferCompleteRequest {
|
||||
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 {
|
||||
name: "GetRPCMethods";
|
||||
}
|
||||
@@ -397,7 +453,8 @@ export type AcsResponse =
|
||||
| InformResponse
|
||||
| GetRPCMethodsResponse
|
||||
| TransferCompleteResponse
|
||||
| RequestDownloadResponse;
|
||||
| RequestDownloadResponse
|
||||
| DUStateChangeCompleteResponse;
|
||||
|
||||
export interface InformResponse {
|
||||
name: "InformResponse";
|
||||
@@ -415,6 +472,9 @@ export interface TransferCompleteResponse {
|
||||
export interface RequestDownloadResponse {
|
||||
name: "RequestDownloadResponse";
|
||||
}
|
||||
export interface DUStateChangeCompleteResponse {
|
||||
name: "DUStateChangeCompleteResponse";
|
||||
}
|
||||
|
||||
export interface QueryOptions {
|
||||
projection?: any;
|
||||
|
||||
Reference in new issue
Block a user