xref: /aosp_15_r20/tools/netsim/ui/ts/device-map.ts (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1import {css, html, LitElement} from 'lit';
2import {customElement, property} from 'lit/decorators.js';
3import {styleMap} from 'lit/directives/style-map.js';
4
5import {Device, Notifiable, SimulationInfo, simulationState,} from './device-observer.js';
6
7@customElement('ns-device-map')
8export class DeviceMap extends LitElement implements Notifiable {
9  /**
10   * List of devices currently on the netsim.
11   */
12  @property() deviceData: Device[] = [];
13
14  /**
15   * Index of the background image displayed.
16   */
17  @property() imageIdx = 0;
18
19  /**
20   * Number of images available for the background.
21   */
22  @property() numImages = 3;
23
24  @property({type: Boolean, reflect: true}) isometric: boolean = false;
25
26  connectedCallback() {
27    super.connectedCallback();  // eslint-disable-line
28    simulationState.registerObserver(this);
29    window.addEventListener('map-button-clicked', this.onChangeMap);
30    window.addEventListener(
31        'isometric-button-clicked', this.handleIsometricView);
32  }
33
34  disconnectedCallback() {
35    window.removeEventListener(
36        'isometric-button-clicked', this.handleIsometricView);
37    window.removeEventListener('map-button-clicked', this.onChangeMap);
38    simulationState.removeObserver(this);
39    super.disconnectedCallback();  // eslint-disable-line
40  }
41
42  static styles = css`
43    #dropzone {
44      margin-left: 200px;
45      margin-right: 200px;
46      transition: transform 2s, top 2s;
47      transform-style: preserve-3d;
48    }
49
50    .box {
51      position: relative;
52      width: 1000px; //40vw;
53      height: 1000px; //40vh;
54      border: solid 1px rgb(198, 210, 255);
55      margin: 2.5em auto;
56    }
57
58    .pattern0 {
59      background-image: url(./assets/grid-background.svg);
60    }
61
62    .pattern1 {
63      background-image: url(./assets/polar-background.svg);
64      background-size: 1150px 1150px;
65      background-position: center;
66    }
67
68    .pattern2 {
69      background-image: url(./assets/hexagonal-background.png);
70      background-size: 1175px 1175px;
71      background-position: center;
72    }
73
74    .container {
75      display: flex;
76      width: 100%;
77    }
78
79    .contentA {
80      flex: 2;
81    }
82
83    .contentB {
84      flex: 2;
85    }
86
87    ns-device-dragzone {
88      transform-style: inherit;
89    }
90  `;
91
92  onNotify(data: SimulationInfo): void {
93    this.deviceData = data.devices;
94    this.requestUpdate();
95  }
96
97  private onChangeMap = () => {
98    this.imageIdx = (this.imageIdx + 1) % this.numImages;
99  };
100
101  private handleIsometricView = () => {
102    this.isometric = !this.isometric;
103  };
104
105  checkBle(device: Device):
106      boolean{return device.chips.at(0)?.bleBeacon !== undefined}
107
108  render() {
109    const rainbow = [
110      'red',
111      'orange',
112      'yellow',
113      'green',
114      'blue',
115      'indigo',
116      'purple',
117    ];
118    const viewStyle = this.isometric ?
119        `perspective(200rem) rotateX(60deg) rotateY(0deg) rotateZ(0deg) scale3d(0.8,0.8,0.8); top: 250px` :
120        'none; top: 0px;';
121
122    return html`
123      <ns-device-dropzone role="widget" tabindex="0" aria-label="Device map">
124        <div id="dropzone" class="box pattern${this.imageIdx}">
125          ${
126        this.deviceData.map(
127            (device, idx) => html`
128              ${
129                true ?  // TODO manage device.visible in Web UI
130                    this.checkBle(device) ?
131                    html`
132                    <ns-device-dragzone
133                      .action=${'move'}
134                      style=${styleMap({
135                      position: 'absolute',
136                      left: `${device.position.x * 100}px`,
137                      top: `${device.position.y * 100}px`,
138                    })}
139                    >
140                      <ns-pyramid-sprite
141                        id=${device.name}
142                        .color=${rainbow[idx % rainbow.length]}
143                        .size=${'30px'}
144                        .controls=${true}
145                        yaw=${device.orientation.yaw}
146                        pitch=${device.orientation.pitch}
147                        roll=${device.orientation.roll}
148                        posZ=${device.position.z * 100}
149                        role="widget"
150                        tabindex="1"
151                        aria-label="${device.name} on Device Map, Position: ${
152                        Math.round(device.position.x * 100)}, ${
153                        Math.round(device.position.y * 100)}, ${
154                        Math.round(
155                            device.position.z * 100)}, Orientation: yaw: ${
156                        device.orientation.yaw}, pitch: ${
157                        device.orientation.pitch}, roll: ${
158                        device.orientation.roll}"
159                        aria-live="polite"
160                      ></ns-pyramid-sprite>
161                    </ns-device-dragzone>
162                  ` :
163                    html`
164                  <ns-device-dragzone
165                    .action=${'move'}
166                    style=${styleMap({
167                      position: 'absolute',
168                      left: `${device.position.x * 100}px`,
169                      top: `${device.position.y * 100}px`,
170                    })}
171                  >
172                    <ns-cube-sprite
173                      id=${device.name}
174                      .color=${rainbow[idx % rainbow.length]}
175                      .size=${'30px'}
176                      .controls=${true}
177                      yaw=${device.orientation.yaw}
178                      pitch=${device.orientation.pitch}
179                      roll=${device.orientation.roll}
180                      posZ=${device.position.z * 100}
181                      role="widget"
182                      tabindex="1"
183                      aria-label="${device.name} on Device Map, Position: ${
184                        Math.round(device.position.x * 100)}, ${
185                        Math.round(device.position.y * 100)}, ${
186                        Math.round(
187                            device.position.z * 100)}, Orientation: yaw: ${
188                        device.orientation.yaw}, pitch: ${
189                        device.orientation.pitch}, roll: ${
190                        device.orientation.roll}"
191                      aria-live="polite"
192                    ></ns-cube-sprite>
193                  </ns-device-dragzone>
194                ` :
195                    html``}
196            `)}
197        </div>
198        <style>
199          #dropzone {
200            transform: ${viewStyle};
201          }
202        </style>
203      </ns-device-dropzone>
204    `;
205  }
206}
207