web
This commit is contained in:
parent
fc516343ed
commit
ed5c03ffec
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 63 KiB |
@ -2,9 +2,8 @@
|
||||
|
||||
Seven-channel multiple thermocouple reader and logger HAT for the Raspberry Pi.
|
||||
|
||||

|
||||
|
||||
This software supports the following thermocouple types:
|
||||
|
||||
- B
|
||||
- E
|
||||
- J
|
||||
@ -15,6 +14,7 @@ This software supports the following thermocouple types:
|
||||
- T
|
||||
|
||||
Features not yet implemented but planned:
|
||||
|
||||
- More database logging options than SQLite3
|
||||
- Advanced filtering, incl. user-selectable filtering stages and sample sizes
|
||||
- Several logging options at once, one local, one or more remote
|
||||
@ -24,5 +24,6 @@ Features not yet implemented but planned:
|
||||
- Request dumps from filter stages for noise study
|
||||
- ... and more!
|
||||
|
||||
|
||||
Note: Most thermocouples that have a coloured and a white lead, usually have the coloured one as the positive.
|
||||
|
||||

|
||||
|
@ -25,9 +25,11 @@ filter.samples = 20
|
||||
|
||||
[Channel_3]
|
||||
type = 'T'
|
||||
gain = 112.28821
|
||||
|
||||
[Channel_4]
|
||||
type = 'K'
|
||||
gain = 2.8129883e2
|
||||
|
||||
[Channel_6]
|
||||
type = 'J'
|
||||
|
10
main.go
10
main.go
@ -28,29 +28,29 @@ func main() {
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGQUIT)
|
||||
|
||||
config, err := config.Load()
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading config: %v", err)
|
||||
}
|
||||
|
||||
pcbPort, err = pcb.NewAdapter(config)
|
||||
pcbPort, err = pcb.NewAdapter(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal error: %v\n", err)
|
||||
}
|
||||
defer pcbPort.Deinit()
|
||||
|
||||
corePort, err = core.NewAdapter(pcbPort, config)
|
||||
corePort, err = core.NewAdapter(pcbPort, cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Error starting TH7 Adapter: %v\n", err)
|
||||
}
|
||||
|
||||
dbPort, err = db.NewAdapter(corePort, config)
|
||||
dbPort, err = db.NewAdapter(corePort, cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal error: %v\n", err)
|
||||
}
|
||||
defer dbPort.Close()
|
||||
|
||||
webPort = web.NewGinAdapter(corePort, config.Board.Port)
|
||||
webPort = web.NewGinAdapter(corePort, cfg)
|
||||
go webPort.Run()
|
||||
|
||||
sig := <-kill
|
||||
|
@ -1,34 +1,91 @@
|
||||
const vdd = document.getElementById('vdd');
|
||||
const vref = document.getElementById('vref');
|
||||
const vadj = document.getElementById('vadj');
|
||||
const ts = document.getElementById('timestamp');
|
||||
const table = document.getElementById('data');
|
||||
const thermocoupleTypeTable = ["uV", "B", "E", "J", "K", "N", "R", "S", "T"];
|
||||
|
||||
let old_channel_count=0;
|
||||
const elements = {
|
||||
read: {
|
||||
vdd: document.getElementById("vdd"),
|
||||
vref: document.getElementById("vref"),
|
||||
vadj: document.getElementById("vadj"),
|
||||
ts: document.getElementById("timestamp"),
|
||||
table: document.getElementById("data"),
|
||||
},
|
||||
config: {
|
||||
table: document.getElementById("thermocouple"),
|
||||
},
|
||||
};
|
||||
|
||||
async function update () {
|
||||
let readOldChannelCount = 0;
|
||||
let configOldChannelCount = 0;
|
||||
|
||||
data = await fetch("/data").then((data)=>{return data.json()});
|
||||
vdd.innerHTML = "V<sub>DD</sub>: " + parseFloat(data["ratio"]["pivdd"]);
|
||||
vref.innerHTML = "V<sub>REF</sub>: " + parseFloat(data["ratio"]["vref"]);
|
||||
vadj.innerHTML = "V<sub>ADJ</sub>: " + parseFloat(data["ratio"]["vadj"]);
|
||||
ts.innerHTML = "Last updated: " + data["time"];
|
||||
for (let i=0; i<old_channel_count; i++) {
|
||||
table.deleteRow(-1);
|
||||
async function fetchData(url) {
|
||||
const response = await fetch(url);
|
||||
return response.json();
|
||||
}
|
||||
old_channel_count =0;
|
||||
|
||||
data["channels"].forEach(e => {
|
||||
let unit = e["unit"];
|
||||
let value = e["value"];
|
||||
let channel = e["id"];
|
||||
function clearTableBody(tableType) {
|
||||
const table = elements[tableType].table;
|
||||
const tbody = table.querySelector("tbody");
|
||||
|
||||
let row = table.insertRow(-1);
|
||||
row.insertCell(0).innerText = channel;
|
||||
row.insertCell(1).innerText = value;
|
||||
row.insertCell(2).innerText = unit;
|
||||
old_channel_count++;
|
||||
if (tbody) {
|
||||
tbody.innerHTML = "";
|
||||
} else {
|
||||
// If tbody doesn't exist, recreate it
|
||||
const newTbody = document.createElement("tbody");
|
||||
table.appendChild(newTbody);
|
||||
}
|
||||
}
|
||||
|
||||
function updateTable(data, tableType, oldChannelCount, updateFunction) {
|
||||
clearTableBody(tableType);
|
||||
|
||||
oldChannelCount = 0;
|
||||
|
||||
data.channels.forEach((channel) => {
|
||||
updateFunction(channel, tableType);
|
||||
oldChannelCount++;
|
||||
});
|
||||
}
|
||||
update();
|
||||
setInterval(update, 1000);
|
||||
|
||||
function updateReadTable(channel, tableType) {
|
||||
const { value, id } = channel;
|
||||
|
||||
const row = elements[tableType].table.querySelector("tbody").insertRow(-1);
|
||||
row.insertCell(0).innerText = id;
|
||||
row.insertCell(1).innerText = value;
|
||||
}
|
||||
|
||||
function updateConfigTable(channel, tableType) {
|
||||
const { id, thermocouple, gain, offset, filter } = channel;
|
||||
|
||||
const row = elements[tableType].table.querySelector("tbody").insertRow(-1);
|
||||
row.insertCell(0).innerText = id;
|
||||
row.insertCell(1).innerText = thermocoupleTypeTable[thermocouple];
|
||||
row.insertCell(2).innerText = gain;
|
||||
row.insertCell(3).innerText = offset;
|
||||
row.insertCell(4).innerText = filter.sample_size;
|
||||
row.insertCell(5).innerText = filter.type;
|
||||
}
|
||||
|
||||
function updateOthers(data) {
|
||||
const { ratio, time } = data;
|
||||
elements.read.ts.innerText = time;
|
||||
elements.read.vref.innerHTML = `V<sub>REF</sub>: ${ratio.vref}`;
|
||||
elements.read.vadj.innerHTML = `V<sub>ADJ</sub>: ${ratio.vadj}`;
|
||||
elements.read.vdd.innerHTML = `V<sub>DD</sub>: ${ratio.pivdd}`;
|
||||
}
|
||||
|
||||
async function updateRead() {
|
||||
const data = await fetchData("/data");
|
||||
updateTable(data, "read", readOldChannelCount, updateReadTable);
|
||||
updateOthers(data);
|
||||
}
|
||||
|
||||
async function updateConfig() {
|
||||
const data = await fetchData("/config");
|
||||
updateTable(data.config, "config", configOldChannelCount, updateConfigTable);
|
||||
}
|
||||
|
||||
updateRead();
|
||||
updateConfig();
|
||||
|
||||
setInterval(updateRead, 1000);
|
||||
setInterval(updateConfig, 5000);
|
||||
|
@ -6,22 +6,87 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.title}}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #4CAF50;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#vdd, #vref, #vadj, #timestamp {
|
||||
margin-top: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#thermocouple {
|
||||
margin-top: 20px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#thermocouple td {
|
||||
min-width: 6rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>TH7</h1>
|
||||
|
||||
<table id="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Channel</th>
|
||||
<th>Value</th>
|
||||
<th>Unit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
<p id="vdd">V<sub>DD</sub>:</p>
|
||||
<p id="vref">V<sub>REF</sub>:</p>
|
||||
<p id="vadj">V<sub>ADJ</sub>:</p>
|
||||
<p id="timestamp">Last updated:</p>
|
||||
|
||||
<h2>Thermocouple config</h2>
|
||||
|
||||
<table id="thermocouple">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Channel</th>
|
||||
<th>Type</th>
|
||||
<th>Gain</th>
|
||||
<th>Offset</th>
|
||||
<th>Filter size</th>
|
||||
<th>Filter type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
<script src="/assets/data-updater.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{{ end }}
|
14
web/gin.go
14
web/gin.go
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"th7/data/config"
|
||||
"th7/data/core"
|
||||
"th7/ports"
|
||||
)
|
||||
@ -15,6 +16,7 @@ import (
|
||||
type GinAdapter struct {
|
||||
router *gin.Engine
|
||||
corePort ports.CorePort
|
||||
cfg config.Config
|
||||
port int
|
||||
}
|
||||
|
||||
@ -89,11 +91,18 @@ func (g *GinAdapter) IndexHandler(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func NewGinAdapter(corePort ports.CorePort, port int) *GinAdapter {
|
||||
func (g *GinAdapter) ConfigHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"config": g.cfg,
|
||||
})
|
||||
}
|
||||
|
||||
func NewGinAdapter(corePort ports.CorePort, cfg config.Config) *GinAdapter {
|
||||
var adapter GinAdapter
|
||||
|
||||
adapter.corePort = corePort
|
||||
adapter.port = port
|
||||
adapter.port = cfg.Board.Port
|
||||
adapter.cfg = cfg
|
||||
|
||||
//adapter.router = gin.Default()
|
||||
adapter.router = gin.New()
|
||||
@ -104,6 +113,7 @@ func NewGinAdapter(corePort ports.CorePort, port int) *GinAdapter {
|
||||
adapter.router.GET("/channel/:id", adapter.ChannelByIDHandler)
|
||||
adapter.router.GET("/time", adapter.TimestampHandler)
|
||||
adapter.router.GET("/data", adapter.DataHandler)
|
||||
adapter.router.GET("/config", adapter.ConfigHandler)
|
||||
|
||||
adapter.router.LoadHTMLGlob("./templates/*.tmpl")
|
||||
adapter.router.Static("/assets", "./static")
|
||||
|
Loading…
Reference in New Issue
Block a user