Created
May 3, 2023 17:38
-
-
Save garethky/3d07906cc5b423caba3cb75809cc46b8 to your computer and use it in GitHub Desktop.
Tool to dump data fron Moonraker's websicket and display in a browser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Graph Load Cell Data</title> | |
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.2/dist/echarts.min.js"></script> | |
</head> | |
<body> | |
<h2>Moonraker Server: | |
<form id="serverForm"> | |
<fieldset id="serverFormFields"> | |
<input id="serverInput" type="text" placeholder="raspberrypi.local"/> | |
<input type="submit" value="Connect"/> | |
</fieldset> | |
</form> | |
</h2> | |
<h4>MCU Awake:<span id="mcuAwakeDisplay">--</span></h4> | |
<h4>MCU Load:<span id="mcuLoadDisplay">--</span></h4> | |
<h4>Grams:<span id="gramsDisplay">--</span></h4> | |
<button id="load_cell_log">load_cell_log.csv</button> | |
<div id="main-chart" style="height: 500px; width: 100%; margin: auto;"></div> | |
<p>Tip: press the space bar to pause data capture.</p> | |
<script> | |
var chartDom = document.getElementById('main-chart'); | |
var myChart = echarts.init(chartDom); | |
var pauseGraph = false; | |
var loadCellSamples = []; | |
//var sampleAverageData = []; | |
//var trendAverageData = []; | |
//var trendDeadbandMin = []; | |
//var trendDeadbandMax = []; | |
//var deadband = 0; | |
var dates = []; | |
var load_cell_state = { | |
is_capturing: false, | |
is_calibrated: false, | |
sps: null, | |
tare_counts: 0, | |
counts_per_gram: 0 | |
} | |
let series = [ | |
{ | |
data: loadCellSamples, | |
name: 'Load Cell Raw Value', | |
type: 'line', | |
step: 'start', | |
showSymbol: false, | |
sampling: 'lttb', | |
zlevel: 1, | |
lineStyle: { | |
opacity: 0.75, | |
width: 1 | |
}, | |
}/*, | |
{ | |
name: 'Deadband Max', | |
type: 'line', | |
data: trendDeadbandMax, | |
zlevel: 0, | |
symbol: 'none', | |
tooltip: { | |
formatter: function (param) { | |
param = param[0]; | |
return [ | |
'whatdoesthisdo?', param.name, param.data[1] + param.data[2], | |
].join(''); | |
} | |
} | |
}, | |
{ | |
name: 'Deadband Min', | |
type: 'line', | |
data: trendDeadbandMin, | |
zlevel: 0, | |
symbol: 'none' | |
}, | |
{ | |
data: sampleAverageData, | |
name: 'Smoothed Sample Value', | |
type: 'line', | |
showSymbol: false, | |
sampling: 'lttb', | |
zlevel: 3, | |
lineStyle: { | |
width: 3 | |
}, | |
},*/ | |
]; | |
let xAxis = { | |
type: 'category', | |
data: dates, | |
} | |
var option = { | |
xAxis: xAxis, | |
series: series, | |
yAxis: { | |
type: 'value', | |
min: 'dataMin', | |
}, | |
animation: false, | |
dataZoom: [ | |
{ | |
type: 'slider', | |
show: true, | |
xAxisIndex: [0], | |
start: 80, | |
end: 100 | |
}, | |
], | |
tooltip: { | |
trigger: 'axis', | |
axisPointer: { | |
type: 'line' | |
}, | |
position: { top: 0, right: 0 }, | |
}, | |
}; | |
option && myChart.setOption(option); | |
// Add event listener to pause data collection on spacebar | |
document.addEventListener('keydown', (event) => { | |
if (event.keyCode == 32) { | |
pauseGraph = !pauseGraph; | |
// Consume the event so it doesn't get handled twice | |
event.preventDefault(); | |
} | |
}, false); | |
const mcuAwakeDisplay = document.getElementById("mcuAwakeDisplay"); | |
const mcuLoadDisplay = document.getElementById("mcuLoadDisplay"); | |
const gramsDisplay = document.getElementById("gramsDisplay"); | |
const serverInput = document.getElementById('serverInput'); | |
const serverFormFields = document.getElementById('serverFormFields'); | |
const serverForm = document.getElementById('serverForm'); | |
const loadCellLogButton = document.getElementById('load_cell_log'); | |
async function fetchDeadband(server) { | |
let jsonDoc = fetch("http://" + server + "/printer/objects/query?load_cell_endstop"); | |
deadband = (await (await jsonDoc).json()).result.status.load_cell_endstop.deadband; | |
} | |
async function oneshotToken(server) { | |
let token = fetch("http://" + server + "/access/oneshot_token"); | |
return (await (await token).json()).result; | |
} | |
let id = Date.now(); | |
function nextId() { | |
id += 1; | |
return id; | |
} | |
function subscribeObject(socket) { | |
const id = nextId(); | |
let message = JSON.stringify({ | |
"jsonrpc": "2.0", | |
"method": "printer.objects.subscribe", | |
"params": { | |
"objects": { | |
"load_cell ads1263 tool1_adc": null, | |
"mcu": null | |
} | |
}, | |
"id": id | |
} | |
); | |
return [id, socket.send(message)]; | |
}; | |
function addLoadCellData(data) { | |
let sampleGroups = data.samples; | |
delete data["samples"]; | |
// copy any state data keys | |
if (Object.keys(data).length !== 0) { | |
load_cell_state = {...load_cell_state, ...data}; | |
console.log(load_cell_state); | |
} | |
if (pauseGraph || !sampleGroups) { | |
return; | |
} | |
let grams = null; | |
var tare = load_cell_state.tare_counts; | |
sampleGroups.forEach(sampleGroup => { | |
sampleGroup.forEach(sample => { | |
dates.push(new Date(sample[0])); | |
loadCellSamples.push(sample[1] - tare); | |
grams = (sample[1] - tare) / load_cell_state.counts_per_gram; | |
}); | |
}); | |
if (grams != null) { | |
gramsDisplay.textContent = "" + Math.round(grams) + "g"; | |
} | |
while (dates.length > 8000) { | |
dates.shift(); | |
loadCellSamples.shift(); | |
} | |
myChart.setOption({ | |
xAxis: xAxis, | |
series: series | |
}); | |
} | |
function updateMcuLoad(data) { | |
const STATS_INTERVAL = 5.0; | |
const TASK_MAX = 0.0025; | |
// this is awake ticks/frequency, so its a % of available clock cycles | |
let mcuAwake = ((data.last_stats.mcu_awake / STATS_INTERVAL) * 100.0).toFixed(2); | |
let mcuLoad = data.last_stats.mcu_task_avg + (3 * data.last_stats.mcu_task_stddev); | |
mcuLoad = (100.0 * mcuLoad / TASK_MAX).toFixed(2); | |
mcuAwakeDisplay.textContent = "" + (mcuAwake) + "%"; | |
mcuLoadDisplay.textContent = "" + (mcuLoad) + "%"; | |
} | |
function handleMessage(messageEvent) { | |
payload = JSON.parse(messageEvent.data); | |
if (payload && payload.method === "notify_proc_stat_update" && payload.params) { | |
} else if (payload.method === "notify_status_update" || payload.result) { | |
var keys = null; | |
if (payload.result) { | |
keys = payload.result.status; | |
} else { | |
keys = payload.params[0]; | |
} | |
if (keys['mcu']) { | |
updateMcuLoad(keys.mcu); | |
} | |
if (keys['load_cell']) { | |
addLoadCellData(keys.load_cell); | |
} | |
if (keys['load_cell_endstop']) { | |
} | |
} | |
} | |
async function openSocket(server) { | |
let token = await oneshotToken(server) | |
if (!token) { | |
throw("oneshotToken error"); | |
} | |
const socket_url = "ws://" + server + "/websocket?token=" + token; | |
//const socket_url = "ws://" + server + "/klippysocket?token=" + token; | |
console.log(socket_url); | |
const socket = new WebSocket(socket_url); | |
let opened, open = new Promise((resolve) => (opened = resolve)); | |
// Callback of even listener used to resolve promise. | |
socket.addEventListener("open", (open) => { | |
opened(); | |
}); | |
await open; | |
return socket; | |
} | |
async function connect(event) { | |
event.preventDefault(); | |
serverFormFields.disabled = true; | |
const server = serverInput.value; | |
let socket = await openSocket(server); | |
socket.addEventListener('message', event => handleMessage(event)); | |
fetchDeadband(server); | |
subscribeObject(socket); | |
} | |
serverForm.addEventListener('submit', connect); | |
// Function to download data to a file | |
function download(data, filename, mimeType) { | |
var file = new Blob([data], {type: mimeType}); | |
var a = document.createElement("a"), | |
url = URL.createObjectURL(file); | |
a.href = url; | |
a.download = filename; | |
document.body.appendChild(a); | |
a.click(); | |
setTimeout(function() { | |
document.body.removeChild(a); | |
window.URL.revokeObjectURL(url); | |
}, 0); | |
} | |
function download_log() { | |
let logCsv = 'DateTime,SampleInRawCounts\n'; | |
logCsv += dates.join(','); | |
logCsv += '\n'; | |
logCsv += loadCellSamples.join(','); | |
logCsv += '\n'; | |
download(logCsv, 'load_cell_log.csv', 'text/csv'); | |
} | |
loadCellLogButton.addEventListener('click', download_log); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment