blob: 11bca97d75e43be1f3a64da91cd4b57ba9eadc32 [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* @fileoverview Manager class to represent and hold state for each instance.
* of a shill manager for a given log being processed.
*/
/**
* @constructor
*
* @param {String} id Identifier for new manager instance.
* @param {long} ms Manager start time in ms.
* @param {long} offset Manager log time offset in ms.
* */
function Manager(id, ms, offset) {
this.id = id;
this.startTime = ms;
this.services = [];
this.scans = 0;
this.scanDetails = [];
this.endTime = -1;
this.suspendDetails = [];
this.connFromSuspend = [];
this.timeOffset = offset;
this.supplicantStates = [];
}
/**
* This method is responsible for getting a service instance for the supplied
* service id. If a matching instance is not found, a new service is created
* and added to the services array.
*
* @param {String} serviceId A service identifier.
* @return {Service} Service instance for the supplied identifier.
*/
Manager.prototype.getService = function(serviceId) {
console.log('getting service ' + serviceId + ' from manager! ' , this);
var services = this.services;
for (var i = 0; i < services.length; i++) {
if (services[i].serviceId == serviceId) {
return services[i];
}
}
var newService = new Service(serviceId);
this.services.push(newService);
console.log('now the manager is: ' , this);
return newService;
};
/**
* This menthod is responsible for getting a service instance for the current
* active service. Returns null if an active service is not found.
*
*
* @return {Service} Service instance for the active service.
*/
Manager.prototype.getActiveService = function() {
console.log('getting active service from manager! ' , this);
var services = this.services;
for (var i = 0; i < services.length; i++) {
if (services[i].isActive) {
return services[i];
}
}
return null;
};
/**
* This method adds detailed dark suspend information. When possible, the
* start time of the event is recorded. Event done messages are used
* to record the completion of an event.
*
* @param {boolean} eventDone Boolean denoting if an event completed.
* @param {long} time event detail time in ms from start of this manager.
*/
Manager.prototype.addDarkSuspendDetails = function(eventDone, time) {
var e;
var events = this.suspendDetails;
if (eventDone) {
e = {'end': time};
} else {
e = {'start': time};
}
console.log('addDarkSuspendDetails: ' + eventDone + ' ' + time);
if (events.length == 0) {
// first suspend event, add new suspend event first
events.push({'start': null});
}
var suspendEvent = events[events.length - 1];
if (suspendEvent.end != null) {
// current suspend event is finished, start a new one
console.log('ERROR: adding dark resume detail to finished suspend');
} else {
if (suspendEvent.darkSuspends == null) {
suspendEvent.darkSuspends = [];
suspendEvent.darkSuspends.push(e);
} else {
var dsCount = suspendEvent.darkSuspends.length;
var lastDS = suspendEvent.darkSuspends[dsCount - 1];
if (lastDS.end == null && eventDone) {
// dark suspend is done, add to last entry
lastDS.end = time;
} else if (lastDS.end != null && !eventDone) {
// new dark suspend event
suspendEvent.darkSuspends.push(e);
} else if (lastDS.end == null && !eventDone) {
// error case: new dark suspend event with previous unfinished
console.log('ERROR: new darkSuspend event before previous completed');
} else {
//error case: dark suspned completion without new start
console.log('ERROR: completed darkSuspend without starting event');
}
}
}
};
/**
* This method adds detailed event information. When possible, the
* start time of the event is recorded. Event done messages are used
* to record the completion of an event.
*
* @param {String} eventType Denotes which event type.
* @param {boolean} eventDone Boolean denoting if an event completed.
* @param {long} time event detail time in ms from start of this manager.
*/
Manager.prototype.addEventDetail = function(eventType, eventDone, time) {
console.log('adding ' + eventType + ' details');
var events;
if (eventType == 'suspend') {
events = this.suspendDetails;
} else if (eventType == 'darksuspend') {
this.addDarkSuspendDetails(eventDone, time);
return;
} else if (eventType == 'scan') {
events = this.scanDetails;
} else {
console.log('unsupported event type: ' + eventType);
return;
}
// add logic for tracking connection time after suspend
if ((eventType == 'suspend' || eventType == 'darksuspend') && eventDone) {
this.connFromSuspend.push({'suspendDone': time, 'conn': null});
}
var eventCount = events.length;
var lastEvent = null;
if (eventDone) {
if (eventCount > 0) {
// we can append the next time
lastEvent = events[eventCount - 1];
if (lastEvent.end != null) {
// last event already has an end time... create new entry
events.push({'end': time});
} else {
lastEvent.end = time;
}
} else {
// create and add a new event
events.push({'end': time});
}
} else {
// this is a new event starting
events.push({'start': time});
}
};
/**
* This method adds supplicant state transitions.
*
* @param {Update} update Object holding previous state, new state and time.
*/
Manager.prototype.addSupplicantState = function(update) {
if (update.time == null) {
this.supplicantStates.push({'x': 0, 'y': update.newState});
return;
}
if (update.oldState == update.newState) {
// not a new state transition... still check if we have anything stored
if (this.supplicantStates.length > 0) {
// not a new transition... return
return;
}
this.supplicantStates.push({'x': 0, 'y': update.oldState});
return;
}
var updateElapsedTime = Date.parse(update.time) - this.startTime;
// now we know the states are different, we need to process this update
if (this.supplicantStates.length > 0) {
// should check states to make sure they match
var lastUpdate = this.supplicantStates[this.supplicantStates.length - 1];
if (lastUpdate.y != update.oldState) {
this.supplicantStates.push({'x': updateElapsedTime,
'y': lastUpdate.y});
this.supplicantStates.push({'x': updateElapsedTime,
'y': update.oldState});
}
} else {
// need to enter previous state from start of log
this.supplicantStates.push({'x': 0, 'y': update.oldState});
}
this.supplicantStates.push({'x': updateElapsedTime,
'y': update.oldState});
this.supplicantStates.push({'x': updateElapsedTime,
'y': update.newState});
};