Better preset loop detection

The RPC and iteration counts are now reset for every cycle.
This commit is contained in:
Zaid Abdulla committed 2017-01-12 23:30:27 -08:00
1 parent 310114a500
commit 2e40ef8622
2 files changed
+65 -32

No files matched your search

+46 -30
View File
@@ -51,6 +51,8 @@ device = require './device'
cache = require './cache'
db = require './db'
MAX_CYCLES = 4
throwError = (err, httpResponse) ->
if httpResponse
@@ -213,6 +215,8 @@ appendProvisions = (original, toAppend) ->
applyPresets = (currentRequest) ->
sessionData = currentRequest.sessionData
cache.getPresets((err, presetsHash, presets) ->
return throwError(err, currentRequest.httpResponse) if err
@@ -221,28 +225,28 @@ applyPresets = (currentRequest) ->
whiteList = null
whiteListProvisions = null
RETRY_DELAY = config.get('RETRY_DELAY', currentRequest.sessionData.deviceId)
RETRY_DELAY = config.get('RETRY_DELAY', sessionData.deviceId)
if currentRequest.sessionData.faults?
for channel, fault of currentRequest.sessionData.faults
if sessionData.faults?
for channel, fault of sessionData.faults
if fault.retryNow
retryTimestamp = 0
else
retryTimestamp = fault.timestamp + (RETRY_DELAY * Math.pow(2, currentRequest.sessionData.retries[channel])) * 1000
retryTimestamp = fault.timestamp + (RETRY_DELAY * Math.pow(2, sessionData.retries[channel])) * 1000
if retryTimestamp <= currentRequest.sessionData.timestamp
if retryTimestamp <= sessionData.timestamp
whiteList = channel
whiteListProvisions = fault.provisions
break
blackList[channel] = if fault.precondition then 1 else 2
currentRequest.sessionData.presetsHash = presetsHash
sessionData.presetsHash = presetsHash
deviceEvents = {}
iter = currentRequest.sessionData.deviceData.paths.subset(['Events', '*'])
iter = sessionData.deviceData.paths.subset(['Events', '*'])
while (p = iter.next().value)
if currentRequest.sessionData.timestamp <= currentRequest.sessionData.deviceData.attributes.get(p)?.value?[1][0]
if sessionData.timestamp <= sessionData.deviceData.attributes.get(p)?.value?[1][0]
deviceEvents[p[1]] = true
parameters = {}
@@ -251,7 +255,7 @@ applyPresets = (currentRequest) ->
for preset in presets
if whiteList?
continue if preset.channel != whiteList
else if blackList[channel] == 1
else if blackList[preset.channel] == 1
continue
eventsMatch = true
@@ -261,11 +265,11 @@ applyPresets = (currentRequest) ->
break
continue if (not eventsMatch) or (preset.schedule and not
(preset.schedule.schedule and testSchedule(preset.schedule, currentRequest.sessionData.timestamp)[0]))
(preset.schedule.schedule and testSchedule(preset.schedule, sessionData.timestamp)[0]))
filteredPresets.push(preset)
for k of preset.precondition
currentRequest.sessionData.channels[preset.channel] = 0
sessionData.channels[preset.channel] = 0
p = k.split(/([^a-zA-Z0-9\-\_\.].*)/, 1)[0]
parameters[p] = common.parsePath(p)
@@ -273,28 +277,28 @@ applyPresets = (currentRequest) ->
for k, v of parameters
declarations.push([v, 1, {value: 1}])
session.rpcRequest(currentRequest.sessionData, declarations, (err, fault, id, rpcRequest) ->
session.rpcRequest(sessionData, declarations, (err, fault, id, rpcRequest) ->
return throwError(err, currentRequest.httpResponse) if err
if fault
recordFault(currentRequest.sessionData, fault, currentRequest.sessionData.provisions, currentRequest.sessionData.channels)
session.clearProvisions(currentRequest.sessionData)
recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
session.clearProvisions(sessionData)
return applyPresets(currentRequest)
if rpcRequest?
return sendRpcRequest(currentRequest, id, rpcRequest)
session.clearProvisions(currentRequest.sessionData)
session.clearProvisions(sessionData)
for k, v of parameters
unpacked = device.unpack(currentRequest.sessionData.deviceData, v)
if unpacked[0] and (vv = currentRequest.sessionData.deviceData.attributes.get(unpacked[0]).value?[1])?
unpacked = device.unpack(sessionData.deviceData, v)
if unpacked[0] and (vv = sessionData.deviceData.attributes.get(unpacked[0]).value?[1])?
parameters[k] = vv[0]
else
delete parameters[k]
if whiteList?
session.addProvisions(currentRequest.sessionData, whiteList, whiteListProvisions)
session.addProvisions(sessionData, whiteList, whiteListProvisions)
appendProvisionsToFaults = {}
for p in filteredPresets
@@ -303,27 +307,39 @@ applyPresets = (currentRequest) ->
appendProvisionsToFaults[p.channel] =
(appendProvisionsToFaults[p.channel] or []).concat(p.provisions)
else
session.addProvisions(currentRequest.sessionData, p.channel, p.provisions)
session.addProvisions(sessionData, p.channel, p.provisions)
for channel, provisions of appendProvisionsToFaults
if appendProvisions(currentRequest.sessionData.faults[channel].provisions, provisions)
currentRequest.sessionData.faultsTouched ?= {}
currentRequest.sessionData.faultsTouched[channel] = true
if appendProvisions(sessionData.faults[channel].provisions, provisions)
sessionData.faultsTouched ?= {}
sessionData.faultsTouched[channel] = true
session.rpcRequest(currentRequest.sessionData, null, (err, fault, id, rpcRequest) ->
sessionData.presetCycles = (sessionData.presetCycles or 0) + 1
if sessionData.presetCycles > MAX_CYCLES
fault = {
code: 'endless_cycle'
message: 'The provision seems to be repeating indefinitely'
timestamp: sessionData.timestamp
}
recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
session.clearProvisions(sessionData)
return sendRpcRequest(currentRequest)
session.rpcRequest(sessionData, null, (err, fault, id, rpcRequest) ->
return throwError(err, currentRequest.httpResponse) if err
if fault
recordFault(currentRequest.sessionData, fault, currentRequest.sessionData.provisions, currentRequest.sessionData.channels)
session.clearProvisions(currentRequest.sessionData)
recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
session.clearProvisions(sessionData)
return applyPresets(currentRequest)
if not rpcRequest?
for channel, flags of currentRequest.sessionData.channels
if channel of currentRequest.sessionData.faults
delete currentRequest.sessionData.faults[channel]
currentRequest.sessionData.faultsTouched ?= {}
currentRequest.sessionData.faultsTouched[channel] = true
for channel, flags of sessionData.channels
if channel of sessionData.faults
delete sessionData.faults[channel]
sessionData.faultsTouched ?= {}
sessionData.faultsTouched[channel] = true
if whiteList?
return applyPresets(currentRequest)
+19 -2
View File
@@ -26,6 +26,8 @@ VersionedMap = require './versioned-map'
InstanceSet = require './instance-set'
defaultProvisions = require './default-provisions'
MAX_ITERATIONS = 64
initDeviceData = () ->
return {
@@ -53,6 +55,7 @@ init = (deviceId, cwmpVersion, timeout, callback) ->
revisions: [0]
rpcCount: 0
iteration: 0
cycle: 0
extensionsCache: {}
declarations: []
}
@@ -105,7 +108,9 @@ loadParameters = (sessionData, callback) ->
generateRpcId = (sessionData) ->
return sessionData.timestamp.toString(16) + "0#{sessionData.rpcCount.toString(16)}".slice(-2)
return sessionData.timestamp.toString(16) +
sessionData.cycle.toString(16).slice(-1) +
"0#{sessionData.rpcCount.toString(16)}".slice(-2)
inform = (sessionData, rpcReq, callback) ->
@@ -282,6 +287,11 @@ addProvisions = (sessionData, channel, provisions) ->
sessionData.revisions = [0]
sessionData.extensionsCache = {}
if sessionData.iteration != sessionData.cycle * MAX_ITERATIONS
sessionData.cycle += 1
sessionData.rpcCount = 0
sessionData.iteration = sessionData.cycle * MAX_ITERATIONS
sessionData.channels[channel] |= 0
for provision, i in provisions
@@ -307,9 +317,15 @@ clearProvisions = (sessionData) ->
sessionData.deviceData.timestamps.collapse(1)
sessionData.deviceData.attributes.collapse(1)
if sessionData.iteration != sessionData.cycle * MAX_ITERATIONS
sessionData.cycle += 1
sessionData.rpcCount = 0
sessionData.iteration = sessionData.cycle * MAX_ITERATIONS
delete sessionData.syncState
delete sessionData.rpcRequest
sessionData.provisions = []
sessionData.virtualParameters = []
sessionData.channels = {}
sessionData.declarations = []
sessionData.doneProvisions = 0
@@ -588,7 +604,8 @@ rpcRequest = (sessionData, _declarations, callback) ->
if sessionData.rpcCount >= 255 or
sessionData.revisions.length >= 8 or
sessionData.iteration >= 128
sessionData.cycle >= 16 or
sessionData.iteration >= MAX_ITERATIONS * (sessionData.cycle + 1)
fault = {
code: 'endless_cycle'