| 103 | } |
| 104 | |
| 105 | class SysinfoViewModel implements ViewModel { |
| 106 | viewType: string; |
| 107 | termMode: jotai.Atom<string>; |
| 108 | htmlElemFocusRef: React.RefObject<HTMLInputElement>; |
| 109 | blockId: string; |
| 110 | viewIcon: jotai.Atom<string>; |
| 111 | viewText: jotai.Atom<string>; |
| 112 | viewName: jotai.Atom<string>; |
| 113 | dataAtom: jotai.PrimitiveAtom<Array<DataItem>>; |
| 114 | addInitialDataAtom: jotai.WritableAtom<unknown, [DataItem[]], void>; |
| 115 | addContinuousDataAtom: jotai.WritableAtom<unknown, [DataItem], void>; |
| 116 | incrementCount: jotai.WritableAtom<unknown, [], Promise<void>>; |
| 117 | loadingAtom: jotai.PrimitiveAtom<boolean>; |
| 118 | numPoints: jotai.Atom<number>; |
| 119 | metrics: jotai.Atom<string[]>; |
| 120 | connection: jotai.Atom<string>; |
| 121 | manageConnection: jotai.Atom<boolean>; |
| 122 | filterOutNowsh: jotai.Atom<boolean>; |
| 123 | connStatus: jotai.Atom<ConnStatus>; |
| 124 | plotMetaAtom: jotai.PrimitiveAtom<Map<string, TimeSeriesMeta>>; |
| 125 | endIconButtons: jotai.Atom<IconButtonDecl[]>; |
| 126 | plotTypeSelectedAtom: jotai.Atom<string>; |
| 127 | env: SysinfoEnv; |
| 128 | |
| 129 | constructor({ blockId, waveEnv }: ViewModelInitType) { |
| 130 | this.viewType = "sysinfo"; |
| 131 | this.blockId = blockId; |
| 132 | this.env = waveEnv; |
| 133 | this.addInitialDataAtom = jotai.atom(null, (get, set, points) => { |
| 134 | const targetLen = get(this.numPoints) + 1; |
| 135 | try { |
| 136 | const newDataRaw = [...points]; |
| 137 | if (newDataRaw.length == 0) { |
| 138 | return; |
| 139 | } |
| 140 | const latestItemTs = newDataRaw[newDataRaw.length - 1]?.ts ?? 0; |
| 141 | const cutoffTs = latestItemTs - 1000 * targetLen; |
| 142 | const blankItemTemplate = { ...newDataRaw[newDataRaw.length - 1] }; |
| 143 | for (const key in blankItemTemplate) { |
| 144 | blankItemTemplate[key] = NaN; |
| 145 | } |
| 146 | |
| 147 | const newDataFiltered = newDataRaw.filter((dataItem) => dataItem.ts >= cutoffTs); |
| 148 | if (newDataFiltered.length == 0) { |
| 149 | return; |
| 150 | } |
| 151 | const newDataWithGaps: Array<DataItem> = []; |
| 152 | if (newDataFiltered[0].ts > cutoffTs) { |
| 153 | const blankItemStart = { ...blankItemTemplate, ts: cutoffTs }; |
| 154 | const blankItemEnd = { ...blankItemTemplate, ts: newDataFiltered[0].ts - 1 }; |
| 155 | newDataWithGaps.push(blankItemStart); |
| 156 | newDataWithGaps.push(blankItemEnd); |
| 157 | } |
| 158 | newDataWithGaps.push(newDataFiltered[0]); |
| 159 | for (let i = 1; i < newDataFiltered.length; i++) { |
| 160 | const prevIdxItem = newDataFiltered[i - 1]; |
| 161 | const curIdxItem = newDataFiltered[i]; |
| 162 | const timeDiff = curIdxItem.ts - prevIdxItem.ts; |
nothing calls this directly
no outgoing calls
no test coverage detected