چگونه با استفاده از جاوااسکریپت و Pusher یک گراف بلادرنگ ایجاد کنیم

گردآوری و تالیف : ارسطو عباسی
تاریخ انتشار : 07 فروردین 1397
دسته بندی ها : جاوا اسکریپت

جهان امروز نیاز دارد که همه چیز در آن سریع اتفاق بیافتد. هر روز جریان‌های داده‌ای زیادی توسط سیستم‌های مختلفی ایجاد می‌شود. آن‌ها تصمیم گیرنده بسیاری از حرفه‌ها و صنعت‌های مدرن امروزی هستند. بدین منظور مانیتورینگ و آنالیز بلادرنگ امری بسیار مهم و حیاتی است. منظور از جریان‌های داده‌ای مواردی مانند مانیتور ترافیک یک وبسایت، کارایی سرور، بروزرسانی آب‌و‌هوا و سنسورهای اینترنت چیزها است. بسیار مهم است که بتوانیم این حجم از داده‌ را آنالیز و بررسی کنیم. برای اینکار استفاده از گراف‌ها و چارت‌های تعاملی راه‌حل بسیار مناسبی است.

در این مطلب قصد داریم یک سرور نودجی‌اس را راه‌اندازی کنیم که با استفاده از APIها، مقدار دقیق هواشناسی مربوط به لندن را به صورت بلادرنگ برای ما به نمایش می‌گذارد.

همچنین این موضوع به ما کمک می‌کند که بتوانیم با استفاده از API نقطه‌ای جدید از داده را تعریف کنیم. همچنین برای نمایش تغییرات آب‌و‌هوا، یک رابط را با استفاده از Line Chart ایجاد می‌کنیم. اپلیکیشنی که قرار است ایجاد کنیم، چیزی شبیه به تصویر زیر خواهد بود:

جاوااسکریپت و Pusher

ثبت‌نام در Pusher

اولین قسمت از این آموزش ثبت نام کردن و وارد شدن به حساب Pusher است. بعد از وارد شدن شما نیاز دارید که یک اپلیکیشن جدید را ایجاد کنید و برای ادامه کار نسخه Vanilla جاوااسکریپت را انتخاب کنید، زیرا با این گزینه می‌توانیم هم برای قسمت فرانت‌اند و هم برای قسمت بک‌اند (نودجی‌اس) کارها را به درستی پیش ببریم. بعد از آن یک صفحه فرود برای شما ایجاد می‌شود که بعدا در همین آموزش از آن استفاده می‌کنیم.

APIهای سرور نودجی‌اس برای سیستم آنالیز و مانیتورینگ

APIهای ضروری برای هر سیستم آنالیز و یا سیستم متریک و… شامل موارد زیر هستند:

  • Ingestion API : این مورد یک API برای دریافت اطلاعات و ورودی از هر موجودیت خاصی است. همانطور که قبلا اشاره شد در این آموزش قصد داریم وضعیت آب و هوای لندن را در نظر بگیریم. این API می‌تواند توسط سیستم‌های هواشناسی جهانی و یا حتی سنسورها فراخوانی شود.
  • Historical Data API: این API در یک بازه زمانی می‌تواند به ما داده‌هایی را برگشت دهد. برای سروری که قصد ایجاد کردن آن را داریم، یک API ساده ایجاد می‌کنیم که یک‌سری تاریخ‌های استاتیک را نشان می‌دهد و در آن تاریخ‌ها، وضعیت آب و هوای لندن در نهایت برگشت داده می‌شود.

پیکربندی اصلی سرور Node.js Express

ما یک سرور ساده Node.js Express ایجاد می‌کنیم و کتابخانه Pusher را نیز در آن قرار می‌دهیم. یک پوشه جدید برای پروژه ایجاد کنید و فایلی به نام server.js را ایجاد کنید. بعد از آن محتوای زیر را در آن قرار دهید:

var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');

var Pusher = require('pusher');

