@ -1,47 +0,0 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title>Feature Requests</title> | |||
<!-- Bootstrap --> | |||
<link href="css/bootstrap.min.css" rel="stylesheet"> | |||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> | |||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | |||
<!--[if lt IE 9]> | |||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> | |||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> | |||
<![endif]--> | |||
<style> | |||
body { | |||
padding:10px; | |||
} | |||
div.textHTMLContent { | |||
display:block; | |||
width:800px; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<h1>Showcase projects</h1> | |||
<div class="textHTMLContent"> | |||
- MIDAS | |||
- Git | |||
- Cool examples (?) | |||
</div> | |||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> | |||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> | |||
<!-- Include all compiled plugins (below), or include individual files as needed --> | |||
<script src="js/bootstrap.min.js"></script> | |||
</body> | |||
</html> |
@ -0,0 +1,93 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title>Feature Requests</title> | |||
<!-- Bootstrap --> | |||
<link href="css/bootstrap.min.css" rel="stylesheet"> | |||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> | |||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | |||
<!--[if lt IE 9]> | |||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> | |||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> | |||
<![endif]--> | |||
<style> | |||
a { | |||
text-decoration: none; | |||
} | |||
body { | |||
padding:10px; | |||
font-family: Lustria,Georgia,Times,"Times New Roman",serif !important; | |||
} | |||
div.textHTMLContent { | |||
display:block; | |||
width:970px; | |||
} | |||
img.showcase { | |||
position:relative; | |||
border: 1px solid #dddddd; | |||
border-radius:20px; | |||
width:900px; | |||
height:350px; | |||
} | |||
div.description{ | |||
width:880px; | |||
position:relative; | |||
border-radius:20px; | |||
padding:20px; | |||
text-decoration: none; | |||
color:#ffffff; | |||
background-color:#064880; | |||
margin-top:-80px; | |||
margin-left:20px; | |||
z-index:2; | |||
box-shadow: 0px 0px 8px #000000; | |||
} | |||
div.descriptionHeader { | |||
font-size:25px; | |||
font-weight:bold; | |||
padding-bottom:5px; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<h1>Showcase projects</h1> | |||
<div class="textHTMLContent"> | |||
<a href="./projects/midas/index.html"> | |||
<div class="showcase"> | |||
<img src="./images/midas.png" class="showcase"> | |||
<div class="description"> | |||
<div class="descriptionHeader"> | |||
M.I.D.A.S. : Manufacturing Incident Detection Agent Solution | |||
</div> | |||
<div class="descriptionContent"> | |||
MIDAS collects statistics of incidents during the production process. | |||
It analyses the manufacturing process and handles disruptive events during the ramp-up phase. | |||
Almende developed the software tool especially for this project, based on the inhouse toolchain, | |||
specifically Eve, AIM and vis.js. | |||
</div> | |||
</div> | |||
</div> | |||
</a> | |||
</div> | |||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> | |||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> | |||
<!-- Include all compiled plugins (below), or include individual files as needed --> | |||
<script src="js/bootstrap.min.js"></script> | |||
</body> | |||
</html> |
@ -0,0 +1,216 @@ | |||
'use strict'; | |||
function AgentGenerator(id) { | |||
// execute super constructor | |||
eve.Agent.call(this, id); | |||
this.rpc = this.loadModule('rpc', this.rpcFunctions); | |||
var me = this; | |||
this.amountOfEvents = 0; | |||
this.eventNumber = 0; | |||
this.eventsToFire = 0; | |||
this.lastEndOfDayTime = null; | |||
this.events = []; | |||
conn = this.connect(eve.system.transports.getAll()); | |||
// connect to websocket if not online only | |||
if (conn[0].connect !== undefined) { | |||
conn[0].connect(EVENTS_AGENT_ADDRESS) | |||
.then(function () { | |||
console.log('Connected to ', EVENTS_AGENT_ADDRESS); | |||
me.rpc.request(EVENTS_AGENT_ADDRESS, { | |||
method: "loadEvents", | |||
params: {filename: "events.csv", actuallySend: true} | |||
}).done(function (reply) { | |||
me.amountOfEvents = reply; | |||
me.getEvents(AMOUNT_OF_INITIAL_EVENTS); | |||
}); | |||
}) | |||
.catch(function (err) { | |||
console.log('Error: Failed to connect to the conductor agent'); | |||
console.log(err); | |||
// keep trying until the conductor agent is online | |||
setTimeout(connect, RECONNECT_DELAY); | |||
}); | |||
} | |||
//use local connection | |||
else { | |||
EVENTS_AGENT_ADDRESS = 'eventGenerator'; | |||
setTimeout(function() { | |||
me.rpc.request(EVENTS_AGENT_ADDRESS, { | |||
method: "loadEvents", | |||
params: {filename: "events.csv", actuallySend: true} | |||
}).done(function (reply) { | |||
me.amountOfEvents = reply; | |||
me.getEvents(AMOUNT_OF_INITIAL_EVENTS); | |||
}); | |||
},40); | |||
} | |||
} | |||
// extend the eve.Agent prototype | |||
AgentGenerator.prototype = Object.create(eve.Agent.prototype); | |||
AgentGenerator.prototype.constructor = AgentGenerator; | |||
// define RPC functions, preferably in a separated object to clearly distinct | |||
// exposed functions from local functions. | |||
AgentGenerator.prototype.rpcFunctions = {}; | |||
AgentGenerator.prototype.rpcFunctions.receiveEvent = function(params) { | |||
// setup timeline | |||
this.events.push(JSON.stringify(params)); | |||
if (params.performedBy == "global") { | |||
this.imposeWorkingHours(params); | |||
} | |||
else { | |||
if (agentList[params.performedBy] === undefined) { | |||
agentList[params.performedBy] = new GenericAgent(params.performedBy, params.type); | |||
} | |||
this.rpc.request(params.performedBy, {method: "newEvent", params: params}); | |||
} | |||
// check if we need to get another event, its done here to avoid raceconditions | |||
if (this.eventsToFire != 0) { | |||
var me = this; | |||
setTimeout(function() { | |||
me.eventNumber += 1; | |||
eventCounter.innerHTML = me.eventNumber +""; // make string so it works | |||
me.rpc.request(EVENTS_AGENT_ADDRESS, {method:'nextEvent', params:{}}).done(); | |||
me.eventsToFire -= 1; | |||
},EVENT_DELAY); | |||
if (INCREASE_SPEED == true) { | |||
EVENT_DELAY = Math.max(0, 1000 - (1000 * (me.eventNumber / 205))); | |||
} | |||
} | |||
}; | |||
AgentGenerator.prototype.getEvents = function (count) { | |||
if (this.eventNumber + count > this.amountOfEvents) { | |||
count = this.amountOfEvents - this.eventNumber; | |||
} | |||
if (count != 0) { | |||
this.eventsToFire = count - 1; | |||
this.rpc.request(EVENTS_AGENT_ADDRESS, {method: 'nextEvent', params: {}}).done(); | |||
this.eventNumber += 1; | |||
eventCounter.innerHTML = this.eventNumber + ""; // make string so it works | |||
} | |||
}; | |||
AgentGenerator.prototype.rpcFunctions.updateOpenJobs = function(params) { | |||
var skipJob = params.jobId; | |||
var time = params.time; | |||
this.moveTimeline(params); | |||
for (var agentId in agentList) { | |||
if (agentList.hasOwnProperty(agentId)) { | |||
agentList[agentId].jobs.updateJobs(time, skipJob); | |||
} | |||
} | |||
}; | |||
AgentGenerator.prototype.moveTimeline = function(params) { | |||
timeline.setCustomTime(params.time); | |||
var range = timeline.getWindow(); | |||
var duration = range.end - range.start; | |||
var hiddenDates = timeline.body.hiddenDates; | |||
var DateUtil = vis.timeline.DateUtil; | |||
var hiddenDuration = DateUtil.getHiddenDurationBetween(hiddenDates, range.start, range.end); | |||
var visibleDuration = duration - hiddenDuration; | |||
var fraction = 0.15; | |||
var requiredStartDuration = (1-fraction) * visibleDuration; | |||
var requiredEndDuration = fraction * visibleDuration; | |||
var convertedTime = new Date(params.time).getTime(); | |||
var newStart; | |||
var newEnd; | |||
var elapsedDuration = 0; | |||
var previousPoint = convertedTime; | |||
for (var i = hiddenDates.length-1; i > 0; i--) { | |||
var startDate = hiddenDates[i].start; | |||
var endDate = hiddenDates[i].end; | |||
// if time after the cutout, and the | |||
if (endDate <= convertedTime) { | |||
elapsedDuration += previousPoint - endDate; | |||
previousPoint = startDate; | |||
if (elapsedDuration >= requiredStartDuration) { | |||
newStart = endDate + (elapsedDuration - requiredStartDuration); | |||
break; | |||
} | |||
} | |||
} | |||
if (newStart === undefined) { | |||
newStart = endDate - (requiredStartDuration - elapsedDuration); | |||
} | |||
elapsedDuration = 0; | |||
previousPoint = convertedTime; | |||
for (var i = 0; i < hiddenDates.length; i++) { | |||
var startDate = hiddenDates[i].start; | |||
var endDate = hiddenDates[i].end; | |||
// if time after the cutout, and the | |||
if (startDate >= convertedTime) { | |||
elapsedDuration += startDate - previousPoint; | |||
previousPoint = endDate; | |||
if (elapsedDuration >= requiredEndDuration) { | |||
newEnd = startDate - (elapsedDuration - requiredEndDuration); | |||
break; | |||
} | |||
} | |||
} | |||
if (newEnd === undefined) { | |||
newEnd = endDate + (requiredEndDuration - elapsedDuration); | |||
} | |||
timeline.setWindow(newStart, newEnd, {animate:Math.min(100,EVENT_DELAY)}); | |||
}; | |||
AgentGenerator.prototype.imposeWorkingHours = function(params) { | |||
var time = params.time; | |||
var operation = params.operation; | |||
for (var agentId in agentList) { | |||
if (agentList.hasOwnProperty(agentId)) { | |||
var agent = agentList[agentId]; | |||
for (var jobId in agent.jobs.openJobs) { | |||
if (agent.jobs.openJobs.hasOwnProperty(jobId)) { | |||
var job = agent.jobs.openJobs[jobId]; | |||
agent.updateAssignment(jobId, job.type, time, operation); | |||
} | |||
} | |||
} | |||
} | |||
if (operation == 'endOfDay') { | |||
this.lastEndOfDayTime = time; | |||
} | |||
else { | |||
if (this.lastEndOfDayTime !== null) { | |||
timelineItems.update({ | |||
id: 'night' + uuid(), | |||
start: this.lastEndOfDayTime, | |||
end: time, | |||
type: 'background', | |||
className: 'night' | |||
}); | |||
this.lastEndOfDayTime = null; | |||
} | |||
} | |||
}; | |||
AgentGenerator.prototype.printEvents = function() { | |||
var str = ""; | |||
str += "["; | |||
for (var i = 0; i < this.events.length; i++) { | |||
str += this.events[i]; | |||
if (i < this.events.length - 1) { | |||
str += "," | |||
} | |||
} | |||
str += "]"; | |||
console.log(str); | |||
} |
@ -0,0 +1,40 @@ | |||
/** | |||
* Created by Alex on 9/25/2014. | |||
*/ | |||
function DurationData() { | |||
this.fields = ['duration','durationWithPause','durationWithStartup','durationWithBoth']; | |||
for (var i = 0; i < this.fields.length; i++) { | |||
this[this.fields[i]] = 0; | |||
} | |||
} | |||
DurationData.prototype.setData = function(otherData) { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
this[this.fields[i]] = otherData[this.fields[i]]; | |||
} | |||
}; | |||
DurationData.prototype.getData = function() { | |||
var dataObj = {}; | |||
for (var i = 0; i < this.fields.length; i++) { | |||
dataObj[this.fields[i]] = this[this.fields[i]]; | |||
} | |||
return dataObj; | |||
}; | |||
DurationData.prototype.useHighest = function(otherData) { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
var field = this.fields[i]; | |||
if (this[field] < otherData[field]) { | |||
this[field] = otherData[field]; | |||
} | |||
} | |||
}; | |||
DurationData.prototype.calculateDuration = function(time, timeStart, elapsedTime, elapsedTimeWithPause, startupTime) { | |||
this.duration = elapsedTime; | |||
this.durationWithPause = elapsedTimeWithPause; | |||
this.durationWithStartup = elapsedTime + startupTime.durationWithStartup; | |||
this.durationWithBoth = elapsedTimeWithPause + startupTime.durationWithBoth; | |||
}; |
@ -0,0 +1,142 @@ | |||
/** | |||
* Created by Alex on 9/25/2014. | |||
*/ | |||
function DurationStats() { | |||
this.fields = ['duration','durationWithPause','durationWithStartup','durationWithBoth']; | |||
for (var i = 0; i < this.fields.length; i++) { | |||
this[this.fields[i]] = {mean: 0, std: 0}; | |||
} | |||
} | |||
DurationStats.prototype.clearStats = function() { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
var field = this.fields[i]; | |||
this[field].mean = 0; | |||
this[field].std = 0; | |||
} | |||
}; | |||
DurationStats.prototype.sumStats = function(otherData) { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
var field = this.fields[i]; | |||
this[field].mean += otherData[field].mean; | |||
this[field].std += Math.pow(otherData[field].std,2); | |||
} | |||
}; | |||
DurationStats.prototype.averageStats = function(datapoints) { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
var field = this.fields[i]; | |||
this[field].mean /= datapoints; | |||
this[field].std = Math.sqrt(this[field].std / datapoints); | |||
} | |||
}; | |||
DurationStats.prototype.getMeanData = function() { | |||
var dataObj = {}; | |||
for (var i = 0; i < this.fields.length; i++) { | |||
dataObj[this.fields[i]] = this[this.fields[i]].mean; | |||
} | |||
return dataObj; | |||
}; | |||
DurationStats.prototype.getData = function() { | |||
var dataObj = {}; | |||
for (var i = 0; i < this.fields.length; i++) { | |||
dataObj[this.fields[i]] = {}; | |||
dataObj[this.fields[i]].mean = this[this.fields[i]].mean; | |||
dataObj[this.fields[i]].std = this[this.fields[i]].std; | |||
} | |||
return dataObj; | |||
}; | |||
DurationStats.prototype.setData = function(otherData) { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
var field = this.fields[i]; | |||
this[field].mean = otherData[field].mean; | |||
this[field].std = otherData[field].std; | |||
} | |||
}; | |||
DurationStats.prototype.generateData = function(otherData) { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
var field = this.fields[i]; | |||
this[field].mean = otherData[i] * 3600000; | |||
this[field].std = otherData[i] * 0.1; | |||
} | |||
}; | |||
DurationStats.prototype.useHighest = function(otherData) { | |||
for (var i = 0; i < this.fields.length; i++) { | |||
var field = this.fields[i]; | |||
if (this[field].mean < otherData[field].mean) { | |||
this[field].mean = otherData[field].mean; | |||
this[field].std = otherData[field].std; | |||
} | |||
} | |||
}; | |||
DurationStats.prototype.getFakeStats = function(type) { | |||
switch (type) { | |||
case "Assemble Coffeemaker": | |||
this.generateData([1.3,1.3,1.3,1.3]); | |||
break; | |||
case "Discuss potential NC": | |||
this.generateData([0.5,0.5,0.9,0.9]); | |||
break; | |||
case "Drilling rework": | |||
this.generateData([5,5,8,8]); | |||
break; | |||
case "Go to station": | |||
var a = 0.3; | |||
this.generateData([a,a,a,a]); | |||
break; | |||
case "Inspect finished Coffeemaker": | |||
var a = 2; | |||
this.generateData([a,a,a,a]); | |||
break; | |||
case "Inspect potential NC": | |||
var a = 0.5; | |||
this.generateData([a,a,a,a]); | |||
break; | |||
case "Kitting Coffeemaker": | |||
var a = 1.2; | |||
this.generateData([a,a,a,a]); | |||
break; | |||
case "NC Meeting": | |||
var a = 15; | |||
this.generateData([3,3.5,a,a]); | |||
break; | |||
case "Go to NC meeting": | |||
var a = 12; | |||
this.generateData([a,a,a,a]); | |||
break; | |||
case "Organise drilling rework": | |||
var a = 2; | |||
this.generateData([a,a,3,3]); | |||
break; | |||
case "Produce Coffeemaker": | |||
var a = 35; | |||
this.generateData([a,a,a,a]); | |||
break; | |||
case "Transport to delivery": | |||
var a = 0.4; | |||
this.generateData([a,a,a,a]); | |||
break; | |||
default: | |||
console.log("CANNOT MATCH", type); | |||
break; | |||
} | |||
}; |
@ -0,0 +1,85 @@ | |||
'use strict'; | |||
if (typeof window === 'undefined') { | |||
var eve = require('evejs'); | |||
} | |||
function GenericAgent(id, type) { | |||
// execute super constructor | |||
eve.Agent.call(this, id); | |||
this.id = id; | |||
this.rpc = this.loadModule('rpc', this.rpcFunctions); | |||
this.connect(eve.system.transports.getAll()); | |||
this.type = type; | |||
this.jobs = new JobManager(this); | |||
this.timelineDataset = timelineItems; | |||
timelineGroups.add({id:id, content:type + ": " + id, className: 'timelineGroup ' + type}); | |||
this.availableSubgroups = [0,1,2,3,4,5,6,7,8,9,10]; | |||
this.freeSubgroups = {}; | |||
for (var i = 0; i < this.availableSubgroups.length; i++) { | |||
this.freeSubgroups[this.availableSubgroups[i]] = true; | |||
} | |||
this.usedSubgroups = {}; | |||
} | |||
// extend the eve.Agent prototype | |||
GenericAgent.prototype = Object.create(eve.Agent.prototype); | |||
GenericAgent.prototype.constructor = GenericAgent; | |||
// define RPC functions, preferably in a separated object to clearly distinct | |||
// exposed functions from local functions. | |||
GenericAgent.prototype.rpcFunctions = {}; | |||
GenericAgent.prototype.allocateSubgroup = function(type) { | |||
for (var i = 0; i < this.availableSubgroups.length; i++) { | |||
if (this.freeSubgroups[this.availableSubgroups[i]] == true) { | |||
this.usedSubgroups[type] = i; | |||
this.freeSubgroups[this.availableSubgroups[i]] = false; | |||
break; | |||
} | |||
} | |||
}; | |||
GenericAgent.prototype.freeSubgroup = function(type) { | |||
this.freeSubgroups[this.usedSubgroups[type]] = true; | |||
delete this.usedSubgroups[type]; | |||
}; | |||
GenericAgent.prototype.newAssignment = function(id, type, time, prerequisites) { | |||
this.allocateSubgroup(type); | |||
this.jobs.add(id, type, time, prerequisites); | |||
}; | |||
/** | |||
* @param id | |||
* @param time | |||
* @param type | |||
*/ | |||
GenericAgent.prototype.finishAssignment = function(id, type, time) { | |||
this.jobs.finish(id, type, time); | |||
}; | |||
GenericAgent.prototype.updateAssignment = function(id, type, time, operation) { | |||
this.jobs.update(id, type, time, operation); | |||
}; | |||
GenericAgent.prototype.rpcFunctions.newEvent = function(params) { | |||
// handle events | |||
if (params.operation == 'start') { | |||
this.newAssignment(params.jobId, params.assignment, params.time, params.prerequisites) | |||
} | |||
else if (params.operation == 'finish') { | |||
this.finishAssignment(params.jobId, params.assignment, params.time); | |||
} | |||
else if (params.operation == 'pause' || params.operation == 'resume') { | |||
this.updateAssignment(params.jobId,params.assignment,params.time, params.operation); | |||
} | |||
}; | |||
if (typeof window === 'undefined') { | |||
module.exports = GenericAgent; | |||
} |
@ -0,0 +1,116 @@ | |||
'use strict'; | |||
function uuid() { | |||
return (Math.random()*1e15).toString(32) + "-" + (Math.random()*1e15).toString(32); | |||
} | |||
/** | |||
* This is a local assignment, this keeps track on how long an assignment takes THIS worker. | |||
* | |||
* @param id | |||
* @param type | |||
* @param timeStart | |||
* @constructor | |||
*/ | |||
function Job(id, type, timeStart, agentId, prerequisites) { | |||
this.id = id; | |||
this.type = type; | |||
this.agentId = agentId; | |||
this.timeStart = timeStart; | |||
this.timeResumed = timeStart; | |||
this.timePaused = 0; | |||
this.elapsedTime = 0; | |||
this.elapsedTimeWithPause = 0; | |||
this.endOfDayPause = false; | |||
this.paused = false; | |||
this.finished = false; | |||
this.duration = new DurationData(); | |||
this.prediction = new DurationStats(); | |||
this.startupTime = new DurationData(); | |||
this.predictedStartupTime = new DurationStats(); | |||
this.prerequisites = prerequisites; | |||
} | |||
Job.prototype.prerequisiteFinished = function(params) { | |||
var uuid = params.uuid; | |||
for (var i = 0; i < this.prerequisites.length; i++) { | |||
var prereq = this.prerequisites[i]; | |||
if (prereq.uuid == uuid) { | |||
prereq.times.setData(params.duration); | |||
break; | |||
} | |||
} | |||
}; | |||
Job.prototype.watchingPrerequisite = function(preliminaryStats, uuid) { | |||
for (var i = 0; i < this.prerequisites.length; i++) { | |||
var prereq = this.prerequisites[i]; | |||
if (prereq.uuid == uuid) { | |||
prereq.stats.setData(preliminaryStats); | |||
this.predictedStartupTime.useHighest(preliminaryStats); | |||
break; | |||
} | |||
} | |||
}; | |||
Job.prototype.finalizePrerequisites = function() { | |||
for (var i = 0; i < this.prerequisites.length; i++) { | |||
this.startupTime.useHighest(this.prerequisites[i].times); | |||
} | |||
}; | |||
Job.prototype.finish = function(time) { | |||
this.finished = true; | |||
this.elapsedTime += new Date(time).getTime() - new Date(this.timeResumed).getTime(); | |||
this.elapsedTimeWithPause += new Date(time).getTime() - new Date(this.timeResumed).getTime(); | |||
this.finalizePrerequisites(); | |||
this.duration.calculateDuration(time, this.timeStart, this.elapsedTime, this.elapsedTimeWithPause, this.startupTime); | |||
}; | |||
Job.prototype.pause = function(time, endOfDay) { | |||
// if this is the endOfDay AND the job is paused, count the pause time and set the endOfDay pause to true | |||
if (endOfDay == true && this.paused == true) { | |||
this.elapsedTimeWithPause += new Date(time).getTime() - new Date(this.timePaused).getTime(); | |||
this.endOfDayPause = true; | |||
} | |||
// if this is the endOfDay AND the job is NOT paused, pause the job, increment the timers | |||
else if (endOfDay == true && this.paused == false) { | |||
this.elapsedTimeWithPause += new Date(time).getTime() - new Date(this.timeResumed).getTime(); | |||
this.elapsedTime += new Date(time).getTime() - new Date(this.timeResumed).getTime(); | |||
this.endOfDayPause = true; | |||
} | |||
else if (this.paused == false) { | |||
this.elapsedTimeWithPause += new Date(time).getTime() - new Date(this.timeResumed).getTime(); | |||
this.elapsedTime += new Date(time).getTime() - new Date(this.timeResumed).getTime(); | |||
this.timePaused = time; | |||
this.paused = true; | |||
} | |||
}; | |||
Job.prototype.resume = function(time, startOfDay) { | |||
// if the job was paused because of the endOfDay, resume it and set the timeResumed to now | |||
if (this.endOfDayPause == true && startOfDay == true && this.paused == false) { | |||
this.timeResumed = time; | |||
this.endOfDayPause = false; | |||
} | |||
// if the job was paused before the endOfDay, keep it paused, but set the paused time to now. | |||
else if (this.endOfDayPause == true && startOfDay == true && this.paused == true) { | |||
this.timePaused = time; | |||
this.endOfDayPause = false; | |||
} | |||
// if this is NOT the start of day and the job was paused, resume job, increment | |||
else if (startOfDay == false && this.paused == true) { | |||
this.elapsedTimeWithPause += new Date(time).getTime() - new Date(this.timePaused).getTime(); | |||
this.timeResumed = time; | |||
this.paused = false; | |||
} | |||
}; | |||
@ -0,0 +1,434 @@ | |||
'use strict'; | |||
function JobAgent(id) { | |||
// execute super constructor | |||
eve.Agent.call(this, id); | |||
this.rpc = this.loadModule('rpc', this.rpcFunctions); | |||
this.connect(eve.system.transports.getAll()); | |||
this.id = id; | |||
this.type = this.id.replace('job_',''); | |||
this.globalStats = new DurationStats(); | |||
this.globalStats.getFakeStats(id); | |||
this.agentStats = {}; | |||
this.allJobs = {}; // used to quickly look up a job ID | |||
this.openJobs = {}; // used to check if there is a watcher to | |||
this.closedJobs = {};// keeps a list of agentIds containing jobs, used for stats collection | |||
// optimization would be nice with running averages, but N samples are needed, wait for demo data. | |||
this.watchers = {}; | |||
} | |||
// extend the eve.Agent prototype | |||
JobAgent.prototype = Object.create(eve.Agent.prototype); | |||
JobAgent.prototype.constructor = JobAgent; | |||
// define RPC functions, preferably in a separated object to clearly distinct | |||
// exposed functions from local functions. | |||
JobAgent.prototype.rpcFunctions = {}; | |||
JobAgent.prototype.expandPrerequisites = function(prerequisites) { | |||
var expanded = []; | |||
if (prerequisites !== undefined) { | |||
for (var i = 0; i < prerequisites.length; i++) { | |||
var prereq = prerequisites[i]; | |||
if (typeof prereq == 'string') { | |||
expanded.push({ | |||
jobId: prereq, | |||
uuid: uuid(), | |||
times : new DurationData(), | |||
stats: new DurationStats() | |||
}); | |||
} | |||
else if (typeof prereq == 'object' && prereq.type !== undefined) { //&& prereq.agentId !== undefined not needed since the same items will be added when only type exists | |||
prereq.uuid = uuid(); | |||
prereq.times = new DurationData(); | |||
prereq.stats = new DurationStats(); | |||
expanded.push(prereq); | |||
} | |||
else { | |||
console.log('ERROR: cannot use the prerequisites! Not in array of strings or array of objects with correct fields format.'); | |||
throw new Error('ERROR: cannot use the prerequisites! Not in array of strings or array of objects with correct fields format.'); | |||
} | |||
} | |||
} | |||
return expanded; | |||
}; | |||
/** | |||
* Create new Job for agent | |||
* @param params | |||
*/ | |||
JobAgent.prototype.rpcFunctions.add = function(params) { | |||
var agentId = params.agentId; | |||
var jobId = params.jobId; | |||
// create stats if not yet exits | |||
if (this.agentStats[agentId] === undefined) { | |||
this.agentStats[agentId] = new DurationStats(); | |||
} | |||
// create open job | |||
if (this.openJobs[agentId] === undefined) { | |||
this.openJobs[agentId] = {}; | |||
} | |||
if (this.openJobs[agentId][jobId] !== undefined) { | |||
console.log('cannot start new job, jobId:', jobId, ' already exists!'); | |||
throw new Error('cannot start new job, jobId:' + jobId + ' already exists!'); | |||
} | |||
var prerequisites = this.expandPrerequisites(params.prerequisites); | |||
this.openJobs[agentId][jobId] = new Job(jobId, this.id, params.time, agentId, prerequisites); | |||
this.allJobs[jobId] = this.openJobs[agentId][jobId]; | |||
this.addWatchers(jobId, prerequisites); | |||
// return prediction | |||
var statsData; | |||
if (this.agentStats[agentId].duration.mean == 0) { | |||
statsData = this.globalStats.getData(); | |||
} | |||
else { | |||
statsData = this.agentStats[agentId].getData(); | |||
} | |||
return statsData; | |||
}; | |||
/** | |||
* finish the job of an agent | |||
* @param params | |||
*/ | |||
JobAgent.prototype.rpcFunctions.finish = function(params) { | |||
var agentId = params.agentId; | |||
var jobId = params.jobId; | |||
var job = this.openJobs[agentId][jobId]; | |||
// finish job | |||
job.finish(params.time); | |||
// notify watchers that a job is finished. | |||
if (this.watchers[jobId] !== undefined) { | |||
for (var i = 0; i < this.watchers[jobId].length; i++) { | |||
var val = this.watchers[jobId][i]; | |||
var address = val.address; | |||
var parentJobId = val.parentJobId; | |||
var uuid = val.uuid; | |||
this.rpc.request(address, {method:'watchedJobFinished', params:{ | |||
uuid: uuid, | |||
parentJobId: parentJobId, | |||
duration: job.duration.getData() | |||
}}).done(); | |||
} | |||
} | |||
// cleanup watchers | |||
delete this.watchers[jobId]; | |||
// move from open to closed jobs. | |||
if (this.closedJobs[agentId] === undefined) { | |||
this.closedJobs[agentId] = {}; | |||
} | |||
if (this.closedJobs[agentId][jobId] !== undefined) { | |||
console.log('cannot close job, jobId:', jobId, ' already exists!'); | |||
throw new Error('cannot close job, jobId:' + jobId + ' already exists!'); | |||
} | |||
this.closedJobs[agentId][jobId] = this.openJobs[agentId][jobId]; | |||
delete this.openJobs[agentId][jobId]; | |||
this.updateStats(); | |||
return { | |||
elapsedTime: this.closedJobs[agentId][jobId].elapsedTime, | |||
elapsedTimeWithPause: this.closedJobs[agentId][jobId].elapsedTimeWithPause, | |||
duration: this.closedJobs[agentId][jobId].duration.getData(), | |||
prediction: this.globalStats.getData() | |||
}; | |||
}; | |||
/** | |||
* update the job of an agent | |||
* @param params | |||
*/ | |||
JobAgent.prototype.rpcFunctions.update = function(params) { | |||
var agentId = params.agentId; | |||
var jobId = params.jobId; | |||
var job = this.openJobs[agentId][jobId]; | |||
var operation = params.operation; | |||
switch (operation) { | |||
case 'pause': | |||
job.pause(params.time, false); | |||
break; | |||
case 'endOfDay': | |||
job.pause(params.time, true); | |||
break; | |||
case 'startOfDay': | |||
job.resume(params.time, true); | |||
break; | |||
case 'resume': | |||
job.resume(params.time, false); | |||
break; | |||
} | |||
return {jobId: jobId, type: this.type, elapsedTime: job.elapsedTime, elapsedTimeWithPause: job.elapsedTimeWithPause}; | |||
}; | |||
/** | |||
* return agent stats | |||
* @param params | |||
* @returns {*} | |||
*/ | |||
JobAgent.prototype.rpcFunctions.getAgentStats = function(params) { | |||
return this.agentStats[params.agentId]; | |||
}; | |||
/** | |||
* return global stats | |||
* @param params | |||
* @returns {{mean: number, std: number}|*} | |||
*/ | |||
JobAgent.prototype.rpcFunctions.getGlobalStats = function(params) { | |||
return this.globalStats; | |||
}; | |||
JobAgent.prototype.rpcFunctions.watchedJobFinished = function(params) { | |||
var jobId = params.parentJobId; | |||
this.allJobs[jobId].prerequisiteFinished(params); | |||
}; | |||
/** | |||
* | |||
* @param params | |||
* @param sender | |||
* @returns {*} | |||
*/ | |||
JobAgent.prototype.rpcFunctions.addWatcherOnJobId = function(params, sender) { | |||
var jobId = params.jobId; | |||
var uuid = params.uuid; | |||
var parentJobId = params.parentJobId; | |||
var job = this.allJobs[jobId]; | |||
// if the job is already finished, call the finished callback | |||
if (job.finished == true) { | |||
this.rpc.request(sender, {method:'watchedJobFinished', params:{ | |||
uuid: uuid, | |||
parentJobId: parentJobId, | |||
duration: job.duration.getData() // we need the pure json data, not the class | |||
}}).done(); | |||
} | |||
else { | |||
// we will create a watcher on a job which will alert the watcher when the job is finished with all the times. | |||
if (this.watchers[jobId] === undefined) { | |||
this.watchers[jobId] = []; | |||
} | |||
this.watchers[jobId].push({address: params.address, uuid: uuid}); | |||
} | |||
// return the best prediction we have | |||
if (this.agentStats[job.agentId].mean == 0) { | |||
return this.globalStats.getData(); // we need the pure json data, not the class | |||
} | |||
return this.agentStats[job.agentId].getData(); // we need the pure json data, not the class | |||
}; | |||
/** | |||
* | |||
* @param params | |||
* @param sender | |||
* @returns {*} | |||
*/ | |||
JobAgent.prototype.rpcFunctions.addWatcherByAgentID = function(params, sender) { | |||
var agentId = params.agentId; | |||
var parentJobId = params.parentJobId; | |||
var jobId = null; | |||
var uuid = params.uuid; | |||
var returnStats; | |||
// see which statistics collection we will need to return. | |||
if (this.agentStats[agentId].duration.mean == 0) { | |||
returnStats = this.globalStats; | |||
} | |||
else { | |||
returnStats = this.agentStats[agentId]; | |||
} | |||
// see if we have an open job with that agent of this type | |||
if (this.openJobs[agentId] !== undefined) { | |||
for (var jId in this.openJobs[agentId]) { | |||
if (this.openJobs[agentId].hasOwnProperty(jId)) { | |||
jobId = jId; | |||
break; | |||
} | |||
} | |||
} | |||
// there is no open job from supplied agent of this type. return the mean of the return stats | |||
if (jobId === null) { | |||
this.rpc.request(params.address, {method:'watchedJobFinished', params:{ | |||
uuid: uuid, | |||
parentJobId: parentJobId, | |||
duration: returnStats.getMeanData(), // we need the pure json data, not the class | |||
oldData: true | |||
}}).done(); | |||
} | |||
else { | |||
params.jobId = jobId; | |||
this.rpcFunctions.addWatcherOnJobId.call(this, params, sender); | |||
} | |||
// return the best prediction we have | |||
return returnStats.getData(); // we need the pure json data, not the class | |||
}; | |||
/** | |||
* | |||
* @param params | |||
* @param sender | |||
* @returns {*} | |||
*/ | |||
JobAgent.prototype.rpcFunctions.addWatcherByType = function(params, sender) { | |||
// since we cannot watch a global type, we return the global stats at that point. | |||
this.rpc.request(params.address, {method:'watchedJobFinished', params:{ | |||
uuid: params.uuid, | |||
parentJobId: params.parentJobId, | |||
duration: this.globalStats.getMeanData(), // we need the pure json data, not the class | |||
oldData: true | |||
}}).done(); | |||
return this.globalStats.getData(); // we need the pure json data, not the class | |||
}; | |||
/** | |||
* | |||
* @param parentJobId | ID from the job that wants to WATCH other jobs | |||
* @param prerequisites | |||
*/ | |||
JobAgent.prototype.addWatchers = function(parentJobId, prerequisites) { | |||
for (var i = 0; i < prerequisites.length; i++) { | |||
var prereq = prerequisites[i]; | |||
var params = { | |||
uuid: prereq.uuid, | |||
address: this.id, // this is the callback address | |||
parentJobId: parentJobId// this is the job that wants to watch the other one. | |||
}; | |||
var me = this; | |||
if (prereq.jobId !== undefined) { | |||
// we now have a parentJobId to watch | |||
// we first need to find the type of job this belongs to. | |||
this.rpc.request('JobAgentGenerator', {method: 'returnJobAddress', params: {jobId: prereq.jobId}}) | |||
// now that we have an address, set a watcher on the job id | |||
.done(function (address) { | |||
if (address != 'doesNotExist') { | |||
params.jobId = prereq.jobId; // this is the job we want to watch | |||
me.rpc.request(address, {method: 'addWatcherOnJobId', params: params}) | |||
.done(function (preliminaryStats) { | |||
me.allJobs[parentJobId].watchingPrerequisite(preliminaryStats, prereq.uuid); | |||
}) | |||
} | |||
else { | |||
console.log('ERROR: watch job does not exist.'); | |||
throw new Error('ERROR: watch job does not exist.'); | |||
} | |||
}); | |||
} | |||
else if (prereq.agentId !== undefined && prereq.type !== undefined) { | |||
// we now have an agentId and a jobType to watch. | |||
params.agentId = prereq.agentId; // this is the job we want to watch | |||
this.rpc.request(prereq.type, {method: 'addWatcherByAgentID', params: params}) | |||
.done(function (preliminaryStats) { | |||
me.allJobs[parentJobId].watchingPrerequisite(preliminaryStats, prereq.uuid); | |||
}) | |||
} | |||
else if (prereq.type !== undefined) { | |||
this.rpc.request(prereq.type, {method: 'addWatcherByType', params: params}) | |||
.done(function (preliminaryStats) { | |||
me.allJobs[parentJobId].watchingPrerequisite(preliminaryStats, prereq.uuid); | |||
}) | |||
} | |||
} | |||
}; | |||
JobAgent.prototype.updatePredictedStartup = function(jobId, prediction) { | |||
var jobPrediction = this.allJobs[jobId].predictedStartupTime; | |||
jobPrediction.mean = Math.max(jobPrediction.mean, prediction.mean); | |||
jobPrediction.std = Math.sqrt(Math.pow(jobPrediction.std,2) + Math.pow(prediction.std,2)); | |||
this.allJobs[jobId].prerequisitesCount += 1; | |||
}; | |||
/** | |||
* Update all statistics | |||
* | |||
*/ | |||
JobAgent.prototype.updateStats = function() { | |||
this.globalStats.clearStats(); | |||
var count = 0; | |||
for (var agentId in this.closedJobs) { | |||
if (this.closedJobs.hasOwnProperty(agentId)) { | |||
var collection = this.closedJobs[agentId]; | |||
// could be optimised with rolling average for efficient memory management | |||
this.agentStats[agentId].setData(this.updateStatsIn(collection)); | |||
this.globalStats.sumStats(this.agentStats[agentId]); | |||
count += 1; | |||
} | |||
} | |||
this.globalStats.averageStats(count); | |||
}; | |||
/** | |||
* | |||
* @param collection | |||
* @returns {{duration: *, durationWithPause: *, durationWithStartup: *, durationWithBoth: *}} | |||
*/ | |||
JobAgent.prototype.updateStatsIn = function(collection) { | |||
var stats = {}; | |||
for (var i = 0; i < this.globalStats.fields.length; i++) { | |||
var field = this.globalStats.fields[i]; | |||
stats[field] = this.collectStatsIn(collection, field); | |||
} | |||
return stats; | |||
}; | |||
JobAgent.prototype.collectStatsIn = function(collection, field) { | |||
var total = 0; | |||
var mean = 0; | |||
var std = 0; | |||
var minVal = 1e16; | |||
var maxVal = 0; | |||
var count = 0; | |||
for (var jobId in collection) { | |||
if (collection.hasOwnProperty(jobId)) { | |||
var value = collection[jobId].duration[field]; | |||
maxVal = value > maxVal ? value : maxVal; | |||
minVal = value < minVal ? value : minVal; | |||
total += collection[jobId].duration[field]; | |||
count += 1; | |||
} | |||
} | |||
if (count > 0) { | |||
mean = total / count; | |||
for (var jobId in collection) { | |||
if (collection.hasOwnProperty(jobId)) { | |||
std += Math.pow(collection[jobId].duration[field] - mean,2); | |||
} | |||
} | |||
std = Math.sqrt(std/count); | |||
return {mean: mean, std: std, min: minVal, max: maxVal}; | |||
} | |||
else { | |||
return {mean: 0, std: 0, min: 0, max: 0}; | |||
} | |||
}; | |||
JobAgent.prototype.hasJob = function(params) { | |||
return this.allJobs[params.jobId] !== undefined; | |||
}; |
@ -0,0 +1,169 @@ | |||
'use strict'; | |||
function JobAgentGenerator(id) { | |||
// execute super constructor | |||
eve.Agent.call(this, id); | |||
this.rpc = this.loadModule('rpc', this.rpcFunctions); | |||
this.connect(eve.system.transports.getAll()); | |||
} | |||
// extend the eve.Agent prototype | |||
JobAgentGenerator.prototype = Object.create(eve.Agent.prototype); | |||
JobAgentGenerator.prototype.constructor = AgentGenerator; | |||
// define RPC functions, preferably in a separated object to clearly distinct | |||
// exposed functions from local functions. | |||
JobAgentGenerator.prototype.rpcFunctions = {}; | |||
JobAgentGenerator.prototype.rpcFunctions.createJob = function(params) { | |||
var jobAgentName = params.type; | |||
if (jobList[jobAgentName] === undefined) { | |||
jobList[jobAgentName] = new JobAgent(jobAgentName); | |||
graph2dGroups.add([ | |||
{ | |||
id: jobAgentName+'_pred_duration_std_lower', | |||
content: "prediction", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithPause_std_lower', | |||
content: "predWithPause", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithStartup_std_lower', | |||
content: "predWithStartup", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithBoth_std_lower', | |||
content: "predWithBoth", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_duration_std_higher', | |||
content: "prediction", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithPause_std_higher', | |||
content: "predWithPause", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithStartup_std_higher', | |||
content: "predWithStartup", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithBoth_std_higher', | |||
content: "predWithBoth", | |||
className: 'prediction_std', | |||
options: {drawPoints:false} | |||
},{ | |||
id: jobAgentName+'_pred_duration_original', | |||
content: "prediction", | |||
className: 'prediction_original' | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithPause_original', | |||
content: "predWithPause", | |||
className: 'prediction_original' | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithStartup_original', | |||
content: "predWithStartup", | |||
className: 'prediction_original' | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithBoth_original', | |||
content: "predWithBoth", | |||
className: 'prediction_original' | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_duration', | |||
content: "prediction", | |||
className: 'prediction' | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithPause', | |||
content: "predWithPause", | |||
className: 'prediction' | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithStartup', | |||
content: "predWithStartup", | |||
className: 'prediction' | |||
}, | |||
{ | |||
id: jobAgentName+'_pred_durationWithBoth', | |||
content: "predWithBoth", | |||
className: 'prediction' | |||
}, | |||
{ | |||
id: jobAgentName+'_duration', | |||
content: "duration", | |||
className: 'duration' | |||
}, | |||
{ | |||
id: jobAgentName+'_durationWithPause', | |||
content: "durationWithPause", | |||
className: 'duration' | |||
}, | |||
{ | |||
id: jobAgentName+'_durationWithStartup', | |||
content: "durationWithStartup", | |||
className: 'duration' | |||
}, | |||
{ | |||
id: jobAgentName+'_durationWithBoth', | |||
content: "durationWithBoth", | |||
className: 'duration' | |||
} | |||
]); | |||
var visibilityUpdate = {}; | |||
//visibilityUpdate[jobAgentName+'_pred'] = false; | |||
//visibilityUpdate[jobAgentName+'_predWithPause'] = false; | |||
//visibilityUpdate[jobAgentName+'_predWithStartup'] = false; | |||
//visibilityUpdate[jobAgentName+'_predWithBoth'] = false; | |||
//visibilityUpdate[jobAgentName+'_duration'] = false; | |||
//visibilityUpdate[jobAgentName+'_durationWithPause'] = false; | |||
//visibilityUpdate[jobAgentName+'_durationWithStartup'] = false; | |||
//visibilityUpdate[jobAgentName+'_durationWithBoth'] = false; | |||
graph2d.setOptions({groups:{visible:visibilityUpdate}}); | |||
refreshJobs(); | |||
} | |||
}; | |||
JobAgentGenerator.prototype.rpcFunctions.returnJobAddress = function(params) { | |||
var instanceId = params.instanceId; | |||
var hasJob = false; | |||
for (var jobAgentName in jobList) { | |||
if (jobList.hasOwnProperty(jobAgentName)) { | |||
hasJob = jobList[jobAgentName].hasJob(instanceId); | |||
if (hasJob == true) { | |||
return jobAgentName; | |||
} | |||
} | |||
} | |||
return "doesNotExist"; | |||
}; | |||
JobAgentGenerator.prototype.getAllJobNames = function() { | |||
var list = []; | |||
for (var jobAgentName in jobList) { | |||
if (jobList.hasOwnProperty(jobAgentName)) { | |||
list.push(jobAgentName); | |||
} | |||
} | |||
return list; | |||
}; |
@ -0,0 +1,428 @@ | |||
'use strict'; | |||
function JobManager(agent) { | |||
this.agent = agent; | |||
this.jobs = { | |||
id:{}, | |||
type:{ | |||
open:{}, | |||
closed:{} | |||
} | |||
}; | |||
this.openJobs = {}; | |||
} | |||
JobManager.prototype.add = function(id, type, time, prerequisites) { | |||
var me = this; | |||
// create job agent. This agent will keep track of the global job stats. Jobs per type. | |||
this.agent.rpc.request('jobAgentGenerator',{method:'createJob', params:{type:type}}).done(); | |||
this.jobs.id[id] = { | |||
type: type, | |||
startTime: time, | |||
prediction: null, | |||
predictionCounter: 0, | |||
elapsedTime: 0, | |||
elapsedTimeWithPause: 0, | |||
pauseCount: 0, | |||
endOfDay: false, | |||
paused: false, | |||
pauseAreaID: "" | |||
}; | |||
// assign agent to job. | |||
this.agent.rpc.request(type, {method:'add', params:{ | |||
agentId: this.agent.id, | |||
time:time, | |||
jobId: id, | |||
prerequisites: prerequisites | |||
}}) | |||
.done(function (prediction) { | |||
if (prediction.duration.mean != 0) { | |||
me.agent.timelineDataset.update({ | |||
id: id + "_predMean0", | |||
start: time, | |||
end: new Date(time).getTime() + prediction.duration.mean, | |||
group: me.agent.id, | |||
type: 'range', | |||
content: "", | |||
subgroup: me.agent.usedSubgroups[type], | |||
className: 'prediction' | |||
}); | |||
} | |||
me.jobs.id[id].prediction = prediction; | |||
}); | |||
if (this.jobs.type.open[type] === undefined) {this.jobs.type.open[type] = {}} | |||
this.jobs.type.open[type][id] = time; | |||
this.openJobs[id] = this.jobs.id[id]; | |||
var addQuery = [{id:id, start:time, content:"started: "+ type, group:this.agent.id, subgroup: this.agent.usedSubgroups[type]}]; | |||
this.agent.timelineDataset.add(addQuery); | |||
this.agent.rpc.request("agentGenerator", {method: 'updateOpenJobs', params:{jobId: id, time: time}}).done(); | |||
}; | |||
JobManager.prototype.finish = function(id, type, time) { | |||
var me = this; | |||
// finish the job. | |||
this.agent.rpc.request(type, {method:'finish', params:{ | |||
agentId: this.agent.id, | |||
time: time, | |||
jobId: id | |||
}}) | |||
.done(function (reply) { | |||
var prediction = reply.prediction; | |||
var originalPrediction = me.jobs.id[id].prediction; | |||
me.jobs.id[id].elapsedTime = reply.elapsedTime; | |||
me.jobs.id[id].elapsedTimeWithPause = reply.elapsedTimeWithPause; | |||
me.updateDataSetsFinish(id, type, time, me.jobs.id[id].prediction); | |||
graph2dDataset.push({x: time, y: reply.duration.duration/3600000 ,group: type + '_duration', type: type}); | |||
graph2dDataset.push({x: time, y: reply.duration.durationWithPause/3600000 ,group: type + '_durationWithPause', type: type}); | |||
graph2dDataset.push({x: time, y: reply.duration.durationWithStartup/3600000 ,group: type + '_durationWithStartup', type: type}); | |||
graph2dDataset.push({x: time, y: reply.duration.durationWithBoth/3600000 ,group: type + '_durationWithBoth', type: type}); | |||
graph2dDataset.push({x: time, y: prediction.duration.mean/3600000 ,group: type + '_pred_duration', type: type}); | |||
graph2dDataset.push({x: time, y: prediction.durationWithPause.mean/3600000 ,group: type + '_pred_durationWithPause', type: type}); | |||
graph2dDataset.push({x: time, y: prediction.durationWithStartup.mean/3600000 ,group: type + '_pred_durationWithStartup', type: type}); | |||
graph2dDataset.push({x: time, y: prediction.durationWithBoth.mean/3600000 ,group: type + '_pred_durationWithBoth', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.duration.mean + prediction.duration.std)/3600000 ,group: type + '_pred_duration_std_higher', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.durationWithPause.mean + prediction.durationWithPause.std)/3600000 ,group: type + '_pred_durationWithPause_std_higher', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.durationWithStartup.mean + prediction.durationWithStartup.std)/3600000 ,group: type + '_pred_durationWithStartup_std_higher', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.durationWithBoth.mean + prediction.durationWithBoth.std)/3600000 ,group: type + '_pred_durationWithBoth_std_higher', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.duration.mean - prediction.duration.std)/3600000 ,group: type + '_pred_duration_std_lower', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.durationWithPause.mean - prediction.durationWithPause.std)/3600000 ,group: type + '_pred_durationWithPause_std_lower', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.durationWithStartup.mean - prediction.durationWithStartup.std)/3600000 ,group: type + '_pred_durationWithStartup_std_lower', type: type}); | |||
graph2dDataset.push({x: time, y: (prediction.durationWithBoth.mean - prediction.durationWithBoth.std)/3600000 ,group: type + '_pred_durationWithBoth_std_lower', type: type}); | |||
graph2dDataset.push({x: time, y: originalPrediction.duration.mean/3600000 ,group: type + '_pred_duration_original', type: type}); | |||
graph2dDataset.push({x: time, y: originalPrediction.durationWithPause.mean/3600000 ,group: type + '_pred_durationWithPause_original', type: type}); | |||
graph2dDataset.push({x: time, y: originalPrediction.durationWithStartup.mean/3600000 ,group: type + '_pred_durationWithStartup_original', type: type}); | |||
graph2dDataset.push({x: time, y: originalPrediction.durationWithBoth.mean/3600000 ,group: type + '_pred_durationWithBoth_original', type: type}); | |||
}); | |||
delete this.jobs.type.open[type][id]; | |||
delete this.openJobs[id]; | |||
if (this.jobs.type.closed[type] === undefined) {this.jobs.type.closed[type] = {}} | |||
this.jobs.type.closed[type][id] = time; | |||
}; | |||
JobManager.prototype.update = function(id, type, time, operation) { | |||
var me = this; | |||
var eventId = uuid(); | |||
if (operation == 'endOfDay' || operation == 'startOfDay') { | |||
for (var jobId in this.openJobs) { | |||
if (this.openJobs.hasOwnProperty(jobId)) { | |||
var type = this.openJobs[jobId].type; | |||
this.agent.rpc.request(type, {method:'update', params:{ | |||
agentId: this.agent.id, | |||
time: time, | |||
jobId: jobId, | |||
operation: operation | |||
}}) | |||
.done(function (reply) { | |||
me.jobs.id[reply.jobId].elapsedTime = reply.elapsedTime; | |||
me.jobs.id[reply.jobId].elapsedTimeWithPause = reply.elapsedTimeWithPause; | |||
me.updateDataSetsOperation(reply.jobId, reply.type, time, operation, eventId); | |||
}); | |||
} | |||
} | |||
} | |||
else { | |||
this.agent.rpc.request(type, {method:'update', params:{ | |||
agentId: this.agent.id, | |||
time: time, | |||
jobId: id, | |||
operation: operation | |||
}}) | |||
.done(function (reply) { | |||
me.jobs.id[id].elapsedTime = reply.elapsedTime; | |||
me.jobs.id[id].elapsedTimeWithPause = reply.elapsedTimeWithPause; | |||
me.updateDataSetsOperation(id, type, time, operation, eventId); | |||
}); | |||
} | |||
}; | |||
JobManager.prototype.updateDataSetsOperation = function(id, type, time, operation, eventId) { | |||
switch (operation) { | |||
case 'pause': | |||
this.updateDataSetsPause(id, type, time, operation, this.jobs.id[id].prediction, eventId); | |||
break; | |||
case 'endOfDay': | |||
this.updateDataSetsPause(id, type, time, operation, this.jobs.id[id].prediction, eventId); | |||
break; | |||
case 'startOfDay': | |||
this.updateDataSetsResume(id, type, time, operation, this.jobs.id[id].prediction, eventId); | |||
break; | |||
case 'resume': | |||
this.updateDataSetsResume(id, type, time, operation, this.jobs.id[id].prediction, eventId); | |||
break; | |||
} | |||
}; | |||
JobManager.prototype.updateDataSetsFinish = function(id, type, time, prediction) { | |||
var updateQuery = []; | |||
var field = 'duration'; | |||
var elapsedTime = this.jobs.id[id].elapsedTime; | |||
// gather statistic indicator data | |||
if (this.jobs.id[id].pauseCount > 0) { | |||
field = 'durationWithPause'; | |||
elapsedTime = this.jobs.id[id].elapsedTimeWithPause; | |||
} | |||
// generate indicator | |||
var predictedTimeLeft = 0; | |||
if (prediction[field].mean != 0) { | |||
predictedTimeLeft = prediction[field].mean - elapsedTime; | |||
this.updatePredictionOnFinish(id, type, time, prediction[field], elapsedTime); | |||
} | |||
updateQuery.push({ | |||
id: id, | |||
end: time, | |||
content: type, | |||
type: 'range', | |||
className: 'finished', | |||
style: 'background-color: ' + this.generateColors(predictedTimeLeft, elapsedTime) + ' !important;' | |||
}); | |||
this.agent.freeSubgroup(type); | |||
this.agent.timelineDataset.update(updateQuery); | |||
this.agent.rpc.request("agentGenerator", {method: 'updateOpenJobs', params:{jobId: id, time: time}}).done(); | |||
}; | |||
JobManager.prototype.updateDataSetsPause = function(id, type, time, operation, prediction, eventId) { | |||
var updateQuery = []; | |||
var image = '<img src="./images/control_pause.png" class="icon"/>'; | |||
var flagId = id + "_pauseNotifier" + eventId; | |||
var title = 'Calling RAO for possible NC'; | |||
// this causes there to be only one flag for the end of day as well as a moon icon | |||
if (operation == 'endOfDay') { | |||
// don't end-of-day jobs twice | |||
if (this.jobs.id[id].endOfDay == true) { | |||
return; | |||
} | |||
this.jobs.id[id].endOfDay = true; | |||
image = '<img src="./images/moon.png" class="icon"/>'; | |||
flagId = id + "endOfDayNotifier" + eventId; | |||
title = "End of working day" | |||
} | |||
else { | |||
this.jobs.id[id].pauseCount += 1; | |||
this.jobs.id[id].pauseAreaID = this.agent.id + "_" + "_pauseArea_" + eventId; | |||
var shadedPauseArea = { | |||
id: this.jobs.id[id].pauseAreaID, | |||
start: time, | |||
end: time, | |||
content: '', | |||
group: this.agent.id, | |||
subgroup: this.agent.usedSubgroups[type], | |||
className: 'pausedArea', | |||
title: 'Job paused.' | |||
}; | |||
updateQuery.push(shadedPauseArea); | |||
} | |||
var field = 'duration'; | |||
var elapsedTime = this.jobs.id[id].elapsedTime; | |||
if (this.jobs.id[id].pauseCount > 0) { | |||
field = 'durationWithPause'; | |||
elapsedTime = this.jobs.id[id].elapsedTimeWithPause; | |||
} | |||
updateQuery.push({id: id, end: time, content: type, type: 'range'}); | |||
var imageUpdate = { | |||
id: flagId, | |||
start: time, | |||
end: time, | |||
content: image, | |||
group: this.agent.id, | |||
subgroup: this.agent.usedSubgroups[type], | |||
className: 'pause', | |||
title: title | |||
}; | |||
if (this.agent.id == "Paolo") { | |||
imageUpdate.title = "Going to inspect possible NC."; | |||
} | |||
updateQuery.push(imageUpdate); | |||
var predictedTimeLeft = prediction[field].mean - elapsedTime; | |||
var predictionExists = prediction[field].mean != 0; | |||
// update the predicted line if the job is not ALREADY pauseds | |||
if (predictedTimeLeft > 0 && predictionExists == true && this.jobs.id[id].paused != true) { | |||
updateQuery.push({ | |||
id: id + "_predMean" + this.jobs.id[id].predictionCounter, | |||
end: time, | |||
group: this.agent.id, | |||
className: 'prediction' | |||
}) | |||
} | |||
// set the status to paused if needed | |||
if (operation != 'endOfDay') { | |||
this.jobs.id[id].paused = true; | |||
} | |||
this.agent.timelineDataset.update(updateQuery); | |||
this.agent.rpc.request("agentGenerator", {method: 'updateOpenJobs', params:{jobId: id, time: time}}) | |||
}; | |||
JobManager.prototype.updateDataSetsResume = function(id, type, time, operation, prediction, eventId) { | |||
var updateQuery = []; | |||
var image = '<img src="./images/control_play.png" class="icon"/>'; | |||
var flagId = id + "_resumeNotifier" + eventId; | |||
// this causes there to be only one flag for the start of day as well as a sun icon | |||
if (operation == 'startOfDay') { | |||
// don't start-of-day jobs twice | |||
if (this.jobs.id[id].endOfDay == false) { | |||
return; | |||
} | |||
this.jobs.id[id].endOfDay = false; | |||
image = '<img src="./images/sun.png" class="icon"/>'; | |||
flagId = id + "startOfDayNotifier_" + eventId; | |||
} | |||
else { | |||
updateQuery.push({id: this.jobs.id[id].pauseAreaID, end: time, content: '', type: 'range'}); | |||
this.jobs.id[id].pauseAreaID = ""; | |||
this.jobs.id[id].pauseCount += 1; | |||
} | |||
var field = 'duration'; | |||
var elapsedTime = this.jobs.id[id].elapsedTime; | |||
if (this.jobs.id[id].pauseCount > 0) { | |||
field = 'durationWithPause'; | |||
elapsedTime = this.jobs.id[id].elapsedTimeWithPause; | |||
} | |||
updateQuery.push({id: id, end: time, content: type, type: 'range'}); | |||
updateQuery.push({ | |||
id: flagId, | |||
start: time, | |||
end: time, | |||
content: image, | |||
group: this.agent.id, | |||
subgroup: this.agent.usedSubgroups[type], | |||
className: 'pause', | |||
title: 'Resuming job' | |||
}); | |||
var predictedTimeLeft = prediction[field].mean - elapsedTime; | |||
// no not increase the prediction line at the start of the day if the job is PAUSED | |||
if (predictedTimeLeft > 0 && !(operation == 'startOfDay' && this.jobs.id[id].paused == true)) { | |||
this.jobs.id[id].predictionCounter += 1; | |||
updateQuery.push({ | |||
id: id + "_predMean" + this.jobs.id[id].predictionCounter, | |||
start: time, | |||
end: new Date(time).getTime() + predictedTimeLeft, | |||
group: this.agent.id, | |||
content: "", | |||
subgroup: this.agent.usedSubgroups[type], | |||
type: 'range', | |||
className: 'prediction' | |||
}); | |||
} | |||
// resume if needed | |||
if (operation != 'startOfDay'){ | |||
this.jobs.id[id].paused = false; | |||
} | |||
this.agent.timelineDataset.update(updateQuery); | |||
this.agent.rpc.request("agentGenerator", {method: 'updateOpenJobs', params:{jobId: id, time: time}}) | |||
}; | |||
JobManager.prototype.updatePredictionOnFinish = function(id,type,time,prediction,elapsedTime) { | |||
if (prediction.mean != 0) { | |||
var predictedTimeLeft = prediction.mean - elapsedTime; | |||
if (predictedTimeLeft > 0) { | |||
this.agent.timelineDataset.remove({id: id + "_predMean" + this.jobs.id[id].predictionCounter}) | |||
} | |||
} | |||
}; | |||
JobManager.prototype.updateJobs = function(time, skipId) { | |||
var updateQuery = []; | |||
for (var jobId in this.openJobs) { | |||
if (this.openJobs.hasOwnProperty(jobId) && jobId != skipId) { | |||
var type = this.openJobs[jobId].type; | |||
var prediction = this.openJobs[jobId].prediction; | |||
var predictedTimeLeft = 0; | |||
// never been paused before | |||
if (this.jobs.id[jobId].pauseCount == 0) { | |||
if (prediction !== null && prediction.duration !== null) { | |||
predictedTimeLeft = prediction.duration.mean - this.jobs.id[jobId].elapsedTime; | |||
} | |||
} | |||
else { | |||
if (prediction !== null && prediction.durationWithPause !== null) { | |||
predictedTimeLeft = prediction.durationWithPause.mean - this.jobs.id[jobId].elapsedTimeWithPause; | |||
} | |||
} | |||
updateQuery.push({id: jobId, end: time, content: type, type: 'range'}); | |||
if (this.openJobs[jobId].paused == true) { | |||
updateQuery.push({id: this.openJobs[jobId].pauseAreaID, end: time}); | |||
} | |||
} | |||
} | |||
this.agent.timelineDataset.update(updateQuery); | |||
}; | |||
JobManager.prototype.generateColors = function(predictedTime, elapsedTime) { | |||
var ratio = (elapsedTime + predictedTime) / elapsedTime; | |||
if (ratio > 1) { | |||
ratio = Math.min(2,ratio) - 1; // 2 -- 1 | |||
var rgb = HSVToRGB(94/360,ratio*0.6 + 0.2,1); | |||
} | |||
else { | |||
ratio = Math.max(0.5,ratio) - 0.5; // 1 -- 0.5 | |||
var rgb = HSVToRGB(40/360,(1-(2*ratio))*0.6 + 0.1 ,1); | |||
} | |||
return "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")"; | |||
}; | |||
function HSVToRGB(h, s, v) { | |||
var r, g, b; | |||
var i = Math.floor(h * 6); | |||
var f = h * 6 - i; | |||
var p = v * (1 - s); | |||
var q = v * (1 - f * s); | |||
var t = v * (1 - (1 - f) * s); | |||
switch (i % 6) { | |||
case 0: r = v, g = t, b = p; break; | |||
case 1: r = q, g = v, b = p; break; | |||
case 2: r = p, g = v, b = t; break; | |||
case 3: r = p, g = q, b = v; break; | |||
case 4: r = t, g = p, b = v; break; | |||
case 5: r = v, g = p, b = q; break; | |||
} | |||
return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) }; | |||
} | |||
function RGBToHSV (red,green,blue) { | |||
red=red/255; green=green/255; blue=blue/255; | |||
var minRGB = Math.min(red,Math.min(green,blue)); | |||
var maxRGB = Math.max(red,Math.max(green,blue)); | |||
// Black-gray-white | |||
if (minRGB == maxRGB) { | |||
return {h:0,s:0,v:minRGB}; | |||
} | |||
// Colors other than black-gray-white: | |||
var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red); | |||
var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5); | |||
var hue = 60*(h - d/(maxRGB - minRGB))/360; | |||
var saturation = (maxRGB - minRGB)/maxRGB; | |||
var value = maxRGB; | |||
return {h:hue,s:saturation,v:value}; | |||
} |
@ -0,0 +1,301 @@ | |||
body { | |||
font-family : Verdana, "Bitstream Vera Sans", "DejaVu Sans", Tahoma, Geneva, Arial, Sans-serif; | |||
font-size: 13px; | |||
min-width:980px; | |||
} | |||
div.group { | |||
background-color: rgba(0, 0, 0, 0.005); | |||
} | |||
/*.vis.timeline .item.background {*/ | |||
/*background-color: rgba(40, 148, 255, 0.20) !important;*/ | |||
/*z-index:9999 !important;*/ | |||
/*}*/ | |||
.vis.timeline .item.range.prediction { | |||
border: 1px solid rgba(0, 164, 255, 0.50) !important; | |||
background-color: rgba(28, 183, 255, 0.10) !important; | |||
box-shadow: rgba(0, 83, 128, 0.15) 0px 0px 10px !important; | |||
z-index:-1 !important; | |||
height:34px !important; | |||
} | |||
.vis.timeline .item.background.night { | |||
background-color: rgba(60, 60, 60, 0.3) !important; | |||
z-index:9999 !important; | |||
} | |||
.vis.timeline .labelset .timelineGroup .inner { | |||
/*height:100px !important;*/ | |||
width:140px !important; | |||
} | |||
.vis.timeline .labelset .timelineGroup.worker{ | |||
background-color: #ffffff; | |||
} | |||
.vis.timeline .labelset .timelineGroup.rao{ | |||
background-color: #f5f5f5; | |||
} | |||
.vis.timeline .labelset .timelineGroup.pm{ | |||
background-color: #dfeaf2; | |||
} | |||
.vis.timeline .labelset .timelineGroup.mt{ | |||
background-color: #b8d6e6; | |||
} | |||
.vis.timeline .item.range { | |||
height:34px !important; | |||
} | |||
.vis.timeline .item.range.pause{ | |||
height:34px !important; | |||
width:26px !important; | |||
box-shadow: rgba(0,0,0,0.2) 0px 0px 10px !important; | |||
padding:3px !important; | |||
z-index:999999; | |||
background-color: #ffffff !important | |||
} | |||
.vis.timeline .item.range.pausedArea{ | |||
height:34px !important; | |||
padding:3px !important; | |||
z-index:999950; | |||
border-width: 0px !important; | |||
background: url('../images/shaded.png'); | |||
background-color: rgba(0,0,0,0) !important; | |||
} | |||
.vis.timeline .item { | |||
border-radius: 0px !important; | |||
border-color: #7d807d !important; | |||
background-color: #ffffff !important; | |||
/*background-color: #c4e0ff !important;*/ | |||
} | |||
.vis.timeline .item.box { | |||
height:22px !important; | |||
border-radius: 0px !important; | |||
border-color: #7d807d !important; | |||
background-color: #ffffff !important; | |||
box-shadow: rgba(0, 0, 0, 0.52) 0px 0px 12px 0px; | |||
} | |||
img.icon{ | |||
width:16px; | |||
height:16px; | |||
} | |||
#multiselect { | |||
position:relative; | |||
top:1px; | |||
width:200px; | |||
height:452px; | |||
} | |||
table.wrapper { | |||
position:relative; | |||
height:500px; | |||
width:100%; | |||
} | |||
/*table.wrapper td {*/ | |||
/*border:1px solid #ff0000;*/ | |||
/*}*/ | |||
#selectTD { | |||
width:200px; | |||
} | |||
div.typeButtons { | |||
height:50px; | |||
width:100%; | |||
overflow:hidden; | |||
} | |||
div.typeButton { | |||
display:inline-block; | |||
width:150px; | |||
height:24px; | |||
border:2px solid #ffffff; | |||
border-radius: 40px; | |||
text-align:center; | |||
font-size:12px; | |||
color: #848484; | |||
padding-top:8px; | |||
background-color: #dedede; | |||
-webkit-touch-callout: none; | |||
-webkit-user-select: none; | |||
-khtml-user-select: none; | |||
-moz-user-select: none; | |||
-ms-user-select: none; | |||
user-select: none; | |||
cursor:pointer; | |||
margin:5px; | |||
box-shadow: rgba(50,50,50,0.3) 0px 0px 3px; | |||
} | |||
div.typeButton.selected { | |||
background-color: #9aff4d; | |||
color: #000000; | |||
} | |||
div.toggleButton { | |||
background-image: url("../images/toggleOff.png"); | |||
background-repeat: no-repeat; | |||
background-position: 2px 2px; | |||
display:inline-block; | |||
width:138px; | |||
height:24px; | |||
font-size:12px; | |||
border:1px solid #d0d0d0; | |||
text-align:left; | |||
padding-top:10px; | |||
padding-left:60px; | |||
padding-right:0px; | |||
border-radius: 40px; | |||
background-color: #ffffff; | |||
-webkit-touch-callout: none; | |||
-webkit-user-select: none; | |||
-khtml-user-select: none; | |||
-moz-user-select: none; | |||
-ms-user-select: none; | |||
user-select: none; | |||
cursor:pointer; | |||
} | |||
div.toggleButton.square{ | |||
height:28px; | |||
border-radius: 0px; | |||
} | |||
div.toggleButton.selected { | |||
background-image: url("../images/toggleOn.png"); | |||
background-color: #ffffff; | |||
} | |||
div.graph2dContent{ | |||
height:450px; | |||
width:100%; | |||
} | |||
#graphWrapper { | |||
display:none; | |||
border: 1px solid #dddddd; | |||
padding:5px; | |||
} | |||
#timelineWrapper { | |||
display:block; | |||
border: 1px solid #dddddd; | |||
padding:5px; | |||
} | |||
.prediction_original { | |||
fill: rgb(123, 199, 255); | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: rgb(123, 199, 255); | |||
} | |||
.prediction { | |||
fill: #77da2e; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #77da2e; | |||
} | |||
.duration { | |||
fill: rgb(170, 34, 206); | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: rgb(170, 34, 206); | |||
} | |||
.fill { | |||
fill-opacity:0.1; | |||
stroke: none; | |||
} | |||
.bar { | |||
fill-opacity:0.5; | |||
stroke-width:1px; | |||
} | |||
.point { | |||
stroke-width:2px; | |||
fill-opacity:1.0; | |||
} | |||
.legendBackground { | |||
stroke-width:1px; | |||
fill-opacity:0.9; | |||
fill: #ffffff; | |||
stroke: #c2c2c2; | |||
} | |||
.outline { | |||
stroke-width:1px; | |||
fill-opacity:1; | |||
fill: #ffffff; | |||
stroke: #e5e5e5; | |||
} | |||
.iconFill { | |||
fill-opacity:0.3; | |||
stroke: none; | |||
} | |||
div.descriptionContainer { | |||
float:left; | |||
height:30px; | |||
min-width:160px; | |||
padding-left:5px; | |||
padding-right:5px; | |||
line-height: 30px; | |||
} | |||
div.iconContainer { | |||
float:left; | |||
} | |||
div.legendElementContainer { | |||
display:inline-block; | |||
height:30px; | |||
border-style:solid; | |||
border-width:1px; | |||
border-color: #d0d0d0; | |||
background-color: #ffffff; | |||
margin-right:6px; | |||
padding:4px; | |||
-webkit-touch-callout: none; | |||
-webkit-user-select: none; | |||
-khtml-user-select: none; | |||
-moz-user-select: none; | |||
-ms-user-select: none; | |||
user-select: none; | |||
cursor:pointer; | |||
} | |||
svg.legendIcon { | |||
width:30px; | |||
height:30px; | |||
} | |||
div.externalLegend { | |||
position:relative; | |||
top:1px; | |||
width: 700px; | |||
} | |||
.differencePositive { | |||
fill: #afff33; | |||
stroke-width:2px; | |||
stroke: #96db22; | |||
} | |||
.differenceNegative { | |||
fill: #ff9f00; | |||
stroke-width:2px; | |||
stroke: #e58f00; | |||
} |
@ -0,0 +1,745 @@ | |||
.vis .overlay { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
/* Must be displayed above for example selected Timeline items */ | |||
z-index: 10; | |||
} | |||
.vis-active { | |||
box-shadow: 0 0 10px #86d5f8; | |||
} | |||
.vis.timeline { | |||
} | |||
.vis.timeline.root { | |||
position: relative; | |||
border: 1px solid #bfbfbf; | |||
overflow: hidden; | |||
padding: 0; | |||
margin: 0; | |||
box-sizing: border-box; | |||
} | |||
.vis.timeline .vispanel { | |||
position: absolute; | |||
padding: 0; | |||
margin: 0; | |||
box-sizing: border-box; | |||
} | |||
.vis.timeline .vispanel.center, | |||
.vis.timeline .vispanel.left, | |||
.vis.timeline .vispanel.right, | |||
.vis.timeline .vispanel.top, | |||
.vis.timeline .vispanel.bottom { | |||
border: 1px #bfbfbf; | |||
} | |||
.vis.timeline .vispanel.center, | |||
.vis.timeline .vispanel.left, | |||
.vis.timeline .vispanel.right { | |||
border-top-style: solid; | |||
border-bottom-style: solid; | |||
overflow: hidden; | |||
} | |||
.vis.timeline .vispanel.center, | |||
.vis.timeline .vispanel.top, | |||
.vis.timeline .vispanel.bottom { | |||
border-left-style: solid; | |||
border-right-style: solid; | |||
} | |||
.vis.timeline .background { | |||
overflow: hidden; | |||
} | |||
.vis.timeline .vispanel > .content { | |||
position: relative; | |||
} | |||
.vis.timeline .vispanel .shadow { | |||
position: absolute; | |||
width: 100%; | |||
height: 1px; | |||
box-shadow: 0 0 10px rgba(0,0,0,0.8); | |||
/* TODO: find a nice way to ensure shadows are drawn on top of items | |||
z-index: 1; | |||
*/ | |||
} | |||
.vis.timeline .vispanel .shadow.top { | |||
top: -1px; | |||
left: 0; | |||
} | |||
.vis.timeline .vispanel .shadow.bottom { | |||
bottom: -1px; | |||
left: 0; | |||
} | |||
.vis.timeline .labelset { | |||
position: relative; | |||
width: 100%; | |||
overflow: hidden; | |||
box-sizing: border-box; | |||
} | |||
.vis.timeline .labelset .vlabel { | |||
position: relative; | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
color: #4d4d4d; | |||
box-sizing: border-box; | |||
} | |||
.vis.timeline .labelset .vlabel { | |||
border-bottom: 1px solid #bfbfbf; | |||
} | |||
.vis.timeline .labelset .vlabel:last-child { | |||
border-bottom: none; | |||
} | |||
.vis.timeline .labelset .vlabel .inner { | |||
display: inline-block; | |||
padding: 5px; | |||
} | |||
.vis.timeline .labelset .vlabel .inner.hidden { | |||
padding: 0; | |||
} | |||
.vis.timeline .itemset { | |||
position: relative; | |||
padding: 0; | |||
margin: 0; | |||
box-sizing: border-box; | |||
} | |||
.vis.timeline .itemset .background, | |||
.vis.timeline .itemset .foreground { | |||
position: absolute; | |||
width: 100%; | |||
height: 100%; | |||
overflow: visible; | |||
} | |||
.vis.timeline .axis { | |||
position: absolute; | |||
width: 100%; | |||
height: 0; | |||
left: 0; | |||
z-index: 1; | |||
} | |||
.vis.timeline .foreground .group { | |||
position: relative; | |||
box-sizing: border-box; | |||
border-bottom: 1px solid #bfbfbf; | |||
} | |||
.vis.timeline .foreground .group:last-child { | |||
border-bottom: none; | |||
} | |||
.vis.timeline .item { | |||
position: absolute; | |||
color: #1A1A1A; | |||
border-color: #97B0F8; | |||
border-width: 1px; | |||
background-color: #D5DDF6; | |||
display: inline-block; | |||
padding: 5px; | |||
} | |||
.vis.timeline .item.selected { | |||
border-color: #FFC200; | |||
background-color: #FFF785; | |||
/* z-index must be higher than the z-index of custom time bar and current time bar */ | |||
z-index: 2; | |||
} | |||
.vis.timeline .editable .item.selected { | |||
cursor: move; | |||
} | |||
.vis.timeline .item.point.selected { | |||
background-color: #FFF785; | |||
} | |||
.vis.timeline .item.box { | |||
text-align: center; | |||
border-style: solid; | |||
border-radius: 2px; | |||
} | |||
.vis.timeline .item.point { | |||
background: none; | |||
} | |||
.vis.timeline .item.dot { | |||
position: absolute; | |||
padding: 0; | |||
border-width: 4px; | |||
border-style: solid; | |||
border-radius: 4px; | |||
} | |||
.vis.timeline .item.range { | |||
border-style: solid; | |||
border-radius: 2px; | |||
box-sizing: border-box; | |||
} | |||
.vis.timeline .item.background { | |||
overflow: hidden; | |||
border: none; | |||
background-color: rgba(213, 221, 246, 0.4); | |||
box-sizing: border-box; | |||
padding: 0; | |||
margin: 0; | |||
} | |||
.vis.timeline .item.range .content { | |||
position: relative; | |||
display: inline-block; | |||
overflow: hidden; | |||
max-width: 100%; | |||
} | |||
.vis.timeline .item.background .content { | |||
position: absolute; | |||
display: inline-block; | |||
overflow: hidden; | |||
max-width: 100%; | |||
margin: 5px; | |||
} | |||
.vis.timeline .item.line { | |||
padding: 0; | |||
position: absolute; | |||
width: 0; | |||
border-left-width: 1px; | |||
border-left-style: solid; | |||
} | |||
.vis.timeline .item .content { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
} | |||
.vis.timeline .item .delete { | |||
background: url('img/timeline/delete.png') no-repeat top center; | |||
position: absolute; | |||
width: 24px; | |||
height: 24px; | |||
top: 0; | |||
right: -24px; | |||
cursor: pointer; | |||
} | |||
.vis.timeline .item.range .drag-left { | |||
position: absolute; | |||
width: 24px; | |||
height: 100%; | |||
top: 0; | |||
left: -4px; | |||
cursor: w-resize; | |||
} | |||
.vis.timeline .item.range .drag-right { | |||
position: absolute; | |||
width: 24px; | |||
height: 100%; | |||
top: 0; | |||
right: -4px; | |||
cursor: e-resize; | |||
} | |||
.vis.timeline .timeaxis { | |||
position: relative; | |||
overflow: hidden; | |||
} | |||
.vis.timeline .timeaxis.foreground { | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
} | |||
.vis.timeline .timeaxis.background { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
.vis.timeline .timeaxis .text { | |||
position: absolute; | |||
color: #4d4d4d; | |||
padding: 3px; | |||
white-space: nowrap; | |||
} | |||
.vis.timeline .timeaxis .text.measure { | |||
position: absolute; | |||
padding-left: 0; | |||
padding-right: 0; | |||
margin-left: 0; | |||
margin-right: 0; | |||
visibility: hidden; | |||
} | |||
.vis.timeline .timeaxis .grid.vertical { | |||
position: absolute; | |||
width: 0; | |||
border-right: 1px solid; | |||
} | |||
.vis.timeline .timeaxis .grid.minor { | |||
border-color: #e5e5e5; | |||
} | |||
.vis.timeline .timeaxis .grid.major { | |||
border-color: #bfbfbf; | |||
} | |||
.vis.timeline .currenttime { | |||
background-color: #FF7F6E; | |||
width: 2px; | |||
z-index: 1; | |||
} | |||
.vis.timeline .customtime { | |||
background-color: #6E94FF; | |||
width: 2px; | |||
cursor: move; | |||
z-index: 1; | |||
} | |||
.vis.timeline.root { | |||
/* | |||
-webkit-transition: height .4s ease-in-out; | |||
transition: height .4s ease-in-out; | |||
*/ | |||
} | |||
.vis.timeline .vispanel { | |||
/* | |||
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out; | |||
transition: height .4s ease-in-out, top .4s ease-in-out; | |||
*/ | |||
} | |||
.vis.timeline .axis { | |||
/* | |||
-webkit-transition: top .4s ease-in-out; | |||
transition: top .4s ease-in-out; | |||
*/ | |||
} | |||
/* TODO: get animation working nicely | |||
.vis.timeline .item { | |||
-webkit-transition: top .4s ease-in-out; | |||
transition: top .4s ease-in-out; | |||
} | |||
.vis.timeline .item.line { | |||
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out; | |||
transition: height .4s ease-in-out, top .4s ease-in-out; | |||
} | |||
/**/ | |||
.vis.timeline .vispanel.background.horizontal .grid.horizontal { | |||
position: absolute; | |||
width: 100%; | |||
height: 0; | |||
border-bottom: 1px solid; | |||
} | |||
.vis.timeline .vispanel.background.horizontal .grid.minor { | |||
border-color: #e5e5e5; | |||
} | |||
.vis.timeline .vispanel.background.horizontal .grid.major { | |||
border-color: #bfbfbf; | |||
} | |||
.vis.timeline .dataaxis .yAxis.major { | |||
width: 100%; | |||
position: absolute; | |||
color: #4d4d4d; | |||
white-space: nowrap; | |||
} | |||
.vis.timeline .dataaxis .yAxis.major.measure{ | |||
padding: 0px 0px 0px 0px; | |||
margin: 0px 0px 0px 0px; | |||
visibility: hidden; | |||
width: auto; | |||
} | |||
.vis.timeline .dataaxis .yAxis.minor{ | |||
position: absolute; | |||
width: 100%; | |||
color: #bebebe; | |||
white-space: nowrap; | |||
} | |||
.vis.timeline .dataaxis .yAxis.minor.measure{ | |||
padding: 0px 0px 0px 0px; | |||
margin: 0px 0px 0px 0px; | |||
visibility: hidden; | |||
width: auto; | |||
} | |||
.vis.timeline .legend { | |||
background-color: rgba(247, 252, 255, 0.65); | |||
padding: 5px; | |||
border-color: #b3b3b3; | |||
border-style:solid; | |||
border-width: 1px; | |||
box-shadow: 2px 2px 10px rgba(154, 154, 154, 0.55); | |||
} | |||
.vis.timeline .legendText { | |||
/*font-size: 10px;*/ | |||
white-space: nowrap; | |||
display: inline-block | |||
} | |||
.vis.timeline .graphGroup0 { | |||
fill:#4f81bd; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #4f81bd; | |||
} | |||
.vis.timeline .graphGroup1 { | |||
fill:#f79646; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #f79646; | |||
} | |||
.vis.timeline .graphGroup2 { | |||
fill: #8c51cf; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #8c51cf; | |||
} | |||
.vis.timeline .graphGroup3 { | |||
fill: #75c841; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #75c841; | |||
} | |||
.vis.timeline .graphGroup4 { | |||
fill: #ff0100; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #ff0100; | |||
} | |||
.vis.timeline .graphGroup5 { | |||
fill: #37d8e6; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #37d8e6; | |||
} | |||
.vis.timeline .graphGroup6 { | |||
fill: #042662; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #042662; | |||
} | |||
.vis.timeline .graphGroup7 { | |||
fill:#00ff26; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #00ff26; | |||
} | |||
.vis.timeline .graphGroup8 { | |||
fill:#ff00ff; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #ff00ff; | |||
} | |||
.vis.timeline .graphGroup9 { | |||
fill: #8f3938; | |||
fill-opacity:0; | |||
stroke-width:2px; | |||
stroke: #8f3938; | |||
} | |||
.vis.timeline .fill { | |||
fill-opacity:0.1; | |||
stroke: none; | |||
} | |||
.vis.timeline .bar { | |||
fill-opacity:0.5; | |||
stroke-width:1px; | |||
} | |||
.vis.timeline .point { | |||
stroke-width:2px; | |||
fill-opacity:1.0; | |||
} | |||
.vis.timeline .legendBackground { | |||
stroke-width:1px; | |||
fill-opacity:0.9; | |||
fill: #ffffff; | |||
stroke: #c2c2c2; | |||
} | |||
.vis.timeline .outline { | |||
stroke-width:1px; | |||
fill-opacity:1; | |||
fill: #ffffff; | |||
stroke: #e5e5e5; | |||
} | |||
.vis.timeline .iconFill { | |||
fill-opacity:0.3; | |||
stroke: none; | |||
} | |||
div.network-manipulationDiv { | |||
border-width: 0; | |||
border-bottom: 1px; | |||
border-style:solid; | |||
border-color: #d6d9d8; | |||
background: #ffffff; /* Old browsers */ | |||
background: -moz-linear-gradient(top, #ffffff 0%, #fcfcfc 48%, #fafafa 50%, #fcfcfc 100%); /* FF3.6+ */ | |||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(48%,#fcfcfc), color-stop(50%,#fafafa), color-stop(100%,#fcfcfc)); /* Chrome,Safari4+ */ | |||
background: -webkit-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* Chrome10+,Safari5.1+ */ | |||
background: -o-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* Opera 11.10+ */ | |||
background: -ms-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* IE10+ */ | |||
background: linear-gradient(to bottom, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* W3C */ | |||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc',GradientType=0 ); /* IE6-9 */ | |||
position: absolute; | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
height: 30px; | |||
} | |||
div.network-manipulation-editMode { | |||
position:absolute; | |||
left: 0; | |||
top: 0; | |||
height: 30px; | |||
margin-top:20px; | |||
} | |||
div.network-manipulation-closeDiv { | |||
position:absolute; | |||
right: 0; | |||
top: 0; | |||
width: 30px; | |||
height: 30px; | |||
background-position: 20px 3px; | |||
background-repeat: no-repeat; | |||
background-image: url("img/network/cross.png"); | |||
cursor: pointer; | |||
-webkit-touch-callout: none; | |||
-webkit-user-select: none; | |||
-khtml-user-select: none; | |||
-moz-user-select: none; | |||
-ms-user-select: none; | |||
user-select: none; | |||
} | |||
div.network-manipulation-closeDiv:hover { | |||
opacity: 0.6; | |||
} | |||
span.network-manipulationUI { | |||
font-family: verdana; | |||
font-size: 12px; | |||
-moz-border-radius: 15px; | |||
border-radius: 15px; | |||
display:inline-block; | |||
background-position: 0px 0px; | |||
background-repeat:no-repeat; | |||
height:24px; | |||
margin: -14px 0px 0px 10px; | |||
vertical-align:middle; | |||
cursor: pointer; | |||
padding: 0px 8px 0px 8px; | |||
-webkit-touch-callout: none; | |||
-webkit-user-select: none; | |||
-khtml-user-select: none; | |||
-moz-user-select: none; | |||
-ms-user-select: none; | |||
user-select: none; | |||
} | |||
span.network-manipulationUI:hover { | |||
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.20); | |||
} | |||
span.network-manipulationUI:active { | |||
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.50); | |||
} | |||
span.network-manipulationUI.back { | |||
background-image: url("img/network/backIcon.png"); | |||
} | |||
span.network-manipulationUI.none:hover { | |||
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0); | |||
cursor: default; | |||
} | |||
span.network-manipulationUI.none:active { | |||
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0); | |||
} | |||
span.network-manipulationUI.none { | |||
padding: 0; | |||
} | |||
span.network-manipulationUI.notification{ | |||
margin: 2px; | |||
font-weight: bold; | |||
} | |||
span.network-manipulationUI.add { | |||
background-image: url("img/network/addNodeIcon.png"); | |||
} | |||
span.network-manipulationUI.edit { | |||
background-image: url("img/network/editIcon.png"); | |||
} | |||
span.network-manipulationUI.edit.editmode { | |||
background-color: #fcfcfc; | |||
border-style:solid; | |||
border-width:1px; | |||
border-color: #cccccc; | |||
} | |||
span.network-manipulationUI.connect { | |||
background-image: url("img/network/connectIcon.png"); | |||
} | |||
span.network-manipulationUI.delete { | |||
background-image: url("img/network/deleteIcon.png"); | |||
} | |||
/* top right bottom left */ | |||
span.network-manipulationLabel { | |||
margin: 0px 0px 0px 23px; | |||
line-height: 25px; | |||
} | |||
div.network-seperatorLine { | |||
display:inline-block; | |||
width:1px; | |||
height:20px; | |||
background-color: #bdbdbd; | |||
margin: 5px 7px 0px 15px; | |||
} | |||
div.network-navigation_wrapper { | |||
position: absolute; | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
div.network-navigation { | |||
width:34px; | |||
height:34px; | |||
-moz-border-radius: 17px; | |||
border-radius: 17px; | |||
position:absolute; | |||
display:inline-block; | |||
background-position: 2px 2px; | |||
background-repeat:no-repeat; | |||
cursor: pointer; | |||
-webkit-touch-callout: none; | |||
-webkit-user-select: none; | |||
-khtml-user-select: none; | |||
-moz-user-select: none; | |||
-ms-user-select: none; | |||
user-select: none; | |||
} | |||
div.network-navigation:hover { | |||
box-shadow: 0px 0px 3px 3px rgba(56, 207, 21, 0.30); | |||
} | |||
div.network-navigation:active { | |||
box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95); | |||
} | |||
div.network-navigation.up { | |||
background-image: url("img/network/upArrow.png"); | |||
bottom:50px; | |||
left:55px; | |||
} | |||
div.network-navigation.down { | |||
background-image: url("img/network/downArrow.png"); | |||
bottom:10px; | |||
left:55px; | |||
} | |||
div.network-navigation.left { | |||
background-image: url("img/network/leftArrow.png"); | |||
bottom:10px; | |||
left:15px; | |||
} | |||
div.network-navigation.right { | |||
background-image: url("img/network/rightArrow.png"); | |||
bottom:10px; | |||
left:95px; | |||
} | |||
div.network-navigation.zoomIn { | |||
background-image: url("img/network/plus.png"); | |||
bottom:10px; | |||
right:15px; | |||
} | |||
div.network-navigation.zoomOut { | |||
background-image: url("img/network/minus.png"); | |||
bottom:10px; | |||
right:55px; | |||
} | |||
div.network-navigation.zoomExtends { | |||
background-image: url("img/network/zoomExtends.png"); | |||
bottom:50px; | |||
right:15px; | |||
} |
@ -0,0 +1,86 @@ | |||
<!doctype html> | |||
<html> | |||
<head> | |||
<title>ARUM - MIDAS</title> | |||
<link href="./css/global.css" rel="stylesheet" type="text/css" /> | |||
<link href="./css/vis.css" rel="stylesheet" type="text/css" /> | |||
<script language="javascript"> | |||
var EVENT_DELAY = 1000; | |||
var EVENTS_AGENT_ADDRESS = 'ws://localhost:8082/ws/events'; | |||
var AMOUNT_OF_INITIAL_EVENTS = 1; | |||
var TIMELINE_START = '2014-09-18 10:00:00'; | |||
var TIMELINE_END = '2014-09-19 18:00:00'; | |||
var INCREASE_SPEED = true; | |||
var ONLINE_ONLY = true; | |||
</script> | |||
<script type="text/javascript" src="./js/vis.js"></script> | |||
<script type="text/javascript" src="./js/evejs.js"></script> | |||
<script type="text/javascript" src="./arumAgents/job.js"></script> | |||
<script type="text/javascript" src="./arumAgents/durationStats.js"></script> | |||
<script type="text/javascript" src="./arumAgents/durationData.js"></script> | |||
<script type="text/javascript" src="./arumAgents/jobManager.js"></script> | |||
<script type="text/javascript" src="./arumAgents/jobAgent.js"></script> | |||
<script type="text/javascript" src="./js/vis_eve_init.js"></script> | |||
<script type="text/javascript" src="./arumAgents/genericAgent.js"></script> | |||
<script type="text/javascript" src="./arumAgents/agentGenerator.js"></script> | |||
<script type="text/javascript" src="./arumAgents/jobAgentGenerator.js"></script> | |||
<script type="text/javascript" src="./arumAgents/eventGenerator.js"></script> | |||
<script type="text/javascript" src="./js/init.js"></script> | |||
</head> | |||
<body onload="draw()"> | |||
<img src="./images/arum_small.png" style="position:relative; top:-6px;"><img src="./images/midas_small.png" style="float:right;"><br> | |||
<div class="typeButton selected" onclick="showTimelineBtn()" id="showTimeline">Show by Timeline</div> | |||
<div class="typeButton" onclick="showGraphBtn()" id="showGraph">Show by Job</div> | |||
<div id="timelineWrapper"> | |||
<input type="button" value="Next Event" onclick="getNewEvent()"> | |||
eventNo:<div id="eventCounter" style="display:inline;">-1</div> | |||
<input type="button" value="Next 10 Events" onclick="getTenNewEvents()"> | |||
<input type="button" value="Get All Events" onclick="getAllEvents()"> | |||
<input type="button" value="Get All Events Quickly" onclick="getAllEventsQuickly()"> <br> | |||
<div id="timeline"></div> | |||
</div> | |||
<div id="graphWrapper"> | |||
<table class="wrapper"> | |||
<tr> | |||
<td> | |||
<div class="toggleButton" onclick="togglePrediction()" id="togglePrediction">Prediction</div> | |||
</td> | |||
<td width="6px"> </td> | |||
<td> | |||
<div class="typeButton selected" onclick="turnOn('duration')" id="duration">Ideal</div> | |||
<div class="typeButton" onclick="turnOn('durationWithPause')" id="durationWithPause">Ideal + Pause</div> | |||
<div class="typeButton" onclick="turnOn('durationWithStartup')" id="durationWithStartup">Ideal + Prerequisites</div> | |||
<div class="typeButton" onclick="turnOn('durationWithBoth')" id="durationWithBoth">Total Time</div> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td id="selectTD"> | |||
<select name="jobs" id="multiselect" multiple onclick="updateGraph()"></select> | |||
</td> | |||
<td colspan="2"> | |||
<div class="graph2dContent"> | |||
<div id="graph2d"></div> | |||
</div> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td> | |||
<div class="toggleButton square" onclick="toggleDifference()" id="difference">Switch View</div> | |||
</td> | |||
<td colspan="2"> | |||
<div id="Legend" class="externalLegend"></div> | |||
</td> | |||
</tr> | |||
</table> | |||
</div> | |||
<p id="selection"></p> | |||
<p id="stabilization"></p> | |||
</body> | |||
</html> |
@ -0,0 +1,350 @@ | |||
/** | |||
* Created by Alex on 10/27/2014. | |||
*/ | |||
var GETTING_EVENTS = false; | |||
var agentList = {}; | |||
var jobList = {}; | |||
var eventGen = new EventGenerator("eventGenerator"); | |||
var agentGen = new AgentGenerator("agentGenerator"); | |||
var jobGen = new JobAgentGenerator("jobAgentGenerator"); | |||
// eventGen.start(); | |||
function getNewEvent() { | |||
agentGen.getEvents(1); | |||
} | |||
function getTenNewEvents() { | |||
agentGen.getEvents(10); | |||
} | |||
function getAllEvents() { | |||
if (GETTING_EVENTS == false) { | |||
GETTING_EVENTS = true; | |||
agentGen.getEvents(agentGen.amountOfEvents); | |||
} | |||
} | |||
function getAllEventsQuickly() { | |||
INCREASE_SPEED = false; | |||
EVENT_DELAY = 0; | |||
if (GETTING_EVENTS == false) { | |||
GETTING_EVENTS = true; | |||
agentGen.getEvents(agentGen.amountOfEvents); | |||
} | |||
else { | |||
} | |||
} | |||
function refreshJobs() { | |||
var multiSelect = document.getElementById("multiselect"); | |||
while (multiSelect.firstChild) { | |||
multiSelect.removeChild(multiSelect.firstChild); | |||
} | |||
var jobNames = jobGen.getAllJobNames(); | |||
for (var i = 0; i < jobNames.length; i++) { | |||
var jobOption = new Option(jobNames[i], jobNames[i]); | |||
multiSelect.appendChild(jobOption); | |||
} | |||
} | |||
function updateGraph() { | |||
var multiSelect = document.getElementById("multiselect"); | |||
var selection = []; | |||
for (var i = 0; i < multiSelect.children.length; i++) { | |||
if (multiSelect.children[i].selected) { | |||
selection.push(multiSelect.children[i].value); | |||
} | |||
} | |||
var filteredValues = []; | |||
var originalPredictionValues = []; | |||
var durationValues = []; | |||
var predictionValues = []; | |||
var diffValues = []; | |||
var graphGroup = null; | |||
var stdGroup = null; | |||
var predictionGroup = null; | |||
var originalPredictionGroup = null; | |||
for (var i = 0; i < graph2dDataset.length; i++) { | |||
if (selection.indexOf(graph2dDataset[i].type) != -1) { | |||
if (showDuration == true) { | |||
if (graph2dDataset[i].group == graph2dDataset[i].type + "_" + selectedGroup) { | |||
filteredValues.push(graph2dDataset[i]); | |||
durationValues.push(graph2dDataset[i]); | |||
graphGroup = graph2dDataset[i].group; | |||
} | |||
} | |||
if (showPrediction == true) { | |||
if (graph2dDataset[i].group == graph2dDataset[i].type + "_pred_" + selectedGroup) { | |||
filteredValues.push(graph2dDataset[i]); | |||
predictionValues.push(graph2dDataset[i]); | |||
predictionGroup = graph2dDataset[i].group; | |||
} | |||
if (graph2dDataset[i].group == graph2dDataset[i].type + "_pred_" + selectedGroup + "_std_higher") { | |||
filteredValues.push(graph2dDataset[i]); | |||
stdGroup = graph2dDataset[i].group; | |||
} | |||
if (graph2dDataset[i].group == graph2dDataset[i].type + "_pred_" + selectedGroup + "_std_lower") { | |||
filteredValues.push(graph2dDataset[i]); | |||
stdGroup = graph2dDataset[i].group; | |||
} | |||
if (graph2dDataset[i].group == graph2dDataset[i].type + "_pred_" + selectedGroup + "_original") { | |||
filteredValues.push(graph2dDataset[i]); | |||
originalPredictionValues.push(graph2dDataset[i]); | |||
originalPredictionGroup = graph2dDataset[i].group; | |||
} | |||
} | |||
} | |||
} | |||
graph2DItems.clear(); | |||
if (differenceWithPrediction == true) { | |||
for (var i = 0; i < durationValues.length; i++) { | |||
var item = {}; | |||
item.x = i < 10 ? '2014-09-0' + i : '2014-09-' + i; | |||
item.y = durationValues[i].y; | |||
item.type = durationValues[i].type; | |||
item.y -= originalPredictionValues[i].y; | |||
var group = 'differenceNegative'; | |||
if (item.y < 0) { | |||
item.y *= -1; | |||
group = 'differencePositive' | |||
} | |||
item.group = group; | |||
diffValues.push(item); | |||
} | |||
var legendDiv = document.getElementById("Legend"); | |||
legendDiv.innerHTML = ""; | |||
populateExternalLegend('differencePositive', "Faster than predicted (hours)"); | |||
populateExternalLegend('differenceNegative', "Slower than predicted (hours)"); | |||
graph2DItems.add(diffValues); | |||
} | |||
else { | |||
var filteredValues = []; | |||
for (var i = 0; i < durationValues.length; i++) { | |||
var durationItem = {}; | |||
durationItem.x = i < 10 ? '2014-09-0' + i : '2014-09-' + i; | |||
durationItem.y = durationValues[i].y; | |||
durationItem.type = durationValues[i].type; | |||
durationItem.group = durationValues[i].group; | |||
filteredValues.push(durationItem); | |||
if (showPrediction == true) { | |||
var predItem = {}; | |||
predItem.x = i < 10 ? '2014-09-0' + i : '2014-09-' + i; | |||
predItem.y = predictionValues[i].y; | |||
predItem.type = predictionValues[i].type; | |||
predItem.group = predictionValues[i].group; | |||
filteredValues.push(predItem); | |||
var originalPred = {}; | |||
originalPred.x = i < 10 ? '2014-09-0' + i : '2014-09-' + i; | |||
originalPred.y = originalPredictionValues[i].y; | |||
originalPred.type = originalPredictionValues[i].type; | |||
originalPred.group = originalPredictionValues[i].group; | |||
filteredValues.push(originalPred); | |||
} | |||
} | |||
var legendDiv = document.getElementById("Legend"); | |||
legendDiv.innerHTML = ""; | |||
if (graphGroup != null) { | |||
populateExternalLegend(graphGroup, "duration (hours)"); | |||
} | |||
// if (stdGroup != null) {populateExternalLegend(stdGroup, "standard deviation");} | |||
if (predictionGroup != null) { | |||
populateExternalLegend(predictionGroup, "updated prediction (hours)"); | |||
} | |||
if (originalPredictionGroup != null) { | |||
populateExternalLegend(originalPredictionGroup, "initial prediction (hours)"); | |||
} | |||
graph2DItems.add(filteredValues); | |||
} | |||
graph2d.fit(); | |||
} | |||
function turnOff(type) { | |||
var btn = document.getElementById(type); | |||
btn.className = btn.className.replace(" selected", ""); | |||
} | |||
function turnOffAll() { | |||
var types = ['duration', 'durationWithPause', 'durationWithStartup', 'durationWithBoth']; | |||
for (var i = 0; i < types.length; i++) { | |||
turnOff(types[i]); | |||
} | |||
} | |||
function turnOn(type) { | |||
turnOffAll(); | |||
var btn = document.getElementById(type); | |||
selectedGroup = type; | |||
btn.className += " selected"; | |||
updateGraph(); | |||
} | |||
function togglePrediction() { | |||
if (differenceWithPrediction != true) { | |||
var btn = document.getElementById('togglePrediction'); | |||
if (showPrediction == true) { | |||
btn.className = btn.className.replace("selected", ""); | |||
showPrediction = false; | |||
} | |||
else { | |||
showPrediction = true; | |||
btn.className += " selected"; | |||
} | |||
updateGraph(); | |||
} | |||
} | |||
function toggleDuration() { | |||
if (differenceWithPrediction != true) { | |||
var btn = document.getElementById('toggleDuration'); | |||
if (showDuration == true) { | |||
btn.className = btn.className.replace("selected", ""); | |||
showDuration = false; | |||
} | |||
else { | |||
showDuration = true; | |||
btn.className += " selected"; | |||
} | |||
updateGraph(); | |||
} | |||
} | |||
function toggleDifference() { | |||
differenceWithPrediction = !differenceWithPrediction; | |||
var btn = document.getElementById('togglePrediction'); | |||
if (showPrediction == false && differenceWithPrediction == true) { | |||
showPrediction = true; | |||
btn.className += " selected"; | |||
} | |||
btn = document.getElementById('toggleDuration'); | |||
if (showDuration == false && differenceWithPrediction == true) { | |||
showDuration = true; | |||
btn.className += " selected"; | |||
} | |||
var btn2 = document.getElementById('difference'); | |||
if (differenceWithPrediction == false) { | |||
btn2.className = btn2.className.replace("selected", ""); | |||
} | |||
else { | |||
btn2.className += " selected"; | |||
} | |||
updateGraph(); | |||
} | |||
function showTimelineBtn() { | |||
var timelineBtn = document.getElementById('showTimeline'); | |||
var graphBtn = document.getElementById('showGraph'); | |||
if (showTimeline == false) { | |||
graphBtn.className = graphBtn.className.replace("selected", ""); | |||
timelineBtn.className += " selected"; | |||
var timelinewrapper = document.getElementById("timelineWrapper"); | |||
var graphwrapper = document.getElementById("graphWrapper"); | |||
graphwrapper.style.display = "none"; | |||
timelinewrapper.style.display = "block"; | |||
showTimeline = true; | |||
showGraph = false; | |||
} | |||
} | |||
function showGraphBtn() { | |||
var timelineBtn = document.getElementById('showTimeline'); | |||
var graphBtn = document.getElementById('showGraph'); | |||
if (showGraph == false) { | |||
timelineBtn.className = graphBtn.className.replace("selected", ""); | |||
graphBtn.className += " selected"; | |||
var timelinewrapper = document.getElementById("timelineWrapper"); | |||
var graphwrapper = document.getElementById("graphWrapper"); | |||
timelinewrapper.style.display = "none"; | |||
graphwrapper.style.display = "block"; | |||
showTimeline = false; | |||
showGraph = true; | |||
} | |||
} | |||
/** | |||
* this function fills the external legend with content using the getLegend() function. | |||
*/ | |||
function populateExternalLegend(groupDataItem, description) { | |||
var legendDiv = document.getElementById("Legend"); | |||
// create divs | |||
var containerDiv = document.createElement("div"); | |||
var iconDiv = document.createElement("div"); | |||
var descriptionDiv = document.createElement("div"); | |||
// give divs classes and Ids where necessary | |||
containerDiv.className = 'legendElementContainer'; | |||
containerDiv.id = groupDataItem + "_legendContainer"; | |||
iconDiv.className = "iconContainer"; | |||
descriptionDiv.className = "descriptionContainer"; | |||
// get the legend for this group. | |||
var legend = graph2d.getLegend(groupDataItem, 30, 30); | |||
// append class to icon. All styling classes from the vis.css have been copied over into the head here to be able to style the | |||
// icons with the same classes if they are using the default ones. | |||
legend.icon.setAttributeNS(null, "class", "legendIcon"); | |||
// append the legend to the corresponding divs | |||
iconDiv.appendChild(legend.icon); | |||
descriptionDiv.innerHTML = description; | |||
// determine the order for left and right orientation | |||
if (legend.orientation == 'left') { | |||
descriptionDiv.style.textAlign = "left"; | |||
containerDiv.appendChild(iconDiv); | |||
containerDiv.appendChild(descriptionDiv); | |||
} | |||
else { | |||
descriptionDiv.style.textAlign = "right"; | |||
containerDiv.appendChild(descriptionDiv); | |||
containerDiv.appendChild(iconDiv); | |||
} | |||
// append to the legend container div | |||
legendDiv.appendChild(containerDiv); | |||
} | |||
function printEvents(events) { | |||
var str = ""; | |||
str += "["; | |||
for (var i = 0; i < events.length; i++) { | |||
str += "{"; | |||
var first = true; | |||
for (var eventField in events[i]) { | |||
if (events[i].hasOwnProperty(eventField)) { | |||
if (first == false) { | |||
str += "," | |||
} | |||
first = false; | |||
if (eventField == "time") { | |||
str += eventField + ": '" + new Date(events[i][eventField]).valueOf() + "'"; | |||
} | |||
else if(events[i][eventField] instanceof Array) { | |||
str += eventField + ": ["; | |||
for (var j = 0; j < events[i][eventField].length; j++) { | |||
str += "'" + events[i][eventField][j] + "'"; | |||
if (j != events[i][eventField].length - 1) { | |||
str += ","; | |||
} | |||
} | |||
} | |||
else { | |||
str += eventField + ": '" + events[i][eventField] + "'"; | |||
} | |||
} | |||
} | |||
str += "}"; | |||
if (i < events.length -1) { | |||
str += ","; | |||
} | |||
} | |||
str += "]"; | |||
console.log(str); | |||
} |
@ -0,0 +1,105 @@ | |||
/** | |||
* Created by Alex on 10/27/2014. | |||
*/ | |||
'use strict'; | |||
var timeline; | |||
var timelineItems = new vis.DataSet(); | |||
var timelineGroups = new vis.DataSet(); | |||
var graph2d; | |||
var graph2dDataset = []; | |||
var graph2DItems = new vis.DataSet(); | |||
var graph2dGroups = new vis.DataSet(); | |||
graph2dGroups.add({ | |||
id: 'differencePositive', content: 'differencePositive', className: "differencePositive", options: { | |||
drawPoints: false, | |||
style: 'bar', | |||
barChart: {width: 50, align: 'center'} // align: left, center, right | |||
} | |||
}); | |||
graph2dGroups.add({ | |||
id: 'differenceNegative', content: 'differenceNegative', className: "differenceNegative", options: { | |||
drawPoints: false, | |||
style: 'bar', | |||
barChart: {width: 50, align: 'center'} // align: left, center, right | |||
} | |||
}); | |||
var eventCounter; | |||
var selectedGroup = 'duration'; | |||
var showDuration = true; | |||
var showPrediction = false; | |||
var showTimeline = true; | |||
var showGraph = false; | |||
var differenceWithPrediction = false; | |||
function draw() { | |||
eventCounter = document.getElementById('eventCounter'); | |||
// add items to the DataSet | |||
var timelineContainer = document.getElementById('timeline'); | |||
var timelineOptions = { | |||
hiddenDates: [ | |||
{start: '2013-10-26 00:00:00', end: '2013-10-28 00:00:00', repeat: 'weekly'}, // daily weekly monthly yearly | |||
{start: '2013-03-29 18:30:00', end: '2013-03-30 08:00:00', repeat: 'daily'} // daily weekly monthly yearly | |||
], | |||
start: TIMELINE_START, | |||
end: TIMELINE_END, | |||
autoResize: false, | |||
showCustomTime: true, | |||
showCurrentTime: false, | |||
stack: false | |||
}; | |||
timeline = new vis.Timeline(timelineContainer, timelineItems, timelineGroups, timelineOptions); | |||
var graph2dContainer = document.getElementById('graph2d'); | |||
var graph2dOptions = { | |||
style:'bar', | |||
barChart: {width:50, align:'center', handleOverlap:'sideBySide'}, | |||
start: '2014-08-25', | |||
end: '2014-09-25', | |||
autoResize: false, | |||
// height: '450px', | |||
showCurrentTime: false, | |||
catmullRom: false, | |||
showMajorLabels: false, | |||
showMinorLabels: false, | |||
graphHeight:'450px', | |||
dataAxis: { | |||
customRange: { | |||
left: { | |||
min:-0.5 | |||
} | |||
} | |||
}, | |||
drawPoints:false //{style:'circle'} | |||
}; | |||
graph2d = new vis.Graph2d(graph2dContainer, graph2DItems, graph2dGroups, graph2dOptions); | |||
} | |||
window.onresize = function () { | |||
timeline.redraw(); | |||
graph2d.redraw(); | |||
} | |||
var conn; | |||
if (ONLINE_ONLY == true) { | |||
eve.system.init({ | |||
transports: [ | |||
{ | |||
type: 'local' | |||
} | |||
] | |||
}); | |||
} | |||
else { | |||
eve.system.init({ | |||
transports: [ | |||
{ | |||
type: 'ws' | |||
} | |||
] | |||
}); | |||
} | |||