我已经实现了使用python flask在浏览器上实时流式传输的ecg图,以及我从该博客中学到的知识。 http://theblogofpeterchen.blogspot.com/2015/02/html5-high-performance-real-time.html
我的图表看起来不错,并且可以按照我想要的方式进行播放,但是我想制作它,以便用户可以改变心率,从而使图表发生变化。任何有关如何实现这一目标的建议将不胜感激。
我尝试调整g.speed设置,但这似乎对速度没有影响。我有信心在用户界面上添加心率控件,但我不确定如何接受用户输入并响应于此输入而使图形循环更快/更慢,因为g.speed参数似乎并不影响数据流的速率。
liveGraph.js:
/*
* Helper function to convert a number to the graph coordinate
* ----------------------------------------------------------- */
function convertToGraphCoord(g, num){
return Math.floor((g.height / 2) * -(num * g.scaleFactor) +
g.height / 2);
}
/*
* Constructor for the LiveGraph object
* ----------------------------------------------------------- */
function LiveGraph(cid, datacb){
var g = this;
g.canvas_id = cid;
g.canvas = $("#" + cid);
g.context = g.canvas[0].getContext("2d");
g.width = $("#" + cid).width();
g.height = $("#" + cid).height();
g.white_out = g.width * 0.01;
g.fade_out = g.width * 0.10;
g.fade_opacity = 0.2;
g.current_x = 0;
g.current_y = 0;
g.erase_x = null;
g.speed = 2;
g.linewidth = 1;
g.scaleFactor = 1;
g.stop_graph = true;
g.plethStarted = false;
g.plethBuffer = new Array();
devicePixelRatio = window.devicePixelRatio || 1,
backingStoreRatio = g.context.webkitBackingStorePixelRatio ||
g.context.mozBackingStorePixelRatio ||
g.context.msBackingStorePixelRatio ||
g.context.oBackingStorePixelRatio ||
g.context.backingStorePixelRatio || 1,
ratio = devicePixelRatio / backingStoreRatio;
var oldWidth = g.canvas[0].width;
var oldHeight = g.canvas[0].height;
g.canvas[0].width = oldWidth * ratio;
g.canvas[0].height = oldHeight * ratio;
g.canvas[0].style.width = oldWidth + 'px';
g.canvas[0].style.height = oldHeight + 'px';
// scale the context to counter
// the fact that we've manually scaled
// the canvas element
g.context.scale(ratio, ratio);
/*
* The call to fill the data buffer using
* the data callback
* ---------------------------------------- */
g.fillData = function() {
g.plethBuffer = datacb();
};
/*
* The call to check whether graphing is on
* ---------------------------------------- */
g.isActive = function() {
return !g.stop_graph;
};
/*
* The call to stop the graphing
* ---------------------------------------- */
g.stop = function() {
g.stop_graph = true;
};
/*
* The call to wrap start the graphing
* ---------------------------------------- */
g.start = function() {
g.stop_graph = false;
g.animate();
};
/*
* The call to start the graphing
* ---------------------------------------- */
g.animate = function() {
reqAnimFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
// Recursive call to do animation frames
if (!g.stop_graph) reqAnimFrame(g.animate);
// fill in data into the buffer so we know what to draw
g.fillData();
// Draw the frame (with the supplied data buffer)
g.draw();
};
g.draw = function() {
// Circle back the draw point back to zero when needed (ring drawing)
g.current_x = (g.current_x > g.width) ? 0 : g.current_x;
// "White out" a region before the draw point
for( i = 0; i < g.white_out ; i++){
g.erase_x = (g.current_x + i) % g.width;
g.context.clearRect(g.erase_x, 0, 1, g.height);
}
// "Fade out" a region before the white out region
for( i = g.white_out ; i < g.fade_out ; i++ ){
g.erase_x = (g.current_x + i) % g.width;
g.context.fillStyle="rgba(255, 255, 255, " + g.fade_opacity.toString() + ")";
g.context.fillRect(g.erase_x, 0, 1, g.height);
}
// If this is first time, draw the first y point depending on the buffer
if (!g.started) {
g.current_y = convertToGraphCoord(g, g.plethBuffer[0]);
g.started = true;
}
// Start the drawing
g.context.beginPath();
// first move to the current x and y position (last point)
g.context.moveTo(g.current_x, g.current_y);
for (i = 0; i < g.plethBuffer.length; i++) {
// Put the new y point in from the buffer
g.current_y = convertToGraphCoord(g, g.plethBuffer[i]);
// Draw the line to the new x and y point
g.context.lineTo(g.current_x += g.speed, g.current_y);
// Set the
g.context.lineWidth = g.linewidth;
g.context.lineJoin = "round";
// Create stroke
g.context.stroke();
}
// Stop the drawing
g.context.closePath();
};
}
var lastData =0;
// Create a random function that is dependent on the last value
function hysteresisRandom(){
lastData += (Math.floor((Math.random() * 5) + 1)-3)/50;
if (Math.abs(lastData) >= 1) lastData = (lastData > 0) ? 1 : -1;
return lastData;
}
// Generate a real time data grab of various length
function generateData(){
buffer = new Array();
var inputLength = Math.floor((Math.random() * 1) + 1);;
for( i = 0 ; i < inputLength ; i++ ) buffer[i] = hysteresisRandom();
return buffer;
}
ecg.js:
/*45 data points*/
var ECG_data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0.08, 0.18, 0.08, 0, 0, 0, 0, 0, 0, -0.04,
-0.08, 0.3, 0.7, 0.3, -0.17, 0.00, 0.04, 0.04,
0.05, 0.05, 0.06, 0.07, 0.08, 0.10, 0.11, 0.11,
0.10, 0.085, 0.06, 0.04, 0.03, 0.01, 0.01, 0.01,
0.01, 0.02, 0.03, 0.05, 0.05, 0.05, 0.03, 0.02, 0, 0, 0];
var ECG_idx = 0;
function get_ECG_data(){
if (ECG_idx++ >= ECG_data.length - 1) ECG_idx=0;
var output = new Array();
output[0] = ECG_data[ECG_idx] + hysteresisRandom()/10;
return output;
}
var ecg;
$(document).ready(function(){
ecg = new LiveGraph("ecg", get_ECG_data);
ecg.speed = 1.4;
ecg.scaleFactor = 0.8;
ecg.start();
});
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>{{ title }}</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/normalize.css">
<link rel="stylesheet" href="/static/main.css">
<script type="text/javascript" src="{{ url_for('static', filename='smoothie.js') }}"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='modernizr-3.6.0.min.js') }}"></script>
</head>
<body>
<script type="text/javascript" src="{{ url_for('static', filename='jquery-3.3.1.min.js') }}"></script>
<div class="hidden-on-main-page">
<script type="text/javascript" src="{{ url_for('static', filename='liveGraph.js') }}"></script>
<h1>{{ title }}!</h1>
<h2> The date and time on the server is: {{ time }}</h2>
<h2> GPIO mode status: {{ gpio_mode }}</h2>
<h2> Last Command sent: {{ last_cmd }}</h3>
<br>
<h2> Commands </h2>
<h3>
Lung Rate Control:
<h4>
<a href="/lung_rate_apnea" class="button"> APNEA</a>
<a href="/lung_rate_5" class="button"> RR=5</a>
<a href="/lung_rate_30" class="button"> RR=30</a>
</h4>
</h3>
<h3>
Pupil Control:
<h4>
<a href="/pupil_r_constricted" class="button"> constricted</a>
<a href="/pupil_r_normal" class="button"> normal</a>
<a href="/pupil_r_dilated" class="button"> dilated</a>
</h4>
</h3>
<div class="container" id="content">
<div class="row">
<p>ECG</p>
<div id="log">
</div> <!-- /#log -->
</div>
</div>
<div class="wrapper">
<h1>ECG</h1>
<div style="width: 100%;">
<center>
<canvas id="ecg" width="1200" height="200" style="border: 1px solid #cccccc; border-radius: 5px; cursor: pointer;"></canvas>
<script type="text/javascript" src="{{ url_for('static', filename='ecg.js') }}"></script>
</center>
</div>
</body>
我希望心电图可以调整,以便客户可以调整心率。
更新:2019/02/13:
我找到了一种方法(可能不是一种很好的方法),但是还是一种通过在ECG_data数组开始时更改零数来更改心率的方法。增加零的数目将使心率出现较慢,减少零的数目使心率出现较快。现在的问题是我没有JavaScript。我可以对零进行硬编码以查看结果,但是当我向javascript中添加一些基本使用array.splice()和array.push()来控制零数目的函数时,我似乎无法使其正常工作。我使用.push()是因为我发现,将所有零都放在数组的末尾而不是如示例中所示的开头,可以更轻松地进行操作。有人可以帮我提供一个可行的工作示例,或者如何使它更好的方法吗?