var pusher = new Pusher({
  appId: '<your-app-id>',
  key: '<your-app-key>',
  secret: '<your-app-secret>',
  cluster: '<your-app-cluster>',
  encrypted: true
});

var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));

// Error Handler for 404 Pages
app.use(function(req, res, next) {
    var error404 = new Error('Route Not Found');
    error404.status = 404;
    next(error404);
});

module.exports = app;

app.listen(9000, function(){
  console.log('Example app listening on port 9000!')
});

API برای دریافت داده دمای هوا مبتنی بر تاریخ

حال ما قصد داریم که یکسری زمان‌های ایستا را تعیین کنیم و درجه دما را در آن زمان در لندن بررسی کرده و در یک متغیر جاوااسکریپتی ذخیره کنیم. همچنین یک مسیر برای فراخوانی داده ها را نیز از طریق GET HTTP ایجاد می‌کنیم.

var londonTempData = {
    city: 'London',
    unit: 'celsius',
    dataPoints: [
      {
        time: 1130,
        temperature: 12 
      },
      {
        time: 1200,
        temperature: 13 
      },
      {
        time: 1230,
        temperature: 15 
      },
      {
        time: 1300,
        temperature: 14 
      },
      {
        time: 1330,
        temperature: 15 
      },
      {
        time: 1406,
        temperature: 12 
      },
    ]
  }

app.get('/getTemperature', function(req,res){
  res.send(londonTempData);
});

API برای خواندن داده‌ها از یک نقطه

حال ما قصد داریم API جدیدی را اضافه کنیم که با استفاده از آن می‌توانیم در یک زمان خاص وضعیت دما را فراخوانی کنیم. همچنین یک ای‌پی‌آی GET HTTP با پارامترهای زمان و دما نیز در نظر می‌گیریم. اینگونه آن ها را اعتبارسنجی می‌کنیم که خالی نباشند. برای ذخیره آن‌ها از یک آرایه dataPoints از متغیرهای ایستا جاوااسکریپتی مربوط به londonTempData استفاده می‌کنیم. برای اینکار کدهای زیر را به فایل server.js اضافه کنید. 

app.get('/addTemperature', function(req,res){
  var temp = parseInt(req.query.temperature);
  var time = parseInt(req.query.time);
  if(temp && time && !isNaN(temp) && !isNaN(time)){
    var newDataPoint = {
      temperature: temp,
      time: time
    };
    londonTempData.dataPoints.push(newDataPoint);
    pusher.trigger('london-temp-chart', 'new-temperature', {
      dataPoint: newDataPoint
    });
    res.send({success:true});
  }else{
    res.send({success:false, errorMessage: 'Invalid Query Paramaters, required - temperature & time.'});
  }
});

جدای از ذخیره داده‌ها در کدهای بالا، ما یک رویداد new-temperature را نیز روی کانال london-temp-chart قرار دادیم. برای هر منبع خاص یا چارت منحصر به فردی می‌توانید یک کانال جدید ایجاد کنیم. 

رویدادی که در نظر گرفته‌ایم با استفاده از فرانت‌-اند پردازش می‌شود و به صورت یک چارت یا گراف بلادرنگ نمایش داده می‌شود. رویدادی که در نظر گرفته‌ایم باید شامل تمام مواردی که برای نمایش چارت و استفاده از آن لازم است باشد. در این مثال ما دمای جدید را با به صورت یک خط جدید در قسمت فرانت‌اند قرار داده‌ایم.

ساخت یک اپلیکیشن فرانت‌اند با استفاده از Vanilla و Chart.js

حال ما قصد داریم که قسمت فرانت اند اپلیکیشن را ایجاد کنیم. در این اپلیکیشن قصد داریم با استفاده از یک چارت خطی تغییرات مربوط به آب و هوای لندن را در زمان‌های مختلفی از روز نمایش دهیم. رویکرد کلیدی برای نمایش گراف‌های بلادرنگ شامل دو مورد می‌شود:

  • ما باید یک آجاکس اولیه ایجاد کنیم که داده را فراخوانی و دریافت می‌کند و در کنار داده‌های قبلی همراه با گراف آن را پردازش می‌کند.
  • باید برای هر رویدادی که در یک نقطه خاص از داده اتفاق می افتد حاضر شویم.

