Skip to content

Commit

Permalink
Add an OSC log widget
Browse files Browse the repository at this point in the history
Signed-off-by: Axel Boberg <[email protected]>
  • Loading branch information
axelboberg committed Feb 21, 2024
1 parent 81a50da commit 6a867e1
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 0 deletions.
3 changes: 3 additions & 0 deletions plugins/osc/app/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as SharedContext from './sharedContext'
import * as Settings from './views/Settings'

import { InspectorTarget } from './views/InspectorTarget'
import { WidgetLog } from './views/WidgetLog'

export default function App () {
const [view, setView] = React.useState()
Expand All @@ -18,6 +19,8 @@ export default function App () {
{
(function () {
switch (view) {
case 'widget/log':
return <WidgetLog />
case 'inspector/target':
return <InspectorTarget />
case 'settings/targets':
Expand Down
20 changes: 20 additions & 0 deletions plugins/osc/app/components/LogHeader/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'
import './style.css'

export const LogHeader = ({ data = {}, onChange = () => {} }) => {
function handleDataChange (key, value) {
onChange({
...data,
[key]: value
})
}

return (
<header className='LogHeader'>
<div className='LogHeader-setting'>
<input type='checkbox' checked={data?.autoScroll || false} onChange={e => handleDataChange('autoScroll', e.target.checked)} />
<div className='LogHeader-settingLabel'>Auto scroll</div>
</div>
</header>
)
}
14 changes: 14 additions & 0 deletions plugins/osc/app/components/LogHeader/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.LogHeader {
width: 100%;
padding: 0 10px 10px;

border-bottom: 1px solid var(--base-color--shade1);
}

.LogHeader-setting {
display: flex;
}

.LogHeader-settingLabel {
margin-left: 10px;
}
24 changes: 24 additions & 0 deletions plugins/osc/app/components/LogItem/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'
import './style.css'

function zeroPad (n) {
if (n < 10) {
return `0${n}`
}
return `${n}`
}

function formatTime (ms) {
const d = new Date(ms)
return `${zeroPad(d.getHours())}:${zeroPad(d.getMinutes())}:${zeroPad(d.getSeconds())}.${d.getMilliseconds()}`
}

export const LogItem = ({ item = {} }) => {
return (
<div className='LogItem' data-direction={item?.direction}>
<div className='LogItem-timestamp'>{formatTime(item?.timestamp)}</div>
<div className='LogItem-direction'>| {item?.direction === 'in' ? '>>' : '<<'}</div>
<div className='LogItem-address'>{item?.address}</div>
</div>
)
}
22 changes: 22 additions & 0 deletions plugins/osc/app/components/LogItem/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.LogItem {
display: flex;
width: 100%;

white-space: nowrap;
}

.LogItem > div {
margin-left: 10px;
}

.LogItem[data-direction='in']{
color: var(--base-color--accent1);
}

.LogItem-timestamp {
width: 100px;
}

.LogItem:nth-child(odd) {
background: var(--base-color--shade1);
}
6 changes: 6 additions & 0 deletions plugins/osc/app/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ html, body {
#root {
display: contents;
}

.LogList {
height: calc(100% - 30px);
overflow-y: scroll;
overflow-x: hidden;
}
51 changes: 51 additions & 0 deletions plugins/osc/app/views/WidgetLog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'

import { SharedContext } from '../sharedContext'

import { LogHeader } from '../components/LogHeader'
import { LogItem } from '../components/LogItem'

const PLUGIN_NAME = 'bridge-plugin-osc'

const INITIAL_SETTINGS = {
autoScroll: true
}

function scrollToBottom (el) {
el.scrollTo(0, el.scrollHeight)
}

export const WidgetLog = () => {
const [state] = React.useContext(SharedContext)
const [settings, setSettings] = React.useState(INITIAL_SETTINGS)

const scrollRef = React.useRef()

const items = React.useMemo(() => {
return state?.plugins?.[PLUGIN_NAME]?.log || []
}, [state?.plugins?.[PLUGIN_NAME]?.log])

React.useEffect(() => {
if (!settings?.autoScroll) {
return
}
scrollToBottom(scrollRef.current)
}, [settings?.autoScroll, items.length])

function handleSettingsChange (newSettings) {
setSettings(newSettings)
}

return (
<>
<LogHeader data={settings} onChange={newSettings => handleSettingsChange(newSettings)} />
<div ref={scrollRef} className='LogList'>
{
items.map((item, i) => {
return <LogItem key={i} item={item} />
})
}
</div>
</>
)
}
22 changes: 22 additions & 0 deletions plugins/osc/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const TCPTransport = require('./lib/TCPTransport')
const handlers = require('./lib/handlers')
const commands = require('./lib/commands')
const types = require('./lib/types')
const log = require('./lib/log')

const Router = require('obj-router')
const router = new Router(handlers)
Expand Down Expand Up @@ -159,6 +160,11 @@ function setupServer (port = DEFAULT_SERVER_PORT, address, mode) {
server = new Server(transport)
server.on('message', async osc => {
try {
log.addLog({
timestamp: Date.now(),
direction: 'in',
address: osc.address
})
await router.execute(osc.address, osc)
} catch (e) {
logger.warn('Failed to execute command', osc.address)
Expand Down Expand Up @@ -224,6 +230,11 @@ async function playTrigger (item) {
})

UDPClient.send(target?.host, target?.port, message)
log.addLog({
timestamp: Date.now(),
direction: 'out',
address: data?.address
})
}

exports.activate = async () => {
Expand All @@ -237,6 +248,17 @@ exports.activate = async () => {
*/
types.init(htmlPath)

/*
Register the
log widget
*/
bridge.widgets.registerWidget({
id: 'bridge.plugins.osc.log',
name: 'OSC log',
uri: `${htmlPath}?path=widget/log`,
description: 'Log the most recent incoming OSC traffic'
})

/*
Register the targets setting as
soon as the widget is setup
Expand Down
62 changes: 62 additions & 0 deletions plugins/osc/lib/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: 2024 Sveriges Television AB
//
// SPDX-License-Identifier: MIT

const bridge = require('bridge')
const manifest = require('../package.json')

/**
* Define the max length of the log stack,
* the oldest items will get pushed off the
* stack when this limit is reached
* @type { Number }
*/
const STACK_MAX_LENGTH = 100

let stack = []
let didLoadStackFromState = false

/**
* Add a log to the stack
*
* @param {{
* timestamp: Number,
* direction: 'in' | 'out',
* address: String
* }} logInit
*/
function addLog (logInit) {
stack.splice(0, stack.length + 1 - STACK_MAX_LENGTH)
stack.push(logInit)

if (didLoadStackFromState) {
return bridge.state.apply({
plugins: {
[manifest.name]: {
log: { $replace: stack }
}
}
})
}

return bridge.state.apply({
plugins: {
[manifest.name]: {
log: stack
}
}
})
}
exports.addLog = addLog

/*
If available read the stack
from the state on init
*/
;(async function () {
const currentStack = await bridge.state.get(`plugins.${manifest.name}.log`)
if (Array.isArray(currentStack)) {
didLoadStackFromState = true
stack = currentStack
}
})()

0 comments on commit 6a867e1

Please sign in to comment.