#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
# Copyright (C) 2025 Bdale Garbee <bdale@gag.com>. GPLv3+
+#
import os
-import cherrypy
-import plotly.express as px
-import gpiod
-import iio
-import signal
-import sys
-import time
-
-# configuration for each channel on ADS8688
-OFFSET = "0"
-SCALE = "0.078127104"
-VOLTAGES = ['voltage0', 'voltage1', 'voltage2', 'voltage3', 'voltage4', 'voltage5', 'voltage6', 'voltage7']
-
-file_path = os.getcwd()
-from gpiod.line import Direction, Value
-
-def set_line_values(chip_path, line_values):
- value_str = {Value.ACTIVE: "Active", Value.INACTIVE: "Inactive"}
-
- request = gpiod.request_lines(
- chip_path,
- consumer=sys.argv[0],
- config={
- tuple(line_values.keys()): gpiod.LineSettings(direction=Direction.OUTPUT)
- },
- )
- request.set_values(line_values)
-def handler(signum, frame):
- set_line_values(
- "/dev/gpiochip0",
- { 4: Value.INACTIVE, # indicate 'not ready' to LPC
- 16: Value.INACTIVE, # pyro off
- 17: Value.INACTIVE, # alarm b off
- 20: Value.INACTIVE, # turn continuity LED off
- 21: Value.INACTIVE, # turn armed LED off
- 27: Value.INACTIVE # alarm a off
- }
- )
- sys.exit(0)
-
-# having systemd use SIGINT to avoid CherryPy consuming the kill signal
-signal.signal(signal.SIGINT, handler)
-
-class Root(object):
- @cherrypy.expose
+import cherrypy
+from cherrypy.lib.static import serve_file
- # define what happens on default (index) page
- def index(self):
- cherrypy.log("index")
- # some sort of demo data
- df = px.data.gapminder().query("country=='Canada'")
+path = os.path.abspath(os.path.dirname(__file__))
+config = {
+ 'global' : {
+ 'server.socket_host' : '127.0.0.1',
+ 'server.socket_port' : 8080,
+ 'server.thread_pool' : 8
+ }
+}
- # constrain height to 600 to avoid having to scroll for other info?
- fig = px.line(df, x="year", y="lifeExp", \
- title='Life expectancy in Canada', height=600)
+class App:
- # fig.to_html gives us the entire interactive graph as a python string!
- plotly_data = fig.to_html(full_html=False)
+ @cherrypy.expose
+ def index(self):
+ return serve_file(os.path.join(path, 'index.html'))
- # for now, just concatenate a header and a footer
- html_data = '<html> <body> <h1>QuantiMotor</h1>' + \
- plotly_data + \
- "<h2>Thats all folks!</h2></body> </html>"
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ def getData(self):
+ return {
+ 'foo' : 'bar',
+ 'baz' : 'another one'
+ }
- # give the complete html to the client
- return html_data
if __name__ == '__main__':
- ctx = iio.LocalContext()
- ctrl = ctx.find_device('ads8688')
-
- # initialize hardware
- set_line_values(
- "/dev/gpiochip0",
- {25: Value.ACTIVE, # take ADS8688 out of reset
- 4: Value.ACTIVE, # indicate 'ready' to LPC
- 16: Value.INACTIVE, # pyro off
- 17: Value.INACTIVE, # alarm b off
- 20: Value.INACTIVE, # turn continuity LED off
- 21: Value.INACTIVE, # turn armed LED off
- 27: Value.INACTIVE # alarm a off
- }
- )
-
- # configure ADC channels
- for id in VOLTAGES:
- chan = ctrl.find_channel(id)
- # must set scale before offset, so offset 0 is valid!
- chan.attrs['scale'].value = SCALE
- chan.attrs['offset'].value = OFFSET
-
- config = {
- 'global': {
- 'server.socket_host': '0.0.0.0',
- 'server.socket_port': 8080,
- },
- }
-
- cherrypy.quickstart(Root(), '/', config)
+ config = {
+ 'global': {
+ 'server.socket_host': '0.0.0.0',
+ 'server.socket_port': 80,
+ },
+ }
+
+ cherrypy.quickstart(App(), '/', config)
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv='content-type' content='text/html; charset=utf-8'>
+<title>CCMF</title>
+<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
+<script type='text/javascript'>
+ $(document).ready(function()
+ {
+ $('button').on('click', function()
+ {
+ var request = $.ajax({'url': '/getData'});
+ request.done(function(response)
+ {
+ $('#foo').text(response.foo);
+ $('#baz').text(response.baz);
+ });
+ request.fail(function(jqXHR, textStatus)
+ {
+ alert('Request failed: ' + textStatus);
+ });
+ })
+ });
+</script>
+</head>
+<body>
+ <button>make ajax call</button>
+ <h1>Foo</h1>
+ <div id='foo'></div>
+ <h1>Baz</h1>
+ <div id='baz'></div>
+</body>
+</html>
--- /dev/null
+#!/usr/bin/env python3
+# Copyright (C) 2025 Bdale Garbee <bdale@gag.com>. GPLv3+
+
+import os
+import cherrypy
+import plotly.express as px
+import gpiod
+import iio
+import signal
+import sys
+import time
+
+# configuration for each channel on ADS8688
+OFFSET = "0"
+SCALE = "0.078127104"
+VOLTAGES = ['voltage0', 'voltage1', 'voltage2', 'voltage3', 'voltage4', 'voltage5', 'voltage6', 'voltage7']
+
+file_path = os.getcwd()
+from gpiod.line import Direction, Value
+
+def set_line_values(chip_path, line_values):
+ value_str = {Value.ACTIVE: "Active", Value.INACTIVE: "Inactive"}
+
+ request = gpiod.request_lines(
+ chip_path,
+ consumer=sys.argv[0],
+ config={
+ tuple(line_values.keys()): gpiod.LineSettings(direction=Direction.OUTPUT)
+ },
+ )
+ request.set_values(line_values)
+
+def handler(signum, frame):
+ set_line_values(
+ "/dev/gpiochip0",
+ { 4: Value.INACTIVE, # indicate 'not ready' to LPC
+ 16: Value.INACTIVE, # pyro off
+ 17: Value.INACTIVE, # alarm b off
+ 20: Value.INACTIVE, # turn continuity LED off
+ 21: Value.INACTIVE, # turn armed LED off
+ 27: Value.INACTIVE # alarm a off
+ }
+ )
+ sys.exit(0)
+
+# having systemd use SIGINT to avoid CherryPy consuming the kill signal
+signal.signal(signal.SIGINT, handler)
+
+class Root(object):
+ @cherrypy.expose
+
+ # define what happens on default (index) page
+ def index(self):
+ cherrypy.log("index")
+
+ # some sort of demo data
+ df = px.data.gapminder().query("country=='Canada'")
+
+ # constrain height to 600 to avoid having to scroll for other info?
+ fig = px.line(df, x="year", y="lifeExp", \
+ title='Life expectancy in Canada', height=600)
+
+ # fig.to_html gives us the entire interactive graph as a python string!
+ plotly_data = fig.to_html(full_html=False)
+
+ # for now, just concatenate a header and a footer
+ html_data = '<html> <body> <h1>QuantiMotor</h1>' + \
+ plotly_data + \
+ "<h2>Thats all folks!</h2></body> </html>"
+
+ # give the complete html to the client
+ return html_data
+
+if __name__ == '__main__':
+ ctx = iio.LocalContext()
+ ctrl = ctx.find_device('ads8688')
+
+ # initialize hardware
+ set_line_values(
+ "/dev/gpiochip0",
+ {25: Value.ACTIVE, # take ADS8688 out of reset
+ 4: Value.ACTIVE, # indicate 'ready' to LPC
+ 16: Value.INACTIVE, # pyro off
+ 17: Value.INACTIVE, # alarm b off
+ 20: Value.INACTIVE, # turn continuity LED off
+ 21: Value.INACTIVE, # turn armed LED off
+ 27: Value.INACTIVE # alarm a off
+ }
+ )
+
+ # configure ADC channels
+ for id in VOLTAGES:
+ chan = ctrl.find_channel(id)
+ # must set scale before offset, so offset 0 is valid!
+ chan.attrs['scale'].value = SCALE
+ chan.attrs['offset'].value = OFFSET
+
+ config = {
+ 'global': {
+ 'server.socket_host': '0.0.0.0',
+ 'server.socket_port': 8080,
+ },
+ }
+
+ cherrypy.quickstart(Root(), '/', config)
+