قالب ساده HTML

در ریشه اصلی پروژه یک پوشه به اسم public ایجاد می‌کنیم و در آن فایل index.html را ایجاد می‌کنیم. این یک قالب کلی از HTML است که برای رندر سربرگ و بخش‌های دیگر اپلیکیشن استفاده می‌شود. همچنین از طریق CDN کتابخانه جاوااسکریپتی Pusher را نیز در آن قرار می‌دهیم.

<!DOCTYPE>
<html>
    <head>
        <title>Realtime Analytics</title>
        <link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway:200">
        <link rel="stylesheet" href="./style.css">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <header>
            <div class="logo">
                <img src="./assets/pusher-logo.png" />
            </div>
            <div id="logout" class="logout">
               <a href="/logout">Logout</a>
            </div>
        </header>
        <section class="subheader">
            <img class="weather-icon" src="./assets/weather.png" />
            <h2>Realtime Weather Updates for London !</h2>
            <img class="weather-icon" src="./assets/london-icon.png" height="70px" />
        </section>
        <section>
           <div id="loader" class="loader">
           </div>
        </section>
        <script src="https://js.pusher.com/4.0/pusher.min.js"></script>
        <script type="text/javascript" src="./app.js"></script>
    </body>
</html>

اضافه کردن کتابخانه چارت

در جاوااسکریپت و HTML برای ایجاد اجزای گرافیکی یا باید از SVG استفاده کنیم و یا Canvas. تعداد زیادی کتابخانه‌های جاوااسکریپتی نیز وجود دارند که به ما برای رندر انواع مختلف از چارت کمک می‌کند. این موارد شامل چارت‌های میله‌ای، لوله‌ای، خطی و پراکنده می‌شود.

برای پروژه ما قصد دارم که از Chart.js استفاده کنم. به این دلیل که APIهای ساده‌ای دارد و المان‌ها را از طریق Canvas مربوط به HTML رندر می‌کند. شما می‌توانید موارد دیگری را نیز انتخاب کنید اما این را به یاد داشته باشید که باید کتابخانه‌ای را انتخاب کنید که بدون رندر کردن کل پروژه، گراف را بروزرسانی کند. چارت‌جی‌اس یکی از مواردی است که این کار را برای‌مان انجام می‌دهد. کدهای زیر را به index.html اضافه کنید.

<section>
   <div id="loader" class="loader">
    Loading...
   </div>
   <canvas id="weatherChart">
   </canvas>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.min.js"></script>
<script src="https://js.pusher.com/4.0/pusher.min.js"></script>

اضافه کردن فایل‌های جاوااسکریپت و کتاب‌خانه Pusher

حال نیاز است که فایل app.js را در پوشه عمومی ایجاد کنیم و برای معرفی pusher به شیوه زیر عمل نماییم:

// Using IIFE for Implementing Module Pattern to keep the Local Space for the JS Variables
(function() {
    // Enable pusher logging - don't include this in production
    Pusher.logToConsole = true;

    var serverUrl = "/",
        members = [],
        pusher = new Pusher('<your-api-key>', {
          encrypted: true
        }),
        channel,weatherChartRef;

    function showEle(elementId){
      document.getElementById(elementId).style.display = 'flex';
    }

    function hideEle(elementId){
      document.getElementById(elementId).style.display = 'none';
    }

    function ajax(url, method, payload, successCallback){
      var xhr = new XMLHttpRequest();
      xhr.open(method, url, true);
      xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
      xhr.onreadystatechange = function () {
        if (xhr.readyState != 4 || xhr.status != 200) return;
        successCallback(xhr.responseText);
      };
      xhr.send(JSON.stringify(payload));
    }

})();

