1import {css, html, LitElement} from 'lit'; 2import {customElement, property} from 'lit/decorators.js'; 3 4import {Device, Notifiable, SimulationInfo, simulationState,} from './device-observer.js'; 5import {Capture} from './netsim/model.js'; 6 7@customElement('ns-packet-info') 8export class PacketInformation extends LitElement implements Notifiable { 9 /** 10 * List of captures currently on the netsim. 11 */ 12 @property() captureData: Capture[] = []; 13 14 /** 15 * List of devices currently on the netsim. 16 */ 17 @property() deviceData: Device[] = []; 18 19 static styles = css` 20 :host { 21 display: flex; 22 justify-content: center; 23 align-items: flex-start; 24 height: 100vh; 25 } 26 27 .panel { 28 cursor: pointer; 29 display: grid; 30 place-content: center; 31 color: black; 32 font-size: 25px; 33 font-family: 'Lato', sans-serif; 34 border: 5px solid black; 35 border-radius: 12px; 36 margin: 10px; 37 padding: 10px; 38 background-color: #ffffff; 39 max-width: max-content; 40 float: left; 41 } 42 43 .title { 44 font-weight: bold; 45 text-transform: uppercase; 46 text-align: center; 47 margin-bottom: 10px; 48 } 49 50 .label { 51 text-align: left; 52 } 53 54 .styled-table { 55 border-collapse: collapse; 56 margin: 25px 0; 57 font-size: 20px; 58 font-family: sans-serif; 59 width: 100%; 60 box-shadow: 0 0 20px rgba(0, 0, 0, 0.15); 61 } 62 63 .styled-table thead tr { 64 background-color: #009879; 65 color: #ffffff; 66 text-align: left; 67 } 68 69 .styled-table th, 70 .styled-table td { 71 padding: 12px 15px; 72 text-align: left; 73 } 74 75 .styled-table tbody tr { 76 border-bottom: 1px solid #dddddd; 77 } 78 79 .styled-table tbody tr:nth-of-type(even) { 80 background-color: #cac0c0; 81 } 82 83 input[type='button'] { 84 height: 2rem; 85 font-size: inherit; 86 } 87 88 input[type='checkbox'].switch_1 { 89 font-size: 30px; 90 -webkit-appearance: none; 91 -moz-appearance: none; 92 appearance: none; 93 width: 3.5em; 94 height: 1.5em; 95 background: #ddd; 96 border-radius: 3em; 97 position: relative; 98 cursor: pointer; 99 outline: none; 100 -webkit-transition: all 0.2s ease-in-out; 101 transition: all 0.2s ease-in-out; 102 } 103 104 input[type='checkbox'].switch_1:checked { 105 background: #0ebeff; 106 } 107 108 input[type='checkbox'].switch_1:after { 109 position: absolute; 110 content: ''; 111 width: 1.5em; 112 height: 1.5em; 113 border-radius: 50%; 114 background: #fff; 115 -webkit-box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.3); 116 box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.3); 117 -webkit-transform: scale(0.7); 118 transform: scale(0.7); 119 left: 0; 120 -webkit-transition: all 0.2s ease-in-out; 121 transition: all 0.2s ease-in-out; 122 } 123 124 input[type='checkbox'].switch_1:checked:after { 125 left: calc(100% - 1.5em); 126 } 127 128 button { 129 display: inline-block; 130 padding: 12px 24px; 131 background-color: #4CAF50; 132 color: #FFFFFF; 133 font-size: 18px; 134 font-weight: bold; 135 text-align: center; 136 text-decoration: none; 137 border: none; 138 cursor: pointer; 139 transition: background-color 0.3s ease; 140 } 141 142 button:hover { 143 background-color: #45a049; 144 transition: 0.5s; 145 } 146 `; 147 148 connectedCallback() { 149 super.connectedCallback(); // eslint-disable-line 150 simulationState.registerObserver(this); 151 } 152 153 disconnectedCallback() { 154 simulationState.removeObserver(this); 155 super.disconnectedCallback(); // eslint-disable-line 156 } 157 158 onNotify(data: SimulationInfo) { 159 this.captureData = data.captures; 160 this.deviceData = data.devices; 161 this.requestUpdate(); 162 } 163 164 toggleCapture(capture: Capture) { 165 let id = capture.id.toString(); 166 let state = capture.state ? '0' : '1'; 167 simulationState.patchCapture(id, state); 168 } 169 170 private handleGetChips(device: Device) { 171 let btTable = html``; 172 let uwbTable = html``; 173 let wifiTable = html``; 174 if ('chips' in device && device.chips) { 175 for (const chip of device.chips) { 176 if ('bt' in chip && chip.bt) { 177 let bleTable = html``; 178 let bclassicTable = html``; 179 if ('lowEnergy' in chip.bt && chip.bt.lowEnergy) { 180 bleTable = html` 181 <tr> 182 <td>BLE</td> 183 <td>${chip.bt.lowEnergy.rxCount ?? 0}</td> 184 <td>${chip.bt.lowEnergy.txCount ?? 0}</td> 185 </tr> 186 `; 187 } 188 if ('classic' in chip.bt && chip.bt.classic) { 189 bclassicTable = html` 190 <tr> 191 <td>Bluetooth Classic</td> 192 <td>${chip.bt.classic.rxCount ?? 0}</td> 193 <td>${chip.bt.classic.txCount ?? 0}</td> 194 </tr> 195 `; 196 } 197 btTable = html`${bleTable} ${bclassicTable}`; 198 } 199 if ('uwb' in chip && chip.uwb) { 200 uwbTable = html` 201 <tr> 202 <td>UWB</td> 203 <td>${chip.uwb.rxCount ?? 0}</td> 204 <td>${chip.uwb.txCount ?? 0}</td> 205 </tr> 206 `; 207 } 208 if ('wifi' in chip && chip.wifi) { 209 wifiTable = html` 210 <tr> 211 <td>WIFI</td> 212 <td>${chip.wifi.rxCount ?? 0}</td> 213 <td>${chip.wifi.txCount ?? 0}</td> 214 </tr> 215 `; 216 } 217 } 218 } 219 return html` 220 ${btTable} 221 ${uwbTable} 222 ${wifiTable} 223 `; 224 } 225 226 private handleListCaptures(capture: Capture) { 227 return html` 228 <tr> 229 <td>${capture.deviceName}</td> 230 <td>${capture.chipKind}</td> 231 <td>${capture.size}</td> 232 <td>${capture.records}</td> 233 <td> 234 <input 235 type="checkbox" 236 class="switch_1" 237 .checked=${capture.state} 238 @click=${() => { 239 this.toggleCapture(capture); 240 }} 241 /> 242 </td> 243 <td> 244 <a 245 href="./v1/captures/${capture.id}" 246 target="_blank" 247 type="application/vnd.tcpdump.pcap" 248 ><button>Download</button></a 249 > 250 </td> 251 </tr> 252 ` 253 } 254 255 render() { 256 return html` 257 <div class="panel"> 258 <div class="title">Packet Info</div> 259 ${this.deviceData.map(device => html` 260 <div class="label">${device.name}</div> 261 <table class="styled-table"> 262 <tr> 263 <th>Radio</th> 264 <th>RX Count</th> 265 <th>TX Count</th> 266 </tr> 267 ${this.handleGetChips(device)} 268 </table> 269 `)} 270 </div> 271 <div class="panel"> 272 <div class="title">Packet Capture</div> 273 <table class="styled-table"> 274 <tr> 275 <th>Device Name</th> 276 <th>Chip Kind</th> 277 <th>Bytes</th> 278 <th>Records</th> 279 <th>Capture State</th> 280 <th>Download Pcap</th> 281 </tr> 282 ${this.captureData.map(capture => this.handleListCaptures(capture))} 283 </table> 284 </div> 285 `; 286 } 287} 288