Refactor local cache
This commit is contained in:
1 parent
8b18f97d12
commit
e909d783e2
3 files changed
+444
-465
No files matched your search
+163
-178
@@ -278,203 +278,195 @@ function appendProvisions(original, toAppend) {
|
||||
|
||||
function applyPresets(sessionContext) {
|
||||
const deviceData = sessionContext.deviceData;
|
||||
localCache.getPresets((err, presetsHash, presets) => {
|
||||
if (err) return void throwError(err, sessionContext.httpResponse);
|
||||
const presets = localCache.getPresets(sessionContext.cacheSnapshot);
|
||||
|
||||
// Filter presets based on existing faults
|
||||
const blackList = {};
|
||||
let whiteList = null;
|
||||
let whiteListProvisions = null;
|
||||
const RETRY_DELAY = config.get("RETRY_DELAY", sessionContext.deviceId);
|
||||
// Filter presets based on existing faults
|
||||
const blackList = {};
|
||||
let whiteList = null;
|
||||
let whiteListProvisions = null;
|
||||
const RETRY_DELAY = config.get("RETRY_DELAY", sessionContext.deviceId);
|
||||
|
||||
if (sessionContext.faults) {
|
||||
for (const [channel, fault] of Object.entries(sessionContext.faults)) {
|
||||
let retryTimestamp = 0;
|
||||
if (!fault.retryNow) {
|
||||
retryTimestamp =
|
||||
fault.timestamp +
|
||||
RETRY_DELAY * Math.pow(2, sessionContext.retries[channel]) * 1000;
|
||||
}
|
||||
if (sessionContext.faults) {
|
||||
for (const [channel, fault] of Object.entries(sessionContext.faults)) {
|
||||
let retryTimestamp = 0;
|
||||
if (!fault.retryNow) {
|
||||
retryTimestamp =
|
||||
fault.timestamp +
|
||||
RETRY_DELAY * Math.pow(2, sessionContext.retries[channel]) * 1000;
|
||||
}
|
||||
|
||||
if (retryTimestamp <= sessionContext.timestamp) {
|
||||
whiteList = channel;
|
||||
whiteListProvisions = fault.provisions;
|
||||
break;
|
||||
}
|
||||
if (retryTimestamp <= sessionContext.timestamp) {
|
||||
whiteList = channel;
|
||||
whiteListProvisions = fault.provisions;
|
||||
break;
|
||||
}
|
||||
|
||||
blackList[channel] = fault.precondition ? 1 : 2;
|
||||
blackList[channel] = fault.precondition ? 1 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
deviceData.timestamps.revision = 1;
|
||||
deviceData.attributes.revision = 1;
|
||||
|
||||
const deviceEvents = {};
|
||||
for (const p of deviceData.paths.find(["Events", "*"], false, true)) {
|
||||
const attrs = deviceData.attributes.get(p);
|
||||
if (attrs && attrs.value && attrs.value[1][0] >= sessionContext.timestamp)
|
||||
deviceEvents[p[1]] = true;
|
||||
}
|
||||
|
||||
const parameters = {};
|
||||
const filteredPresets = [];
|
||||
|
||||
for (const preset of presets) {
|
||||
if (whiteList != null) {
|
||||
if (preset.channel !== whiteList) continue;
|
||||
} else if (blackList[preset.channel] === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let eventsMatch = true;
|
||||
for (const [k, v] of Object.entries(preset.events)) {
|
||||
if (!v !== !deviceEvents[k.replace(/\s+/g, "_")]) {
|
||||
eventsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
deviceData.timestamps.revision = 1;
|
||||
deviceData.attributes.revision = 1;
|
||||
if (!eventsMatch) continue;
|
||||
|
||||
const deviceEvents = {};
|
||||
for (const p of deviceData.paths.find(["Events", "*"], false, true)) {
|
||||
const attrs = deviceData.attributes.get(p);
|
||||
if (attrs && attrs.value && attrs.value[1][0] >= sessionContext.timestamp)
|
||||
deviceEvents[p[1]] = true;
|
||||
}
|
||||
|
||||
const parameters = {};
|
||||
const filteredPresets = [];
|
||||
|
||||
for (const preset of presets) {
|
||||
if (whiteList != null) {
|
||||
if (preset.channel !== whiteList) continue;
|
||||
} else if (blackList[preset.channel] === 1) {
|
||||
if (preset.schedule && preset.schedule.schedule) {
|
||||
const r = scheduling.cron(
|
||||
sessionContext.timestamp,
|
||||
preset.schedule.schedule
|
||||
);
|
||||
if (!(r[0] + preset.schedule.duration > sessionContext.timestamp))
|
||||
continue;
|
||||
}
|
||||
|
||||
filteredPresets.push(preset);
|
||||
for (const k of Object.keys(preset.precondition)) {
|
||||
sessionContext.channels[preset.channel] = 0;
|
||||
const p = k.split(/([^a-zA-Z0-9\-_.].*)/, 1)[0];
|
||||
parameters[p] = common.parsePath(p);
|
||||
}
|
||||
}
|
||||
|
||||
const declarations = Object.values(parameters).map(v => ({
|
||||
path: v,
|
||||
pathGet: 1,
|
||||
pathSet: null,
|
||||
attrGet: { value: 1 },
|
||||
attrSet: null,
|
||||
defer: true
|
||||
}));
|
||||
|
||||
session.rpcRequest(
|
||||
sessionContext,
|
||||
declarations,
|
||||
(err, flt, reqId, acsReq) => {
|
||||
if (err) return void throwError(err, sessionContext.httpResponse);
|
||||
|
||||
if (flt) {
|
||||
recordFault(sessionContext, flt);
|
||||
session.clearProvisions(sessionContext);
|
||||
return void applyPresets(sessionContext);
|
||||
}
|
||||
|
||||
let eventsMatch = true;
|
||||
for (const [k, v] of Object.entries(preset.events)) {
|
||||
if (!v !== !deviceEvents[k.replace(/\s+/g, "_")]) {
|
||||
eventsMatch = false;
|
||||
break;
|
||||
if (acsReq) return void sendAcsRequest(sessionContext, reqId, acsReq);
|
||||
|
||||
session.clearProvisions(sessionContext);
|
||||
|
||||
const parameterValues = {};
|
||||
for (const [k, v] of Object.entries(parameters)) {
|
||||
const unpacked = device.unpack(deviceData, v);
|
||||
if (!unpacked[0]) continue;
|
||||
const attrs = deviceData.attributes.get(unpacked[0]);
|
||||
if (attrs && attrs.value && attrs.value[1])
|
||||
parameterValues[k] = attrs.value[1][0];
|
||||
}
|
||||
|
||||
if (whiteList != null)
|
||||
session.addProvisions(sessionContext, whiteList, whiteListProvisions);
|
||||
|
||||
const appendProvisionsToFaults = {};
|
||||
for (const p of filteredPresets) {
|
||||
if (query.testFilter(parameterValues, p.precondition)) {
|
||||
if (blackList[p.channel] === 2) {
|
||||
appendProvisionsToFaults[p.channel] = (
|
||||
appendProvisionsToFaults[p.channel] || []
|
||||
).concat(p.provisions);
|
||||
} else {
|
||||
session.addProvisions(sessionContext, p.channel, p.provisions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!eventsMatch) continue;
|
||||
|
||||
if (preset.schedule && preset.schedule.schedule) {
|
||||
const r = scheduling.cron(
|
||||
sessionContext.timestamp,
|
||||
preset.schedule.schedule
|
||||
);
|
||||
if (!(r[0] + preset.schedule.duration > sessionContext.timestamp))
|
||||
continue;
|
||||
for (const [channel, provisions] of Object.entries(
|
||||
appendProvisionsToFaults
|
||||
)) {
|
||||
if (
|
||||
appendProvisions(
|
||||
sessionContext.faults[channel].provisions,
|
||||
provisions
|
||||
)
|
||||
) {
|
||||
if (!sessionContext.faultsTouched) sessionContext.faultsTouched = {};
|
||||
sessionContext.faultsTouched[channel] = true;
|
||||
}
|
||||
}
|
||||
|
||||
filteredPresets.push(preset);
|
||||
for (const k of Object.keys(preset.precondition)) {
|
||||
sessionContext.channels[preset.channel] = 0;
|
||||
const p = k.split(/([^a-zA-Z0-9\-_.].*)/, 1)[0];
|
||||
parameters[p] = common.parsePath(p);
|
||||
// Don't increment when processing a single channel (e.g. after fault)
|
||||
if (whiteList == null)
|
||||
sessionContext.presetCycles = (sessionContext.presetCycles || 0) + 1;
|
||||
|
||||
if (sessionContext.presetCycles > MAX_CYCLES) {
|
||||
const fault = {
|
||||
code: "preset_loop",
|
||||
message: "The presets are stuck in an endless configuration loop",
|
||||
timestamp: sessionContext.timestamp
|
||||
};
|
||||
recordFault(sessionContext, fault);
|
||||
// No need to save retryNow
|
||||
for (const f of Object.values(sessionContext.faults)) delete f.retryNow;
|
||||
session.clearProvisions(sessionContext);
|
||||
return void sendAcsRequest(sessionContext);
|
||||
}
|
||||
}
|
||||
|
||||
const declarations = Object.values(parameters).map(v => ({
|
||||
path: v,
|
||||
pathGet: 1,
|
||||
pathSet: null,
|
||||
attrGet: { value: 1 },
|
||||
attrSet: null,
|
||||
defer: true
|
||||
}));
|
||||
|
||||
session.rpcRequest(
|
||||
sessionContext,
|
||||
declarations,
|
||||
(err, flt, reqId, acsReq) => {
|
||||
deviceData.timestamps.dirty = 0;
|
||||
deviceData.attributes.dirty = 0;
|
||||
session.rpcRequest(sessionContext, null, (err, fault, id, acsRequest) => {
|
||||
if (err) return void throwError(err, sessionContext.httpResponse);
|
||||
|
||||
if (flt) {
|
||||
recordFault(sessionContext, flt);
|
||||
if (fault) {
|
||||
recordFault(sessionContext, fault);
|
||||
session.clearProvisions(sessionContext);
|
||||
return void applyPresets(sessionContext);
|
||||
}
|
||||
|
||||
if (acsReq) return void sendAcsRequest(sessionContext, reqId, acsReq);
|
||||
|
||||
session.clearProvisions(sessionContext);
|
||||
|
||||
const parameterValues = {};
|
||||
for (const [k, v] of Object.entries(parameters)) {
|
||||
const unpacked = device.unpack(deviceData, v);
|
||||
if (!unpacked[0]) continue;
|
||||
const attrs = deviceData.attributes.get(unpacked[0]);
|
||||
if (attrs && attrs.value && attrs.value[1])
|
||||
parameterValues[k] = attrs.value[1][0];
|
||||
}
|
||||
|
||||
if (whiteList != null)
|
||||
session.addProvisions(sessionContext, whiteList, whiteListProvisions);
|
||||
|
||||
const appendProvisionsToFaults = {};
|
||||
for (const p of filteredPresets) {
|
||||
if (query.testFilter(parameterValues, p.precondition)) {
|
||||
if (blackList[p.channel] === 2) {
|
||||
appendProvisionsToFaults[p.channel] = (
|
||||
appendProvisionsToFaults[p.channel] || []
|
||||
).concat(p.provisions);
|
||||
} else {
|
||||
session.addProvisions(sessionContext, p.channel, p.provisions);
|
||||
if (!acsRequest) {
|
||||
for (const channel of Object.keys(sessionContext.channels)) {
|
||||
if (sessionContext.faults[channel]) {
|
||||
delete sessionContext.faults[channel];
|
||||
if (!sessionContext.faultsTouched)
|
||||
sessionContext.faultsTouched = {};
|
||||
sessionContext.faultsTouched[channel] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [channel, provisions] of Object.entries(
|
||||
appendProvisionsToFaults
|
||||
)) {
|
||||
if (whiteList != null) return void applyPresets(sessionContext);
|
||||
|
||||
if (
|
||||
appendProvisions(
|
||||
sessionContext.faults[channel].provisions,
|
||||
provisions
|
||||
)
|
||||
) {
|
||||
if (!sessionContext.faultsTouched)
|
||||
sessionContext.faultsTouched = {};
|
||||
sessionContext.faultsTouched[channel] = true;
|
||||
}
|
||||
sessionContext.deviceData.timestamps.dirty > 1 ||
|
||||
sessionContext.deviceData.attributes.dirty > 1
|
||||
)
|
||||
return void applyPresets(sessionContext);
|
||||
}
|
||||
|
||||
// Don't increment when processing a single channel (e.g. after fault)
|
||||
if (whiteList == null)
|
||||
sessionContext.presetCycles = (sessionContext.presetCycles || 0) + 1;
|
||||
|
||||
if (sessionContext.presetCycles > MAX_CYCLES) {
|
||||
const fault = {
|
||||
code: "preset_loop",
|
||||
message: "The presets are stuck in an endless configuration loop",
|
||||
timestamp: sessionContext.timestamp
|
||||
};
|
||||
recordFault(sessionContext, fault);
|
||||
// No need to save retryNow
|
||||
for (const f of Object.values(sessionContext.faults))
|
||||
delete f.retryNow;
|
||||
session.clearProvisions(sessionContext);
|
||||
return void sendAcsRequest(sessionContext);
|
||||
}
|
||||
|
||||
deviceData.timestamps.dirty = 0;
|
||||
deviceData.attributes.dirty = 0;
|
||||
session.rpcRequest(
|
||||
sessionContext,
|
||||
null,
|
||||
(err, fault, id, acsRequest) => {
|
||||
if (err) return void throwError(err, sessionContext.httpResponse);
|
||||
|
||||
if (fault) {
|
||||
recordFault(sessionContext, fault);
|
||||
session.clearProvisions(sessionContext);
|
||||
return void applyPresets(sessionContext);
|
||||
}
|
||||
|
||||
if (!acsRequest) {
|
||||
for (const channel of Object.keys(sessionContext.channels)) {
|
||||
if (sessionContext.faults[channel]) {
|
||||
delete sessionContext.faults[channel];
|
||||
if (!sessionContext.faultsTouched)
|
||||
sessionContext.faultsTouched = {};
|
||||
sessionContext.faultsTouched[channel] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (whiteList != null) return void applyPresets(sessionContext);
|
||||
|
||||
if (
|
||||
sessionContext.deviceData.timestamps.dirty > 1 ||
|
||||
sessionContext.deviceData.attributes.dirty > 1
|
||||
)
|
||||
return void applyPresets(sessionContext);
|
||||
}
|
||||
|
||||
sendAcsRequest(sessionContext, id, acsRequest);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
sendAcsRequest(sessionContext, id, acsRequest);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function nextRpc(sessionContext) {
|
||||
@@ -718,6 +710,7 @@ function sendAcsRequest(sessionContext, id, acsRequest) {
|
||||
return void writeResponse(sessionContext, soap.response(null), true);
|
||||
|
||||
if (acsRequest.name === "Download") {
|
||||
acsRequest.fileSize = 0;
|
||||
if (!acsRequest.url) {
|
||||
const FS_PORT = config.get("FS_PORT");
|
||||
const FS_HOSTNAME = config.get("FS_HOSTNAME");
|
||||
@@ -726,18 +719,10 @@ function sendAcsRequest(sessionContext, id, acsRequest) {
|
||||
acsRequest.url += FS_HOSTNAME;
|
||||
if (FS_PORT !== 80) acsRequest.url += ":" + FS_PORT;
|
||||
acsRequest.url += "/" + encodeURI(acsRequest.fileName);
|
||||
}
|
||||
|
||||
if (acsRequest.fileSize == null) {
|
||||
return void localCache.getFiles((err, hash, files) => {
|
||||
if (err) return void throwError(err, sessionContext.httpResponse);
|
||||
|
||||
if (files[acsRequest.fileName])
|
||||
acsRequest.fileSize = files[acsRequest.fileName].length;
|
||||
else acsRequest.fileSize = 0;
|
||||
|
||||
sendAcsRequest(sessionContext, id, acsRequest);
|
||||
});
|
||||
const files = localCache.getFiles(sessionContext.cacheSnapshot);
|
||||
if (files[acsRequest.fileName])
|
||||
acsRequest.fileSize = files[acsRequest.fileName].length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user