Skip to content

Commit

Permalink
Lots of improvements, and initial github actions
Browse files Browse the repository at this point in the history
  • Loading branch information
scottbez1 committed Dec 18, 2023
1 parent ca53ee4 commit 0eb644e
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 61 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: JS

on:
push:
pull_request:
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: './out'

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
if: github.repository == 'scottbez1/webdp100' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
.next
out
20 changes: 19 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}

const isGithubActions = process.env.GITHUB_ACTIONS || false

let assetPrefix = ''
let basePath = ''

if (isGithubActions) {
// trim off `<owner>/`
const repo = process.env.GITHUB_REPOSITORY.replace(/.*?\//, '')

assetPrefix = `/${repo}/`
basePath = `/${repo}`
}

const nextConfig = {
assetPrefix,
basePath,
output: 'export',
}

module.exports = nextConfig
93 changes: 76 additions & 17 deletions src/app/dp100/dp100.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { FRAME_FUNC, Frame, inputReportDataToFrame } from "./hid-reports";
import { BasicInfo, BasicSet, basicInfoFromFrame, basicSetFrameData, basicSetFromFrame } from "./frame-data";
import { crc16modbus } from "crc";
Expand All @@ -11,20 +11,37 @@ export class DP100 {
this.device = device
}

private queue: Array<() => Promise<void>> = []
private runningTask: boolean = false

// TODO: Leaking memory?
private pendingTask: Promise<void> = Promise.resolve()
private enqueue(task: () => Promise<void>) {
this.queue.push(task)
this.serviceQueue()
}

private async serviceQueue() {
if (this.runningTask) {
return
}
this.runningTask = true;
try {
let task
while (task = this.queue.shift()) {
await task()
}
} finally {
this.runningTask = false
}
}

private async sendFrameForResponse(frame: Frame, expectedFunctionResponse: number) {
return new Promise<Frame>((reqResolve, reqReject) => {
this.pendingTask = this.pendingTask.then(async () => {
console.log('starting new task')
this.enqueue(async () => {
return new Promise<void>((taskResolve) => {
// TODO: timeout to prevent queue backup

const eventListener = (e: HIDInputReportEvent) => {
console.log(e.device.productName + ": got input report " + e.reportId);
console.debug(e.device.productName + ": got input report " + e.reportId);
const frame = inputReportDataToFrame(e.data.buffer)
if (frame !== null && frame.functionType === expectedFunctionResponse) {
success(frame)
Expand All @@ -35,12 +52,12 @@ export class DP100 {
reqResolve(result)
taskResolve()
}
const failure = (error: any) => {
this.device.removeEventListener('inputreport', eventListener)
reqReject(error)
taskResolve()
}
console.log('registering listener')
// const failure = (error: any) => {
// console.log('unregistering listener')
// this.device.removeEventListener('inputreport', eventListener)
// reqReject(error)
// taskResolve()
// }
this.device.addEventListener('inputreport', eventListener)
this.sendFrame(frame)
})
Expand All @@ -54,7 +71,7 @@ export class DP100 {
frame.functionType,
frame.sequence,
frame.dataLen,
...frame.data,
...(frame.data as any), // TODO: fix types
0,
0,
]);
Expand All @@ -63,7 +80,7 @@ export class DP100 {
const frameBufferDv = new DataView(frameBuffer.buffer, frameBuffer.byteOffset, frameBuffer.byteLength)
frameBufferDv.setUint16(frameBuffer.length - 2, checksum, true); // little-endian

console.log('sendReport', frameBuffer)
console.debug('sendReport', {functionType: frame.functionType})
await this.device.sendReport(0, frameBuffer);
}

Expand All @@ -79,7 +96,7 @@ export class DP100 {
}
const response = await this.sendFrameForResponse(frame, FRAME_FUNC.FRAME_BASIC_INFO)
const basicInfo = basicInfoFromFrame(response)
console.log('basic info', basicInfo)
console.debug('basic info', basicInfo)
return basicInfo
}

Expand All @@ -95,7 +112,7 @@ export class DP100 {
}
const response = await this.sendFrameForResponse(frame, FRAME_FUNC.FRAME_BASIC_SET)
const basicSet = basicSetFromFrame(response)
console.log('basic set', basicSet)
console.debug('basic set', basicSet)
return basicSet
}

Expand All @@ -111,7 +128,6 @@ export class DP100 {
data: frameData,
}
const response = await this.sendFrameForResponse(frame, FRAME_FUNC.FRAME_BASIC_SET)
console.log('setBasic response', response)
return response.data[0] === 1
}
}
Expand All @@ -121,3 +137,46 @@ export const useDP100 = (device: HIDDevice) => {
return new DP100(device)
}, [device])
}

type WithTimestamp<T> = T & {_ts: Date}

export function useInfoSubscription<T>(loadData: () => Promise<T> , delayMs: number): { data: WithTimestamp<T> | null, refresh: () => Promise<void> } {
const [data, setData] = useState<WithTimestamp<T> | null>(null)

const mountedRef = useRef<boolean>(false)
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const refresh = useMemo(() => {
return async () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
timeoutRef.current = null
}
if (!mountedRef.current) {
return
}
const newData = await loadData()
setData({
...newData,
_ts: new Date(),
})
if (!mountedRef.current) {
return
}
timeoutRef.current = setTimeout(refresh, delayMs)
}
}, [delayMs])

useEffect(() => {
mountedRef.current = true
refresh()
return () => {
mountedRef.current = false
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
timeoutRef.current = null
}
}
}, [refresh])

return { data, refresh }
}
2 changes: 0 additions & 2 deletions src/app/dp100/hid-reports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export type Frame = {
// TODO: clean up the array/buffer types?
export const inputReportDataToFrame = (buf: ArrayBuffer): Frame | null => {
const frameData = new Uint8Array(buf)
console.log(frameData);
const frameDv = new DataView(frameData.buffer, frameData.byteOffset, frameData.byteLength)

const dataLen = frameDv.getUint8(3);
Expand All @@ -41,7 +40,6 @@ export const inputReportDataToFrame = (buf: ArrayBuffer): Frame | null => {
data: frameData.slice(4, 4 + dataLen),
}
const checksum = frameDv.getUint16(4 + dataLen, true)
console.log('got frame', frame)
const computedChecksum = crc16modbus(frameData.slice(0, 4 + dataLen))
if (computedChecksum !== checksum) {
console.warn('checksum mismatch in received frame', { expected: computedChecksum, received: checksum })
Expand Down
Loading

0 comments on commit 0eb644e

Please sign in to comment.