From 3273fbcad42f8e4dafc2b117758669d02d89783c Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 25 Nov 2015 17:10:38 -0800 Subject: [PATCH] Creating the master / agent hiring system --- examples/acScripts/AgentPoolControler.js | 448 +++++++++++++---------- examples/acScripts/playbackMaster.js | 56 +-- 2 files changed, 292 insertions(+), 212 deletions(-) diff --git a/examples/acScripts/AgentPoolControler.js b/examples/acScripts/AgentPoolControler.js index 60b906381a..ec0fdf4b87 100644 --- a/examples/acScripts/AgentPoolControler.js +++ b/examples/acScripts/AgentPoolControler.js @@ -10,235 +10,305 @@ // function printDebug(message) { - print(message); + print(message); } (function() { - var COMMAND_CHANNEL = "com.highfidelity.PlaybackChannel1"; - var ANNOUNCE_CHANNEL = "com.highfidelity.playbackAgent.announceID"; + var COMMAND_CHANNEL = "com.highfidelity.PlaybackChannel1"; + var ANNOUNCE_CHANNEL = "com.highfidelity.playbackAgent.announceID"; - // The time between alive messages on the command channel - var ALIVE_PERIOD = 3; - var NUM_CYCLES_BEFORE_RESET = 5; + // The time between alive messages on the command channel + var ALIVE_PERIOD = 3; + var NUM_CYCLES_BEFORE_RESET = 8; - // Service Actions - var AGENT_READY = "ready"; - var INVALID_ACTOR = -2; + // Service Actions + var MASTER_ID = -1; + var AGENT_READY = "ready"; + var AGENT_LOST = "agentLost"; - var MASTER_INDEX = -1; + var INVALID_ACTOR = -2; - var MASTER_ALIVE = -1; - var MASTER_FIRE_AGENT = -2; - var makeMessage = function(id, action, argument) { - var message = { + var AGENTS_BROADCAST = -1; + var MASTER_ALIVE = -1; + var MASTER_FIRE_AGENT = -2; + + var makeUniqueUUID = function(SEUUID) { + //return SEUUID + Math.random(); + // forget complexity, just give me a four digit pin + return (Math.random() * 10000).toFixed(0); + } + + var packAnnounceMessage = function(dest, command, src) { + var message = { + dest: dest, + command: command, + src: src + }; + return JSON.stringify(message); + }; + + var unpackAnnounceMessage = function(message) { + return JSON.parse(message); + }; + + var packCommandMessage = function(id, action, argument) { + var message = { id_key: id, action_key: action, argument_key: argument - }; - return message; - }; - - var unpackMessage = function(message) { - return JSON.parse(message); - }; - - // master side - //--------------------------------- - var MasterController = function() { - this.timeSinceLastAlive = 0; - this.knownAgents = new Array; - this.subscribed = false; + }; + return JSON.stringify(message); }; - MasterController.prototype.destroy = function() { - if (this.subscribed) { - Messages.unsubscribe(ANNOUNCE_CHANNEL); - Messages.unsubscribe(COMMAND_CHANNEL); - this.subscribed = true; - } - }; + var unpackCommandMessage = function(message) { + return JSON.parse(message); + }; + + // master side + //--------------------------------- + var MasterController = function() { + this.timeSinceLastAlive = 0; + this.knownAgents = new Array; + this.hiringAgentsQueue = new Array; + this.subscribed = false; + }; - MasterController.prototype.reset = function() { - this.timeSinceLastAlive = 0; + MasterController.prototype.destroy = function() { + if (this.subscribed) { + Messages.unsubscribe(ANNOUNCE_CHANNEL); + Messages.unsubscribe(COMMAND_CHANNEL); + this.subscribed = true; + } + }; - if (!this.subscribed) { - Messages.subscribe(COMMAND_CHANNEL); - Messages.subscribe(ANNOUNCE_CHANNEL); - var localThis = this; - Messages.messageReceived.connect(function (channel, message, senderID) { - if (channel == ANNOUNCE_CHANNEL) { - localThis._processAnnounceMessage(message, senderID); - return; - } - }); - } - // ready to roll, enable - this.subscribed = true; - printDebug("Master Started"); + MasterController.prototype.reset = function() { + this.timeSinceLastAlive = 0; + + if (!this.subscribed) { + Messages.subscribe(COMMAND_CHANNEL); + Messages.subscribe(ANNOUNCE_CHANNEL); + var localThis = this; + Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel == ANNOUNCE_CHANNEL) { + localThis._processAnnounceMessage(message, senderID); + return; + } + }); + } + // ready to roll, enable + this.subscribed = true; + printDebug("Master Started"); }; MasterController.prototype._processAnnounceMessage = function(message, senderID) { - if (message == AGENT_READY) { - - // check to see if we know about this agent - if (this.knownAgents.indexOf(senderID) < 0) { - - var indexOfNewAgent = this.knownAgents.length; - this.knownAgents[indexOfNewAgent] = senderID; - printDebug("New agent available to be hired " + senderID); - - var acknowledgeMessage = senderID + "." + indexOfNewAgent; - Messages.sendMessage(ANNOUNCE_CHANNEL, acknowledgeMessage); - } else { - printDebug("New agent still sending ready ? " + senderID); - } - } + var message = unpackAnnounceMessage(message); + if (message.dest == MASTER_ID) { + if (message.command == AGENT_READY) { + // check to see if we know about this agent + var agentIndex = this.knownAgents.indexOf(message.src); + if (agentIndex < 0) { + if (this.hiringAgentsQueue.length > 0) { + var hiringHandler = this.hiringAgentsQueue.pop(); + hiringHandler(message.src); + } else { + //No hiring in queue so bail + return; + } + } else { + // Master think the agent is hired but not the other way around, forget about it + printDebug("New agent still sending ready ? " + message.src + " " + agentIndex + " Forgeting about it"); + this.knownAgents[agentIndex] = INVALID_ACTOR; + } + } else if (message.command == AGENT_LOST) { + // check to see if we know about this agent + var agentIndex = this.knownAgents.indexOf(message.src); + if (agentIndex < 0) { + printDebug("Lost agent " + message.src + " " + agentIndex + " Forgeting about it"); + this.knownAgents[agentIndex] = INVALID_ACTOR; + } + } + } }; MasterController.prototype.sendCommand = function(target, action, argument) { if (this.subscribed) { - var messageJSON = JSON.stringify(makeMessage(target, action, argument)); + var messageJSON = packCommandMessage(target, action, argument); Messages.sendMessage(COMMAND_CHANNEL, messageJSON); - printDebug("Master sent message: " + messageJSON); } - }; - - MasterController.prototype.update = function(deltaTime) { - this.timeSinceLastAlive += deltaTime; - if (this.timeSinceLastAlive > ALIVE_PERIOD) { - this.timeSinceLastAlive = 0; - printDebug("Master ping alive"); - this.sendCommand(MASTER_INDEX, MASTER_ALIVE); - } - }; - - this.MasterController = MasterController; - - // agent side - //--------------------------------- - var AgentController = function() { - this.subscribed = false; - - this.timeSinceLastAlive = 0; - this.numCyclesWithoutAlive = 0; - this.actorIndex = INVALID_ACTOR; - this.notifyAlive = false; - - this.onHired = function() {}; - this.onCommand = function(command) {}; - this.onFired = function() {}; }; - AgentController.prototype.destroy = function() { - if (this.subscribed) { - this.fire(); - Messages.unsubscribe(ANNOUNCE_CHANNEL); - Messages.unsubscribe(COMMAND_CHANNEL); - this.subscribed = true; - } - }; - - AgentController.prototype.reset = function() { - // If already hired, fire - this.fired(); - - if (!this.subscribed) { - Messages.subscribe(COMMAND_CHANNEL); - Messages.subscribe(ANNOUNCE_CHANNEL); - var localThis = this; - Messages.messageReceived.connect(function (channel, message, senderID) { - if (channel == ANNOUNCE_CHANNEL) { - localThis._processAnnounceMessage(message, senderID); - return; - } - if (channel == COMMAND_CHANNEL) { - localThis._processCommandMessage(message, senderID); - return; - } - }); - } - this.subscribed = true; - printDebug("Client Started"); + MasterController.prototype.update = function(deltaTime) { + this.timeSinceLastAlive += deltaTime; + if (this.timeSinceLastAlive > ALIVE_PERIOD) { + this.timeSinceLastAlive = 0; + this.sendCommand(AGENTS_BROADCAST, MASTER_ALIVE); + } }; - AgentController.prototype._processAnnounceMessage = function(message, senderID) { - //printDebug("Client " + this.actorIndex + " Received Announcement = " + message); - if (message != AGENT_READY) { - // this may be a message to hire me if i m not already - if (this.actorIndex == INVALID_ACTOR) { - var parts = message.split("."); - var agentID = parts[0]; - var actorIndex = parts[1]; - //printDebug("Client " + Agent.sessionUUID + " - " + agentID + " Hired!"); - if (agentID == Agent.sessionUUID) { - this.actorIndex = actorIndex; - printDebug("Client " + this.actorIndex + " Hired!"); - this.onHired(); - // Messages.unsubscribe(ANNOUNCE_CHANNEL); // id announce channel - } - } - } - } - AgentController.prototype._processCommandMessage = function(message, senderID) { - var command = unpackMessage(message); - //printDebug("Received command = " + JSON.stringify(command)); + MasterController.prototype.hireAgent = function(onHired) { + var localThis = this; + this.hiringAgentsQueue.unshift(function(agentID) { + printDebug("hiring callback with agent " + agentID); + + var indexOfNewAgent = localThis.knownAgents.length; + localThis.knownAgents[indexOfNewAgent] = agentID; + + printDebug("New agent available to be hired " + agentID + " " + index); + var hireMessage = "HIRE." + index; + var answerMessage = packAnnounceMessage(agentID, hireMessage, MASTER_ID); + Messages.sendMessage(ANNOUNCE_CHANNEL, answerMessage); - if (command.id_key == localThis.actorIndex || command.id_key == MASTER_INDEX) { - if (command.action_key == MASTER_ALIVE) { - this.notifyAlive = true; - } else if (command.action_key == MASTER_FIRE_AGENT) { - printDebug("Master firing Agent"); - this.fire(); - } else { - printDebug("True action received = " + JSON.stringify(command) + senderID); - this.onCommand(command); - } - } else { - // ignored - } + if (onHired != null) { + onHired(index); + } + }) }; - AgentController.prototype.update = function(deltaTime) { - this.timeSinceLastAlive += deltaTime; - if (this.timeSinceLastAlive > ALIVE_PERIOD) { - if (this.subscribed) { - if (this.actorIndex == INVALID_ACTOR) { - Messages.sendMessage(ANNOUNCE_CHANNEL, AGENT_READY); - //printDebug("Client Ready" + ANNOUNCE_CHANNEL + AGENT_READY); - } else { - this.numCyclesWithoutAlive++; - if (this.notifyAlive) { - this.notifyAlive = false; - this.numCyclesWithoutAlive = 0; - printDebug("Master Alive"); - } else if (this.numCyclesWithoutAlive > NUM_CYCLES_BEFORE_RESET) { - printDebug("Master Lost, firing Agent"); - this.fired(); - } - } - } + this.MasterController = MasterController; - this.timeSinceLastAlive = 0; - } - }; + // agent side + //--------------------------------- + var AgentController = function() { + this.subscribed = false; + + this._init(); - AgentController.prototype.fired = function() { - // clear the state first - var wasHired = (this.actorIndex != INVALID_ACTOR); - this.actorIndex= INVALID_ACTOR; + this.onHired = function() {}; + this.onCommand = function(command) {}; + this.onFired = function() {}; + }; + + AgentController.prototype._init = function() { + this.actorIndex= INVALID_ACTOR; this.notifyAlive = false; this.timeSinceLastAlive = 0; this.numCyclesWithoutAlive = 0; + this.actorUUID = makeUniqueUUID(Agent.sessionUUID); + printDebug("this.actorUUID = " + this.actorUUID); + } + + AgentController.prototype.destroy = function() { + if (this.subscribed) { + this.fire(); + Messages.unsubscribe(ANNOUNCE_CHANNEL); + Messages.unsubscribe(COMMAND_CHANNEL); + this.subscribed = true; + } + }; + + AgentController.prototype.reset = function() { + // If already hired, fire + this.fired(); + + if (!this.subscribed) { + Messages.subscribe(COMMAND_CHANNEL); + Messages.subscribe(ANNOUNCE_CHANNEL); + var localThis = this; + Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel == ANNOUNCE_CHANNEL) { + localThis._processAnnounceMessage(message, senderID); + return; + } + if (channel == COMMAND_CHANNEL) { + localThis._processCommandMessage(message, senderID); + return; + } + }); + } + this.subscribed = true; + printDebug("Client Started"); + }; + + AgentController.prototype._processAnnounceMessage = function(message, senderID) { + var announce = unpackCommandMessage(message); + //printDebug("Client " + this.actorIndex + " Received Announcement = " + message); + if (announce.dest == this.actorUUID) { + if (announce.command != AGENT_READY) { + // this may be a message to hire me if i m not already + if (this.actorIndex == INVALID_ACTOR) { + printDebug(announce.command); + + var parts = announce.command.split("."); + var agentID = parts[0]; + var actorIndex = parts[1]; + //printDebug("Client " + Agent.sessionUUID + " - " + agentID + " Hired!"); + // if (agentID == Agent.sessionUUID) { + this.actorIndex = actorIndex; + printDebug("Client " + this.actorIndex + " Hired!"); + this.onHired(); + // Messages.unsubscribe(ANNOUNCE_CHANNEL); // id announce channel + // } + } + } + } + } + + AgentController.prototype._processCommandMessage = function(message, senderID) { + var command = unpackCommandMessage(message); + //printDebug("Received command = " + JSON.stringify(command)); + + if ((command.id_key == this.actorUUID) || (command.id_key == AGENTS_BROADCAST)) { + if (command.action_key == MASTER_ALIVE) { + this.notifyAlive = true; + } else if (command.action_key == MASTER_FIRE_AGENT) { + printDebug("Master firing Agent"); + this.fire(); + } else { + printDebug("True action received = " + JSON.stringify(command) + senderID); + this.onCommand(command); + } + } else { + // ignored + } + }; + + + AgentController.prototype.update = function(deltaTime) { + this.timeSinceLastAlive += deltaTime; + if (this.timeSinceLastAlive > ALIVE_PERIOD) { + if (this.subscribed) { + if (this.actorIndex == INVALID_ACTOR) { + Messages.sendMessage(ANNOUNCE_CHANNEL, packAnnounceMessage(MASTER_ID, AGENT_READY, this.actorUUID)); + //printDebug("Client Ready" + ANNOUNCE_CHANNEL + AGENT_READY); + } else { + this.numCyclesWithoutAlive++; + if (this.notifyAlive) { + this.notifyAlive = false; + this.numCyclesWithoutAlive = 0; + printDebug("Master Alive"); + } else if (this.numCyclesWithoutAlive > NUM_CYCLES_BEFORE_RESET) { + printDebug("Master Lost, firing Agent"); + this.fired(); + } + } + } + + this.timeSinceLastAlive = 0; + } + }; + + AgentController.prototype.fired = function() { + // clear the state first + var wasHired = (this.actorIndex != INVALID_ACTOR); + + // Post a last message to master in case it still listen to warn that this agent is losing it + if (wasHired) { + Messages.sendMessage(ANNOUNCE_CHANNEL, packAnnounceMessage(MASTER_ID, AGENT_LOST, this.actorUUID)); + } + + // reset + this._init(); + // then custom fire if was hired - if (wasHired) { - this.onFired(); - } - } + if (wasHired) { + this.onFired(); + } + } - this.AgentController = AgentController; + this.AgentController = AgentController; })(); diff --git a/examples/acScripts/playbackMaster.js b/examples/acScripts/playbackMaster.js index 1444ae3cf1..929f384514 100644 --- a/examples/acScripts/playbackMaster.js +++ b/examples/acScripts/playbackMaster.js @@ -54,13 +54,23 @@ var performanceLoadedNeedUpdate = false; setupPlayback(); +function onHiring(agentID, index) { + print("agent hired from playbackMaster! " + agentID + " " + index) +} + function setupPlayback() { ac_number = Window.prompt("Insert number of agents: ","1"); if (ac_number === "" || ac_number === null) { ac_number = 1; - } - setupToolBars(); + } masterController.reset(); + + for (var i = 0; i < ac_number; i++) { + masterController.hireAgent(onHiring); + } + + setupToolBars(); + } function setupToolBars() { @@ -96,13 +106,13 @@ function setupToolBars() { var playLoopWidthFactor = 1.65; playLoopIcon[i] = toolBars[i].addTool({ - imageURL: TOOL_ICON_URL + "play-and-loop.svg", - subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: playLoopWidthFactor * Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_OFF, - visible: true - }, false); + imageURL: TOOL_ICON_URL + "play-and-loop.svg", + subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: playLoopWidthFactor * Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); stopIcon[i] = toolBars[i].addTool({ imageURL: TOOL_ICON_URL + "recording-stop.svg", @@ -121,20 +131,20 @@ function setupToolBars() { }, false); nameOverlays.push(Overlays.addOverlay("text", { - backgroundColor: { red: 0, green: 0, blue: 0 }, - font: { size: TEXT_HEIGHT }, - text: (i == ac_number) ? "Master" : i + ". " + - ((i < names.length) ? names[i] : - "AC" + i), - x: 0, y: 0, - width: toolBars[i].width + ToolBar.SPACING, - height: TEXT_HEIGHT + TEXT_MARGIN, - leftMargin: TEXT_MARGIN, - topMargin: TEXT_MARGIN, - alpha: ALPHA_OFF, - backgroundAlpha: ALPHA_OFF, - visible: true - })); + backgroundColor: { red: 0, green: 0, blue: 0 }, + font: { size: TEXT_HEIGHT }, + text: (i == ac_number) ? "Master" : i + ". " + + ((i < names.length) ? names[i] : + "AC" + i), + x: 0, y: 0, + width: toolBars[i].width + ToolBar.SPACING, + height: TEXT_HEIGHT + TEXT_MARGIN, + leftMargin: TEXT_MARGIN, + topMargin: TEXT_MARGIN, + alpha: ALPHA_OFF, + backgroundAlpha: ALPHA_OFF, + visible: true + })); } }