| // 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}); |
| }; |