Niko Sohmers | e92516c | 2024-03-23 18:40:30 -0700 | [diff] [blame^] | 1 | import {ByteBuffer} from 'flatbuffers' |
| 2 | import {Connection} from '../../aos/network/www/proxy' |
| 3 | import {Status, ApplicationStatus, State, LastStopReason, FileState} from '../../aos/starter/starter_generated' |
| 4 | |
| 5 | const NODES = ['/orin1', '/imu', '/roborio']; |
| 6 | |
| 7 | export class StarterHandler { |
| 8 | private statuses = new Map<string, ApplicationStatus[]>(); |
| 9 | |
| 10 | private statusList: HTMLElement = |
| 11 | (document.getElementById('status_list') as HTMLElement); |
| 12 | |
| 13 | constructor(private readonly connection: Connection) { |
| 14 | for (const node in NODES) { |
| 15 | this.connection.addConfigHandler(() => { |
| 16 | this.connection.addHandler( |
| 17 | NODES[node] + '/aos', 'aos.starter.Status', (data) => { |
| 18 | this.handleStatus(data, NODES[node]); |
| 19 | }); |
| 20 | }); |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | private handleStatus(data: Uint8Array, node: string): void { |
| 25 | const fbBuffer = new ByteBuffer(data); |
| 26 | const status: Status = Status.getRootAsStatus(fbBuffer); |
| 27 | |
| 28 | const temp: ApplicationStatus[] = new Array(status.statusesLength()); |
| 29 | |
| 30 | for (let i = 0; i < status.statusesLength(); i++) { |
| 31 | temp[i] = status.statuses(i); |
| 32 | } |
| 33 | |
| 34 | this.statuses.set(node, temp); |
| 35 | } |
| 36 | |
| 37 | setStateColor(div: HTMLElement): void { |
| 38 | div.className = ''; |
| 39 | switch (div.innerHTML) { |
| 40 | case 'WAITING': |
| 41 | div.classList.add('yellow'); |
| 42 | break; |
| 43 | case 'STARTING': |
| 44 | div.classList.add('yellowgreen'); |
| 45 | break; |
| 46 | case 'RUNNING': |
| 47 | div.classList.add('lightgreen'); |
| 48 | break; |
| 49 | case 'STOPPING': |
| 50 | div.classList.add('lightcoral'); |
| 51 | break; |
| 52 | case 'STOPPED': |
| 53 | div.classList.add('lightcoral'); |
| 54 | break; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | setFileStateColor(div: HTMLElement): void { |
| 59 | div.className = ''; |
| 60 | switch (div.innerHTML) { |
| 61 | case 'NOT_RUNNING': |
| 62 | div.classList.add('lightgreen'); |
| 63 | break; |
| 64 | case 'NO_CHANGE': |
| 65 | div.classList.add('lightgreen'); |
| 66 | break; |
| 67 | case 'CHANGED_DURING_STARTUP': |
| 68 | div.classList.add('lightcoral'); |
| 69 | break; |
| 70 | case 'CHANGED': |
| 71 | div.classList.add('lightcoral'); |
| 72 | break; |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | private populateStatusList() : void { |
| 77 | this.clearStatusList(); |
| 78 | |
| 79 | const ELEMENTS: string[] = [ |
| 80 | 'name', |
| 81 | 'node', |
| 82 | 'state', |
| 83 | 'last_exit_code', |
| 84 | 'pid', |
| 85 | // 'id', |
| 86 | 'last_start_time', |
| 87 | 'last_stop_reason', |
| 88 | // 'process_info', |
| 89 | // 'has_active_timing_report', |
| 90 | 'file_state' |
| 91 | ]; |
| 92 | |
| 93 | // This is to add the field names as the top row |
| 94 | //--------------------------------------------------- |
| 95 | const row = document.createElement('div'); |
| 96 | row.className = "column_names"; |
| 97 | |
| 98 | for (const e in ELEMENTS) { |
| 99 | const element = document.createElement('div'); |
| 100 | element.className = ELEMENTS[e]; |
| 101 | element.innerHTML = ELEMENTS[e].toUpperCase(); |
| 102 | element.style.fontWeight = 'bold'; |
| 103 | row.appendChild(element); |
| 104 | } |
| 105 | |
| 106 | this.statusList.appendChild(row); |
| 107 | //--------------------------------------------------- |
| 108 | |
| 109 | for (const node in NODES) { |
| 110 | const currentStatus = this.statuses.get(NODES[node]); |
| 111 | |
| 112 | if (currentStatus) { |
| 113 | currentStatus.forEach(status => { |
| 114 | const row = document.createElement('div'); |
| 115 | row.className = status.name(); |
| 116 | |
| 117 | for (const e in ELEMENTS) { |
| 118 | const element = document.createElement('div'); |
| 119 | element.className = ELEMENTS[e]; |
| 120 | |
| 121 | switch (element.className) { |
| 122 | case 'name': |
| 123 | element.innerHTML = status.name(); |
| 124 | break; |
| 125 | case 'node': |
| 126 | element.innerHTML = NODES[node]; |
| 127 | break; |
| 128 | case 'state': |
| 129 | element.innerHTML = State[status.state()]; |
| 130 | this.setStateColor(element); |
| 131 | break; |
| 132 | case 'last_exit_code': |
| 133 | element.innerHTML = status.lastExitCode().toString(); |
| 134 | break; |
| 135 | case 'pid': |
| 136 | element.innerHTML = status.pid().toString(); |
| 137 | break; |
| 138 | case 'last_start_time': |
| 139 | element.innerHTML = Number(status.lastStartTime() / 1000000000n).toString() + ' sec'; |
| 140 | break; |
| 141 | case 'last_stop_reason': |
| 142 | element.innerHTML = LastStopReason[status.lastStopReason()]; |
| 143 | break; |
| 144 | case 'file_state': |
| 145 | element.innerHTML = FileState[status.fileState()]; |
| 146 | this.setFileStateColor(element); |
| 147 | break; |
| 148 | default: |
| 149 | element.innerHTML = "NA"; |
| 150 | break; |
| 151 | } |
| 152 | row.appendChild(element); |
| 153 | } |
| 154 | |
| 155 | this.statusList.appendChild(row); |
| 156 | }) |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | private clearStatusList(): void { |
| 162 | if (!this.statusList) { |
| 163 | return; |
| 164 | } |
| 165 | |
| 166 | while (this.statusList.firstChild) { |
| 167 | this.statusList.removeChild(this.statusList.firstChild); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | draw(): void { |
| 172 | if (this.statuses) { |
| 173 | this.populateStatusList(); |
| 174 | } |
| 175 | |
| 176 | window.requestAnimationFrame(() => this.draw()); |
| 177 | } |
| 178 | } |