<commit_before>var ws = require('ws');
var https = require('https');

function debuglog(msg) {
  if (ModeDevice.debug) {
    console.log(msg);
  }
}

var defaultEventErrorCallback = function(error) {
  debuglog('Event Error: ' + error);
};

var defaultErrorCallback = function(error) {
  debuglog('Error: ' + error);
  this.scheduleReconnect(false);
};

var defaultCloseCallback = function(code, message) {
  debuglog('Connection is closed: ' + code + ' ' + message);
  if (this.pingTimer != null) {
    clearInterval(this.pingTimer);
  }
  // reconnect
  this.scheduleReconnect(false);
};

var defaultOpenCallback = function() {
  debuglog('Websocket client is connected');
  this.retryWait = 1;
  // start pinging
  var pingHandler = function() {
    debuglog('Sending a websocket ping');
    this.triggerPing();
  };

  this.pingTimer = setInterval(pingHandler.bind(this), 20 * 1000);
};

var defaultCommandCallback = function(message, flags) {
  debuglog('Received JSON message: "' + message + '"');
  debuglog('Received flag: "' + JSON.stringify(flags) + '"');
};

var defaultPongCallback = function() {
  debuglog('Received a websocket pong');
};

var commandHandler = function(message, flags) {
  debuglog('Received a command');
  debuglog('Raw message: "' + message + '"');
  var commandJson = JSON.parse(message);
  this.commandCallback(commandJson, flags);
};

var ModeDevice = function(deviceId, token) {
  this.debug = false;
  this.token = token;
  this.deviceId = deviceId;
  this.retryWait = 1;  // retry wait in msec
  this.retryWaitFib = 1;  // retry wait in msec
  this.timeout = 30 * 1000; // request timeout in msec
  this.websocket = null;
  this.eventCounter = 0;

  this.host = 'api.tinkermode.com';
  this.port = 443;  // default to wss.

  this.eventErrorCallback = defaultEventErrorCallback;
  this.errorCallback = defaultErrorCallback;
  this.closeCallback = defaultCloseCallback;
  this.openCallback = defaultOpenCallback;
  this.commandCallback = defaultCommandCallback;
  this.pongCallback = defaultPongCallback;
  this.eventFinishedCallback = defaultEventFinishedCallback;
  this.pingTimer = null;
};

ModeDevice.prototype.reconnect = function() {
  debuglog('Reconnecting websocket');
  if (this.websocket != null) {
    debuglog('Closing websocket');
    this.websocket.close();
    this.websocket = null;
  }
  var target = 'wss://' + this.host + ':' + this.port + '/devices/' + this.deviceId + '/command';
  debuglog("Connecting to " + target);
  this.websocket = new ws(target, {
    headers: {
      "Authorization": 'ModeCloud ' + this.token
    }
  });
  this.websocket.on('error', this.errorCallback.bind(this));
  this.websocket.on('close', this.closeCallback.bind(this));
  this.websocket.on('open', this.openCallback.bind(this));
  this.websocket.on('message', commandHandler.bind(this));
  this.websocket.on('pong', this.pongCallback.bind(this));
};

ModeDevice.prototype.scheduleReconnect = function(firstConnect) {
  var wait = firstConnect ? 0 : this.retryWait;
  debuglog('Reconnect websocket in ' + wait + ' seconds');
  var device = this;
  setTimeout(function() {
    device.reconnect();
  }, wait * 1000);

  // Only if less than 60 sec, we increment waiting time according to Fibonacci numbers.
  if (this.retryWait < 60) {
    var fib = this.retryWaitFib;
    this.retryWaitFib = this.retryWait;
    this.retryWait += fib;
  }
};

ModeDevice.prototype.listenCommands = function() {
  this.scheduleReconnect(true);
};

ModeDevice.prototype.triggerPing = function() {
  this.websocket.ping();
};

var defaultEventFinishedCallback = function() {
};

