460 lines
16 KiB
JavaScript
460 lines
16 KiB
JavaScript
// DJ_Dispatch_Zone_Server.js
|
|
//
|
|
// Created by Milad Nazeri on 2018-06-19
|
|
//
|
|
// Copyright 2018 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
// Dispatcher that manages Generators(things that create data), Sensors(things that take that data and normalize it),
|
|
// Endpoints(Where the disptach will send to Sensors input)
|
|
|
|
(function () {
|
|
// Polyfill
|
|
Script.require("https://hifi-content.s3.amazonaws.com/milad/ROLC/Organize/O_Projects/Hifi/Scripts/hifi-content/Utilities/Polyfills.js")();
|
|
// "../../../Utilities/Polyfills.js"
|
|
// Helper Functions
|
|
var Util = Script.require("https://hifi-content.s3.amazonaws.com/milad/ROLC/Organize/O_Projects/Hifi/Scripts/hifi-content/Utilities/Helper.js");
|
|
// "../../../Utilities/Helper.js"
|
|
var getProps = Util.Entity.getProps,
|
|
getUserData = Util.Entity.getUserData,
|
|
searchForChildren = Util.Entity.searchForChildren;
|
|
|
|
// Log Setup
|
|
var LOG_CONFIG = {},
|
|
LOG_ENTER = Util.Debug.LOG_ENTER,
|
|
LOG_UPDATE = Util.Debug.LOG_UPDATE,
|
|
LOG_ERROR = Util.Debug.LOG_ERROR,
|
|
LOG_VALUE = Util.Debug.LOG_VALUE,
|
|
LOG_ARCHIVE = Util.Debug.LOG_ARCHIVE;
|
|
|
|
LOG_CONFIG[LOG_ENTER] = false;
|
|
LOG_CONFIG[LOG_UPDATE] = false;
|
|
LOG_CONFIG[LOG_ERROR] = false;
|
|
LOG_CONFIG[LOG_VALUE] = false;
|
|
LOG_CONFIG[LOG_ARCHIVE] = false;
|
|
var log = Util.Debug.log(LOG_CONFIG);
|
|
|
|
// Init
|
|
var entityID,
|
|
debugCubeID = null,
|
|
isOn = false,
|
|
name = null,
|
|
lastHeartBeat = null,
|
|
loadedChildren = false,
|
|
HEARTBEAT_CHECK_INTERVAL = 1500,
|
|
HEARTBEAT_TIMEOUT = 2000,
|
|
heartbeatCheck = null,
|
|
lastEdit = 0,
|
|
LAST_EDIT_TIMEOUT = 2500,
|
|
SEARCH_FOR_CHILDREN_TIMEOUT = 5000,
|
|
SEARCH_FOR_CHILDNAME_TIMEOUT = 1000,
|
|
TURN_ON = "turnOn",
|
|
TURN_OFF = "turnOff",
|
|
EDIT = "edit",
|
|
IN_BOX = "inBox",
|
|
IN_MARGIN = "inMargin",
|
|
GENERATOR = "generator",
|
|
SENSOR = "sensor",
|
|
ENDPOINT = "endPoint",
|
|
DEBUG = false,
|
|
wantsCube = false,
|
|
self;
|
|
|
|
// Collections
|
|
var currentProperties = {},
|
|
userData = {},
|
|
userdataProperties = {},
|
|
childrenIDS = {},
|
|
avatarsInZone = {},
|
|
childNames = [],
|
|
sensors = [],
|
|
endPoints = [],
|
|
generators = [];
|
|
|
|
// Constructor Functions
|
|
function EndPoint(id, endPointGroupID) {
|
|
this.id = id;
|
|
this.endPointGroupID = endPointGroupID;
|
|
}
|
|
|
|
EndPoint.prototype = {
|
|
updateDebugCubeID: function (id) {
|
|
Entities.callEntityMethod(this.id, "updateDebugCubeID", [id]);
|
|
},
|
|
turnOn: function () {
|
|
Entities.callEntityMethod(this.id, "turnOn");
|
|
},
|
|
turnOff: function () {
|
|
Entities.callEntityMethod(this.id, "turnOff");
|
|
}
|
|
};
|
|
|
|
function Generator(id) {
|
|
this.id = id;
|
|
}
|
|
|
|
Generator.prototype = {
|
|
clearDebugEndpointInfo: function () {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "clearDebugEndpointInfo");
|
|
}
|
|
},
|
|
storeDebugSensorInfo: function (event) {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "storeDebugSensorInfo", [event]);
|
|
}
|
|
},
|
|
storeDebugEndpointInfo: function (event, name) {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "storeDebugEndpointInfo", [event, name]);
|
|
}
|
|
},
|
|
turnOn: function () {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "turnOn");
|
|
}
|
|
},
|
|
turnOff: function () {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "turnOff");
|
|
}
|
|
}
|
|
};
|
|
|
|
function Sensor(id, endPointGroups) {
|
|
this.id = id;
|
|
this.endPointGroups = endPointGroups;
|
|
this.endPoints = [];
|
|
this.currentGenerators = {};
|
|
this.canEdit = false;
|
|
this.activeGenerator = null;
|
|
this.activeUUID = null;
|
|
this.lastEdit = 0;
|
|
}
|
|
|
|
Sensor.prototype = {
|
|
grabEndPointIDsFromGroup: function (groupID) {
|
|
return endPoints.filter(function (endPoint) {
|
|
return endPoint.endPointGroupID === groupID;
|
|
});
|
|
},
|
|
getEndPoints: function () {
|
|
var allEndpoints = [];
|
|
this.endPointGroups.forEach(function (endPointGroup) {
|
|
allEndpoints = allEndpoints.concat(this.grabEndPointIDsFromGroup(endPointGroup));
|
|
}, this);
|
|
this.endPoints = allEndpoints;
|
|
},
|
|
updateDebugCubeID: function (debugCubeID) {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "updateDebugCubeID");
|
|
}
|
|
},
|
|
turnOn: function () {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "turnOn");
|
|
}
|
|
},
|
|
turnOff: function () {
|
|
for (var id in avatarsInZone) {
|
|
Entities.callEntityClientMethod(id, this.id, "turnOff");
|
|
}
|
|
}
|
|
};
|
|
|
|
// Procedural Functions
|
|
// Entity Definition
|
|
function DJ_Dispatch_Zone_Server() {
|
|
self = this;
|
|
}
|
|
|
|
DJ_Dispatch_Zone_Server.prototype = {
|
|
remotelyCallable: [
|
|
"clearDebugEndpointInfo",
|
|
"receiveHeartBeat",
|
|
"requestTurnOff",
|
|
"scan",
|
|
"sendEdit",
|
|
"sendOff",
|
|
"sendOn",
|
|
"storeDebugEndpointInfo",
|
|
"storeDebugSensorInfo",
|
|
"submitEvent",
|
|
"turnOff",
|
|
"turnOn",
|
|
"updateComponents"
|
|
],
|
|
clearDebugEndpointInfo: function (id) {
|
|
generators[0].clearDebugEndpointInfo();
|
|
},
|
|
heartBeatHelper: function () {
|
|
var shouldKeepActive = false,
|
|
now = Date.now(),
|
|
avatars = Object.keys(avatarsInZone);
|
|
|
|
avatars.forEach(function(avatar) {
|
|
var timeToCheck = now - avatarsInZone[avatar];
|
|
if (timeToCheck > HEARTBEAT_TIMEOUT) {
|
|
delete avatarsInZone[avatar];
|
|
} else {
|
|
shouldKeepActive = true;
|
|
}
|
|
});
|
|
if (!shouldKeepActive) {
|
|
self.turnOff();
|
|
}
|
|
},
|
|
|
|
preload: function (id) {
|
|
entityID = id;
|
|
currentProperties = Entities.getEntityProperties(entityID, ["name", "userData"]);
|
|
name = currentProperties.name;
|
|
|
|
userData = currentProperties.userData;
|
|
try {
|
|
userdataProperties = JSON.parse(userData);
|
|
DEBUG = userdataProperties.performance.DEBUG;
|
|
wantsCube = userdataProperties.performance.wantsCube;
|
|
|
|
if (DEBUG) {
|
|
LOG_CONFIG[LOG_ENTER] = true;
|
|
LOG_CONFIG[LOG_UPDATE] = true;
|
|
LOG_CONFIG[LOG_ERROR] = true;
|
|
LOG_CONFIG[LOG_VALUE] = true;
|
|
log = Util.Debug.log(LOG_CONFIG);
|
|
}
|
|
|
|
var childNameTimeOutFunction = function () {
|
|
userdataProperties = getUserData(entityID);
|
|
if (!userdataProperties.performance.childNamesUpdated) {
|
|
Script.setTimeout(childNameTimeOutFunction, SEARCH_FOR_CHILDNAME_TIMEOUT);
|
|
} else {
|
|
childNames = userdataProperties.performance.childNames;
|
|
var childNamesToSearch = Array.prototype.slice.call(childNames);
|
|
childNames.forEach(function (name) {
|
|
childrenIDS[name] = null;
|
|
});
|
|
|
|
var searchCallback = function (children, foundAllEntities, names) {
|
|
if (foundAllEntities) {
|
|
loadedChildren = true;
|
|
Object.keys(children).forEach(function (name) {
|
|
childrenIDS[name] = children[name];
|
|
});
|
|
self.updateComponents();
|
|
} else {
|
|
searchForChildren(entityID, names, searchCallback, SEARCH_FOR_CHILDREN_TIMEOUT, true);
|
|
}
|
|
};
|
|
|
|
searchForChildren(entityID, childNamesToSearch, searchCallback, SEARCH_FOR_CHILDREN_TIMEOUT, true);
|
|
}
|
|
};
|
|
|
|
Script.setTimeout(childNameTimeOutFunction, SEARCH_FOR_CHILDNAME_TIMEOUT);
|
|
|
|
|
|
} catch (e) {
|
|
log(LOG_ERROR, "ERROR READING USERDATA", e);
|
|
}
|
|
|
|
},
|
|
receiveHeartBeat: function (id, param) {
|
|
var avatarID = param[0];
|
|
avatarsInZone[avatarID] = Date.now();
|
|
sensors.forEach(function(sensor) {
|
|
if (sensor.canEdit && Date.now() - sensor.lastEdit > LAST_EDIT_TIMEOUT) {
|
|
var sensorEndpoints = sensor.endPoints;
|
|
sensor.activeUUID = null;
|
|
sensor.activeGenerator = null;
|
|
self.sendOff(sensorEndpoints);
|
|
sensor.canEdit = false;
|
|
}
|
|
});
|
|
|
|
log(LOG_VALUE, "AVATRS IN ZONE ON RECEIVE HEART BEAT", avatarsInZone);
|
|
},
|
|
requestTurnOff: function() {
|
|
this.heartBeatHelper();
|
|
},
|
|
returnSensorIndex: function (id) {
|
|
var foundIndex;
|
|
sensors.forEach(function(sensor, index) {
|
|
if (sensor.id === id) {
|
|
foundIndex = index;
|
|
}
|
|
});
|
|
return foundIndex;
|
|
},
|
|
scan: function () {
|
|
var foundSensors = [];
|
|
var foundEndPoints = [];
|
|
var foundGenerators = [];
|
|
|
|
childNames.forEach(function (name) {
|
|
var idToCheck = childrenIDS[name];
|
|
var properties = getProps(idToCheck);
|
|
try {
|
|
var userData = JSON.parse(properties.userData);
|
|
var id = properties.id;
|
|
if (userData.performance.type === SENSOR) {
|
|
var endPointGroups = userData.performance.endPointGroups;
|
|
foundSensors.push(new Sensor(id, endPointGroups));
|
|
}
|
|
if (userData.performance.type === ENDPOINT) {
|
|
var endPointGroupID = userData.performance.endPointGroupID;
|
|
foundEndPoints.push(new EndPoint(id, endPointGroupID));
|
|
}
|
|
if (userData.performance.type === GENERATOR) {
|
|
foundGenerators.push(new Generator(id));
|
|
if (properties.name.indexOf("Debug-Cube") > -1) {
|
|
debugCubeID = id;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
log(LOG_ERROR, "PARSE ERROR LOOKING FOR USERDATA", e);
|
|
}
|
|
});
|
|
sensors = foundSensors;
|
|
endPoints = foundEndPoints;
|
|
generators = foundGenerators;
|
|
},
|
|
sendEdit: function (groupEndPoints, range, direction) {
|
|
groupEndPoints.forEach(function(endPoint) {
|
|
Entities.callEntityMethod(
|
|
endPoint.id,
|
|
'edit',
|
|
[range, direction]
|
|
);
|
|
});
|
|
if (DEBUG && wantsCube) {
|
|
this.storeDebugSensorInfo(range);
|
|
}
|
|
},
|
|
sendOff: function (groupEndPoints) {
|
|
groupEndPoints.forEach(function(endPoint) {
|
|
Entities.callEntityMethod(endPoint.id, 'turnOff');
|
|
});
|
|
if (DEBUG && wantsCube) {
|
|
this.clearDebugEndpointInfo();
|
|
}
|
|
},
|
|
sendOn: function (groupEndPoints) {
|
|
groupEndPoints.forEach(function(endPoint) {
|
|
Entities.callEntityMethod(endPoint.id, 'turnOn');
|
|
});
|
|
if (DEBUG && wantsCube) {
|
|
this.clearDebugEndpointInfo();
|
|
}
|
|
},
|
|
storeDebugEndpointInfo: function (id, parm) {
|
|
generators[0].storeDebugEndpointInfo(parm[0], parm[1]);
|
|
},
|
|
storeDebugSensorInfo: function (range) {
|
|
generators[0].storeDebugSensorInfo(range);
|
|
},
|
|
submitEvent: function (id, param) {
|
|
var range = param[0],
|
|
direction = param[1],
|
|
generator = param[2],
|
|
box = param[3],
|
|
sensorID = param[4],
|
|
uuid = param[5];
|
|
|
|
var sensorIndex = this.returnSensorIndex(sensorID);
|
|
var sensor = sensors[sensorIndex];
|
|
var sensorEndpoints = sensors[sensorIndex].endPoints;
|
|
|
|
if (box === IN_BOX) {
|
|
if (!sensor.activeUUID && !sensor.activeGenerator) {
|
|
sensor.activeUUID = uuid;
|
|
sensor.activeGenerator = generator;
|
|
this.sendOn(sensorEndpoints);
|
|
sensors[sensorIndex].canEdit = true;
|
|
return;
|
|
}
|
|
if (sensor.activeGenerator === generator &&
|
|
sensor.activeUUID === uuid &&
|
|
sensor.canEdit) {
|
|
sensor.lastEdit = Date.now();
|
|
this.sendEdit(sensorEndpoints, range, direction);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (box === IN_MARGIN) {
|
|
if (sensor.activeUUID === uuid &&
|
|
sensor.activeGenerator === generator) {
|
|
sensor.activeUUID = null;
|
|
sensor.activeGenerator = null;
|
|
this.sendOff(sensorEndpoints);
|
|
sensor.canEdit = false;
|
|
}
|
|
}
|
|
},
|
|
turnOff: function () {
|
|
|
|
isOn = false;
|
|
if (heartbeatCheck) {
|
|
Script.clearInterval(heartbeatCheck);
|
|
heartbeatCheck = null;
|
|
}
|
|
sensors.forEach(function (sensor) {
|
|
sensor.turnOff();
|
|
});
|
|
generators.forEach(function (generator) {
|
|
generator.turnOff();
|
|
});
|
|
endPoints.forEach(function (endPoint) {
|
|
endPoint.turnOff();
|
|
});
|
|
},
|
|
turnOn: function (id, param) {
|
|
var avatarID = param[0];
|
|
avatarsInZone[avatarID] = Date.now();
|
|
log(LOG_ENTER, "Turn on activated", avatarsInZone);
|
|
|
|
if (isOn) {
|
|
return;
|
|
} else {
|
|
isOn = true;
|
|
sensors.forEach(function (sensor) {
|
|
sensor.turnOn();
|
|
});
|
|
generators.forEach(function (generator) {
|
|
generator.turnOn();
|
|
});
|
|
heartbeatCheck = Script.setInterval( function() {
|
|
self.heartBeatHelper();
|
|
}, HEARTBEAT_CHECK_INTERVAL);
|
|
}
|
|
},
|
|
unload: function () {
|
|
if (heartbeatCheck) {
|
|
Script.clearInterval(heartbeatCheck);
|
|
}
|
|
this.turnOff();
|
|
},
|
|
updateComponents: function () {
|
|
this.scan();
|
|
this.updateDispatch();
|
|
if (DEBUG && wantsCube) {
|
|
this.updateDebugCubeID(debugCubeID);
|
|
}
|
|
},
|
|
updateDebugCubeID: function (debugCubeID) {
|
|
sensors.forEach(function (sensor) {
|
|
sensor.updateDebugCubeID(debugCubeID);
|
|
});
|
|
},
|
|
updateDispatch: function () {
|
|
sensors.forEach(function (sensor) {
|
|
sensor.getEndPoints();
|
|
});
|
|
}
|
|
};
|
|
|
|
return new DJ_Dispatch_Zone_Server();
|
|
});
|