Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ nul

# debug artefacts
*.sln
test/server-test-config/debug
test/server-test-config/debug
120 changes: 110 additions & 10 deletions packages/server-admin-ui/src/views/ServerConfig/BasicProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ interface TypeComponentProps {
value: ProviderValue
onChange: OnChangeHandler
hasAnalyzer?: boolean
hasWasm?: boolean
}

// Defined outside component to avoid recreation on render
Expand All @@ -151,6 +152,7 @@ const TYPE_COMPONENTS: Record<
React.ComponentType<TypeComponentProps>
> = {
NMEA2000: NMEA2000,
J1939: J1939,
NMEA0183: NMEA0183,
SignalK: SignalK,
Seatalk: Seatalk,
Expand All @@ -163,6 +165,7 @@ export default function BasicProvider({
onPropChange
}: BasicProviderProps) {
const [hasAnalyzer, setHasAnalyzer] = useState(false)
const [hasWasm, setHasWasm] = useState(false)

useEffect(() => {
fetch(`${window.serverRoutesPrefix}/hasAnalyzer`, {
Expand All @@ -172,6 +175,17 @@ export default function BasicProvider({
.then((data) => {
setHasAnalyzer(data)
})
fetch(`${window.serverRoutesPrefix}/hasWasm`, {
credentials: 'include'
})
.then((response) => response.json())
.then((data) => {
setHasWasm(data)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Capability probe: a failure means "no wasm", which is already the
// initial state. Swallow it rather than leaving an unhandled
// rejection in the console — the wasm options stay hidden either way.
.catch(() => setHasWasm(false))
}, [])

const TypeComponent = TYPE_COMPONENTS[value.type]
Expand All @@ -191,6 +205,11 @@ export default function BasicProvider({
onChange={(event) => onChange(event)}
>
<option value="NMEA2000">NMEA 2000</option>
{/* J1939 has no canboatjs fallback — without wasm there
is no source that could serve the connection. */}
<option value="J1939" disabled={!hasWasm}>
J1939
</option>
<option value="NMEA0183">NMEA 0183</option>
<option value="SignalK">Signal K</option>
<option value="Seatalk">Seatalk (GPIO)</option>
Expand Down Expand Up @@ -220,7 +239,9 @@ export default function BasicProvider({
</Form.Label>
</Col>
</Form.Group>
{value.type !== 'FileStream' && (
{/* No logging for J1939: the CAN source emits frame objects with
no text form, so logged lines would be unreplayable junk. */}
{value.type !== 'FileStream' && value.type !== 'J1939' && (
<LoggingInput value={value} onChange={onChange} />
)}
<Form.Group as={Row} className="mb-3">
Expand Down Expand Up @@ -255,6 +276,7 @@ export default function BasicProvider({
value={value}
onChange={onChange}
hasAnalyzer={hasAnalyzer}
hasWasm={hasWasm}
/>
)}
<OverrideTimestamps value={value.options} onChange={onChange} />
Expand Down Expand Up @@ -910,6 +932,7 @@ function DataTypeInput({
value: ProviderValue
onChange: OnChangeHandler
hasAnalyzer?: boolean
hasWasm?: boolean
}) {
return (
<Form.Group as={Row} className="mb-3">
Expand Down Expand Up @@ -1670,7 +1693,12 @@ function CollectNetworkStatsInput({
)
}

function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
function NMEA2000({
value,
onChange,
hasAnalyzer,
hasWasm
}: TypeComponentProps) {
return (
<div>
<Form.Group as={Row} className="mb-3">
Expand All @@ -1689,6 +1717,9 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
<option value="ngt-1" disabled={!hasAnalyzer}>
Actisense NGT-1 (canboat)
</option>
<option value="ngt-1-wasm" disabled={!hasWasm}>
Actisense NGT-1 (canboat wasm)
</option>
<option value="ikonvert-canboatjs">iKonvert (canboatjs)</option>
<option value="navlink2-tcp-canboatjs">NavLink2 (canboatjs)</option>
<option value="canboat-csv-canboatjs">
Expand All @@ -1697,25 +1728,40 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
<option value="ydwg02-canboatjs">
Yacht Devices RAW TCP (canboatjs)
</option>
<option value="ydwg02-wasm" disabled={!hasWasm}>
Yacht Devices RAW TCP (canboat wasm)
</option>
<option value="ydwg02-udp-canboatjs">
Yacht Devices RAW UDP (canboatjs)
</option>
<option value="ydwg02-usb-canboatjs">
Yacht Devices RAW USB (canboatjs)
</option>
<option value="canbus-canboatjs">Canbus (canboatjs)</option>
<option value="canbus-wasm" disabled={!hasWasm}>
Canbus (canboat wasm)
</option>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<option value="n2k-ip-gateway-canboatjs">
N2K IP Gateway (canboatjs)
</option>
<option value="w2k-1-n2k-ascii-canboatjs">
W2K-1 N2K ASCII (canboatjs)
</option>
<option value="w2k-1-n2k-ascii-wasm" disabled={!hasWasm}>
W2K-1 N2K ASCII (canboat wasm)
</option>
<option value="w2k-1-n2k-actisense-canboatjs">
W2K-1 N2K ACTISENSE (canboatjs)
</option>
<option value="maretron-ipg-canboatjs">
Maretron IPG 100 (canboatjs)
</option>
<option value="maretron-ipg-wasm" disabled={!hasWasm}>
Maretron IPG 100 (canboat wasm)
</option>
<option value="w2k-1-n2k-actisense-wasm" disabled={!hasWasm}>
W2K-1 N2K ACTISENSE (canboat wasm)
</option>
<option value="canbus" disabled={!hasAnalyzer}>
Canbus (canboat)
</option>
Expand All @@ -1724,14 +1770,16 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
</Form.Group>
{(value.options.type === 'ngt-1' ||
value.options.type === 'ngt-1-canboatjs' ||
value.options.type === 'ngt-1-wasm' ||
value.options.type === 'ydwg02-usb-canboatjs' ||
value.options.type === 'ikonvert-canboatjs') && (
<div>
<DeviceInput value={value.options} onChange={onChange} />
<BaudRateInputCanboat value={value.options} onChange={onChange} />
</div>
)}
{value.options.type === 'ydwg02-canboatjs' && (
{(value.options.type === 'ydwg02-canboatjs' ||
value.options.type === 'ydwg02-wasm') && (
<div>
<HostInput value={value.options} onChange={onChange} />
<PortInput value={value.options} onChange={onChange} />
Expand Down Expand Up @@ -1775,7 +1823,8 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
</div>
)}
{(value.options.type === 'canbus' ||
value.options.type === 'canbus-canboatjs') && (
value.options.type === 'canbus-canboatjs' ||
value.options.type === 'canbus-wasm') && (
<div>
<TextInput
title="Interface"
Expand All @@ -1794,7 +1843,7 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
<TextInput
title="ManufacturerCode"
name="options.mfgCode"
helpText="Example: 999 - Unknown (default), 0 - Internal, or any other mabufacturer code to emulate. Leave empty for default 999. Set to 0 if you have problem with source identification on some B&G MFD's after SignalK restart."
helpText="Example: 999 - Unknown (default), 0 - Internal, or any other manufacturer code to emulate. Leave empty for default 999. Set to 0 if you have problem with source identification on some B&G MFD's after SignalK restart."
value={value.options.mfgCode}
onChange={onChange}
/>
Expand All @@ -1807,7 +1856,9 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
<CollectNetworkStatsInput value={value.options} onChange={onChange} />
)}
{(value.options.type === 'w2k-1-n2k-ascii-canboatjs' ||
value.options.type === 'w2k-1-n2k-actisense-canboatjs') && (
value.options.type === 'w2k-1-n2k-actisense-canboatjs' ||
value.options.type === 'w2k-1-n2k-ascii-wasm' ||
value.options.type === 'w2k-1-n2k-actisense-wasm') && (
<div>
<HostInput value={value.options} onChange={onChange} />
<PortInput value={value.options} onChange={onChange} />
Expand Down Expand Up @@ -1852,7 +1903,8 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
</div>
</div>
)}
{value.options.type === 'maretron-ipg-canboatjs' && (
{(value.options.type === 'maretron-ipg-canboatjs' ||
value.options.type === 'maretron-ipg-wasm') && (
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<div>
<HostInput value={value.options} onChange={onChange} />
<PortInput value={value.options} onChange={onChange} />
Expand All @@ -1876,19 +1928,28 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
</Form.Group>
<div className="text-muted small mt-1 mb-2">
Maretron IPG 100 Ethernet gateway. Default TCP port 6543. Uses
Maretron&apos;s 0xA5-framed binary protocol (handled by canboatjs).
Maretron&apos;s 0xA5-framed binary protocol.
</div>
</div>
)}
{/* Address-claim identity. canbus-wasm runs through the same canbus
transport and candevice as canbus-canboatjs — only the decoder
downstream differs — so it takes the same identity options. */}
{value.options.type !== undefined &&
value.options.type.indexOf('canboatjs') !== -1 && (
(value.options.type.indexOf('canboatjs') !== -1 ||
value.options.type === 'canbus-wasm') && (
<>
<UseCanNameInput value={value.options} onChange={onChange} />
<DeviceInstanceInput value={value.options} onChange={onChange} />
<SystemInstanceInput value={value.options} onChange={onChange} />
<CamelCaseCompatInput value={value.options} onChange={onChange} />
</>
)}
{/* CamelCase compat is a canboatjs decoder option; the wasm element
pins camelCase in its compat shim and ignores the flag. */}
{value.options.type !== undefined &&
value.options.type.indexOf('canboatjs') !== -1 && (
<CamelCaseCompatInput value={value.options} onChange={onChange} />
)}
{value.options.type !== undefined &&
/^ydwg02/.test(value.options.type) && (
<CreateDeviceInput value={value.options} onChange={onChange} />
Expand All @@ -1897,6 +1958,45 @@ function NMEA2000({ value, onChange, hasAnalyzer }: TypeComponentProps) {
)
}

function J1939({ value, onChange, hasWasm }: TypeComponentProps) {
return (
<div>
<Form.Group as={Row} className="mb-3">
<Col md="3">
<Form.Label htmlFor="j1939-source-type">J1939 Source</Form.Label>
</Col>
<Col xs="12" md="3">
<Form.Select
id="j1939-source-type"
value={value.options.type || 'none'}
name="options.type"
onChange={(event) => onChange(event)}
>
<option value="none">Select a source</option>
<option value="j1939-wasm" disabled={!hasWasm}>
Canbus listen-only (canboat wasm)
</option>
</Form.Select>
</Col>
</Form.Group>
{value.options.type === 'j1939-wasm' && (
<TextInput
title="Interface"
name="options.interface"
helpText="Example: can0"
value={value.options.interface}
onChange={onChange}
/>
)}
<div className="text-muted small mt-1 mb-2">
Listen-only: the server never transmits or claims an address on the
J1939 bus. Manufacturer names resolve from the ISO 11783/J1939 registry,
not the NMEA 2000 one.
</div>
</div>
)
}

function NMEA0183({ value, onChange }: TypeComponentProps) {
return (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ const ProvidersConfiguration: React.FC = () => {
set(updatedProvider, 'options.createDevice', true)
}
}
// Switching the Data Type restarts the options from that type's
// seed. Options are type-specific (source type, N2K identity,
// filters); carrying them across silently persists settings the
// new type's form never shows — e.g. useCanName on a J1939
// connection, or a source type the target type's select doesn't
// list. The type select only renders for new connections, so
// nothing configured is lost.
if (target.name === 'type' && typeof value === 'string') {
updatedProvider.options =
value === 'NMEA2000' ? { useCanName: true } : {}
}
setSelectedProvider(updatedProvider)
},
[selectedProvider]
Expand Down
Loading
Loading