Skip to content
Merged
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
20 changes: 20 additions & 0 deletions src/main/nodes/cockpit-snapshot/connection-summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,23 @@ describe('nodesSummary rows', () => {
expect([s.rows, s.more, s.footer]).toEqual([[], 0, 'healthy nodes hidden'])
})
})

describe('nodesSummary with no board of your cockpit (#160)', () => {
const panelNew = (nodeId: number) => node(nodeId, { source: 'new', identified: 'panel' })

it('points to the new boards on the bus — the Overview notice’s count — and where to add them', () => {
const one = summary([panelNew(41)])
expect([one.known, one.newCount]).toEqual([0, 1])
expect(one.empty).toBe('1 new board on the bus — add it from the Cockpit.')
const two = summary([panelNew(41), pdu(42, 'Left', { source: 'new', identified: 'pdu' })])
expect(two.newCount).toBe(2)
expect(two.empty).toBe('2 new boards on the bus — add them from the Cockpit.')
})

it('says the bus reports nothing only when it does: none on it, or none identified yet', () => {
expect(summary([]).empty).toBe('No nodes reported. Power on a PanelGroup.')
// Still identifying: not news yet (isReportableNew), so not counted.
const s = summary([node(41, { source: 'new' })])
expect([s.newCount, s.empty]).toEqual([0, 'No nodes reported. Power on a PanelGroup.'])
})
})
19 changes: 18 additions & 1 deletion src/main/nodes/cockpit-snapshot/summaries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
// adopted (node-model.ts newState), each one a board up and a warning, and an Attention item.
// What is on the bus is one rule (node-model.ts onBusNow, X2): while the link is down the tile says
// "no live node status", and the banner, the notice and Connected nodes say nothing.
// With none of your cockpit's boards to count, Connected nodes points to the new ones on the bus
// (#160): the same count as the Overview's notice and the Cockpit's "New on the bus".
// A Connected nodes row names its board's resolved power zone (D7, the same one the Cockpit reads):
// `PDU_Right`, `PDU_Right (hidden)`, else "no power zone".
import { hhmmss, lastSeenLabel, plural } from '@shared/format'
import {
COCKPIT_IDLE,
NO_NODES,
type AttentionItem,
type AttentionSummary,
type CockpitTile,
Expand Down Expand Up @@ -139,6 +142,16 @@ export function overviewSummary(

// ── Connection › Connected nodes ────────────────────────────────────────────

/**
* The card's line with no board of your cockpit to count: the new boards on the bus and where to
* add them (#160) — `1 new board on the bus — add it from the Cockpit.` — else that the bus
* reports nothing.
*/
function emptyLine(newCount: number): string {
if (newCount === 0) return NO_NODES
return `${plural(newCount, 'new board')} on the bus — add ${newCount === 1 ? 'it' : 'them'} from the Cockpit.`
}

/**
* Connected nodes: the counts and the rows that need attention, the Cockpit's strays included (X3:
* each one a board up and a warning, with its Attention row). Quiet while the link is down — it
Expand All @@ -157,6 +170,8 @@ export function nodesSummary(
const byId = new Map(views.map((v) => [v.entry.nodeId, v.entry]))
const items = attentionItems(views, outages, strays)
const healthy = band.online - band.warn
// The Overview's notice's count (the Cockpit's "New on the bus · N"): one rule, isReportableNew.
const newCount = newDevices(views, live)?.count ?? 0

const rows = items.slice(0, ATTENTION_SHOWN).map((i): SummaryRow => {
const e = byId.get(i.nodeId)!
Expand Down Expand Up @@ -202,6 +217,8 @@ export function nodesSummary(
watts: band.watts,
rows,
more,
footer
footer,
newCount,
empty: emptyLine(newCount)
}
}
2 changes: 1 addition & 1 deletion src/renderer/src/nodes/NodesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function NodesCard({ autoConnect }: { autoConnect: boolean }) {
{!linkUp ? (
<div className="hint">{deviceView(deviceState, { autoConnect }).nodesHint}</div>
) : sum.known === 0 ? (
<div className="hint">No nodes reported. Power on a PanelGroup.</div>
<div className="hint">{sum.empty}</div>
) : (
<>
<div className="nsum">
Expand Down
20 changes: 20 additions & 0 deletions src/renderer/test/t5-connection-nodes-card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,26 @@ describe('T5 Connection › nodes summary card (B63, B14, B37, X2)', () => {
expect(rowButtons()).toEqual([])
})

it('with none of your cockpit’s boards, it points to the new ones on the bus (#160)', async () => {
const T0 = Date.UTC(2026, 9, 16, 12, 0, 0)
const fresh = (n: number) => newBoard(n, T0, { identified: 'panel' })
const { sky } = await renderScenario(<Connection />, { device: connected(), model: model([]) })
expect(screen.getByText('No nodes reported. Power on a PanelGroup.')).toBeInTheDocument()
sky.emitScenario({ model: model([fresh(1)]) })
expect(
screen.getByText('1 new board on the bus — add it from the Cockpit.')
).toBeInTheDocument()
expect(screen.queryByText('No nodes reported. Power on a PanelGroup.')).toBeNull()
sky.emitScenario({ model: model([fresh(1), fresh(2)]) })
expect(
screen.getByText('2 new boards on the bus — add them from the Cockpit.')
).toBeInTheDocument()
// Added to your cockpit: the normal card again.
sky.emitScenario({ model: model([panel(1, 'Center_Armament')]) })
expect(count('healthy')).toBe('1healthy')
expect(screen.queryByText(/new boards? on the bus/)).toBeNull()
})

it('"›" opens the Cockpit on that board', async () => {
const { user } = await renderScenario(<App />, { device: connected(), model: scene() })
await user.click(screen.getByRole('tab', { name: 'Connection' }))
Expand Down
17 changes: 16 additions & 1 deletion src/shared/nodes/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,16 @@ export interface NodesSummary {
more: number
/** `+5 more · healthy nodes hidden`; empty when there is nothing to say. */
footer: string
/**
* New boards on the bus that can be added: the Overview's new-devices count (the Cockpit's "New
* on the bus · N"), not part of `known`.
*/
newCount: number
/**
* The card's line when `known` is 0: where to add the new boards on the bus (#160), else that
* the bus reports nothing.
*/
empty: string
}

/** A registry row's "On the bus" column. */
Expand Down Expand Up @@ -848,6 +858,9 @@ export interface CockpitSnapshot {
settings: SettingsNodes
}

/** Connected nodes' line while the bus reports nothing at all (NodesSummary `empty`). */
export const NO_NODES = 'No nodes reported. Power on a PanelGroup.'

/** What the Cockpit shows before main's first snapshot: no model, link down. */
export const COCKPIT_IDLE: CockpitSnapshot = {
rev: 0,
Expand Down Expand Up @@ -899,7 +912,9 @@ export const COCKPIT_IDLE: CockpitSnapshot = {
watts: null,
rows: [],
more: 0,
footer: ''
footer: '',
newCount: 0,
empty: NO_NODES
},
settings: { differing: 0, canReset: false, hidden: [], registry: { summary: '', rows: [] } }
}
Expand Down
Loading