跳转至

网页版串口助手的开发

Demo:https://serial.wiki-power.com/
项目仓库:linyuxuanlin/Serial_on_Web

本项目是在 web-serial-terminal 的基础上增添 debugout.js,实现打印日志及导出的功能。以下是详细的步骤:

添加 debug.js 代码

// save all the console.logs
function debugout() {
  var self = this;

  // OPTIONS
  self.realTimeLoggingOn = true; // log in real time (forwards to console.log)
  self.useTimestamps = false; // insert a timestamp in front of each log
  self.useLocalStorage = false; // store the output using window.localStorage() and continuously add to the same log each session
  self.recordLogs = true; // set to false after you're done debugging to avoid the log eating up memory
  self.autoTrim = true; // to avoid the log eating up potentially endless memory
  self.maxLines = 2500; // if autoTrim is true, this many most recent lines are saved
  self.tailNumLines = 100; // how many lines tail() will retrieve
  self.logFilename = "log.txt"; // filename of log downloaded with downloadLog()
  self.maxDepth = 25; // max recursion depth for logged objects

  // vars
  self.depth = 0;
  self.parentSizes = [0];
  self.currentResult = "";
  self.startTime = new Date();
  self.output = "";

  this.version = function () {
    return "0.5.0";
  };

  /*
        USER METHODS
    */
  this.getLog = function () {
    var retrievalTime = new Date();
    // if recording is off, so dev knows why they don't have any logs
    if (!self.recordLogs) {
      self.log("[debugout.js] log recording is off.");
    }
    // if using local storage, get values
    if (self.useLocalStorage) {
      var saved = window.localStorage.getItem("debugout.js");
      if (saved) {
        saved = JSON.parse(saved);
        self.startTime = new Date(saved.startTime);
        self.output = saved.log;
        retrievalTime = new Date(saved.lastLog);
      }
    }
    return (
      self.output +
      "\n---- Log retrieved: " +
      retrievalTime +
      " ----\n" +
      self.formatSessionDuration(self.startTime, retrievalTime)
    );
  };
  // accepts optional number or uses the default for number of lines
  this.tail = function (numLines) {
    var numLines = numLines || self.tailLines;
    return self.trimLog(self.getLog(), numLines);
  };
  // accepts a string to search for
  this.search = function (string) {
    var lines = self.output.split("\n");
    var rgx = new RegExp(string);
    var matched = [];
    // can't use a simple Array.prototype.filter() here
    // because we need to add the line number
    for (var i = 0; i < lines.length; i++) {
      var addr = "[" + i + "] ";
      if (lines[i].match(rgx)) {
        matched.push(addr + lines[i]);
      }
    }
    var result = matched.join("\n");
    if (result.length == 0) result = 'Nothing found for "' + string + '".';
    return result;
  };
  // accepts the starting line and how many lines after the starting line you want
  this.getSlice = function (lineNumber, numLines) {
    var lines = self.output.split("\n");
    var segment = lines.slice(lineNumber, lineNumber + numLines);
    return segment.join("\n");
  };
  // immediately downloads the log - for desktop browser use
  this.downloadLog = function () {
    var file = "data:text/plain;charset=utf-8,";
    var logFile = self.getLog();
    var encoded = encodeURIComponent(logFile);
    file += encoded;
    var a = document.createElement("a");
    a.href = file;
    a.target = "_blank";
    a.download = self.logFilename;
    document.body.appendChild(a);
    a.click();
    a.remove();
  };
  // clears the log
  this.clear = function () {
    var clearTime = new Date();
    self.output = "---- Log cleared: " + clearTime + " ----\n";
    if (self.useLocalStorage) {
      // local storage
      var saveObject = {
        startTime: self.startTime,
        log: self.output,
        lastLog: clearTime,
      };
      saveObject = JSON.stringify(saveObject);
      window.localStorage.setItem("debugout.js", saveObject);
    }
    if (self.realTimeLoggingOn) console.log("[debugout.js] clear()");
  };
  // records a log
  this.log = function (obj) {
    // log in real time
    if (self.realTimeLoggingOn) console.log(obj);
    // record log
    var type = self.determineType(obj);
    if (type != null && self.recordLogs) {
      var addition = self.formatType(type, obj);
      // timestamp, formatted for brevity
      if (self.useTimestamps) {
        var logTime = new Date();
        self.output += self.formatTimestamp(logTime);
      }
      self.output += addition;
      if (self.autoTrim) self.output = self.trimLog(self.output, self.maxLines);
      // local storage
      if (self.useLocalStorage) {
        var last = new Date();
        var saveObject = {
          startTime: self.startTime,
          log: self.output,
          lastLog: last,
        };
        saveObject = JSON.stringify(saveObject);
        window.localStorage.setItem("debugout.js", saveObject);
      }
    }
    self.depth = 0;
    self.parentSizes = [0];
    self.currentResult = "";
  };
  /*
        METHODS FOR CONSTRUCTING THE LOG
    */

  // like typeof but classifies objects of type 'object'
  // kept separate from formatType() so you can use at your convenience!
  this.determineType = function (object) {
    if (object != null) {
      var typeResult;
      var type = typeof object;
      if (type == "object") {
        var len = object.length;
        if (len == null) {
          if (typeof object.getTime == "function") {
            typeResult = "Date";
          } else if (typeof object.test == "function") {
            typeResult = "RegExp";
          } else {
            typeResult = "Object";
          }
        } else {
          typeResult = "Array";
        }
      } else {
        typeResult = type;
      }
      return typeResult;
    } else {
      return null;
    }
  };
  // format type accordingly, recursively if necessary
  this.formatType = function (type, obj) {
    if (self.maxDepth && self.depth >= self.maxDepth) {
      return "... (max-depth reached)";
    }

    switch (type) {
      case "Object":
        self.currentResult += "{\n";
        self.depth++;
        self.parentSizes.push(self.objectSize(obj));
        var i = 0;
        for (var prop in obj) {
          self.currentResult += self.indentsForDepth(self.depth);
          self.currentResult += prop + ": ";
          var subtype = self.determineType(obj[prop]);
          var subresult = self.formatType(subtype, obj[prop]);
          if (subresult) {
            self.currentResult += subresult;
            if (i != self.parentSizes[self.depth] - 1)
              self.currentResult += ",";
            self.currentResult += "\n";
          } else {
            if (i != self.parentSizes[self.depth] - 1)
              self.currentResult += ",";
            self.currentResult += "\n";
          }
          i++;
        }
        self.depth--;
        self.parentSizes.pop();
        self.currentResult += self.indentsForDepth(self.depth);
        self.currentResult += "}";
        if (self.depth == 0) return self.currentResult;
        break;
      case "Array":
        self.currentResult += "[";
        self.depth++;
        self.parentSizes.push(obj.length);
        for (var i = 0; i < obj.length; i++) {
          var subtype = self.determineType(obj[i]);
          if (subtype == "Object" || subtype == "Array")
            self.currentResult += "\n" + self.indentsForDepth(self.depth);
          var subresult = self.formatType(subtype, obj[i]);
          if (subresult) {
            self.currentResult += subresult;
            if (i != self.parentSizes[self.depth] - 1)
              self.currentResult += ", ";
            if (subtype == "Array") self.currentResult += "\n";
          } else {
            if (i != self.parentSizes[self.depth] - 1)
              self.currentResult += ", ";
            if (subtype != "Object") self.currentResult += "\n";
            else if (i == self.parentSizes[self.depth] - 1)
              self.currentResult += "\n";
          }
        }
        self.depth--;
        self.parentSizes.pop();
        self.currentResult += "]";
        if (self.depth == 0) return self.currentResult;
        break;
      case "function":
        obj += "";
        var lines = obj.split("\n");
        for (var i = 0; i < lines.length; i++) {
          if (lines[i].match(/\}/)) self.depth--;
          self.currentResult += self.indentsForDepth(self.depth);
          if (lines[i].match(/\{/)) self.depth++;
          self.currentResult += lines[i] + "\n";
        }
        return self.currentResult;
        break;
      case "RegExp":
        return "/" + obj.source + "/";
        break;
      case "Date":
      case "string":
        if (self.depth > 0 || obj.length == 0) {
          return '"' + obj + '"';
        } else {
          return obj;
        }
      case "boolean":
        if (obj) return "true";
        else return "false";
      case "number":
        return obj + "";
        break;
    }
  };
  this.indentsForDepth = function (depth) {
    var str = "";
    for (var i = 0; i < depth; i++) {
      str += "\t";
    }
    return str;
  };
  this.trimLog = function (log, maxLines) {
    var lines = log.split("\n");
    if (lines.length > maxLines) {
      lines = lines.slice(lines.length - maxLines);
    }
    return lines.join("\n");
  };
  this.lines = function () {
    return self.output.split("\n").length;
  };
  // calculate testing time
  this.formatSessionDuration = function (startTime, endTime) {
    var msec = endTime - startTime;
    var hh = Math.floor(msec / 1000 / 60 / 60);
    var hrs = ("0" + hh).slice(-2);
    msec -= hh * 1000 * 60 * 60;
    var mm = Math.floor(msec / 1000 / 60);
    var mins = ("0" + mm).slice(-2);
    msec -= mm * 1000 * 60;
    var ss = Math.floor(msec / 1000);
    var secs = ("0" + ss).slice(-2);
    msec -= ss * 1000;
    return "---- Session duration: " + hrs + ":" + mins + ":" + secs + " ----";
  };
  this.formatTimestamp = function (timestamp) {
    var year = timestamp.getFullYear();
    var date = timestamp.getDate();
    var month = ("0" + (timestamp.getMonth() + 1)).slice(-2);
    var hrs = Number(timestamp.getHours());
    var mins = ("0" + timestamp.getMinutes()).slice(-2);
    var secs = ("0" + timestamp.getSeconds()).slice(-2);
    return (
      "[" +
      year +
      "-" +
      month +
      "-" +
      date +
      " " +
      hrs +
      ":" +
      mins +
      ":" +
      secs +
      "]: "
    );
  };
  this.objectSize = function (obj) {
    var size = 0,
      key;
    for (key in obj) {
      if (obj.hasOwnProperty(key)) size++;
    }
    return size;
  };

  /*
        START/RESUME LOG
    */
  if (self.useLocalStorage) {
    var saved = window.localStorage.getItem("debugout.js");
    if (saved) {
      saved = JSON.parse(saved);
      self.output = saved.log;
      var start = new Date(saved.startTime);
      var end = new Date(saved.lastLog);
      self.output += "\n---- Session end: " + saved.lastLog + " ----\n";
      self.output += self.formatSessionDuration(start, end);
      self.output += "\n\n";
    }
  }
  self.output += "---- Session started: " + self.startTime + " ----\n\n";
}

添加下载日志的按钮

<button id="SerialConnectButton" type="button" disabled>Connect</button> 语句后加上:

<button onclick="bugout.downloadLog();">下载日志</button>

调用对象

<script> 中加入:

var bugout = new debugout();

输出日志

term.write(chunk); 后加入:

bugout.log(chunk);

参考与致谢

原文地址:https://wiki-power.com/
本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。