ModeDevice.prototype.triggerEvent = function(eventType, eventData) {
  this.eventCounter += 1;
  var eventId = this.eventCounter;
  debuglog('Triggering event #' + eventId);

  if((typeof eventType) != "string" && !(eventType instanceof String)) {
    throw "eventType must be string";
  }

  if((typeof eventData) != "object" || (eventData instanceof Array)) {
    throw "eventData must be object";
  }

  var event = {
    "eventType": eventType,
    "eventData": eventData
  };

  var jsonData = JSON.stringify(event);

  var options = {
    host: this.host,
    port: this.port,
    path: '/devices/' + this.deviceId + '/event',
    method: 'PUT',
    headers: {<commit_msg>Limit number of outstanding requests under unreliable network environment (deal with packet losses).<commit_after>var ws = require('ws');
var https = require('https');

function debuglog(msg) {
  if (ModeDevice.debug) {
    console.log(msg);
  }
}

var defaultEventErrorCallback = function(error) {
  debuglog('Event Error: ' + error);
};

var defaultErrorCallback = function(error) {
  debuglog('Error: ' + error);
  this.scheduleReconnect(false);
};

var defaultCloseCallback = function(code, message) {
  debuglog('Connection is closed: ' + code + ' ' + message);
  if (this.pingTimer != null) {
    clearInterval(this.pingTimer);
  }
  // reconnect
  this.scheduleReconnect(false);
};

var defaultOpenCallback = function() {
  debuglog('Websocket client is connected');
  this.retryWait = 1;
  // start pinging
  var pingHandler = function() {
    debuglog('Sending a websocket ping');
    this.triggerPing();
  };

  this.pingTimer = setInterval(pingHandler.bind(this), 20 * 1000);
};

var defaultCommandCallback = function(message, flags) {
  debuglog('Received JSON message: "' + message + '"');
  debuglog('Received flag: "' + JSON.stringify(flags) + '"');
};

var defaultPongCallback = function() {
  debuglog('Received a websocket pong');
};

var commandHandler = function(message, flags) {
  debuglog('Received a command');
  debuglog('Raw message: "' + message + '"');
  var commandJson = JSON.parse(message);
  this.commandCallback(commandJson, flags);
};

var ModeDevice = function(deviceId, token) {
  this.debug = false;
  this.token = token;
  this.deviceId = deviceId;
  this.retryWait = 1;  // retry wait in msec
  this.retryWaitFib = 1;  // retry wait in msec
  this.timeout = 10 * 1000; // request timeout in msec
  this.maxRequests = 10;  // number of simultaneous requests it will process
  this.websocket = null;
  this.eventCounter = 0;  // sequence id for events

  this.host = 'api.tinkermode.com';
  this.port = 443;  // default to wss.

  this.eventErrorCallback = defaultEventErrorCallback;
  this.errorCallback = defaultErrorCallback;
  this.closeCallback = defaultCloseCallback;
  this.openCallback = defaultOpenCallback;
  this.commandCallback = defaultCommandCallback;
  this.pongCallback = defaultPongCallback;
  this.eventFinishedCallback = defaultEventFinishedCallback;
  this.pingTimer = null;
  // Increase the number of sockets for slow networks.
  https.globalAgent.maxSockets = 20;  // originally 5.
};

ModeDevice.prototype.reconnect = function() {
  debuglog('Reconnecting websocket');
  if (this.websocket != null) {
    debuglog('Closing websocket');
    this.websocket.close();
    this.websocket = null;
  }
  var target = 'wss://' + this.host + ':' + this.port + '/devices/' + this.deviceId + '/command';
  debuglog("Connecting to " + target);
  this.websocket = new ws(target, {
    headers: {
      "Authorization": 'ModeCloud ' + this.token
    }
  });
  this.websocket.on('error', this.errorCallback.bind(this));
  this.websocket.on('close', this.closeCallback.bind(this));
  this.websocket.on('open', this.openCallback.bind(this));
  this.websocket.on('message', commandHandler.bind(this));
  this.websocket.on('pong', this.pongCallback.bind(this));
};

ModeDevice.prototype.scheduleReconnect = function(firstConnect) {
  var wait = firstConnect ? 0 : this.retryWait;
  debuglog('Reconnect websocket in ' + wait + ' seconds');
  var device = this;
  setTimeout(function() {
    device.reconnect();
  }, wait * 1000);

  // Only if less than 60 sec, we increment waiting time according to Fibonacci numbers.
  if (this.retryWait < 60) {
    var fib = this.retryWaitFib;
    this.retryWaitFib = this.retryWait;
    this.retryWait += fib;
  }
};

ModeDevice.prototype.listenCommands = function() {
  this.scheduleReconnect(true);
};

ModeDevice.prototype.triggerPing = function() {
  this.websocket.ping();
};

var defaultEventFinishedCallback = function() {
};

ModeDevice.prototype.triggerEvent = function(eventType, eventData) {
  this.eventCounter += 1;
  var eventId = this.eventCounter;
  debuglog('Triggering event #' + eventId);

  if((typeof eventType) != "string" && !(eventType instanceof String)) {
    throw "eventType must be string";
  }

  if((typeof eventData) != "object" || (eventData instanceof Array)) {
    throw "eventData must be object";
  }

  // Try not to pile up requests when the network is unstable.
  var outstandingRequests = https.globalAgent.requests;
  var hostPort = this.host + ':' + this.port;
  if (outstandingRequests[hostPort] !== undefined) {
    if (outstandingRequests[hostPort].length >= this.maxRequests) {
      // If there are enough requests queued up, it doesn't attempt to issue a request.
      var msg = 'Too many outstanding requests:' + outstandingRequests[hostPort].length;
      debuglog(msg);
      this.eventErrorCallback(msg);
      return;
    }
  }

  var event = {
    "eventType": eventType,
    "eventData": eventData
  };

  var jsonData = JSON.stringify(event);

  var options = {
    host: this.host,
    port: this.port,
    path: '/devices/' + this.deviceId + '/event',
    method: 'PUT',
    headers: {
{'fraction_non_alphanumeric': 0.1394087622288388, 'fraction_numerical': 0.0066992769034453425, 'mean_word_length': 4.0213561131874, 'pattern_counts': {'":': 6, '<': 5, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6549077', 'n_tokens_mistral': 2992, 'n_tokens_neox': 2903, 'n_words': 679}