• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {css, html, LitElement} from 'lit';
2import {customElement, property} from 'lit/decorators.js';
3import {live} from 'lit/directives/live.js';
4import {styleMap} from 'lit/directives/style-map.js';
5
6import {Device, Notifiable, SimulationInfo, simulationState,} from './device-observer.js';
7import {State} from './model.js'
8
9    @customElement('ns-device-info') export class DeviceInformation extends LitElement implements Notifiable {
10
11      // Selected Device on scene
12      @property() selectedDevice: Device|undefined;
13
14      /**
15   * the yaw value in orientation for ns-cube-sprite
16   * unit: deg
17   */
18      @property({type : Number}) yaw = 0;
19
20      /**
21   * the pitch value in orientation for ns-cube-spriteß
22   * unit: deg
23   */
24      @property({type : Number}) pitch = 0;
25
26      /**
27   * the roll value in orientation for ns-cube-sprite
28   * unit: deg
29   */
30      @property({type : Number}) roll = 0;
31
32      /**
33   * The state of device info. True if edit mode.
34   */
35      @property({type : Boolean}) editMode = false;
36
37      /**
38   * the x value in position for ns-cube-sprite
39   * unit: cm
40   */
41      @property({type : Number}) posX = 0;
42
43      /**
44   * the y value in position for ns-cube-sprite
45   * unit: cm
46   */
47      @property({type : Number}) posY = 0;
48
49      /**
50   * the z value in position for ns-cube-sprite
51   * unit: cm
52   */
53      @property({type : Number}) posZ = 0;
54
55      holdRange = false;
56
57      static styles = css`
58    :host {
59      cursor: pointer;
60      display: grid;
61      place-content: center;
62      color: white;
63      font-size: 25px;
64      font-family: 'Lato', sans-serif;
65      border: 5px solid black;
66      border-radius: 12px;
67      padding: 10px;
68      background-color: #9199a5;
69      max-width: 600px;
70    }
71
72    .title {
73      font-weight: bold;
74      text-transform: uppercase;
75      text-align: center;
76      margin-bottom: 10px;
77    }
78
79    .setting {
80      display: grid;
81      grid-template-columns: auto auto;
82      margin-top: 0px;
83      margin-bottom: 30px;
84      //border: 3px solid black;
85      padding: 10px;
86    }
87
88    .setting .name {
89      grid-column: 1 / span 2;
90      text-transform: uppercase;
91      text-align: left;
92      margin-bottom: 10px;
93      font-weight: bold;
94    }
95
96    .label {
97      grid-column: 1;
98      text-align: left;
99    }
100
101    .info {
102      grid-column: 2;
103      text-align: right;
104      margin-bottom: 10px;
105    }
106
107    .switch {
108      position: relative;
109      float: right;
110      width: 60px;
111      height: 34px;
112    }
113
114    .switch input {
115      opacity: 0;
116      width: 0;
117      height: 0;
118    }
119
120    .slider {
121      position: absolute;
122      cursor: pointer;
123      top: 0;
124      left: 0;
125      right: 0;
126      bottom: 0;
127      background-color: #ccc;
128      -webkit-transition: 0.4s;
129      transition: 0.4s;
130    }
131
132    .slider:before {
133      position: absolute;
134      content: '';
135      height: 26px;
136      width: 26px;
137      left: 4px;
138      bottom: 4px;
139      background-color: white;
140      -webkit-transition: 0.4s;
141      transition: 0.4s;
142    }
143
144    input:checked + .slider {
145      background-color: #2196f3;
146    }
147
148    input:focus + .slider {
149      box-shadow: 0 0 1px #2196f3;
150    }
151
152    input:checked + .slider:before {
153      -webkit-transform: translateX(26px);
154      -ms-transform: translateX(26px);
155      transform: translateX(26px);
156    }
157
158    /* Rounded sliders */
159    .slider.round {
160      border-radius: 34px;
161    }
162
163    .slider.round:before {
164      border-radius: 50%;
165    }
166
167    .text {
168      display: inline-block;
169      position: relative;
170      width: 50px;
171    }
172
173    input[type='range'] {
174      width: 400px;
175    }
176
177    input[type='text'] {
178      width: 50%;
179      font-size: inherit;
180      text-align: right;
181      max-height: 25px;
182    }
183
184    input[type='text'].orientation {
185      max-width: 50px;
186    }
187
188    input[type='button'] {
189      display: inline;
190      font-size: inherit;
191      max-width: 200px;
192    }
193  `;
194
195      connectedCallback() {
196    super.connectedCallback(); // eslint-disable-line
197    simulationState.registerObserver(this);
198      }
199
200      disconnectedCallback() {
201    simulationState.removeObserver(this);
202    super.disconnectedCallback(); // eslint-disable-line
203      }
204
205      onNotify(data: SimulationInfo) {
206    if (data.selectedId && this.editMode === false) {
207      for (const device of data.devices) {
208        if (device.name === data.selectedId) {
209          this.selectedDevice = device;
210          if (!this.holdRange){
211            this.yaw = device.orientation.yaw;
212            this.pitch = device.orientation.pitch;
213            this.roll = device.orientation.roll;
214          }
215          this.posX = Math.floor(device.position.x * 100);
216          this.posY = Math.floor(device.position.y * 100);
217          this.posZ = Math.floor(device.position.z * 100);
218          break;
219        }
220      }
221    }
222      }
223
224      private changeRange(ev: InputEvent) {
225    this.holdRange = true;
226    console.assert(this.selectedDevice !== null); // eslint-disable-line
227    const range = ev.target as HTMLInputElement;
228    const event = new CustomEvent('orientationEvent', {
229      detail: {
230        name: this.selectedDevice?.name,
231        type: range.id,
232        value: range.value,
233      },
234    });
235    window.dispatchEvent(event);
236    if (range.id === 'yaw') {
237      this.yaw = Number(range.value);
238    } else if (range.id === 'pitch') {
239      this.pitch = Number(range.value);
240    } else {
241      this.roll = Number(range.value);
242    }
243      }
244
245      private patchOrientation() {
246    this.holdRange = false;
247    console.assert(this.selectedDevice !== undefined); // eslint-disable-line
248    if (this.selectedDevice === undefined) return;
249    this.selectedDevice.orientation = {yaw: this.yaw, pitch: this.pitch, roll: this.roll};
250    simulationState.patchDevice({
251      device: {
252        name: this.selectedDevice.name,
253        orientation: this.selectedDevice.orientation,
254      },
255    });
256      }
257
258      private patchRadio() {
259    console.assert(this.selectedDevice !== undefined); // eslint-disable-line
260    if (this.selectedDevice === undefined) return;
261    simulationState.patchDevice({
262      device: {
263        name: this.selectedDevice.name,
264        chips: this.selectedDevice.chips,
265      },
266    });
267      }
268
269      private handleEditForm() {
270    if (this.editMode) {
271      simulationState.invokeGetDevice();
272      this.editMode = false;
273    } else {
274      this.editMode = true;
275    }
276      }
277
278      static checkPositionBound(value: number) {
279    return value > 10 ? 10 : value < 0 ? 0 : value; // eslint-disable-line
280      }
281
282      static checkOrientationBound(value: number) {
283    return value > 90 ? 90 : value < -90 ? -90 : value; // eslint-disable-line
284      }
285
286      private handleSave() {
287    console.assert(this.selectedDevice !== undefined); // eslint-disable-line
288    if (this.selectedDevice === undefined) return;
289    const elements = this.renderRoot.querySelectorAll(`[id^="edit"]`);
290    const obj: Record<string, any> = {
291      name: this.selectedDevice.name,
292      position: this.selectedDevice.position,
293      orientation: this.selectedDevice.orientation,
294    };
295    elements.forEach(element => {
296      const inputElement = element as HTMLInputElement;
297      if (inputElement.id === 'editName') {
298        obj.name = inputElement.value;
299      } else if (inputElement.id.startsWith('editPos')) {
300        if (!Number.isNaN(Number(inputElement.value))) {
301          obj.position[inputElement.id.slice(7).toLowerCase()] =
302            DeviceInformation.checkPositionBound(
303              Number(inputElement.value) / 100
304            );
305        }
306      } else if (inputElement.id.startsWith('editOri')) {
307        if (!Number.isNaN(Number(inputElement.value))) {
308          obj.orientation[inputElement.id.slice(7).toLowerCase()] =
309            DeviceInformation.checkOrientationBound(Number(inputElement.value));
310        }
311      }
312    });
313    this.selectedDevice.name = obj.name;
314    this.selectedDevice.position = obj.position;
315    this.selectedDevice.orientation = obj.orientation;
316    this.handleEditForm();
317    simulationState.patchDevice({
318      device: obj,
319    });
320      }
321
322      private handleGetChips() {
323    const disabledCheckbox = html`
324      <input type="checkbox" disabled />
325        <span
326          class="slider round"
327          style=${styleMap({ opacity: '0.7' })}
328        ></span>
329    `;
330    let lowEnergyCheckbox = disabledCheckbox;
331    let classicCheckbox = disabledCheckbox;
332    let wifiCheckbox = disabledCheckbox;
333    let uwbCheckbox = disabledCheckbox;
334    if (this.selectedDevice) {
335      if ('chips' in this.selectedDevice && this.selectedDevice.chips) {
336        for (const chip of this.selectedDevice.chips) {
337          if ('bt' in chip && chip.bt) {
338            if ('lowEnergy' in chip.bt && chip.bt.lowEnergy && 'state' in chip.bt.lowEnergy) {
339              lowEnergyCheckbox = html `
340                <input
341                  id="lowEnergy"
342                  type="checkbox"
343                  .checked=${live(chip.bt.lowEnergy.state === State.ON)}
344                  @click=${() => {
345                    // eslint-disable-next-line
346                    this.selectedDevice?.toggleChipState(chip, 'lowEnergy');
347                    this.patchRadio();
348                  }}
349                />
350                <span class="slider round"></span>
351              `;
352            }
353            if ('classic' in chip.bt && chip.bt.classic && 'state' in chip.bt.classic) {
354              classicCheckbox = html`
355                <input
356                  id="classic"
357                  type="checkbox"
358                  .checked=${live(chip.bt.classic.state === State.ON)}
359                  @click=${() => {
360                    // eslint-disable-next-line
361                    this.selectedDevice?.toggleChipState(chip, 'classic');
362                    this.patchRadio();
363                  }}
364                />
365                <span class="slider round"></span>
366              `;
367            }
368          }
369
370          if ('wifi' in chip && chip.wifi) {
371            wifiCheckbox = html`
372              <input
373                id="wifi"
374                type="checkbox"
375                .checked=${live(chip.wifi.state === State.ON)}
376                @click=${() => {
377                  // eslint-disable-next-line
378                  this.selectedDevice?.toggleChipState(chip);
379                  this.patchRadio();
380                }}
381              />
382              <span class="slider round"></span>
383            `;
384          }
385
386          if ('uwb' in chip && chip.uwb) {
387            uwbCheckbox = html`
388              <input
389                id="uwb"
390                type="checkbox"
391                .checked=${live(chip.uwb.state === State.ON)}
392                @click=${() => {
393                  // eslint-disable-next-line
394                  this.selectedDevice?.toggleChipState(chip);
395                  this.patchRadio();
396                }}
397              />
398              <span class="slider round"></span>
399            `;
400          }
401        }
402      }
403    }
404    return html`
405      <div class="label">BLE</div>
406      <div class="info">
407        <label class="switch">
408          ${lowEnergyCheckbox}
409        </label>
410      </div>
411      <div class="label">Classic</div>
412      <div class="info">
413        <label class="switch">
414          ${classicCheckbox}
415        </label>
416      </div>
417      <div class="label">WIFI</div>
418      <div class="info">
419        <label class="switch">
420          ${wifiCheckbox}
421        </label>
422      </div>
423      <div class="label">UWB</div>
424      <div class="info">
425        <label class="switch">
426          ${uwbCheckbox}
427        </label>
428      </div>
429    `;
430      }
431
432      render() {
433    return html`${this.selectedDevice
434      ? html`
435          <div class="title">Device Info</div>
436          <div class="setting">
437            <div class="name">Name</div>
438            <div class="info">${this.selectedDevice.name}</div>
439          </div>
440          <div class="setting">
441            <div class="name">Position</div>
442            <div class="label">X</div>
443            <div class="info" style=${styleMap({ color: 'red' })}>
444              ${this.editMode
445                ? html`<input
446                    type="text"
447                    id="editPosX"
448                    .value=${this.posX.toString()}
449                  />`
450                : html`${this.posX}`}
451            </div>
452            <div class="label">Y</div>
453            <div class="info" style=${styleMap({ color: 'green' })}>
454              ${this.editMode
455                ? html`<input
456                    type="text"
457                    id="editPosY"
458                    .value=${this.posY.toString()}
459                  />`
460                : html`${this.posY}`}
461            </div>
462            <div class="label">Z</div>
463            <div class="info" style=${styleMap({ color: 'blue' })}>
464              ${this.editMode
465                ? html`<input
466                    type="text"
467                    id="editPosZ"
468                    .value=${this.posZ.toString()}
469                  />`
470                : html`${this.posZ}`}
471            </div>
472          </div>
473          <div class="setting">
474            <div class="name">Orientation</div>
475            <div class="label">Yaw</div>
476            <div class="info">
477              <input
478                id="yaw"
479                type="range"
480                min="-90"
481                max="90"
482                .value=${this.yaw.toString()}
483                .disabled=${this.editMode}
484                @input=${this.changeRange}
485                @change=${this.patchOrientation}
486              />
487              ${this.editMode
488                ? html`<input
489                    type="text"
490                    id="editOriYaw"
491                    class="orientation"
492                    .value=${this.yaw.toString()}
493                  />`
494                : html`<div class="text">(${this.yaw})</div>`}
495            </div>
496            <div class="label">Pitch</div>
497            <div class="info">
498              <input
499                id="pitch"
500                type="range"
501                min="-90"
502                max="90"
503                .value=${this.pitch.toString()}
504                .disabled=${this.editMode}
505                @input=${this.changeRange}
506                @change=${this.patchOrientation}
507              />
508              ${this.editMode
509                ? html`<input
510                    type="text"
511                    id="editOriPitch"
512                    class="orientation"
513                    .value=${this.pitch.toString()}
514                  />`
515                : html`<div class="text">(${this.pitch})</div>`}
516            </div>
517            <div class="label">Roll</div>
518            <div class="info">
519              <input
520                id="roll"
521                type="range"
522                min="-90"
523                max="90"
524                .value=${this.roll.toString()}
525                .disabled=${this.editMode}
526                @input=${this.changeRange}
527                @change=${this.patchOrientation}
528              />
529              ${this.editMode
530                ? html`<input
531                    type="text"
532                    id="editOriRoll"
533                    class="orientation"
534                    .value=${this.roll.toString()}
535                  />`
536                : html`<div class="text">(${this.roll})</div>`}
537            </div>
538          </div>
539          <div class="setting">
540            ${this.editMode
541              ? html`
542                  <input type="button" value="Save" @click=${this.handleSave} />
543                  <input
544                    type="button"
545                    value="Cancel"
546                    @click=${this.handleEditForm}
547                  />
548                `
549              : html`<input
550                  type="button"
551                  value="Edit"
552                  @click=${this.handleEditForm}
553                />`}
554          </div>
555          <div class="setting">
556            <div class="name">Radio States</div>
557            ${this.handleGetChips()}
558          </div>
559        `
560      : html`<div class="title">Device Info</div>`}`;
561      }}
562