0. 서론
이번 프로젝트를 진행하며 동적 그래프를 웹 페이지 위에 구현하기 위해 공부한 라이브러리이다.
본 프로젝트에서는 가변저항을 통하여 PID 제어에 필요한 게인 값들(P값, I값, D값을) 아두이노(esp32)가 받은 후, 그 값들을 JSON format으로 웹과 소켓 통신을 이용해 전송했다.
영상 대부분이 날라가고 남은 영상이 이거뿐이다.. 보면 PCB 발주 전 프로토타입용으로 빵판에 이것 저것 꽂은 상태로 진행했다.
참고로 관련 지식들을 정석대로 공부했기보다는 프로젝트에 적용하기 위해 주먹구구식으로 공부해서 설명이나 용어가 자연스럽지 못하거나 잘못된 부분이 있을 수도 있다..
필요한 지식들
1. 얕은 html 지식
2. 얕은 js 지식
1. Plotly란?
https://plotly.com/graphing-libraries/
Plotly란 interactive한 그래프와 맵을 구현할 수 있게 해주는 오픈 소스 라이브러리이다.
javascript뿐만 아니라 python이나 R등 다른 언어들도 제공한다.
2. NodeJs로 로컬 서버 열기
1. Javascript(파일명 server.js)
// 서버 사용을 위한 http 모듈 불러오기
const http = require('http');
// 파일 입출력 처리를 하기 위한(== page.html을 열기 위해) fs모듈 불러오기
const fs = require('fs');
const PORT = 3000;
// page.html 파일 열기
const serverbody = (request,response)=>{
fs.readFile('page.html','utf8',(err,data)=>{
response.writeHead(200,{'Content-type':'text/html'});
response.end(data);
console.log("connected page");
})
}
// http 모듈로 서버를 생성
const server = http.createServer(serverbody);
// 3000번 포트로 서버를 연다
server.listen(PORT),()=>{
console.log("Connected to Server");
};
2. html(파일명 page.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>hello world</h1>
</body>
</html>
3. 터미널 js파일 실행
터미널에서
node server
을 입력 후 웹 url에
localhost:3000
을 입력하여 웹 페이지가 열리는 걸 확인
3. Plotly를 이용하여 그래프 그리기(example graph)
이제 plotly reference 적당히 참고하면서 웹 페이지를 만들어주자
<script src="https://cdn.plot.ly/plotly-2.18.2.min.js"></script>
태그 사이에 다음과 같이 plolty CDN 링크를 넣어준다.
<div id="tester" style="width:50%;height:700px"></div>
태그 사이에 다음과 같이 그래프를 넣어주기 위한 레이아웃을 넣어준다
그 다음 https://plotly.com/javascript/line-charts/ 에 있는 예제를 복붙해준다. (Basic Line Plot)
<body>
<div id="tester" style="width:50%;height:700px"></div>
<script>
var trace1 = {
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
type: 'scatter'
};
var trace2 = {
x: [1, 2, 3, 4],
y: [16, 5, 11, 9],
type: 'scatter'
};
var data = [trace1, trace2];
Plotly.newPlot('tester', data);
</script>
</body>
추가설명
여기서 중요한 점은 Plotly.newPlot()의 첫 번째 파라미터로 div 이름이 들어가야한다. 예제 코드는 2023 03. 03일 기준 'myDiv'라고 되어있는데 아까 tester라는 이름으로 레이아웃을 생성했으니 tester라고 바꿔줘야한다.
이제 터미널에서 로컬 서버를 열고 접속하면
다음과 같이 그래프가 생성된걸 확인할 수 있다.
추가 설명
trace1 (파란색 그래프)를 보면
var trace1 = {
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
type: 'scatter'
};
순서대로 (1, 10), (2, 15), (3, 13), (4,17)에 점이 찍히고 이어진거라 볼 수 있다.
근데 이는 정적 그래프를 생성한것이다. (== 그래프가 실시간으로 변하지 않고 고정됨)
이번에는 그래프가 어떠한 조건에 따라 실시간으로 바뀌는 동적 그래프를 생성해보자
4. 동적 그래프 그리기
다음과 같이 코드 작성
var trace1 = {y: [0], name: 'testLine 1', mode: 'line', maker:{color: "rgb(255, 0, 0)"} };
var trace2 = {y: [0], name: 'testLine 2', mode: 'line', maker:{color: "rgb(0, 255, 0)"} };
var trace3 = {y: [0], name: 'testLine 3', mode: 'line', maker:{color: "rgb(0, 0, 255)"} };
var layout = {
title: 'Test Dynamic Chart'
}
var data = [trace1, trace2, trace3];
Plotly.newPlot('tester', data, layout);
각 trace들에 있는 파라미터를 설명하자면
name : 각 그래프 이름
mode : 그래프 형태
maker : 여기서는 rgb값을 이용하여 그래프 색을 지정해줄 수 있다.
var layout에 그래프의(==레이아웃의) 이름을 정해주고 이를 newplot()의 세 번째 파라미터로 넣어준다.
그러면 다음과 같이 레이아웃이 생성된걸 확인할 수 있다. 여기서 차이점은 그래프 위에 layout 이름이 추가된 것을 확인할 수 있다.
당연하지만 아직 모든 trace를 x값은 지정안하고 y값만 0으로 지정했기 때문에 모두 (0, 0) 상태라 모두 정 중앙에 점만 찍혀있다.
5. Socket통신을 이용하여 동적 그래프 생성
이제 절반을 끝냈다고 할 수 있다.
socket을 이용하여 trace1, 2, 3에 대해서 y값만 받고 데이터를 받을때마다 x축을 오른쪽으로 한 칸 이동시키고 얻은 y값을 추가하는 방식으로 동적 그래프를 구현한다.
Plotly.extendTrace라는 메소드를 이용
This allows you to add data to traces in an existing graphDiv.
reference에서 발췌한 해당 메소드 설명 (아마 영문 그대로 보는게 이해가 더 잘 될거같아 가져왔다)
1. NodeJs
npm install socket.io
터미널에 다음과 같이 입력해 npm을 통하여 Socket.IO 모듈을 설치한다.
package.json 파일에서 해당 모듈이 설치되었는지 확인
const socketio = require('socket.io')(server);
Socket.IO 모듈 불러오고
socketio.on('connection',client=>{
console.log('socket connect!');
client.on('startmsg',function(data){
console.log("message(%s)",data);
socketio.emit('page',data);
})
})
다음과 같이 아까 만들었던 socketio 객체로 on 함수에 'connection' 이벤트를 받는다.
2. html
<script src="/socket.io/socket.io.js"></script>
다음과 같이 헤드 사이에 소켓 모듈을 import 하고
<script>
window.onload = function () {
console.log("페이지 열렸습니다~");
var widthcount = 1;
var socket = io.connect();
socket.on('page', function(data){
const dataInJson = JSON.parse(data);
console.log(dataInJson.trace1);
console.log(dataInJson.trace2);
console.log(dataInJson.trace3);
Plotly.extendTraces('tester', { y: [ [dataInJson.trace1], [dataInJson.trace2], [dataInJson.trace3]] }, [0, 1, 2]);
widthcount++;
if (widthcount > 100){ // 100이상일경우 챠트동적이동
Plotly.relayout('tester', {
xaxis: { range: [widthcount-99, widthcount] } // x축 동적이동
});
}
})
}
</script>
다음과 같이 코드 작성 (헤드 사이에 작성)
6. Postman으로 확인
1. 우측상단에 New 누르고
2. WebSocket Request 클릭
그럼 다음과 같이 창이 뜬다
3. Socket.io로 변경
4. 로컬 서버 ip주소 적어주고 Connect (localhost도 괜찮)
5. 다음과 같이 JSON Format으로 전송 할 데이터 작성 및 Event name을 startmsg로 하기
6. 이제 Send 누르고 그래프를 보자
console 창에서도 받은 데이터를 잘 출력하는 것을 확인할 수 있다.
7. 전체 코드
1. page.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<title>TEST GRAPH</title>
</head>
<script>
window.onload = function () {
console.log("페이지 열렸습니다~");
var widthcount = 1;
var socket = io.connect();
socket.on('page', function(data){
const dataInJson = JSON.parse(data);
console.log(dataInJson.trace1);
console.log(dataInJson.trace2);
console.log(dataInJson.trace3);
Plotly.extendTraces('tester', { y: [ [dataInJson.trace1], [dataInJson.trace2], [dataInJson.trace3]] }, [0, 1, 2]);
widthcount++;
if (widthcount > 100){ // 100이상일경우 챠트동적이동
Plotly.relayout('tester', {
xaxis: {range: [widthcount-99, widthcount] } // x축 동적이동
});
}
})
}
</script>
<body>
<div id="tester" style="width:50%;height:700px"></div>
<script>
var trace1 = {y: [0], name: 'testLine 1', mode: 'line', maker:{color: "rgb(255, 0, 0)"} };
var trace2 = {y: [0], name: 'testLine 2', mode: 'line', maker:{color: "rgb(0, 255, 0)"} };
var trace3 = {y: [0], name: 'testLine 3', mode: 'line', maker:{color: "rgb(0, 0, 255)"} };
var layout = {
title: 'Test Dynamic Chart'
}
var data = [trace1, trace2, trace3];
Plotly.newPlot('tester', data, layout);
</script>
</body>
</html>
2. server.js
// 서버 사용을 위한 http 모듈 불러오기
const http = require('http');
// 파일 입출력 처리를 하기 위한(== page.html을 열기 위해) fs모듈 불러오기
const fs = require('fs');
const PORT = 3000;
// page.html 파일 열기
const serverbody = (request,response)=>{
fs.readFile('page.html','utf8',(err,data)=>{
response.writeHead(200,{'Content-type':'text/html'});
response.end(data);
console.log("connected page");
})
}
// http 모듈로 서버를 생성
const server = http.createServer(serverbody);
// 3000번 포트로 서버를 연다
server.listen(PORT),()=>{
console.log("Connected to Server");
};
// socket.io 모듈 불러오기
const io = require('socket.io')(server);
io.on('connection', client=>{
console.log('socket connect!');
client.on('startmsg', function(data){
console.log("message(%s)",data);
io.emit('page',data);
})
})
'1&2학년 > [2022 09 ~ 12] 융합 프로젝트' 카테고리의 다른 글
[Esp32] 아두이노 Scoket 통신 (0) | 2023.01.11 |
---|---|
[융합프로젝트] 결과 (0) | 2022.12.27 |