Better preset loop detection
The RPC and iteration counts are now reset for every cycle.
This commit is contained in:
1 parent
310114a500
commit
2e40ef8622
2 files changed
+65
-32
No files matched your search
+46
-30
@@ -51,6 +51,8 @@ device = require './device'
|
|||||||
cache = require './cache'
|
cache = require './cache'
|
||||||
db = require './db'
|
db = require './db'
|
||||||
|
|
||||||
|
MAX_CYCLES = 4
|
||||||
|
|
||||||
|
|
||||||
throwError = (err, httpResponse) ->
|
throwError = (err, httpResponse) ->
|
||||||
if httpResponse
|
if httpResponse
|
||||||
@@ -213,6 +215,8 @@ appendProvisions = (original, toAppend) ->
|
|||||||
|
|
||||||
|
|
||||||
applyPresets = (currentRequest) ->
|
applyPresets = (currentRequest) ->
|
||||||
|
sessionData = currentRequest.sessionData
|
||||||
|
|
||||||
cache.getPresets((err, presetsHash, presets) ->
|
cache.getPresets((err, presetsHash, presets) ->
|
||||||
return throwError(err, currentRequest.httpResponse) if err
|
return throwError(err, currentRequest.httpResponse) if err
|
||||||
|
|
||||||
@@ -221,28 +225,28 @@ applyPresets = (currentRequest) ->
|
|||||||
whiteList = null
|
whiteList = null
|
||||||
whiteListProvisions = null
|
whiteListProvisions = null
|
||||||
|
|
||||||
RETRY_DELAY = config.get('RETRY_DELAY', currentRequest.sessionData.deviceId)
|
RETRY_DELAY = config.get('RETRY_DELAY', sessionData.deviceId)
|
||||||
|
|
||||||
if currentRequest.sessionData.faults?
|
if sessionData.faults?
|
||||||
for channel, fault of currentRequest.sessionData.faults
|
for channel, fault of sessionData.faults
|
||||||
if fault.retryNow
|
if fault.retryNow
|
||||||
retryTimestamp = 0
|
retryTimestamp = 0
|
||||||
else
|
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
|
whiteList = channel
|
||||||
whiteListProvisions = fault.provisions
|
whiteListProvisions = fault.provisions
|
||||||
break
|
break
|
||||||
|
|
||||||
blackList[channel] = if fault.precondition then 1 else 2
|
blackList[channel] = if fault.precondition then 1 else 2
|
||||||
|
|
||||||
currentRequest.sessionData.presetsHash = presetsHash
|
sessionData.presetsHash = presetsHash
|
||||||
|
|
||||||
deviceEvents = {}
|
deviceEvents = {}
|
||||||
iter = currentRequest.sessionData.deviceData.paths.subset(['Events', '*'])
|
iter = sessionData.deviceData.paths.subset(['Events', '*'])
|
||||||
while (p = iter.next().value)
|
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
|
deviceEvents[p[1]] = true
|
||||||
|
|
||||||
parameters = {}
|
parameters = {}
|
||||||
@@ -251,7 +255,7 @@ applyPresets = (currentRequest) ->
|
|||||||
for preset in presets
|
for preset in presets
|
||||||
if whiteList?
|
if whiteList?
|
||||||
continue if preset.channel != whiteList
|
continue if preset.channel != whiteList
|
||||||
else if blackList[channel] == 1
|
else if blackList[preset.channel] == 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
eventsMatch = true
|
eventsMatch = true
|
||||||
@@ -261,11 +265,11 @@ applyPresets = (currentRequest) ->
|
|||||||
break
|
break
|
||||||
|
|
||||||
continue if (not eventsMatch) or (preset.schedule and not
|
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)
|
filteredPresets.push(preset)
|
||||||
for k of preset.precondition
|
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]
|
p = k.split(/([^a-zA-Z0-9\-\_\.].*)/, 1)[0]
|
||||||
parameters[p] = common.parsePath(p)
|
parameters[p] = common.parsePath(p)
|
||||||
|
|
||||||
@@ -273,28 +277,28 @@ applyPresets = (currentRequest) ->
|
|||||||
for k, v of parameters
|
for k, v of parameters
|
||||||
declarations.push([v, 1, {value: 1}])
|
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
|
return throwError(err, currentRequest.httpResponse) if err
|
||||||
|
|
||||||
if fault
|
if fault
|
||||||
recordFault(currentRequest.sessionData, fault, currentRequest.sessionData.provisions, currentRequest.sessionData.channels)
|
recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
|
||||||
session.clearProvisions(currentRequest.sessionData)
|
session.clearProvisions(sessionData)
|
||||||
return applyPresets(currentRequest)
|
return applyPresets(currentRequest)
|
||||||
|
|
||||||
if rpcRequest?
|
if rpcRequest?
|
||||||
return sendRpcRequest(currentRequest, id, rpcRequest)
|
return sendRpcRequest(currentRequest, id, rpcRequest)
|
||||||
|
|
||||||
session.clearProvisions(currentRequest.sessionData)
|
session.clearProvisions(sessionData)
|
||||||
|
|
||||||
for k, v of parameters
|
for k, v of parameters
|
||||||
unpacked = device.unpack(currentRequest.sessionData.deviceData, v)
|
unpacked = device.unpack(sessionData.deviceData, v)
|
||||||
if unpacked[0] and (vv = currentRequest.sessionData.deviceData.attributes.get(unpacked[0]).value?[1])?
|
if unpacked[0] and (vv = sessionData.deviceData.attributes.get(unpacked[0]).value?[1])?
|
||||||
parameters[k] = vv[0]
|
parameters[k] = vv[0]
|
||||||
else
|
else
|
||||||
delete parameters[k]
|
delete parameters[k]
|
||||||
|
|
||||||
if whiteList?
|
if whiteList?
|
||||||
session.addProvisions(currentRequest.sessionData, whiteList, whiteListProvisions)
|
session.addProvisions(sessionData, whiteList, whiteListProvisions)
|
||||||
|
|
||||||
appendProvisionsToFaults = {}
|
appendProvisionsToFaults = {}
|
||||||
for p in filteredPresets
|
for p in filteredPresets
|
||||||
@@ -303,27 +307,39 @@ applyPresets = (currentRequest) ->
|
|||||||
appendProvisionsToFaults[p.channel] =
|
appendProvisionsToFaults[p.channel] =
|
||||||
(appendProvisionsToFaults[p.channel] or []).concat(p.provisions)
|
(appendProvisionsToFaults[p.channel] or []).concat(p.provisions)
|
||||||
else
|
else
|
||||||
session.addProvisions(currentRequest.sessionData, p.channel, p.provisions)
|
session.addProvisions(sessionData, p.channel, p.provisions)
|
||||||
|
|
||||||
for channel, provisions of appendProvisionsToFaults
|
for channel, provisions of appendProvisionsToFaults
|
||||||
if appendProvisions(currentRequest.sessionData.faults[channel].provisions, provisions)
|
if appendProvisions(sessionData.faults[channel].provisions, provisions)
|
||||||
currentRequest.sessionData.faultsTouched ?= {}
|
sessionData.faultsTouched ?= {}
|
||||||
currentRequest.sessionData.faultsTouched[channel] = true
|
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
|
return throwError(err, currentRequest.httpResponse) if err
|
||||||
|
|
||||||
if fault
|
if fault
|
||||||
recordFault(currentRequest.sessionData, fault, currentRequest.sessionData.provisions, currentRequest.sessionData.channels)
|
recordFault(sessionData, fault, sessionData.provisions, sessionData.channels)
|
||||||
session.clearProvisions(currentRequest.sessionData)
|
session.clearProvisions(sessionData)
|
||||||
return applyPresets(currentRequest)
|
return applyPresets(currentRequest)
|
||||||
|
|
||||||
if not rpcRequest?
|
if not rpcRequest?
|
||||||
for channel, flags of currentRequest.sessionData.channels
|
for channel, flags of sessionData.channels
|
||||||
if channel of currentRequest.sessionData.faults
|
if channel of sessionData.faults
|
||||||
delete currentRequest.sessionData.faults[channel]
|
delete sessionData.faults[channel]
|
||||||
currentRequest.sessionData.faultsTouched ?= {}
|
sessionData.faultsTouched ?= {}
|
||||||
currentRequest.sessionData.faultsTouched[channel] = true
|
sessionData.faultsTouched[channel] = true
|
||||||
|
|
||||||
if whiteList?
|
if whiteList?
|
||||||
return applyPresets(currentRequest)
|
return applyPresets(currentRequest)
|
||||||
|
|||||||
+19
-2
@@ -26,6 +26,8 @@ VersionedMap = require './versioned-map'
|
|||||||
InstanceSet = require './instance-set'
|
InstanceSet = require './instance-set'
|
||||||
defaultProvisions = require './default-provisions'
|
defaultProvisions = require './default-provisions'
|
||||||
|
|
||||||
|
MAX_ITERATIONS = 64
|
||||||
|
|
||||||
|
|
||||||
initDeviceData = () ->
|
initDeviceData = () ->
|
||||||
return {
|
return {
|
||||||
@@ -53,6 +55,7 @@ init = (deviceId, cwmpVersion, timeout, callback) ->
|
|||||||
revisions: [0]
|
revisions: [0]
|
||||||
rpcCount: 0
|
rpcCount: 0
|
||||||
iteration: 0
|
iteration: 0
|
||||||
|
cycle: 0
|
||||||
extensionsCache: {}
|
extensionsCache: {}
|
||||||
declarations: []
|
declarations: []
|
||||||
}
|
}
|
||||||
@@ -105,7 +108,9 @@ loadParameters = (sessionData, callback) ->
|
|||||||
|
|
||||||
|
|
||||||
generateRpcId = (sessionData) ->
|
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) ->
|
inform = (sessionData, rpcReq, callback) ->
|
||||||
@@ -282,6 +287,11 @@ addProvisions = (sessionData, channel, provisions) ->
|
|||||||
sessionData.revisions = [0]
|
sessionData.revisions = [0]
|
||||||
sessionData.extensionsCache = {}
|
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
|
sessionData.channels[channel] |= 0
|
||||||
|
|
||||||
for provision, i in provisions
|
for provision, i in provisions
|
||||||
@@ -307,9 +317,15 @@ clearProvisions = (sessionData) ->
|
|||||||
sessionData.deviceData.timestamps.collapse(1)
|
sessionData.deviceData.timestamps.collapse(1)
|
||||||
sessionData.deviceData.attributes.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.syncState
|
||||||
delete sessionData.rpcRequest
|
delete sessionData.rpcRequest
|
||||||
sessionData.provisions = []
|
sessionData.provisions = []
|
||||||
|
sessionData.virtualParameters = []
|
||||||
sessionData.channels = {}
|
sessionData.channels = {}
|
||||||
sessionData.declarations = []
|
sessionData.declarations = []
|
||||||
sessionData.doneProvisions = 0
|
sessionData.doneProvisions = 0
|
||||||
@@ -588,7 +604,8 @@ rpcRequest = (sessionData, _declarations, callback) ->
|
|||||||
|
|
||||||
if sessionData.rpcCount >= 255 or
|
if sessionData.rpcCount >= 255 or
|
||||||
sessionData.revisions.length >= 8 or
|
sessionData.revisions.length >= 8 or
|
||||||
sessionData.iteration >= 128
|
sessionData.cycle >= 16 or
|
||||||
|
sessionData.iteration >= MAX_ITERATIONS * (sessionData.cycle + 1)
|
||||||
|
|
||||||
fault = {
|
fault = {
|
||||||
code: 'endless_cycle'
|
code: 'endless_cycle'
|
||||||
|
|||||||
Reference in new issue
Block a user