Одна из ниш компьютеров, которая мне всегда нравилась, — это цифровое искусство и дисплеи. Я нашел возможность сделать новый дисплей для отслеживания поступающих данных в API. Ниже я подготовил краткий отчет о том, как я преобразовывал прерывистые вызовы API в аналитический дисплей, отражающий данные в реальном времени.

Эта программа достаточно проста, чтобы ее можно было написать на одной HTML-странице со встроенным javascript. Не лучшая форма, но она выполнит работу без накладных расходов.

Общая структура программы выглядит следующим образом:

  • Получить текущую сумму данных из API аналитики
  • Подсчитайте, насколько эта сумма выше предыдущей, а затем рассчитайте изменение в секунду.
  • Используйте setInterval() JavaScript для обновления счетчика на экране несколько раз в секунду, чтобы отразить рассчитанное изменение в секунду.

Единственная сложность этого плана заключается в том, что функция setInterval() не дает гарантий времени. Это означает, что если вы установите интервал 10 раз в секунду, Javascript будет пытаться поместить функцию в стек 10 раз в секунду, но функция может не выполняться 10 раз в секунду. Это приводит к тому, что счетчик сильно сбивается с пути, если его оставить работать достаточно долго.

Чтобы решить эту проблему, я добавил дополнительную переменную «speedUp», используемую для регулировки скорости счета как вверх, так и вниз по мере необходимости во время выполнения программы. Возможно, проще было бы просто пересчитать точную величину увеличения 10 раз в секунду, но я хотел как вызов, так и очень гладкий счетчик. Код настройки скорости можно найти в методе stepClock().

В итоге у нас есть очень простой и надежный счетчик, который можно запустить на Raspberry Pi или любом другом микрокомпьютере. Потенциальное хорошее применение для любых старых мониторов, лежащих в офисе.

Код ниже. Одна HTML-страница со встроенным скриптом и стилями. Я настоятельно рекомендую использовать файл ENV для ваших токенов и URL-адресов API.

<!DOCTYPE html>
<html>
<head>
 <title>ticker</title>
</head>
<body>
<div class="title">Total Data</div>
<div id="count" class="count">50,000</div>
<div id="velocity" class="velocity">10,000 per Hour</div>
<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
<script type="text/javascript">
var currentData = 0;
var oldTarget = 0;
var targetData = 0;
var increment = 0;
var perSec = 10;
var intervalDate = new Date();
var speedUp = 1;
var speedUpBase = 1.1;
//distance between api calls in seconds
var checkDistance = (10 * 60);
//calculating the velocity of data
function setPerHr() {
 var change = targetData - oldTarget;
 var checkToHour = (60 * 60) / checkDistance ;
 var perHr = change * checkToHour;
 document.getElementById('velocity').innerHTML = perHr.toLocaleString() + ' per Hour';
}
//parsing the api response
function examineData(data) {
 var total = data;
if (currentData == 0) {
  currentData = total - 10000;
  oldTarget = total - 10000;
 } else {
  console.log('skipped amount');
  console.log(targetData - currentData);
  currentData = targetData;
  oldTarget = targetData;
 }
console.log('new target set at:')
 console.log(new Date().toLocaleString());
 console.log(total);
 
 intervalDate = new Date();
 targetData = total;
 increment = (targetData - currentData) / checkDistance;
setPerHr();
}
function updateTotals() {
  //token in gitignore file.
  return $.ajax({
    'url': [API URL HERE],
    'contentType': 'application/json; charset=utf-8',
    'data': [AUTH TOKEN HERE],
    'type': 'POST',
    success: examineData
  })
}
function stepClock(element) {
 var useIncrement = increment;
 var time = new Date();
 var distanceCovered = (time - intervalDate) / (checkDistance * 1000);
 //if ground covered less than what should be covered
 var toBeCovered = (targetData - oldTarget) * (1 - distanceCovered);
 if ((targetData - currentData) > toBeCovered) {
  speedUp += 0.01;
  useIncrement *= speedUp;
  console.log('faster');
 //if counter is too far ahead ~ 1% buffer
 } else if ((targetData - currentData) < (toBeCovered * 0.99)) {
  if (speedUp > speedUpBase) {
   speedUp = speedUpBase;
  }
  if (speedUp > 0.01) {
   speedUp -= 0.01; 
  } 
  useIncrement *= speedUp;
  console.log('slower');
 } else {
  if (speedUp > (speedUpBase)) {
   speedUp -= 0.01;
  } else {
   speedUp = speedUpBase;
  }
 }
element.innerHTML = (Math.round(currentData += (useIncrement / perSec))).toLocaleString();
 //check both high and low to avoid a blast of api requests
 if (((time - intervalDate) > checkDistance * 1000) && ((time - intervalDate) < (checkDistance * 1000) + 10000)) {
  //setting this to try to reduce multiple triggers of this condition
  intervalDate = time;
  updateTotals();
 }
}
function onStart() {
 var element = document.getElementById('count');
 //checking the api every "checkDistance" num of seconds apart.
 updateTotals();
function incrementer() {
  stepClock(element);
 }
 
 //updating the on screen counter
 //updates the counter and checks if api needs to be rechecked.
 setInterval(incrementer, (1000 / perSec));
}
onStart();
</script>
<link href='https://fonts.googleapis.com/css?family=Orbitron' rel='stylesheet' type='text/css'>
<style type="text/css">
 body {
  background-color: black;
 }
.count {
  color: #00E500;
  font-size: 150px;
   font-family: 'Orbitron', sans-serif;
   border-bottom: solid 5px #00E500;
   border-top: solid 5px #00E500;
   padding: 25px 30px 0px 50px;
 }
.title {
  color: #00E500;
  font-size: 150px;
   font-family: 'Orbitron', sans-serif;
   padding: 10px 30px 10px 50px;
 }
.velocity {
  color: #00E500;
  font-size: 40px;
   font-family: 'Orbitron', sans-serif;
   padding: 15px 30px 0px 50px;
 }
</style>
</body>
</html>