From 739d215a92306f28126ac706a16a7611091ae86a Mon Sep 17 00:00:00 2001 From: Zaid Abdulla Date: Wed, 21 Sep 2016 17:26:21 -0700 Subject: [PATCH] Improve paramter set and load routines --- lib/config.coffee | 1 + lib/db.coffee | 162 ++++++++++++++++++++------------------- lib/device.js | 187 ++++++++++++++++++++++++--------------------- lib/session.coffee | 157 ++++++++++++++++++++++--------------- 4 files changed, 282 insertions(+), 225 deletions(-) diff --git a/lib/config.coffee b/lib/config.coffee index b295472..d2111b2 100644 --- a/lib/config.coffee +++ b/lib/config.coffee @@ -49,6 +49,7 @@ options = { SESSION_TIMEOUT : {type : 'int', default : 30}, GET_PARAMETER_NAMES_DEPTH_THRESHOLD : {type : 'int', default : 0}, TASK_PARAMETERS_BATCH_SIZE : {type : 'int', default : 32}, + MAX_DEPTH : {type: 'int', default : 16}, COOKIES_PATH : {type : 'string'}, # Libxml related configuration diff --git a/lib/db.coffee b/lib/db.coffee index e3a8800..050d833 100644 --- a/lib/db.coffee +++ b/lib/db.coffee @@ -126,94 +126,98 @@ optimizeProjection = (obj) -> fetchDevice = (id, timestamp, patterns, callback) -> + MAX_DEPTH = config.get('MAX_DEPTH', id) res = [] loaded = [] # Build projection projection = {_id: 1} - for pattern in patterns - loaded.push([pattern, 1]) - if pattern.length == 0 - projection['_timestamp'] = 1 - else if pattern[0] == '*' + for [pattern, depth] in patterns + loaded.push([pattern, depth]) + if depth & 1 and (pattern.length < 1 or pattern[0] == '*') projection[''] = 1 - else if pattern[0] == 'Events' - if pattern.length == 1 - res.push([['Events'], - {exist: timestamp, object: timestamp, writable: timestamp}, - {exist: 1, object: 1, writable: 0}]) - else if pattern.length == 2 - if pattern[1] == '*' - projection['_registered'] = 1 - projection['_lastInform'] = 1 - projection['_lastBootstrap'] = 1 - projection['_lastBoot'] = 1 - res.push([['Events', '*'], {exist: timestamp}]) - else if pattern[1] == 'Registered' - projection['_registered'] = 1 - else if pattern[1] == 'Inform' - projection['_lastInform'] = 1 - else if pattern[1] == '0_BOOTSTRAP' - projection['_lastBootstrap'] = 1 - else if pattern[1] == '1_BOOT' - projection['_lastBoot'] = 1 - else if pattern[0] == 'DeviceID' - if pattern.length == 1 - res.push([['DeviceID'], - {exist: timestamp, object: timestamp, writable: timestamp}, - {exist: 1, object: 1, writable: 0}]) - else if pattern.length == 2 - if pattern[1] == '*' - projection['_id'] = 1 - projection['_deviceId._Manufacturer'] = 1 - projection['_deviceId._ProductClass'] = 1 - projection['_deviceId._SerialNumber'] = 1 - projection['_deviceId._OUI'] = 1 - res.push([['DeviceID', '*'], {exist: timestamp}]) + if depth & 1 and (pattern.length < 1 or pattern[0] == '*' or pattern[0] == 'Events') + res.push([['Events'], + {exist: timestamp, object: timestamp, writable: timestamp}, + {exist: 1, object: 1, writable: 0}]) - else if pattern[1] == 'ID' - projection['_id'] = 1 - else if pattern[1] == 'Manufacturer' - projection['_deviceId._Manufacturer'] = 1 - else if pattern[1] == 'ProductClass' - projection['_deviceId._ProductClass'] = 1 - else if pattern[1] == 'OUI' - projection['_deviceId._OUI'] = 1 - else if pattern[1] == 'SerialNumber' - projection['_deviceId._SerialNumber'] = 1 - else if pattern[0] == 'Tags' - if pattern.length == 1 - res.push([['Tags'], - {exist: timestamp, object: timestamp, writable: timestamp}, - {exist: 1, object: 1, writable: 0}]) + if depth & 2 and (pattern.length < 2 or pattern[0] == '*' or pattern[0] == 'Events') + if not pattern[1]? or pattern[1] == '*' + projection['_registered'] = 1 + projection['_lastInform'] = 1 + projection['_lastBootstrap'] = 1 + projection['_lastBoot'] = 1 + res.push([['Events', '*'], {exist: timestamp}]) + else if pattern[1] == 'Registered' + projection['_registered'] = 1 + else if pattern[1] == 'Inform' + projection['_lastInform'] = 1 + else if pattern[1] == '0_BOOTSTRAP' + projection['_lastBootstrap'] = 1 + else if pattern[1] == '1_BOOT' + projection['_lastBoot'] = 1 - else if pattern.length == 2 - res.push([['Tags', '*'], {exist: timestamp}]) - projection['_tags'] = 1 - else - wildcardIndex = pattern.indexOf('*') + if depth & 1 and (pattern.length < 1 or pattern[0] == '*' or pattern[0] == 'DeviceID') + res.push([['DeviceID'], + {exist: timestamp, object: timestamp, writable: timestamp}, + {exist: 1, object: 1, writable: 0}]) - if wildcardIndex == -1 - s = pattern.join('.') - projection["#{s}._value"] = 1 - projection["#{s}._timestamp"] = 1 - projection["#{s}._type"] = 1 - projection["#{s}._writable"] = 1 - projection["#{s}._object"] = 1 - projection["#{s}._orig"] = 1 + if depth & 2 and (pattern.length < 2 or pattern[0] == '*' or pattern[0] == 'DeviceID') + if not pattern[1]? or pattern[1] == '*' + projection['_id'] = 1 + projection['_deviceId._Manufacturer'] = 1 + projection['_deviceId._ProductClass'] = 1 + projection['_deviceId._SerialNumber'] = 1 + projection['_deviceId._OUI'] = 1 + res.push([['DeviceID', '*'], {exist: timestamp}]) + else if pattern[1] == 'ID' + projection['_id'] = 1 + else if pattern[1] == 'Manufacturer' + projection['_deviceId._Manufacturer'] = 1 + else if pattern[1] == 'ProductClass' + projection['_deviceId._ProductClass'] = 1 + else if pattern[1] == 'OUI' + projection['_deviceId._OUI'] = 1 + else if pattern[1] == 'SerialNumber' + projection['_deviceId._SerialNumber'] = 1 - # Timestamp from parent is needed for writable timestamp - if pattern.length <= 1 - projection['_timestamp'] = 1 - else - projection["#{pattern.slice(0, -1).join('.')}._timestamp"] = 1 - else - p = pattern.slice(0, wildcardIndex) - loaded.push([p, 99]) - s = p.join('.') - projection[s] = 1 - projection[pattern.slice(0, wildcardIndex - 1).concat('_timestamp').join('.')] = 1 + if depth & 1 and (pattern.length < 1 or pattern[0] == '*' or pattern[0] == 'Tags') + res.push([['Tags'], + {exist: timestamp, object: timestamp, writable: timestamp}, + {exist: 1, object: 1, writable: 0}]) + + if depth & 2 and (pattern.length < 1 or pattern[0] == '*' or pattern[0] == 'Tags') + res.push([['Tags', '*'], {exist: timestamp}]) + projection['_tags'] = 1 + + if pattern[0] not in ['Tags', 'Events', 'DeviceID'] + i = 0 + while (1 << i) <= depth + if pattern.length <= i or pattern[i] == '*' + p = pattern.slice(0, i) + loaded.push([p, ((1 << p.length) - 1) ^ ((1 << MAX_DEPTH) - 1)]) + s = p.join('.') + projection[s] = 1 + projection[pattern.slice(0, i - 1).concat('_timestamp').join('.')] = 1 + break + + if depth & (1 << i) + s = pattern.slice(0, i + 1).join('.') + projection["#{s}._value"] = 1 + projection["#{s}._timestamp"] = 1 + projection["#{s}._orig"] = 1 + projection["#{s}._type"] = 1 + projection["#{s}._writable"] = 1 + projection["#{s}._object"] = 1 + + # Timestamp from parent is needed for writable timestamp + if i <= 1 + projection['_timestamp'] = 1 + else + projection["#{pattern.slice(0, i).join('.')}._timestamp"] = 1 + + ++ i if projection[''] projection = {} @@ -253,8 +257,8 @@ fetchDevice = (id, timestamp, patterns, callback) -> t.exist = Math.max(t.exist || 0, t.object) v.exist = 1 - if not obj['_object'] - loaded.push([path, 99]) + if not descendantsFetched and v.object == 0 + loaded.push([path, ((1 << path.length) - 1) ^ ((1 << MAX_DEPTH) - 1)]) res.push([path, t, v]) diff --git a/lib/device.js b/lib/device.js index b5c6adb..94aaab1 100644 --- a/lib/device.js +++ b/lib/device.js @@ -226,26 +226,6 @@ function unpack(deviceData, path, revision) { } -function invalidateSuperset(deviceData, path, attributes) { - let depth = 0; - if (attributes.exist != null) - depth = 99; - - for (let sup of deviceData.paths.superset(path, 99)) { - if (sup.wildcard == path.wildcard && sup.length == path.length) - continue; - - for (let attrName in deviceData.timestamps) { - let t = deviceData.timestamps[attrName].get(sup); - if (t <= attributes[attrName] || t <= attributes.exist) { - deviceData.timestamps[attrName].delete(sup); - deviceData.values[attrName].delete(sup); - } - } - } -} - - function normalizeAttributes(timestamps, values) { if ('value' in values && timestamps.value >= (timestamps.object || 0)) { values.object = 0; @@ -274,97 +254,132 @@ function normalizeAttributes(timestamps, values) { } -function set(deviceData, path, timestamps, values) { +function clear(deviceData, path, timestamps, toClear) { + if (!toClear) + toClear = new Map(); + + // Clear redundant subsets and deleted params + for (let sub of deviceData.paths.subset(path)) { + if (path.wildcard == sub.wildcard) + continue; + + if (deviceData.values.exist.has(sub)) { + let existTimestamp = deviceData.timestamps.exist.get(sub); + for (let attrName in timestamps) { + if (timestamps[attrName] > existTimestamp) { + let inv = toClear.get(sub); + if (inv == null) + toClear.set(sub, inv = {}); + for (let attrName in deviceData.timestamps) { + inv[attrName] = timestamps[attrName]; + deviceData.timestamps[attrName].delete(sub); + deviceData.values[attrName].delete(sub); + } + break; + } + + if (timestamps[attrName] > deviceData.timestamps[attrName].get(sub)) { + if (deviceData.values[attrName].has(sub)) { + let inv = toClear.get(sub); + if (inv == null) + toClear.set(sub, inv = {}); + inv[attrName] = timestamps[attrName]; + deviceData.values[attrName].delete(sub); + } + deviceData.timestamps[attrName].set(sub, timestamps[attrName]); + } + } + } + else { + for (let attrName in timestamps) { + if (attrName == 'exist') { + for (let an in deviceData.timestamps) + if (timestamps[attrName] > deviceData.timestamps[an].get(sub)) + deviceData.timestamps[an].delete(sub); + continue; + } + + if (timestamps[attrName] > deviceData.timestamps[attrName].get(sub)) + deviceData.timestamps[attrName].delete(sub); + } + } + } + return toClear; +} + + +function clearSupersets(deviceData, path, timestamps) { + let depth = 0; + if (timestamps.exist != null) + depth = 99; + + for (let sup of deviceData.paths.superset(path, 99)) { + if (sup.wildcard == path.wildcard && sup.length == path.length) + continue; + + for (let attrName in deviceData.timestamps) { + let t = deviceData.timestamps[attrName].get(sup); + if (t <= timestamps[attrName] || t <= timestamps.exist) { + deviceData.timestamps[attrName].delete(sup); + deviceData.values[attrName].delete(sup); + } + } + } +} + + +function set(deviceData, path, timestamps, values, toClear) { path = deviceData.paths.add(path); if (path.wildcard || values == null) values = {}; - let toInvalidate = new Map(); - if (values.exist) { normalizeAttributes(timestamps, values); - if (timestamps.exist && path.length > 1) { - set(deviceData, path.slice(0, path.length - 1), + if (timestamps.exist && path.length > 1) + toClear = set(deviceData, path.slice(0, path.length - 1), {exist: timestamps.exist, object: timestamps.exist}, - {exist: 1, object: 1}); - } + {exist: 1, object: 1}, + toClear); + else if (!toClear) + toClear = new Map(); + // Clear attribute patterns for (let attrName in timestamps) { if (deviceData.values[attrName].has(path) != (attrName in values)) { - let inv = toInvalidate.get(path); + let inv = toClear.get(path); if (inv == null) - toInvalidate.set(path, inv = {}); + toClear.set(path, inv = {}); inv[attrName] = timestamps[attrName]; if (!(attrName in values)) deviceData.values[attrName].delete(path); } } - } - else { - // Clear redundant timestamps - let current = inferTimestamps(deviceData, path); - for (let c in timestamps) - if (timestamps[c] <= current[c]) - delete timestamps[c]; - else if (c != 'exist' && - (timestamps[c] <= timestamps.exist || timestamps[c] <= current.exist)) - delete timetamps[c]; - // Clear redundant subsets and deleted params - for (let sub of deviceData.paths.subset(path)) { - if (path.wildcard == sub.wildcard) - continue; - - if (deviceData.values.exist.has(sub)) { - let existTimestamp = deviceData.timestamps.exist.get(sub); - for (let attrName in timestamps) { - if (timestamps[attrName] > existTimestamp) { - let inv = toInvalidate.get(sub); - if (inv == null) - toInvalidate.set(sub, inv = {}); - for (let attrName in deviceData.timestamps) { - inv[attrName] = timestamps[attrName]; - deviceData.timestamps[attrName].delete(sub); - deviceData.values[attrName].delete(sub); - } - break; - } - - if (timestamps[attrName] > deviceData.timestamps[attrName].get(sub)) - if (deviceData.values[attrName].has(sub)) { - let inv = toInvalidate.get(sub); - if (inv == null) - toInvalidate.set(sub, inv = {}); - inv[attrName] = timestamps[attrName]; - deviceData.values[attrName].delete(sub); - } - } - } - else { - for (let attrName in timestamps) { - if (attrName == 'exist') { - for (let an in deviceData.timestamps) - if (timestamps[attrName] > deviceData.timestamps[an].get(sub)) - deviceData.timestamps[an].delete(sub); - continue; - } - - if (timestamps[attrName] > deviceData.timestamps[attrName].get(sub)) - deviceData.timestamps[attrName].delete(sub); - } - } + for (let attrName in timestamps) { + deviceData.timestamps[attrName].set(path, timestamps[attrName]); + if (attrName in values) + deviceData.values[attrName].set(path, values[attrName]); } + + return toClear; } - toInvalidate.forEach(function(a, p) { - invalidateSuperset(deviceData, p, a); - }); + // Clear redundant timestamps + let current = inferTimestamps(deviceData, path); + for (let c in timestamps) + if (timestamps[c] <= current[c]) + delete timestamps[c]; + else if (c != 'exist' && + (timestamps[c] <= timestamps.exist || timestamps[c] <= current.exist)) + delete timetamps[c]; for (let attrName in timestamps) { deviceData.timestamps[attrName].set(path, timestamps[attrName]); if (attrName in values) deviceData.values[attrName].set(path, values[attrName]); } + + return clear(deviceData, path, timestamps, toClear); } @@ -373,3 +388,5 @@ exports.getAliasDeclarations = getAliasDeclarations; exports.inferTimestamps = inferTimestamps; exports.unpack = unpack; exports.set = set; +exports.clear = clear; +exports.clearSupersets = clearSupersets diff --git a/lib/session.coffee b/lib/session.coffee index 3eb0d8c..4b15d2d 100644 --- a/lib/session.coffee +++ b/lib/session.coffee @@ -77,7 +77,7 @@ loadParameters = (sessionData, callback) -> if not parameters? # Device not available in database, mark as new sessionData.new = true - loaded = [[[], 99]] + loaded = [[[], (1 << config.get('MAX_DEPTH', sessionData.deviceId)) - 1]] parameters = [ [['DeviceID', '*'], {exist: sessionData.timestamp}], [['Events', '*'], {exist: sessionData.timestamp}] @@ -89,19 +89,17 @@ loadParameters = (sessionData, callback) -> path = sessionData.deviceData.paths.add(p[0]) if p[1] - l = sessionData.deviceData.loaded.get(path) || 0 - sessionData.deviceData.loaded.set(path, Math.max(l, p[1])) + l = sessionData.deviceData.loaded.get(path) | 0 + sessionData.deviceData.loaded.set(path, l | p[1]) - if path.length <= 2 and path.length + p[1] > 2 - if path.length < 1 or path[0] == '*' or path[0] == 'VirtualParameters' - if path.length < 2 or path[1] == '*' - vparams['*'] = 1 - else - vparams[path[1]] = 1 + if p[1] & 2 and (path.length < 1 or path[0] == '*' or path[0] == 'VirtualParameters') + if path.length < 2 or path[1] == '*' + vparams['*'] = 1 + else + vparams[path[1]] = 1 - if path.length <= 1 and path.length + p[1] > 1 - if path.length < 1 or path[0] == '*' or path[0] == 'VirtualParameters' - vparams[''] = 1 + if p[1] & 1 and (path.length < 1 or path[0] == '*' or path[0] == 'VirtualParameters') + vparams[''] = 1 for p in parameters path = sessionData.deviceData.paths.add(p[0]) @@ -112,32 +110,37 @@ loadParameters = (sessionData, callback) -> for k, v of p[2] sessionData.deviceData.values[k].set(path, v, 0) + toClear = null # Virtual parameters if vparams[''] - device.set(sessionData.deviceData, ['VirtualParameters'], + toClear = device.set(sessionData.deviceData, ['VirtualParameters'], {exist: sessionData.timestamp, object: sessionData.timestamp, writable: sessionData.timestamp}, - {exist: 1, object: 1, writable: 0}) + {exist: 1, object: 1, writable: 0}, + toClear) delete vparams[''] if vparams['*'] for k, v of sessionData.cache.virtualParameters - device.set(sessionData.deviceData, ['VirtualParameters', k], + toClear = device.set(sessionData.deviceData, ['VirtualParameters', k], {exist: sessionData.timestamp, object: sessionData.timestamp}, - {exist: 1, object: 0}) + {exist: 1, object: 0}, + toClear) - device.set(sessionData.deviceData, ['VirtualParameters', '*'], - {exist: sessionData.timestamp}) + toClear = device.set(sessionData.deviceData, ['VirtualParameters', '*'], + {exist: sessionData.timestamp}, toClear) else for k of vparams if k of sessionData.cache.virtualParameters - device.set(sessionData.deviceData, ['VirtualParameters', k] + toClear = device.set(sessionData.deviceData, ['VirtualParameters', k] {exist: sessionData.timestamp, object: sessionData.timestamp}, - {exist: 1, object: 0}) + {exist: 1, object: 0}, + toClear) else - device.set(sessionData.deviceData, ['VirtualParameters', k], {exist: sessionData.timestamp}) + toClear = device.set(sessionData.deviceData, ['VirtualParameters', k], {exist: sessionData.timestamp}, toClear) delete sessionData.toLoad - return callback() + + return clear(sessionData, toClear, callback) ) @@ -192,10 +195,13 @@ inform = (sessionData, rpcReq, callback) -> v.revision = 1 for k, v of sessionData.deviceData.timestamps v.revision = 1 for k, v of sessionData.deviceData.values + toClear = null for p in params - device.set(sessionData.deviceData, p[0], p[1], p[2]) + toClear = device.set(sessionData.deviceData, p[0], p[1], p[2], toClear) - return callback(null, {type : 'InformResponse'}) + clear(sessionData, toClear, (err) -> + return callback(err, {type : 'InformResponse'}) + ) ) @@ -323,11 +329,14 @@ runVirtualParameters = (sessionData, provisions, startRevision, endRevision, cal return runVirtualParameters(sessionData, provisions, startRevision, endRevision, callback) ) + toClear = null if virtualParameterUpdates.length == provisions.length for vpu, i in virtualParameterUpdates - commitVirtualParameter(sessionData, vpu[0], provisions[i][1], vpu[1]) + toClear = commitVirtualParameter(sessionData, vpu[0], provisions[i][1], vpu[1], toClear) - return callback(null, done, decs) + clear(sessionData, toClear, (err) -> + return callback(err, done, decs) + ) runDeclarations = (sessionData, declarations, callback) -> @@ -865,29 +874,27 @@ processDeclaration = (sessionData, path, _timestamps, values) -> return ret -loadPath = (sessionData, path) -> +loadPath = (sessionData, path, depth) -> return true if sessionData.new - tl = [] - for i in [path.length...0] by -1 - p = path.slice(0, i) - loaded = 0 - iter = sessionData.deviceData.paths.superset(p) + depth ?= (1 << path.length) - 1 + + # Trim trailing wildcards + trimWildcard = path.length + -- trimWildcard while trimWildcard and path[trimWildcard - 1] != '*' + path = path.slice(0, trimWildcard) if trimWildcard < path.length + + for i in [0..path.length] by 1 + iter = sessionData.deviceData.paths.superset(path.slice(0, i)) while sup = iter.next().value - if (l = sessionData.deviceData.loaded.get(sup)) > loaded - loaded = l + v = sessionData.deviceData.loaded.get(sup) + depth &= depth ^ sessionData.deviceData.loaded.get(sup) - if loaded - tl = tl.slice(0, tl.length - (loaded - 1)) - break + return true if depth == 0 - tl.push(p) - - if not tl.length - return true sessionData.toLoad ?= [] - sessionData.toLoad = sessionData.toLoad.concat(tl) + sessionData.toLoad.push([path, depth]) return false @@ -991,7 +998,27 @@ applyDeclaration = (sessionData, path, timestamps, values) -> return true -commitVirtualParameter = (sessionData, name, declaration, update) -> +clear = (sessionData, toClear, callback) -> + return callback() if not toClear?.size + MAX_DEPTH = config.get('MAX_DEPTH', sessionData.deviceId) + + toClear.forEach((t, p) -> + if t.exist? + loadPath(sessionData, p, ((1 << p.length) - 1) ^ (1 << MAX_DEPTH) - 1) + else + loadPath(sessionData, p, 1 << (p.length - 1)) + ) + + loadParameters(sessionData, (err) -> + return callback(err) if err + + toClear.forEach((t, p) -> + device.clearSupersets(sessionData.deviceData, p, t) + ) + return callback() + ) + +commitVirtualParameter = (sessionData, name, declaration, update, toClear) -> t = {} v = {} if update.writable? @@ -1020,7 +1047,7 @@ commitVirtualParameter = (sessionData, name, declaration, update) -> else if declaration.timestamps.value? throw new Error('Virtual parameter must provide declared attributes') - device.set(sessionData.deviceData, ['VirtualParameters', name], t, v) + return device.set(sessionData.deviceData, ['VirtualParameters', name], t, v, toClear) runExtensions = (sessionData, revision, _extensions, callback) -> @@ -1093,14 +1120,17 @@ rpcResponse = (sessionData, id, rpcRes, callback) -> v.revision = revision for k, v of sessionData.deviceData.timestamps v.revision = revision for k, v of sessionData.deviceData.values + toClear = null + switch rpcRes.type when 'GetParameterValuesResponse' return callback(new Error('Response type does not match request type')) if rpcReq.type isnt 'GetParameterValues' for p in rpcRes.parameterList - device.set(sessionData.deviceData, common.parsePath(p[0]), + toClear = device.set(sessionData.deviceData, common.parsePath(p[0]), {exist: timestamp, object: timestamp, value: timestamp}, - {exist: 1, object: 0, value: p.slice(1)}) + {exist: 1, object: 0, value: p.slice(1)}, + toClear) when 'GetParameterNamesResponse' return callback(new Error('Response type does not match request type')) if rpcReq.type isnt 'GetParameterNames' @@ -1137,16 +1167,18 @@ rpcResponse = (sessionData, id, rpcRes, callback) -> return al - bl ) - for p in params - loadPath(sessionData, p[0]) + if rpcReq.nextLevel + loadPath(sessionData, root, (1 << (root.length + 1)) - 1) + else + loadPath(sessionData, root, (1 << config.get('MAX_DEPTH', sessionData.deviceId) - 1)) loadParameters(sessionData, (err) -> return callback(err) if err for p in params - device.set(sessionData.deviceData, p[0], p[1], p[2]) + toClear = device.set(sessionData.deviceData, p[0], p[1], p[2], toClear) - return callback() + clear(sessionData, toClear, callback) ) return @@ -1154,33 +1186,36 @@ rpcResponse = (sessionData, id, rpcRes, callback) -> return callback(new Error('Response type does not match request type')) if rpcReq.type isnt 'SetParameterValues' for p in rpcReq.parameterList - device.set(sessionData.deviceData, common.parsePath(p[0]), + toClear = device.set(sessionData.deviceData, common.parsePath(p[0]), {exist: timestamp, object: timestamp, writable: timestamp, value: timestamp}, - {exist: 1, object: 0, writable: 1, value: p.slice(1)}) + {exist: 1, object: 0, writable: 1, value: p.slice(1)}, toClear) when 'AddObjectResponse' - device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName + rpcRes.instanceNumber), + toClear = device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName + rpcRes.instanceNumber), {exist: timestamp, object: timestamp}, - {exist: 1, object: 1}) + {exist: 1, object: 1}, + toClear) when 'DeleteObjectResponse' - device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName.slice(0, -1)), - {exist: timestamp}) + toClear = device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName.slice(0, -1)), + {exist: timestamp}, toClear) when 'RebootResponse' - device.set(sessionData.deviceData, common.parsePath('Reboot'), + toClear = device.set(sessionData.deviceData, common.parsePath('Reboot'), {exist: sessionData.timestamp, value: sessionData.timestamp}, - {exist: 1, value: [sessionData.timestamp, 'xsd:unsignedInt']}) + {exist: 1, value: [sessionData.timestamp, 'xsd:unsignedInt']}, + toClear) when 'FactoryResetResponse' - device.set(sessionData.deviceData, common.parsePath('FactoryReset'), + toClear = device.set(sessionData.deviceData, common.parsePath('FactoryReset'), {exist: sessionData.timestamp, value: sessionData.timestamp}, - {exist: 1, value: [sessionData.timestamp, 'xsd:unsignedInt']}) + {exist: 1, value: [sessionData.timestamp, 'xsd:unsignedInt']}, + toClear) else return callback(new Error('Response type not recognized')) - return callback() + return clear(sessionData, toClear, callback) rpcFault = (sessionData, id, faultResponse, callback) ->