module.exports = internals.Server = function (/* host, port, options */) { // all optional var self = this; Utils.assert(this.constructor === internals.Server, 'Server must be instantiated using new'); // Register as event emitter Events.EventEmitter.call(this); // Validate arguments Utils.assert(arguments.length <= 3, 'Too many arguments'); var argMap = { string: 'host', number: 'port', object: 'options' }; var args = {}; for (var a = 0, al = arguments.length; a < al; ++a) { var type = typeof arguments[a]; var key = argMap[type]; Utils.assert(key, 'Bad server constructor arguments: no match for arg type ' + type); Utils.assert(!args[key], 'Bad server constructor arguments: duplicated arg type: ' + type); args[key] = arguments[a]; } // Set basic configuration this._started = false; this.settings = Utils.applyToDefaults(Defaults.server, args.options); this.settings.host = args.host ? args.host.toLowerCase() : '0.0.0.0'; this.settings.port = typeof args.port !== 'undefined' ? args.port : (this.settings.tls ? 443 : 80); if (this.settings.port) { this.settings.nickname = this.settings.host + ':' + this.settings.port; this.settings.uri = (this.settings.tls ? 'https://' : 'http://') + this.settings.host + ':' + this.settings.port; } // Extensions this._ext = { // The following extension functions use the following signature: // function (request, next) { next(); } onRequest: null, // New request, before handing over to the router (allows changes to the request method, url, etc.) onPreHandler: null, // After validation and body parsing, before route handler onPostHandler: null, // After route handler returns, before sending response onPostRoute: null // After response sent }; // Set optional configuration // false -> null, true -> defaults, {} -> override defaults this.settings.cors = Utils.applyToDefaults(Defaults.cors, this.settings.cors); this.settings.monitor = Utils.applyToDefaults(Defaults.monitor, this.settings.monitor); this.settings.debug = Utils.applyToDefaults(Defaults.debug, this.settings.debug); this.settings.docs = Utils.applyToDefaults(Defaults.docs, this.settings.docs); // Initialize process monitoring if (this.settings.monitor) { if (!this.settings.monitor.subscribers) { this.settings.monitor.subscribers = { console: ['ops', 'request', 'log'] }; } this._monitor = new Log.Monitor(this); } // Create routing table Utils.assert(!this.settings.router.routeDefaults || !this.settings.router.routeDefaults.handler, 'Route defaults cannot include a handler'); this._router = { table: {} // Array per HTTP method, including * for catch-all }; this._router.notFound = new Route({ method: 'notFound', path: '/{p*}', config: { auth: { mode: 'none' }, // In case defaults are set otherwise handler: 'notFound' } }, this); // Plugin interface (pack) this._pack = null; this.plugins = {}; // Loaded plugins by plugin name this.api = {}; // Registered plugin APIs by plugin name // Initialize Views if (this.settings.views) { this.views = new Views(this.settings.views); } // Create server if (this.settings.tls) { this.listener = Https.createServer(this.settings.tls, this._dispatch()); } else { this.listener = Http.createServer(this._dispatch()); } // Helpers registry this.helpers = []; // State management this._stateDefinitions = {}; // Generate CORS headers if (this.settings.cors) { this.settings.cors._origin = (this.settings.cors.origin || []).join(' '); this.settings.cors._headers = (this.settings.cors.headers || []).concat(this.settings.cors.additionalHeaders || []).join(', '); this.settings.cors._methods = (this.settings.cors.methods || []).concat(this.settings.cors.additionalMethods || []).join(', '); var optionsConfig = { path: '/{p*}', method: 'options', config: { auth: { mode: 'none' }, // In case defaults are set otherwise handler: function (request) { request.reply({}); } } }; this._router.cors = new Route(optionsConfig, this); } // Initialize cache engine if (this.settings.cache) { this.cache = new Catbox.Client(this.settings.cache); } // Authentication if (this.settings.auth) { this.auth = new Auth(this, this.settings.auth); } // Setup debug endpoint if (this.settings.debug) { this._debugConsole = new Helmet(this.settings.debug); var debugMarkup = this._debugConsole.getMarkup(); this.route({ method: 'GET', path: this.settings.debug.debugEndpoint, config: { auth: { mode: 'none' }, // In case defaults are set otherwise handler: function (request) { request.reply(debugMarkup); } } }); } // Setup batch endpoint if (this.settings.batch) { this.settings.batch = Utils.applyToDefaults(Defaults.batch, (typeof this.settings.batch === 'boolean' ? {} : this.settings.batch)); this.route({ method: 'POST', path: this.settings.batch.batchEndpoint, config: Batch.config }); } return this; };
function Route () { if (!(this instanceof Route)) return new Route(); events.EventEmitter.call(this); }
function Importer () { events.EventEmitter.call(this); }
function FakeServer(options) { EventEmitter.call(this); this._server = null; this._connections = []; }
function Formatter() { EventEmitter.call(this); }
/** * @public * @constructor * @memberof log.logger * @since 1.0.0 * * Represents a logger. * * @param {LoggerSettings} [settings] - the settings to be used to configure the logger. */ export default function Logger (settings) { EventEmitter.call(this); this.configure(settings); }
function User(data) { this.data = data; this.on('user:save', sendMail); this.on('error', sendError); EventEmitter.call(this); };
var Base = module.exports = function Base(args, options) { events.EventEmitter.call(this); if (!Array.isArray(args)) { options = args; args = []; } this.options = options || {}; this._args = args || []; this._options = {}; this._arguments = []; this._hooks = []; this._composedWith = []; this._transformStreams = []; this.option('help', { alias: 'h', desc: 'Print the generator\'s options and usage' }); this.option('skip-cache', { type: Boolean, desc: 'Do not remember prompt answers', default: false }); this.option('skip-install', { type: Boolean, desc: 'Do not automatically install dependencies', default: false }); // checks required paramaters assert(this.options.env, 'You must provide the environment object. Use env#create() to create a new generator.'); assert(this.options.resolved, 'You must provide the resolved path value. Use env#create() to create a new generator.'); this.env = this.options.env; this.resolved = this.options.resolved; // Ensure the environment support features this yeoman-generator version require. require('yeoman-environment').enforceUpdate(this.env); this.description = this.description || ''; this.async = function () { return function () {}; }; this.fs = FileEditor.create(this.env.sharedFs); this.conflicter = new Conflicter(this.env.adapter, this.options.force); // Mirror the adapter log method on the generator. // // example: // this.log('foo'); // this.log.error('bar'); this.log = this.env.adapter.log; // determine the app root var rootPath = findUp.sync('.yo-rc.json', { cwd: this.env.cwd }); rootPath = rootPath ? path.dirname(rootPath) : this.env.cwd; if (rootPath !== this.env.cwd) { this.log([ '', 'Just found a `.yo-rc.json` in a parent directory.', 'Setting the project root at: ' + rootPath ].join('\n')); this.destinationRoot(rootPath); } var deprecatedFileUtils = deprecate.log.bind(null, [ '#src() and #dest() are deprecated. Please read the documentation to learn about', 'the new ways of handling files. http://yeoman.io/authoring/file-system.html' ].join('\n')); Object.defineProperty(this, 'src', { get: deprecatedFileUtils }); Object.defineProperty(this, 'dest', { get: deprecatedFileUtils }); Object.defineProperty(this, '_', { get: deprecate.log.bind(null, [ '#_ is deprecated. Require your own version of', chalk.cyan('Lodash'), 'or', chalk.cyan('underscore.string') ].join(' ')) }); this.appname = this.determineAppname(); this.config = this._getStorage(); this._globalConfig = this._getGlobalStorage(); // ensure source/destination path, can be configured from subclasses this.sourceRoot(path.join(path.dirname(this.resolved), 'templates')); // Only instantiate the Gruntfile API when requested Object.defineProperty(this, 'gruntfile', { get: function () { if (!this.env.gruntfile) { var gruntfile = ''; var gruntPath = this.destinationPath('Gruntfile.js'); if (this.fs.exists(gruntPath)) { gruntfile = this.fs.read(gruntPath); } this.env.gruntfile = new GruntfileEditor(gruntfile); } // Schedule the creation/update of the Gruntfile this.env.runLoop.add('writing', function (done) { this.fs.write( this.destinationPath('Gruntfile.js'), this.env.gruntfile.toString() ); done(); }.bind(this), { once: 'gruntfile:write' }); return this.env.gruntfile; } }); };
var ViewEngine = exports.ViewEngine = function() { events.EventEmitter.call(this); };
// Channel // ======= // Channels can receive message from one or multiple Chakras it is connected to and // send messages to the Chakra in order to have the message transmitted over other // Channels connected to the Chakra. // A `config` object can be used to configure the Channel and will be passed to the // `configure` function. function Channel(config) { EventEmitter.call(this); // Event emitter for message to separate message from the Channels events this.messages = new Vortex(); }
var Connection = function(hostname, username, password, dbname, port) { events.EventEmitter.call(this); this.protocol = undefined; this.active = false; this.connect_parameter = Array.prototype.slice.call(arguments); }
function Web() { EventEmitter.call(this); }
exports = module.exports = internals.Connection = function (server, options) { const now = Date.now(); Events.EventEmitter.call(this); this.settings = options; // options cloned in server.connection() this.server = server; // Normalize settings this.settings.labels = Hoek.unique(this.settings.labels || []); // Remove duplicates if (this.settings.port === undefined) { this.settings.port = 0; } this.type = (typeof this.settings.port === 'string' ? 'socket' : 'tcp'); if (this.type === 'socket') { this.settings.port = (this.settings.port.indexOf('/') !== -1 ? Path.resolve(this.settings.port) : this.settings.port.toLowerCase()); } if (this.settings.autoListen === undefined) { this.settings.autoListen = true; } Hoek.assert(this.settings.autoListen || !this.settings.port, 'Cannot specify port when autoListen is false'); Hoek.assert(this.settings.autoListen || !this.settings.address, 'Cannot specify address when autoListen is false'); // Connection facilities this._started = false; this._connections = {}; this._onConnection = null; // Used to remove event listener on stop this.registrations = {}; // Tracks plugin for dependency validation { name -> { version } } this._extensions = { onRequest: new Ext(this.server), onPreAuth: new Ext(this.server), onPostAuth: new Ext(this.server), onPreHandler: new Ext(this.server), onPostHandler: new Ext(this.server), onPreResponse: new Ext(this.server) }; this._requestCounter = { value: internals.counter.min, min: internals.counter.min, max: internals.counter.max }; this._load = server._heavy.policy(this.settings.load); this.states = new Statehood.Definitions(this.settings.state); this.auth = new Auth(this); this._router = new Call.Router(this.settings.router); this._defaultRoutes(); this.plugins = {}; // Registered plugin APIs by plugin name this.app = {}; // Place for application-specific state without conflicts with hapi, should not be used by plugins // Create listener this.listener = this.settings.listener || (this.settings.tls ? Https.createServer(this.settings.tls) : Http.createServer()); this.listener.on('request', this._dispatch()); this._init(); this.listener.on('clientError', (err, socket) => { this.server._log(['connection', 'client', 'error'], err); }); // Connection information this.info = { created: now, started: 0, host: this.settings.host || Os.hostname() || 'localhost', port: this.settings.port, protocol: this.type === 'tcp' ? (this.settings.tls ? 'https' : 'http') : this.type, id: Os.hostname() + ':' + process.pid + ':' + now.toString(36) }; this.info.uri = (this.settings.uri || (this.info.protocol + ':' + (this.type === 'tcp' ? '//' + this.info.host + (this.info.port ? ':' + this.info.port : '') : this.info.port))); this.on('route', Cors.options); };
var JqueryFileUploadMiddleware = function () { EventEmitter.call(this); // setting default options this.options = this.prepareOptions({}); };
function Client(opts) { events.EventEmitter.call(this); this.parseOptions(opts); this.initState(); this.createConnection(); }
var moduleFunction = function(args) { events.EventEmitter.call(this); this.forceEvent = forceEvent; this.args = args; qtools.validateProperties({ subject: args, targetScope: this, //will add listed items to targetScope propList: [ { name: 'version', optional: false }, { name: 'name', optional: false } ] }); var self = this, forceEvent = function(eventName, outData) { this.emit(eventName, { eventName: eventName, data: outData }); }; //LOCAL FUNCTIONS ==================================== var extractFields = function(inString) { return inString.replace(/\/$/, '').split('/'); } var checkQuery1 = function(callback) { var newQuery = self.queryInfo; for (var i = 0, len = newQuery.length; i < len; i++) { var element = newQuery[i].name; if (self.validSegmentNames.indexOf(element) == -1) { if (typeof (outList) == 'undefined') { var outList = []; } outList.push(element); } } if (typeof (outList) == 'undefined') { var badSequence = false; for (var i = 0, len = newQuery.length; i < len; i++) { var element = newQuery[i].name, comparison = self.validSegmentNames[i]; if (element != comparison) { badSequence = true; } } if (badSequence) { callback({ data: { message: 'Bad URI. Segments are out of sequence. Use: ' + self.validSegmentNames.join('/value/') + '/value', evidence: self.originalQueryString }, meta: qtools.getMetaData() }, ''); } else { callback('', {}); } } else { callback({ data: { message: 'Bad segment(s) in URI ('+outList.join(' ')+')', evidence: outList }, meta: qtools.getMetaData() }, ''); } } //PARSERS ==================================== this.parsers = {}; this.parsers.uff = {}; this.parsers.uff['1.0'] = function(uriPath) { /* Note to self: fileAccessor, the thing that uses the result of this, loops over the queryInfo, assuming it's in the right order (after validating with checkQuery()). That means that the sequence in validSegmentNames is significant. I think that I should make it so that fileAccessor grabs this stuff and sequences it to match its file system layout before it builds a path. That means that the URI probably should not care what order the values arrive in. */ self.validSegmentNames = ['districts', 'schools', 'segments']; //the sequence is important self.originalQueryString = uriPath; var queryInfo = []; var list = extractFields(uriPath); for (var i = 0, len = list.length; i < len; i = i + 2) { var operand = list[i + 1]; queryInfo.push({ name: list[i], value: operand ? operand : 'list' }); if (!operand) { break; } } this.queryInfo = queryInfo; self.checkQuery=checkQuery1; //ps, the client uses this to check the query when it has fabricated the callback self.accessorList={}; self.accessorList.file='fileAccessor'; self.accessorList.mssql='mssqlAccessor'; return queryInfo; } //METHODS AND PROPERTIES ==================================== this.parse = self.parsers[self.name][self.version]; //INITIALIZATION ==================================== return this; };
var Server = function(options) { var self = this; // Add event listener EventEmitter.call(this); // BSON Parser, ensure we have a single instance if(bsonInstance == null) { bsonInstance = new BSON(bsonTypes); } // Reconnect retries var reconnectTries = options.reconnectTries || 30; // Keeps all the internal state of the server this.s = { // Options options: options // Contains all the callbacks , callbacks: new Callbacks() // Logger , logger: Logger('Server', options) // Server state , state: DISCONNECTED // Reconnect option , reconnect: typeof options.reconnect == 'boolean' ? options.reconnect : true , reconnectTries: reconnectTries , reconnectInterval: options.reconnectInterval || 1000 // Swallow or emit errors , emitError: typeof options.emitError == 'boolean' ? options.emitError : false // Current state , currentReconnectRetry: reconnectTries // Contains the ismaster , ismaster: null // Contains any alternate strategies for picking , readPreferenceStrategies: options.readPreferenceStrategies // Auth providers , authProviders: options.authProviders || {} // Server instance id , id: serverId++ // Grouping tag used for debugging purposes , tag: options.tag // Do we have a not connected handler , disconnectHandler: options.disconnectHandler // wireProtocolHandler methods , wireProtocolHandler: options.wireProtocolHandler || new PreTwoSixWireProtocolSupport() // Factory overrides , Cursor: options.cursorFactory || BasicCursor // BSON Parser, ensure we have a single instance , bsonInstance: bsonInstance // Pick the right bson parser , bson: options.bson ? options.bson : bsonInstance // Internal connection pool , pool: null // Server details , serverDetails: { host: options.host , port: options.port , name: options.port ? f("%s:%s", options.host, options.port) : options.host } } // Reference state var s = this.s; // Add bson parser to options options.bson = s.bson; // Set error properties getProperty(this, 'name', 'name', s.serverDetails, {}); getProperty(this, 'bson', 'bson', s.options, {}); getProperty(this, 'wireProtocolHandler', 'wireProtocolHandler', s.options, {}); getSingleProperty(this, 'id', s.id); // Add auth providers this.addAuthProvider('mongocr', new MongoCR()); this.addAuthProvider('x509', new X509()); this.addAuthProvider('plain', new Plain()); this.addAuthProvider('gssapi', new GSSAPI()); this.addAuthProvider('sspi', new SSPI()); this.addAuthProvider('scram-sha-1', new ScramSHA1()); }
var Server = function() { EventEmitter.call(this); this._socket = null; this._connections = {}; };
let Process = function ( id ) { EventEmitter.call( this ); this.pid = id; };
function Request(uri, options) { events.EventEmitter.call(this); this.url = url.parse(uri); this.options = options; this.headers = { 'Accept': '*/*', 'User-Agent': 'Restler for node.js', 'Host': this.url.host }; if (zlib) { this.headers['Accept-Encoding'] = 'gzip, deflate'; } mixin(this.headers, options.headers || {}); // set port and method defaults if (!this.url.port) this.url.port = (this.url.protocol == 'https:') ? '443' : '80'; if (!this.options.method) this.options.method = (this.options.data) ? 'POST' : 'GET'; if (typeof this.options.followRedirects == 'undefined') this.options.followRedirects = true; // stringify query given in options of not given in URL if (this.options.query && !this.url.query) { if (typeof this.options.query == 'object') this.url.query = qs.stringify(this.options.query); else this.url.query = this.options.query; } this._applyBasicAuth(); if (this.options.multipart) { this.headers['Content-Type'] = 'multipart/form-data; boundary=' + multipart.defaultBoundary; var multipart_size = multipart.sizeOf(this.options.data, multipart.defaultBoundary); if (typeof multipart_size === 'number' && multipart_size === multipart_size) { this.headers['Content-Length'] = multipart_size; } else { console.log("Building multipart request without Content-Length header, please specify all file sizes"); } } else { if (typeof this.options.data == 'object') { this.options.data = qs.stringify(this.options.data); this.headers['Content-Type'] = 'application/x-www-form-urlencoded'; this.headers['Content-Length'] = this.options.data.length; } if(typeof this.options.data == 'string') { var buffer = new Buffer(this.options.data, this.options.encoding || 'utf8'); this.options.data = buffer; this.headers['Content-Length'] = buffer.length; } } var proto = (this.url.protocol == 'https:') ? https : http; this.request = proto.request({ host: this.url.hostname, port: this.url.port, path: this._fullPath(), method: this.options.method, headers: this.headers }); this._makeRequest(); }
var Fluke289 = function() { // Init the EventEmitter events.EventEmitter.call(this); ///////// // Public variables ///////// this.name = "fluke28x"; ///////// // Private variables ///////// // We have a lot of internal variables to manage // the link protocol: // Session layer. Whenever a command is sent, the DMM // replies first with a response code ('ACK' below) then // the actual response if needed. var state = { idle: 0, // session is idle wait_ack: 1, // command send, expecting ACK code wait_response: 2, // ACK received, waiting for command response error: 3 }; // Low level parsing of incoming binary data var protostate = { stx:0, // Looking for 'STX' sequence etx: 1, // Looking for 'ETX' sequence }; // Link layer protocol: state of the link layer var linkstate = { closed:0, // Link closed wantconnect: 1, // Link open request is sent by computer open: 2 // Link open }; // Tables that contain the mapping of various fields in binary structures: var mapReadingID = [], mapUnit = [], mapState = [], mapAttribute = [], mapRecordType = [], mapIsStableFlag = [], mapPrimFunction = [], mapSecFunction = [], mapAutoRange = [], mapBolt = [], mapMode = [], // We keep track of the last sent command for which we are // expecting a response, to enable parsing of the response: pendingCommand = "", pendingCommandArgument = "", noReplyCommands = [ "LEDT", "PRESS", "MPQ", "SAVNAME", "MP" ], // Some commands don't send a reply except the ACK code // We manage a command queue on the link, as well as a // command timeout: commandQueue = [], timeoutTimer = null, // See 'state' above. This is session level currentState = 0; // Pointer to the serial port & socket, since we need to handle it directly for // some protocol link layer operations and command queue management var port = null, uidrequested = false, instrumentid = null, isopen = false, recording = false, // to call the main app in case we need to record readings streaming = false, livePoller = null, port_close_requested = false, self = this, // Link state handling currentLinkstate = 0, // see linkstate above currentStatusByte = 0x00, // TODO: fully clarify how this one works... // Binary buffer handling: currentProtoState= 0, // see protostate above inputBuffer = new Buffer(2048), // meter never sends more than 1024 bytes anyway ibIdx =0, // Special handling of the bitmap download case: we get the whole // bitmap in several calls, so the variable below stores the parts tmpBitmap = [], tmpBitmapIndex = 0; ///////// // Private methods ///////// var status = function(stat) { debug('Port status change', stat); isopen = stat.portopen; if (isopen) { // Should run any "onOpen" initialization routine here if // necessary. } else { // We remove the listener so that the serial port can be GC'ed if (port_close_requested) { port.removeListener('status', status); port_close_requested = false; } } }; // How the device is connected on the serial port var portSettings = function() { return { baudRate: 115200, dataBits: 8, parity: 'none', stopBits: 1, flowControl: false, parser: serialport.parsers.raw, } }; var resetState = function() { currentLinkstate = 0; currentStatusByte = 0x00; currentProtoState = 0; ibIdx = 0; tmpBitmapIndex = 0; commandQueue = []; currentState = 0; pendingCommand = ""; }; var sendData = function(data) { if (data) { self.emit('data',data); } }; // Returns starting index of 0x10 0x02 var sync = function(buffer, maxIndex) { for (var i= 0; i < maxIndex-1; i++) { if (buffer[i] == 0x10 && buffer[i+1] == 0x10) i += 2; if (buffer[i] == 0x10 && buffer[i+1] == 0x02) { return i; } } return -1; }; var etx = function(buffer, maxIndex) { for (var i= 0; i < maxIndex-3; i++) { if (buffer[i] == 0x10 && buffer[i+1] == 0x10) i += 2; if (buffer[i] == 0x10 && buffer[i+1] == 0x03) { return i+4; // Include CRC } } return -1; }; // Unescapes character 0x10: var unescape = function(buffer) { var readIdx = 0; var writeIdx = 0; var tmpBuffer = new Buffer(buffer.length); while (readIdx < buffer.length) { tmpBuffer[writeIdx] = buffer[readIdx]; if (buffer[readIdx] == 0x10) readIdx++; writeIdx++; readIdx++; } // Now generate a recut buffer of the right size: var retBuffer = new Buffer(writeIdx); tmpBuffer.copy(retBuffer,0,0,writeIdx); return retBuffer; }; var queryMeasurementFull = function() { self.output("QDDA"); // Query battery level every minute if (new Date().getSeconds() == 0) { self.output("QBL"); } }; // Link layer protocol management: receives raw data from // the serial port, saves it and as soon as a complete data packet // is received, forward it to the upper layer. // // data is a buffer var format = function(data) { if (data) { // we sometimes get called without data, to further process the // existing buffer // First of all, append the incoming data to our input buffer: debug("LLP: Received new serial data, appended at index " + ibIdx); data.copy(inputBuffer,ibIdx); ibIdx += data.length; } var start=-1, stop=-1; if (currentProtoState == protostate.stx) { start = sync(inputBuffer, ibIdx); debug("Found STX: " + start); if (start > -1) { currentProtoState = protostate.etx; // Realign our buffer (we can copy over overlapping regions): inputBuffer.copy(inputBuffer,0,start); ibIdx -= start; } else { return; } } if (currentProtoState == protostate.etx) { stop = etx(inputBuffer, ibIdx); debug("Found ETX: " + stop); currentProtoState = protostate.stx; } if (stop == -1) { // We are receiving a packet but have not reached the end yet return; } // We have reached the end of the packet: copy the packet to a new buffer // for processing, and realign the input buffer: var controlByte = inputBuffer[2]; debug("Control byte: " + controlByte.toString(16)); // Check for control byte value: // I was not able to fully understand the logic of this byte... switch(controlByte) { case 0x05: // CRC Error debug("LLP: CRC Error on packet coming from computer"); break; case 0x07: // Response to link open request debug("LLP: Link open"); currentLinkstate = linkstate.open; // Now that our link is open, request a few basic infos that // we will need to decode binary logs: commandQueue.push("QEMAP readingID"); commandQueue.push("QEMAP primFunction"); commandQueue.push("QEMAP secFunction"); commandQueue.push("QEMAP autoRange"); commandQueue.push("QEMAP state"); commandQueue.push("QEMAP unit"); commandQueue.push("QEMAP attribute"); commandQueue.push("QEMAP recordType"); commandQueue.push("QEMAP isStableFlag"); commandQueue.push("QEMAP bolt"); commandQueue.push("QEMAP mode"); break; case 0x0b: // Error (?) debug("LLP: Link closed - error (resetting LLP)"); resetState(); // Reset our state, we have lost our sync... currentLinkstate = linkstate.closed; break; case 0x01: // Command reception acknowledge case 0x41: debug("LLP: Command ACK received"); break; case 0x20: // Need to send an Acknowledge // Send packet with 0x21 // - just send the prepackaged data... debug("LLP: Sending ACK"); try {port.write(Buffer("10022110032138","hex")); }catch (err) { debug("Error on serial port while writing : " + err); } currentStatusByte = 0x40; break; case 0x60: // Send packet with 0x61 // - just send the prepackaged data... debug("LLP: Sending ACK"); try { port.write(Buffer("1002611003573e","hex")); }catch (err) { debug("Error on serial port while writing : " + err); } currentStatusByte = 0x00; break; } var response = ''; // Process the packet if it contains a payload if (stop > 7) { var escapedPacket = new Buffer(stop-7); inputBuffer.copy(escapedPacket,0,3,stop-4); // One last thing before the packet is ready: 0x10 is escaped, // so we need to replace any instance of 0x10 0x10 in the buffer // by a single 0x10 var packet = unescape(escapedPacket); debug("LLP: New packet ready:"); debug(packet); response = processPacket(packet); } else if (controlByte == 0x07) { response = processPacket(); } inputBuffer.copy(inputBuffer,0,stop); ibIdx -= stop; if (ibIdx > stop) { sendData(response); format(); // We still have data to process, so we call ourselves recursively return; } sendData(response); }; // processPacket is called by format once a packet is received, for actual // processing and sending over the socket.io pipe var processPacket = function(buffer) { debug('Fluke 289 - Packet received - execting it to be a response to ' + pendingCommand); if (timeoutTimer) { // Disarm watchdog clearTimeout(timeoutTimer); timeoutTimer = null; } // We process in two stages: // 1. Check the response code // 2. Parse the response data // Response data parsing is split in two: // 2.1 If expected response is binary, parse it // 2.2 If expected response is ASCII, parse it var response = {}; if (currentState == state.wait_ack) { // Get the response code from the buffer: in case the response // is binary, data[1] as a string is not what we want, but we'll // address this in time var data = buffer.toString().split('\x0d'); switch (data[0]) { case "0": // OK // Some commands don't return anything else than an ACK, identify them here // for performance reasons (skips all the processing below...) if (noReplyCommands.indexOf(pendingCommand) != -1) { currentState = state.idle; } else { currentState = state.wait_response; } break; case "1": // Syntax Error currentState = state.error; break; case "2": // Execution Error currentState = state.error; break; case "5": // No data available currentState = state.idle; break; default: currentState = state.error; break; } if (currentState == state.error) { currentState = state.idle; response = { "error":true }; } else { response = { "error": false}; } } if (currentState == state.wait_response) { var commandProcessed = false; ////////////////// // First, process binary replies ////////////////// switch(pendingCommand) { case "QLCDBM": commandProcessed = true; // The meter only returns data in chunks of 1024 bytes // so we need to request two QLCDBM commands to get // everything debug("Processing screenshot part 1"); tmpBitmap.push(buffer); // Find start of data (after #0) var idx = 0; while(idx < buffer.length) { if (buffer[idx]==0x23 && buffer[idx+1] == 0x30) break; idx++; } tmpBitmapIndex += buffer.length-(idx+2); debug("Received " + buffer.length + " bytes of Bitmap data"); // if we got a full buffer (1024 bytes), then we are not at the end // of our bitmap if (buffer.length == 1024) { debug("Requesting more bitmap data"); // Bitmap processing is asynchronous... commandQueue.push("QLCDBM " + tmpBitmapIndex); } else { // Got less than a full buffer, this means we have the // complete bitmap: processBitmap(); debug("Bitmap processing requested"); } break; case "QSMR": commandProcessed = true; // Query Saved Measurement Reading debug(Hexdump.dump(buffer.toString('binary'))); break; case "QRSI": commandProcessed = true; // Query Recording Summary Information commandProcessed = true; response = processRecordingSummary(buffer); response.recordingID = pendingCommandArgument; break; case "QSRR": commandProcessed = true; // Query Saved Recording Record (??? :) ) response = processRecordingEntry(buffer); response.recordingID = pendingCommandArgument; break; case "QMMSI": commandProcessed = true; response = processMinMaxRecording(buffer); response.minmaxRecordingID = pendingCommandArgument; debug(Hexdump.dump(buffer.toString('binary'))); break; case "QPSI": commandProcessed = true; debug(Hexdump.dump(buffer.toString('binary'))); break; default: commandProcessed = false; break; } ////////////////// // Then process ASCII replies ////////////////// if (!commandProcessed && ! (data[1] == undefined)) { // Below are ASCII replies, so it's time to // do the split on CSV fields: var fields = data[1].split(','); switch (pendingCommand) { case "ID": // Short Identification of meter response.model = fields[0]; response.version = fields[1]; response.serial = fields[2]; break; case "IM": // Long identification of meter response.model = fields[0]; response.version = fields[1]; response.serial = fields[2] response.mspversion = fields[3]; response.buildbranch = fields[4]; response.buildrevision = fields[5]; response.boardid = fields[6]; break; case "QM": // Query Measurement: READING_VALUE, UNIT, STATE, ATTRIBUTE response.value = Number(fields[0]); response.unit = fields[1]; response.readingState = fields[2]; response.attribute = fields[3]; break; case "QCCV": // Calibration counter response.calcounter = data[1]; break; case "QCVN": // Calibration version response.calversion = data[1]; break; case "QBL": // Query battery life response.battery = data[1]; break; case "QMPQ": // Query Meter asset properties // Remove single quotes: data[1] = data[1].replace(/'/g,''); switch(pendingCommandArgument) { case 'operator': response.operator = data[1]; break; case 'company': response.company = data[1]; break; case 'site': response.site = data[1]; break; case 'contact': response.contact = data[1]; break; } break; case "QMEMLEVEL": response.memlevel = data[1]; break; case "QSN": response.serial = data[1]; if (uidrequested) { debug("Sending uniqueID message"); self.emit('data', {uniqueID: data[1]}); uidrequested = false; } break; case "QSAVNAME": response.savname = { id: pendingCommandArgument, value:data[1] }; break; case "QSLS": // TODO: confirm this ? response.savedlogs = { record: fields[0], // This is a log session minmax: fields[1], peak: fields[2], measurement: fields[3] }; break; case "QDDA": // Extended version of meter value reading response = processQDDA(data[1]); response.error = false; break; case "QEMAP": // Property mapping response = processEmap(data[1]); response.error = false; break; default: // We don't know what we received, just // pass it on: response.raw = data[1]; break; } } if (data[1] == undefined) { debug("WARNING: no return value for " + pendingCommand + ", you should add it to the noReplyCommands list"); } currentState = state.idle; } // If we have more commands in the queue, now is the time to process them if (commandQueue.length && currentState == state.idle) { var cmd = commandQueue.pop(); debug("Command queue: dequeuing command " + cmd ); self.output(cmd); } debug("Sending response "); debug(response); // TODO: move sending on socket in here return response; }; // Sends back a JSON-formatted structure describing the complete meter reading. // This is also what can/should be recorded in logs // { primaryFunction: , // secondaryFunction: , // rangeData: { autoRangeState:, // baseUnit: , // rangeNumber: , // rangeMultiplier: // }, // lightningBold: boolean, // minMaxStartTime: number of milliseconds since January 1 1970 // measurementModes: [ 'measurementMode' ], // Array of measurement modes, can be empty // readingData: [ // reading1 // ... // readingN // ], // } var processQDDA = function(data) { var fields = data.split(','); var res = {}; res.primaryFunction = fields[0]; res.secondaryFunction = fields[1]; res.rangeData = { autoRangeState: fields[2], baseUnit: fields[3], rangeNumber: Number(fields[4]), rangeMultiplier: Number(fields[5]) }; res.lightningBolt = (fields[6] == "OFF") ? false: true; res.minMaxStartTime = fields[7]*1000; res.measurementModes = []; var i = 0; while (i < fields[8]) { res.measurementModes.push(fields[9+i++]); } // Now decode the readings: var numberOfReadings = fields[9+i]; res.readings = []; var j = 0; while (j < numberOfReadings) { res.readings.push(decodeReading(fields.slice(10+i+j*9, 19+i+j*9))); j++; } return { reading:res }; }; var decodeReading = function(reading) { var res = {}; res.readingID = reading[0]; res.readingValue = Number(reading[1]); res.baseUnit = reading[2]; res.unitMultiplier = Number(reading[3]); res.decimalPlaces = Number(reading[4]); res.displayDigits = Number(reading[5]); res.readingState = reading[6]; res.readingAttribute = reading[7]; res.timeStamp = reading[8]*1000; return res; }; var waitTimeout = function() { debug("Timeout waiting for command response"); sendData({error:true}); currentState = state.idle; // We timed out waiting for a command, process the next one in the queue if there is one if (commandQueue.length) { var cmd = commandQueue.pop(); debug("Command queue: dequeuing command " + cmd ); self.output(cmd); } }; // We now have a gzipped BMP contained in those two buffers // we need to decompress it, turn it into a structure that is intelligible // to a browser. var processBitmap = function() { var bmBuffers = []; // First of all, we need to remove the remaining framing: for (var i=0; i < tmpBitmap.length; i++) { // Find start of data (after #0) var idx = 0; while(idx < tmpBitmap[i].length) { if (tmpBitmap[i][idx]== 0x23 && tmpBitmap[i][idx+1] == 0x30) break; idx++; } var bmBuffer = new Buffer(tmpBitmap[i].length-(idx+2)); tmpBitmap[i].copy(bmBuffer,0,idx+2); bmBuffers.push(bmBuffer); //this.debug("GZipped data buffer length is " + bmBuffer.length); //this.debug(Hexdump.dump(bmBuffer2.toString("binary"))); } // Flush our temp buffer tmpBitmap = []; tmpBitmapIndex = 0; // Now assemble buffers & dezip: var bmBuffer = Buffer.concat(bmBuffers); debug("Compressed bitmap data is " + bmBuffer.length + " bytes long."); //this.debug(Hexdump.dump(bmBuffer.toString("binary"))); zlib.unzip(bmBuffer, function(err, buffer) { if (!err) { // Also copy the result to a temporary file: var stream = fs.createWriteStream('screenshot.bmp'); stream.write(buffer); stream.end(); debug("Decompress successful, bitmap data length is " + buffer.length); try { // Package the BMP bitmap into an RGBA image to send // to a canvas element: var bm = new Bitmap(buffer); bm.init(); var data = bm.getData(); debug("Sending bitmap to application"); debug(data); sendData({screenshot: data, width:bm.getWidth(), height: bm.getHeight()}); } catch (err) { debug("Something went wrong during bitmap decoding, data was probably corrupted ?\n" +err); } } }); }; var syncBuffer = function(buffer) { var idx = 0; while(idx < buffer.length) { if (buffer[idx]==0x23 && buffer[idx+1] == 0x30) break; idx++; } idx += 2; // Now idx is at the start of our data: return idx; }; var processMinMaxRecording = function(buffer) { // Find start of data (after #0) var idx = syncBuffer(buffer); var summary = { address0: buffer.readUInt32LE(idx), // We use Unix timestamps for our stamps: startTime: Math.floor(decodeFloat(buffer,idx +=4)*1000), endTime: Math.floor(decodeFloat(buffer,idx += 8)*1000), }; var ret = decodeBinaryReading(buffer, idx += 8); summary.reading = ret[0]; idx = ret[1]; summary.recordingName = buffer.toString('ascii',idx); return summary; }; var processMeasurementRecording = function(buffer) { // To be implemented... }; // Decode a Trendlog recording summary var processRecordingSummary = function(buffer) { // Find start of data (after #0) var idx = syncBuffer(buffer); var summary = { address0: buffer.readUInt32LE(idx), // We use Unix timestamps for our stamps: startTime: Math.floor(decodeFloat(buffer,idx +=4)*1000), endTime: Math.floor(decodeFloat(buffer,idx += 8)*1000), interval : decodeFloat(buffer,idx +=8), evtThreshold: decodeFloat(buffer,idx +=8), recordingAddress: buffer.readUInt32LE(idx +=8), numberOfRecords: buffer.readUInt32LE(idx +=4), }; var ret = decodeBinaryReading(buffer, idx +=4); summary.reading = ret[0]; idx = ret[1]; summary.recordingName = buffer.toString('ascii',idx); debug(summary); return summary; }; // A Reading contains range info and primary/secondary functions, // then all the readingIDs. // idx needs to be the starting offset of the structure // returns an object containing the decoded reading + the updated index. var decodeBinaryReading = function(buffer, idx) { var reading = { primaryFunction: mapPrimFunction[buffer.readUInt16LE(idx)], secondaryFunction: mapSecFunction[buffer.readUInt16LE(idx += 2)] , rangeData: { autoRangeState: mapAutoRange[buffer.readUInt16LE(idx += 2)], baseUnit: mapUnit[buffer.readUInt16LE(idx += 2)], rangeNumber: decodeFloat(buffer, idx +=2), rangeMultiplier: buffer.readInt16LE(idx +=8) }, lightningBolt: mapBolt[buffer.readUInt16LE(idx +=2)], minMaxStartTime: Math.floor(decodeFloat(buffer, idx += 2)*1000), // TODO: not 100% sure about the below ! measurementMode1: mapMode[buffer.readUInt16LE(idx +=8)], measurementMode2: buffer.readUInt16LE(idx +=2), }; var numberOfReadings = buffer.readUInt16LE( idx +=2); // Now decode the readings: idx += 2; var readings = []; for (var i = 0; i < numberOfReadings; i++) { readings.push(decodeBinaryReadingId(buffer,idx)); idx += 30; } reading.readings = readings; return [reading, idx]; }; // Decode a readingId located at offset idx in the buffer var decodeBinaryReadingId = function(buffer,idx) { var reading = { readingID: mapReadingID[buffer.readUInt16LE(idx)], readingValue: decodeFloat(buffer, idx += 2), baseUnit: mapUnit[buffer.readUInt16LE(idx +=8)], unitMultiplier: buffer.readInt16LE(idx +=2), decimalPlaces: buffer.readUInt16LE(idx +=2), displayDigits: buffer.readUInt16LE(idx +=2), readingState: mapState[buffer.readUInt16LE(idx +=2)], readingAttribute: mapAttribute[buffer.readUInt16LE(idx +=2)], timeStamp: Math.floor(decodeFloat(buffer,idx+=2)*1000) }; //console.log(reading); return reading; }; var decodeFloat = function(buffer,idx) { // Unless I missed something, data is packed as 32bit little endian // integers, so the 64bit floats have to be reassembled as two separate // reversed buffers to be put back in order. Strange... var b2 = new Buffer(8); var v1 = buffer.readUInt32LE(idx+0); var v2 = buffer.readUInt32LE(idx+4); b2.writeUInt32BE(v1,0); b2.writeUInt32BE(v2,4); //console.log(b2); return b2.readDoubleBE(0); }; // Decode a Trendlog entry: var processRecordingEntry = function(buffer) { console.log(Hexdump.dump(buffer.toString('binary'))); // Find start of data (after #0) var idx = syncBuffer(buffer); var record = { startTime: Math.floor(decodeFloat(buffer,idx)*1000), endTime: Math.floor(decodeFloat(buffer,idx +=8)*1000), maxReading: decodeBinaryReadingId(buffer, idx +=8), minReading: decodeBinaryReadingId(buffer, idx +=30), averageReading: decodeBinaryReadingId(buffer, idx +=30), averageSamples: buffer.readUInt32LE(idx +=30), primaryReading: decodeBinaryReadingId(buffer, idx +=4), recordType: mapRecordType[buffer.readUInt16LE(idx +=30)], isStableFlag: mapIsStableFlag[buffer.readUInt16LE(idx +=2)], otherFlag: buffer.readUInt16LE(idx +=2), }; console.log(record); // Now package the trendlog record return { record: record}; }; // Transform a comma-separated list of props into a JSON object // and also catches any interesting proplist for our own use. var processEmap = function(data) { var fields = data.split(','); debug(fields); var emap = []; for (var i=1; i < fields.length; i++) { // Note: some prop fields have very high indexes, but... emap[fields[i++]] = fields[i]; } debug(emap); switch (pendingCommandArgument) { case "unit": mapUnit = emap; break; case "readingID": mapReadingID = emap; break; case "state": mapState = emap; break; case "attribute": mapAttribute = emap; break; case "isStableFlag": mapIsStableFlag = emap; break; case "recordType": mapRecordType = emap; break; case "primFunction": mapPrimFunction = emap; break; case "secFunction": mapSecFunction = emap; break; case "autoRange": mapAutoRange = emap; break; case "bolt": mapBolt = emap; break; case "mode": mapMode = emap; break; } return { emap : {id: pendingCommandArgument, props: emap }}; } ///////// // Public API ///////// // Creates and opens the connection to the instrument. // for all practical purposes, this is really the init method of the // driver this.openPort = function(id) { instrumentid = id; dbs.instruments.get(id, function(err,item) { port = new serialconnection(item.port, portSettings()); port.on('data', format); port.on('status', status); }); } this.closePort = function(data) { // We need to remove all listeners otherwise the serial port // will never be GC'ed this.stopLiveStream(); port.removeListener('data', format); port_close_requested = true; port.close(); } this.isOpen = function() { return isopen; } this.getInstrumentId = function(format) { return instrumentid; }; // Called when the HTML app needs a unique identifier. // this is a standardized call across all drivers. // For the Fluke, the UID is the serial number, so this is // what we will request, but we will return it inside a special // message. this.sendUniqueID = function() { debug("[fluke289] Asking for serial number for UID request"); uidrequested = true; this.output("QSN"); }; this.isStreaming = function() { return streaming; }; // period is in seconds this.startLiveStream = function(period) { if (!streaming) { debug("Starting live data stream"); this.output("QBL"); // Query battery level first livePoller = setInterval(queryMeasurementFull.bind(this), (period) ? period*1000: 1000); streaming = true; } }; this.stopLiveStream = function(period) { if (streaming) { debug("[fluke289] Stopping live data stream"); clearInterval(livePoller); this.streaming = false; } }; // output takes data and sends it to the port after // protocol encapsulation. For this driver, this is fairly // complex because we have a queue of commands, etc... this.output = function(data) { // before being able to send commands, we need to ask to open // the link by sending status byte 0x03: if (currentLinkstate == linkstate.closed) { currentLinkstate = linkstate.wantconnect; var buf = new Buffer("1002031003a28e","hex"); commandQueue.push(data); debug("Link closed: requested link open, queued command (" + data + ")"); port.write(buf); return; } if (currentLinkstate == linkstate.wantconnect) { debug("Waiting for link to open, queue command"); commandQueue.push(data); return; } if (currentState == null) currentState = state.idle; if (currentState != state.idle) { // We are working on a command, so queue this one commandQueue.push(data); debug("Waiting for command response, queuing command - " + data); return; } // We need to save the previous command name because the meter does // not echo the command we send pendingCommand = data.split(" ")[0]; pendingCommandArgument = data.split(" ")[1]; debug("Sending command " + data ); currentState = state.wait_ack; // We'll wait for 300ms for a response, otherwise we reset. timeoutTimer = setTimeout(waitTimeout, 300); var cmdToBuffer = new Buffer(data,'ascii'); // Turn our command to a buffer var tmp = new Buffer(cmdToBuffer.length+5); tmp.writeUInt16BE(0x1002,0); tmp.writeUInt8(currentStatusByte,2); cmdToBuffer.copy(tmp,3); tmp.writeUInt16BE(0x1003,tmp.length-2); var crc = crcCalc.fluke_crc(tmp); //console.log('crc: ' + crc.toString(16)); var finalBuffer = new Buffer(tmp.length+2); tmp.copy(finalBuffer,0); finalBuffer.writeUInt16LE(crc,finalBuffer.length-2); debug(finalBuffer); try { port.write(finalBuffer); } catch (err) { debug("Error on serial port while writing data : " + err); } }; }
function RedisClient(stream, options) { this.stream = stream; this.options = options = options || {}; this.connection_id = ++connection_id; this.connected = false; this.ready = false; this.connections = 0; if (this.options.socket_nodelay === undefined) { this.options.socket_nodelay = true; } this.should_buffer = false; this.command_queue_high_water = this.options.command_queue_high_water || 1000; this.command_queue_low_water = this.options.command_queue_low_water || 0; this.max_attempts = null; if (options.max_attempts && !isNaN(options.max_attempts) && options.max_attempts > 0) { this.max_attempts = +options.max_attempts; } this.command_queue = new Queue(); // holds sent commands to de-pipeline them this.offline_queue = new Queue(); // holds commands issued but not able to be sent this.commands_sent = 0; this.connect_timeout = false; if (options.connect_timeout && !isNaN(options.connect_timeout) && options.connect_timeout > 0) { this.connect_timeout = +options.connect_timeout; } this.enable_offline_queue = true; if (typeof this.options.enable_offline_queue === "boolean") { this.enable_offline_queue = this.options.enable_offline_queue; } this.retry_max_delay = null; if (options.retry_max_delay !== undefined && !isNaN(options.retry_max_delay) && options.retry_max_delay > 0) { this.retry_max_delay = options.retry_max_delay; } this.initialize_retry_vars(); this.pub_sub_mode = false; this.subscription_set = {}; this.monitoring = false; this.closing = false; this.server_info = {}; this.auth_pass = null; if (options.auth_pass !== undefined) { this.auth_pass = options.auth_pass; } this.parser_module = null; this.selected_db = null; // save the selected db here, used when reconnecting this.old_state = null; var self = this; this.stream.on("connect", function () { self.on_connect(); }); this.stream.on("data", function (buffer_from_socket) { self.on_data(buffer_from_socket); }); this.stream.on("error", function (msg) { self.on_error(msg.message); }); this.stream.on("close", function () { self.connection_gone("close"); }); this.stream.on("end", function () { self.connection_gone("end"); }); this.stream.on("drain", function () { self.should_buffer = false; self.emit("drain"); }); events.EventEmitter.call(this); }
function CheckForDatabaseCreated () { events.EventEmitter.call(this); }
function HttpClient(options) { EventEmitter.call(this); options = options || {}; this.agent = options.agent || exports.agent; this.httpsAgent = options.httpsAgent || exports.httpsAgent; }
function Decoder(options) { EventEmitter.call(this); options = options || {}; this.formatter = options.formatter || Der.defaultFormatter; }
function Messi(){ this.trophies = 0; this.lastEventAmount = 0; this.lastEventAmountFlag = 0; events.EventEmitter.call(this); }
function CarShow() { events.EventEmitter.call(this); this.seeCar = function(make){ this.emit('sawCar', make); }; }
var Pool = function(options) { // Add event listener EventEmitter.call(this); // Add the options this.options = assign({ // Host and port settings host: 'localhost', port: 27017, // Pool default max size size: 5, // socket settings connectionTimeout: 30000, socketTimeout: 360000, keepAlive: true, keepAliveInitialDelay: 300000, noDelay: true, // SSL Settings ssl: false, checkServerIdentity: true, ca: null, crl: null, cert: null, key: null, passPhrase: null, rejectUnauthorized: false, promoteLongs: true, promoteValues: true, promoteBuffers: false, // Reconnection options reconnect: true, reconnectInterval: 1000, reconnectTries: 30, // Enable domains domainsEnabled: false }, options); // console.log("=================================== pool options") // console.dir(this.options) // Identification information this.id = _id++; // Current reconnect retries this.retriesLeft = this.options.reconnectTries; this.reconnectId = null; // No bson parser passed in if(!options.bson || (options.bson && (typeof options.bson.serialize != 'function' || typeof options.bson.deserialize != 'function'))) { throw new Error("must pass in valid bson parser"); } // Logger instance this.logger = Logger('Pool', options); // Pool state this.state = DISCONNECTED; // Connections this.availableConnections = []; this.inUseConnections = []; this.connectingConnections = []; // Currently executing this.executing = false; // Operation work queue this.queue = []; // All the authProviders this.authProviders = options.authProviders || { 'mongocr': new MongoCR(options.bson), 'x509': new X509(options.bson) , 'plain': new Plain(options.bson), 'gssapi': new GSSAPI(options.bson) , 'sspi': new SSPI(options.bson), 'scram-sha-1': new ScramSHA1(options.bson) } // Contains the reconnect connection this.reconnectConnection = null; // Are we currently authenticating this.authenticating = false; this.loggingout = false; this.nonAuthenticatedConnections = []; this.authenticatingTimestamp = null; // Number of consecutive timeouts caught this.numberOfConsecutiveTimeouts = 0; // Current pool Index this.connectionIndex = 0; }
/** * @module Core */ /** * Provides the RiakConnection class. * @class RiakConnection * @constructor * @param {Object} options - the options to use. */ function RiakConnection(options) { events.EventEmitter.call(this); this.remoteAddress = options.remoteAddress; this.remotePort = options.remotePort; // This is to facilitate debugging if (!cid[this.remotePort]) { cid[this.remotePort] = 1; } this.name = util.format('[RiakConnection] (%s:%d-%d)', this.remoteAddress, this.remotePort, cid[this.remotePort]); cid[this.remotePort]++; if (options.cork) { this.cork = true; } if (options.auth) { this.auth = options.auth; this.auth.ciphers = RIAK_R16_CIPHERS; } if (options.healthCheck) { this.healthCheck = options.healthCheck; } this.connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; if (options.hasOwnProperty('connectionTimeout')) { this.connectionTimeout = options.connectionTimeout; } this.requestTimeout = DEFAULT_REQUEST_TIMEOUT; if (options.hasOwnProperty('requestTimeout')) { this.requestTimeout = options.requestTimeout; } this.maxBufferSize = DEFAULT_MAX_BUFFER; if (options.hasOwnProperty('maxBufferSize')) { this.maxBufferSize = options.maxBufferSize; } var initBufferSize = DEFAULT_INIT_BUFFER; if (options.hasOwnProperty('initBufferSize')) { initBufferSize = options.initBufferSize; } this._emitAndClose = function(evt, evt_args) { if (!this.closed) { // NB: this can be useful // logger.debug("%s emitting '%s' args '%s'", this.name, evt, evt_args); // NB: RiakNode checks inFlight to re-try command if necessary // so don't set inFlight to false here, it will be set to false in close() this.closed = true; this._connection.end(); this.emit(evt, this, evt_args); this.close(); } }; this._connHandleEnd = function () { logger.debug('%s handling "end" event', this.name); this._emitAndClose('connectionClosed'); }; this._connHandleTimeout = function (command) { var err = util.format("%s command '%s' timed out (in-flight: %s)", this.name, command.name, this.inFlight); if (logger.debug) { logger.debug(err); } this._emitAndClose('connectionClosed'); }; this._clearSocketTimeout = function() { if (this._connection) { if (this._boundConnectionTimeout) { this._connection.removeListener('timeout', this._boundConnectionTimeout); this._boundConnectionTimeout = null; } this._connection.setTimeout(0); } }; // buffer is private var buffer = null; // private buffer functions function initBuffer(data) { // Create a new buffer to receive data if needed if (buffer === null) { buffer = new ByteBuffer(initBufferSize); } buffer.append(data); buffer.flip(); } function getProtobufsFromBuffer(protobufArray) { if (arguments.length === 0) { protobufArray = []; } if (buffer.remaining() >= 4) { buffer.mark(); var messageLength = buffer.readUint32(); // See if we have the complete message if (buffer.remaining() >= messageLength) { // We have a complete message from riak var slice = buffer.slice(undefined, buffer.offset + messageLength); var code = slice.readUint8(); // Our fun API does some creative things like ... returning only // a code, with 0 bytes following. In those cases we want to set // decoded to null. var decoded = null; if (messageLength - 1 > 0) { var ResponseProto = rpb.getProtoFor(code); // GH issue #45 // Must use 'true' as argument to force copy of data // otherwise, subsequent fetches will clobber data decoded = ResponseProto.decode(slice.toBuffer(true)); } protobufArray[protobufArray.length] = { msgCode : code, protobuf : decoded }; // skip past message in buffer buffer.skip(messageLength); // recursively call this until we are out of messages return getProtobufsFromBuffer(protobufArray); } else { // rewind the offset buffer.reset(); } } // ByteBuffer's 'flip()' effectively clears the buffer which we don't // want. We want to flip while preserving anything in the buffer and // compact if necessary. var newOffset = buffer.remaining(); // Compact if necessary if (newOffset > 0 && buffer.offset !== 0) { buffer.copyTo(buffer, 0); } buffer.offset = newOffset; buffer.limit = buffer.capacity(); return protobufArray; } function closeBuffer() { if (buffer) { buffer.clear(); buffer = null; } } // protected buffer functions this._closeBuffer = function () { closeBuffer(); }; this._resetBuffer = function () { if (buffer && buffer.capacity() > this.maxBufferSize) { closeBuffer(); } }; this._buildProtobufArray = function (data) { initBuffer(data); return getProtobufsFromBuffer(); }; // protected execute functions this._executeInit = function() { this.lastUsed = Date.now(); this.executeDone(); }; this._executeStart = function(command) { this.command = command; logger.debug('%s execute command:', this.name, command.name); this.inFlight = true; this.lastUsed = Date.now(); }; this._executeInit(); this.closed = false; this._connectedEmitted = false; this._connection = new net.Socket(); if (this._connection.setKeepAlive) { this._connection.setKeepAlive(true, 0); } if (this._connection.setNoDelay) { this._connection.setNoDelay(true); } // Note: useful for debugging event issues /* debugOutputConnectionListeners(this.name, this._connection); this.setMaxListeners(1); this._connection.setMaxListeners(1); */ if (this.cork && !this._connection.cork) { logger.warn('%s wanted to use cork/uncork but not supported!', this.name); this.cork = false; } else { logger.debug('%s using cork() / uncork()', this.name); } }
// util.inherits(constructor, superConstructor) // RyStream inherits from superConstrutor function RyStream() { "use strict" events.EventEmitter.call(this) }