در کدهای بالا متد کمکی را برای اینکه از یک فراخوانی Ajax استفاده کنیم قرار داده‌ایم و همچنین از طریق API مربوط به DOM هم نمایش و پنهان‌سازی المان‌ها را بررسی کرده ایم.

اضافه کردن کد برای دریافت‌ داده‌های مبتنی بر تاریخ

حال نیاز است که داده‌های مربوط به دما را با استفاده از مقادیر پیشفرضی که تنظیم کردیم روی گراف نمایش دهیم. همچنین از یک شئ Chart استفاده کرده‌ایم که چارت با استفاده از Line Chart رندر شود. می‌توانید در مورد پیکربندی این حالت مستندات را مطالعه کنید. 

برای ادامه، کدهای زیر را به فایل app.js اضافه کنید:

function renderWeatherChart(weatherData) {
      var ctx = document.getElementById("weatherChart").getContext("2d");
      var options = { };
      weatherChartRef = new Chart(ctx, {
        type: "line",
        data: weatherData,
        options: options
      });
   }

   var chartConfig = {
      labels: [],
      datasets: [
         {
            label: "London Weather",
            fill: false,
            lineTension: 0.1,
            backgroundColor: "rgba(75,192,192,0.4)",
            borderColor: "rgba(75,192,192,1)",
            borderCapStyle: 'butt',
            borderDash: [],
            borderDashOffset: 0.0,
            borderJoinStyle: 'miter',
            pointBorderColor: "rgba(75,192,192,1)",
            pointBackgroundColor: "#fff",
            pointBorderWidth: 1,
            pointHoverRadius: 5,
            pointHoverBackgroundColor: "rgba(75,192,192,1)",
            pointHoverBorderColor: "rgba(220,220,220,1)",
            pointHoverBorderWidth: 2,
            pointRadius: 1,
            pointHitRadius: 10,
            data: [],
            spanGaps: false,
         }
      ]
   };

   ajax("/getTemperature", "GET",{}, onFetchTempSuccess);

   function onFetchTempSuccess(response){
      hideEle("loader");
      var respData = JSON.parse(response);
      chartConfig.labels = respData.dataPoints.map(dataPoint => dataPoint.time);
      chartConfig.datasets[0].data = respData.dataPoints.map(dataPoint => dataPoint.temperature);
      renderWeatherChart(chartConfig)
  }

در کد بالا ما یک تابع جدید به نام renderWeatherChart ایجاد کردیم. این تابع به ما کمک می‌کند که بتوانیم چارت را از طریق داده‌هایی که در chartConfig پیاده‌سازی کرده‌ایم، با استفاده از آخرین داده‌های موجود بروزرسانی کنیم. اگر می‌خواهید تعداد خطوط بیشتری را به این چارت اضافه کنید، می‌توانید تعداد المان‌های بیشتری را به آرایه اضافه کنید.

کلید داده‌ای موجود در این رابطه نقطه متفاوتی از گراف را به ما نشان می‌دهد. با استفاده از این حالت می‌توانیم آجاکس را برای ای‌پی‌آی getTemperature در جهت دریافت داده‌های بیشتر فراخوانی کنیم. در نهایت متد رندر را برای نمایش گراف فراخوانی می‌کنیم. حال می‌توانیم دستور node server.js را اجرا کنیم و در مرورگر وارد آدرس زیر شویم:

http://localhost:9000/

برای اینکه استایل اپلیکیشن‌مان زیبا‌تر باشد و بهتر نمایش داده شود، می‌توانید کدهای زیر را به یک فایل استایل مجزا اضافه کنید:

body{
    margin:0;
    padding:0;
    overflow: hidden;
    font-family: Raleway;
}

header{
    background: #2b303b;
    height: 50px;
    width:100%;
    display: flex;
    color:#fff;
}

.logo img{
  height: 45px;
}

.subheader{
    display: flex;
    align-items: center;
    margin: 0px;
}

