Implement creating and deleting instances
This commit is contained in:
1 parent
ac759e1d04
commit
0a5697f06a
2 files changed
+276
-13
No files matched your search
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright 2013-2016 Zaid Abdulla
|
||||
*
|
||||
* GenieACS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* GenieACS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
|
||||
class InstanceSet {
|
||||
|
||||
constructor() {
|
||||
this.set = new Set();
|
||||
}
|
||||
|
||||
add(instance) {
|
||||
this.set.add(instance);
|
||||
}
|
||||
|
||||
delete(instance) {
|
||||
this.set.delete(instance);
|
||||
}
|
||||
|
||||
superset(instance) {
|
||||
let res = [];
|
||||
for (let inst of this.set) {
|
||||
let match = true;
|
||||
for (let k in instance) {
|
||||
if (inst[k] !== instance[k]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
res.push(inst);
|
||||
}
|
||||
|
||||
res.sort(function(a, b) {
|
||||
let keysA = Object.keys(a);
|
||||
let keysB = Object.keys(b);
|
||||
|
||||
if (keysA.length != keysB.length)
|
||||
return keysB.length - keysA.length;
|
||||
|
||||
keysA.sort();
|
||||
keysB.sort();
|
||||
|
||||
for (let i = 0; i < keysA.length; ++ i) {
|
||||
if (keysA[i] > keysB[i])
|
||||
return 1;
|
||||
else if (keysA[i] < keysB[i])
|
||||
return -1;
|
||||
else if (a[keysA[i]] > b[keysB[i]])
|
||||
return 1;
|
||||
else if (a[keysA[i]] < b[keysB[i]])
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
subset(instance) {
|
||||
let res = [];
|
||||
|
||||
for (let inst of this.set) {
|
||||
let match = true;
|
||||
for (let k in inst) {
|
||||
if (inst[k] !== instance[k]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
res.push(inst);
|
||||
}
|
||||
|
||||
res.sort(function(a, b) {
|
||||
let keysA = Object.keys(a);
|
||||
let keysB = Object.keys(b);
|
||||
|
||||
if (keysA.length != keysB.length)
|
||||
return keysA.length - keysB.length;
|
||||
|
||||
keysA.sort();
|
||||
keysB.sort();
|
||||
|
||||
for (let i = 0; i < keysA.length; ++ i) {
|
||||
if (keysA[i] > keysB[i])
|
||||
return 1;
|
||||
else if (keysA[i] < keysB[i])
|
||||
return -1;
|
||||
else if (a[keysA[i]] > b[keysB[i]])
|
||||
return 1;
|
||||
else if (a[keysA[i]] < b[keysB[i]])
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
forEach(callback) {
|
||||
return this.set.forEach(callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InstanceSet;
|
||||
+152
-13
@@ -26,6 +26,7 @@ cache = require './cache'
|
||||
extensions = require './extensions'
|
||||
PathSet = require './path-set'
|
||||
VersionedMap = require './versioned-map.js'
|
||||
InstanceSet = require './instance-set'
|
||||
|
||||
|
||||
initDeviceData = () ->
|
||||
@@ -326,6 +327,9 @@ runVirtualParameters = (sessionData, inception, callback) ->
|
||||
|
||||
|
||||
rpcRequest = (sessionData, declarations, callback) ->
|
||||
if sessionData.rpcRequest?
|
||||
return callback(null, generateRpcId(sessionData), sessionData.rpcRequest)
|
||||
|
||||
revision = sessionData.revisions.reduce(((a, b) -> a + (b >> 1)), 0) + 1
|
||||
|
||||
v.revision = revision for k, v of sessionData.deviceData.timestamps
|
||||
@@ -448,7 +452,38 @@ generateRpcRequest = (sessionData) ->
|
||||
rpcReq = null
|
||||
completed = true
|
||||
|
||||
# Delete instance
|
||||
syncState.instancesToDelete.forEach((instances, parent) ->
|
||||
instances.forEach((instance) ->
|
||||
if rpcReq?
|
||||
completed = false
|
||||
else
|
||||
rpcReq = {
|
||||
type: 'DeleteObject'
|
||||
objectName: instance.join('.') + '.'
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# Create instance
|
||||
syncState.instancesToCreate.forEach((instances, parent) ->
|
||||
instances.forEach((instance) ->
|
||||
if rpcReq?
|
||||
completed = false
|
||||
else
|
||||
rpcReq = {
|
||||
type: 'AddObject'
|
||||
objectName: parent.join('.') + '.'
|
||||
next: 'getInstanceKeys'
|
||||
instanceValues: instance
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if syncState.refreshGpn.size
|
||||
if rpcReq?
|
||||
completed = false
|
||||
else
|
||||
GET_PARAMETER_NAMES_DEPTH_THRESHOLD =
|
||||
config.get('GET_PARAMETER_NAMES_DEPTH_THRESHOLD', sessionData.deviceId)
|
||||
|
||||
@@ -560,19 +595,6 @@ processVirtualparameterDeclaration = (sessionData, path, timestamps, values) ->
|
||||
|
||||
|
||||
processDeclaration = (sessionData, path, _timestamps, values) ->
|
||||
sessionData.syncState ?= {
|
||||
paths: new PathSet()
|
||||
refreshAttributes: {
|
||||
exist: new Set()
|
||||
object: new Set()
|
||||
writable: new Set()
|
||||
value: new Set()
|
||||
}
|
||||
setValues: new Map()
|
||||
refreshGpn: new Map()
|
||||
virtualParameters: {}
|
||||
}
|
||||
|
||||
syncState = sessionData.syncState
|
||||
deviceData = sessionData.deviceData
|
||||
|
||||
@@ -747,12 +769,66 @@ loadPath = (sessionData, path) ->
|
||||
return false
|
||||
|
||||
|
||||
processInstances = (sessionData, parent, parameters, keys, minInstances, maxInstances) ->
|
||||
instancesToDelete = sessionData.syncState.instancesToDelete.get(parent)
|
||||
if not instancesToDelete?
|
||||
instancesToDelete = new Set()
|
||||
sessionData.syncState.instancesToDelete.set(parent, instancesToDelete)
|
||||
|
||||
instancesToCreate = sessionData.syncState.instancesToCreate.get(parent)
|
||||
if not instancesToCreate?
|
||||
instancesToCreate = new InstanceSet()
|
||||
sessionData.syncState.instancesToCreate.set(parent, instancesToCreate)
|
||||
|
||||
counter = 0
|
||||
for p in parameters
|
||||
++ counter
|
||||
if counter > maxInstances
|
||||
instancesToDelete.add(p)
|
||||
else if counter <= minInstances
|
||||
instancesToDelete.delete(p)
|
||||
|
||||
superset = instancesToCreate.superset(keys)
|
||||
for inst in superset
|
||||
++ counter
|
||||
if counter > maxInstances
|
||||
instancesToCreate.delete(inst)
|
||||
|
||||
subset = instancesToCreate.subset(keys)
|
||||
for inst in subset
|
||||
++ counter
|
||||
if counter <= minInstances
|
||||
instancesToCreate.delete(inst)
|
||||
instancesToCreate.add(JSON.parse(JSON.stringify(keys)))
|
||||
|
||||
while counter < minInstances
|
||||
++ counter
|
||||
instancesToCreate.add(JSON.parse(JSON.stringify(keys)))
|
||||
|
||||
return
|
||||
|
||||
|
||||
applyDeclaration = (sessionData, path, state, timestamps, values) ->
|
||||
if state == 100
|
||||
if not values? or Object.keys(values).length == 0
|
||||
return state
|
||||
timestamps = {}
|
||||
|
||||
sessionData.syncState ?= {
|
||||
paths: new PathSet()
|
||||
refreshAttributes: {
|
||||
exist: new Set()
|
||||
object: new Set()
|
||||
writable: new Set()
|
||||
value: new Set()
|
||||
}
|
||||
setValues: new Map()
|
||||
refreshGpn: new Map()
|
||||
virtualParameters: {}
|
||||
instancesToDelete: new Map()
|
||||
instancesToCreate: new Map()
|
||||
}
|
||||
|
||||
decs = device.getAliasDeclarations(path, timestamps.exist ? 1)
|
||||
|
||||
if state <= 1
|
||||
@@ -777,6 +853,30 @@ applyDeclaration = (sessionData, path, state, timestamps, values) ->
|
||||
return 2 if not satisfied
|
||||
|
||||
unpacked = device.unpack(sessionData.deviceData, path)
|
||||
|
||||
if values.exist?
|
||||
if Array.isArray(values.exist)
|
||||
minInstances = values.exist[0]
|
||||
maxInstances = values.exist[1]
|
||||
else
|
||||
minInstances = maxInstances = values.exist
|
||||
|
||||
parent = path.slice(0, -1)
|
||||
|
||||
if Array.isArray(path[path.length - 1])
|
||||
keys = {}
|
||||
for p, i in path[path.length - 1] by 2
|
||||
keys[p] = path[path.length - 1][i + 1]
|
||||
else if path[path.length - 1] == '*'
|
||||
keys = {}
|
||||
|
||||
if (path.wildcard | path.alias) & ((1 << (path.length - 1)) - 1) == 0
|
||||
processInstances(sessionData, parent, unpacked, keys, minInstances, maxInstances)
|
||||
else
|
||||
parentsUnpacked = device.unpack(sessionData.deviceData, parent)
|
||||
for parent in parentsUnpacked
|
||||
processInstances(sessionData, parent, device.unpack(sessionData.deviceData, parent.concat([path[parent.length]])), keys, minInstances, maxInstances)
|
||||
|
||||
satisfied = true
|
||||
for up in unpacked
|
||||
satisfied &&= processDeclaration(sessionData, path, timestamps, values)
|
||||
@@ -848,7 +948,37 @@ rpcResponse = (sessionData, id, rpcRes, callback) ->
|
||||
++ sessionData.rpcCount
|
||||
|
||||
rpcReq = sessionData.rpcRequest
|
||||
if not rpcReq.next?
|
||||
sessionData.rpcRequest = null
|
||||
else if rpcReq.next == 'getInstanceKeys'
|
||||
instanceNumber = rpcRes.instanceNumber
|
||||
parameterNames = []
|
||||
instanceValues = {}
|
||||
for k, v of rpcReq.instanceValues
|
||||
n = "#{rpcReq.objectName}#{rpcRes.instanceNumber}.#{k}"
|
||||
parameterNames.push(n)
|
||||
instanceValues[n] = v
|
||||
|
||||
if parameterNames.length == 0
|
||||
sessionData.rpcRequest = null
|
||||
else
|
||||
sessionData.rpcRequest = {
|
||||
type: 'GetParameterValues'
|
||||
parameterNames: parameterNames
|
||||
next: 'setInstanceKeys'
|
||||
instanceValues: instanceValues
|
||||
}
|
||||
else if rpcReq.next == 'setInstanceKeys'
|
||||
parameterList = []
|
||||
for p in rpcRes.parameterList
|
||||
if p[1] != rpcReq.instanceValues[p[0]]
|
||||
parameterList.push(
|
||||
[p[0]].concat(device.sanitizeParameterValue([rpcReq.instanceValues[p[0]], p[2]])))
|
||||
|
||||
sessionData.rpcRequest = {
|
||||
type: 'SetParameterValues'
|
||||
parameterList: parameterList
|
||||
}
|
||||
|
||||
timestamp = sessionData.timestamp
|
||||
revision = sessionData.revisions.reduce(((a, b) -> a + (b >> 1)), 0) + 1
|
||||
@@ -918,6 +1048,15 @@ rpcResponse = (sessionData, id, rpcRes, callback) ->
|
||||
{exist: timestamp, object: timestamp, writable: timestamp, value: timestamp},
|
||||
{exist: 1, object: 0, writable: 1, value: p.slice(1)})
|
||||
|
||||
when 'AddObjectResponse'
|
||||
device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName + rpcRes.instanceNumber),
|
||||
{exist: timestamp, object: timestamp},
|
||||
{exist: 1, object: 1})
|
||||
|
||||
when 'DeleteObjectResponse'
|
||||
device.set(sessionData.deviceData, common.parsePath(rpcReq.objectName.slice(0, -1)),
|
||||
{exist: timestamp})
|
||||
|
||||
else
|
||||
return callback(new Error('Response type not recognized'))
|
||||
|
||||
|
||||
Reference in new issue
Block a user