.logout{
    flex:1;
    justify-content: flex-end;
    padding:15px;
    display: none;
}

.logout a{
    color:#fff;
    text-decoration: none;
}

#weatherChart{
    height: 80% !important;
    width: 95% !important;
    margin: 0 auto;
}

کدهای مربوط به بروزرسانی گراف در زمان دریافت رویداد جدید

حال نیاز داریم که در یک کانال برای دریافت اطلاعات مشترک شویم و بعد از آن رویدادها را از طریق چارت دنبال کنیم. برای این کار در این مطلب ما عضو کانال london-temp-chart می‌شویم و رویداد را new-temperature نامگذاری می‌کنیم. برای پردازش رویداد و بروزرسانی بلادرنگ چارت کد زیر را اضافه کنید:

channel = pusher.subscribe('london-temp-chart');
channel.bind('new-temperature', function(data) {
    var newTempData = data.dataPoint;
    if(weatherChartRef.data.labels.length > 15){
      weatherChartRef.data.labels.shift();  
      weatherChartRef.data.datasets[0].data.shift();
    }
    weatherChartRef.data.labels.push(newTempData.time);
    weatherChartRef.data.datasets[0].data.push(newTempData.temperature);
    weatherChartRef.update();
});

برای اینکه نتیجه را مشاهده کنید نیاز است که مرورگر را رفرش کنید. حال می‌توانیم به یک نقطه خاص از داده ها اشاره کنیم. آدرس زیر را وارد نمایید:

http://localhost:9000/addTemperature?temperature=17&time=1500

برای اینکه بتوانید بروزرسانی کدهای‌تان را تست کنید کدهای زیر را در فایل app.js قرار دهید. در این کد یک درخواست آجاکس درست می‌شود که بعد از هر چند ثانیه به صورت خودکار داده‌ها را بروزرسانی می‌کند.

/* TEMP CODE FOR TESTING */
  var dummyTime = 1500;
  setInterval(function(){
    dummyTime = dummyTime + 10;
    ajax("/addTemperature?temperature="+ getRandomInt(10,20) +"&time="+dummyTime,"GET",{},() => {});
  }, 1000);

  function getRandomInt(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
  }
/* TEMP CODE ENDS */

در این لینک می‌توانید مخزن گیت هاب مربوط به این پروژه را نیز مشاهده کنید.

در پایان

در نهایت اپلیکیشن آنالیز بلادرنگ ما آماده است. می‌توانیم وضعیت هوای لندن را با استفاده از این اپلیکیشن به صورت بلادرنگ مشاهده کنیم.

منبع

مقالات پیشنهادی

چگونه با استفاده از Meteor و Vuejs یک پیشخوان بلادرنگ ایجاد کنیم؟

در این آموزش ما از دو ابزار کاملا متفاوت استفاده می‌کنیم، مطمئنا با ویوجی‌اس آشنایی دارید، اما در کنار ویوجی‌اس می‌خواهیم با Meteor نیز کار کنیم. ما ا...

چگونه یک منو واکنشگرا با FlexBox ایجاد کنیم

در این مطلب قصد دارم به شما شیوه ساخت یک نوار منو توسط FlexBox و قدرت Media queries را توضیح دهم. این نوار منو توانایی تطبیق یافتن با اندازه‌های مختلف...

چگونه یک وبسایت وردپرسی را به یک اپلیکیشن موبایل تبدیل کنیم

داشتن یک وبسایت واکنشگرا یکی از مهمترین مواردی است که دارندگان وبسایت باید به آن توجه کنند، با این کار آن ها می توانند تمام اطلاعات وبسایت را در دستگا...

چگونه با استفاده از جاوااسکریپت پیش‌نمایش‌های چندگانه ایجاد کنیم؟

در این مطلب آموزشی قصد داریم با استفاده از جاوااسکریپت و رویدادهای ماوس یک چیز جالب و مفید را درست کنیم: قصد داریم تصاویر بندانگشتی را ایجاد کنیم که ب...