blob: 7d368c50a3a42f40998f00c58f30903d54c23b94 [file] [log] [blame] [edit]
import {css, html, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {live} from 'lit/directives/live.js';
import {styleMap} from 'lit/directives/style-map.js';
import {Device, Notifiable, SimulationInfo, simulationState,} from './device-observer.js';
import {State} from './model.js'
@customElement('ns-device-info') export class DeviceInformation extends LitElement implements Notifiable {
// Selected Device on scene
@property() selectedDevice: Device|undefined;
/**
* the yaw value in orientation for ns-cube-sprite
* unit: deg
*/
@property({type : Number}) yaw = 0;
/**
* the pitch value in orientation for ns-cube-spriteß
* unit: deg
*/
@property({type : Number}) pitch = 0;
/**
* the roll value in orientation for ns-cube-sprite
* unit: deg
*/
@property({type : Number}) roll = 0;
/**
* The state of device info. True if edit mode.
*/
@property({type : Boolean}) editMode = false;
/**
* the x value in position for ns-cube-sprite
* unit: cm
*/
@property({type : Number}) posX = 0;
/**
* the y value in position for ns-cube-sprite
* unit: cm
*/
@property({type : Number}) posY = 0;
/**
* the z value in position for ns-cube-sprite
* unit: cm
*/
@property({type : Number}) posZ = 0;
holdRange = false;
static styles = css`
:host {
cursor: pointer;
display: grid;
place-content: center;
color: white;
font-size: 25px;
font-family: 'Lato', sans-serif;
border: 5px solid black;
border-radius: 12px;
padding: 10px;
background-color: #9199a5;
max-width: 600px;
}
.title {
font-weight: bold;
text-transform: uppercase;
text-align: center;
margin-bottom: 10px;
}
.setting {
display: grid;
grid-template-columns: auto auto;
margin-top: 0px;
margin-bottom: 30px;
//border: 3px solid black;
padding: 10px;
}
.setting .name {
grid-column: 1 / span 2;
text-transform: uppercase;
text-align: left;
margin-bottom: 10px;
font-weight: bold;
}
.label {
grid-column: 1;
text-align: left;
}
.info {
grid-column: 2;
text-align: right;
margin-bottom: 10px;
}
.switch {
position: relative;
float: right;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: '';
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked + .slider {
background-color: #2196f3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196f3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
.text {
display: inline-block;
position: relative;
width: 50px;
}
input[type='range'] {
width: 400px;
}
input[type='text'] {
width: 50%;
font-size: inherit;
text-align: right;
max-height: 25px;
}
input[type='text'].orientation {
max-width: 50px;
}
input[type='button'] {
display: inline;
font-size: inherit;
max-width: 200px;
}
`;
connectedCallback() {
super.connectedCallback(); // eslint-disable-line
simulationState.registerObserver(this);
}
disconnectedCallback() {
simulationState.removeObserver(this);
super.disconnectedCallback(); // eslint-disable-line
}
onNotify(data: SimulationInfo) {
if (data.selectedId && this.editMode === false) {
for (const device of data.devices) {
if (device.name === data.selectedId) {
this.selectedDevice = device;
if (!this.holdRange){
this.yaw = device.orientation.yaw;
this.pitch = device.orientation.pitch;
this.roll = device.orientation.roll;
}
this.posX = Math.floor(device.position.x * 100);
this.posY = Math.floor(device.position.y * 100);
this.posZ = Math.floor(device.position.z * 100);
break;
}
}
}
}
private changeRange(ev: InputEvent) {
this.holdRange = true;
console.assert(this.selectedDevice !== null); // eslint-disable-line
const range = ev.target as HTMLInputElement;
const event = new CustomEvent('orientationEvent', {
detail: {
name: this.selectedDevice?.name,
type: range.id,
value: range.value,
},
});
window.dispatchEvent(event);
if (range.id === 'yaw') {
this.yaw = Number(range.value);
} else if (range.id === 'pitch') {
this.pitch = Number(range.value);
} else {
this.roll = Number(range.value);
}
}
private patchOrientation() {
this.holdRange = false;
console.assert(this.selectedDevice !== undefined); // eslint-disable-line
if (this.selectedDevice === undefined) return;
this.selectedDevice.orientation = {yaw: this.yaw, pitch: this.pitch, roll: this.roll};
simulationState.patchDevice({
device: {
name: this.selectedDevice.name,
orientation: this.selectedDevice.orientation,
},
});
}
private patchRadio() {
console.assert(this.selectedDevice !== undefined); // eslint-disable-line
if (this.selectedDevice === undefined) return;
simulationState.patchDevice({
device: {
name: this.selectedDevice.name,
chips: this.selectedDevice.chips,
},
});
}
private handleEditForm() {
if (this.editMode) {
simulationState.invokeGetDevice();
this.editMode = false;
} else {
this.editMode = true;
}
}
static checkPositionBound(value: number) {
return value > 10 ? 10 : value < 0 ? 0 : value; // eslint-disable-line
}
static checkOrientationBound(value: number) {
return value > 90 ? 90 : value < -90 ? -90 : value; // eslint-disable-line
}
private handleSave() {
console.assert(this.selectedDevice !== undefined); // eslint-disable-line
if (this.selectedDevice === undefined) return;
const elements = this.renderRoot.querySelectorAll(`[id^="edit"]`);
const obj: Record<string, any> = {
name: this.selectedDevice.name,
position: this.selectedDevice.position,
orientation: this.selectedDevice.orientation,
};
elements.forEach(element => {
const inputElement = element as HTMLInputElement;
if (inputElement.id === 'editName') {
obj.name = inputElement.value;
} else if (inputElement.id.startsWith('editPos')) {
if (!Number.isNaN(Number(inputElement.value))) {
obj.position[inputElement.id.slice(7).toLowerCase()] =
DeviceInformation.checkPositionBound(
Number(inputElement.value) / 100
);
}
} else if (inputElement.id.startsWith('editOri')) {
if (!Number.isNaN(Number(inputElement.value))) {
obj.orientation[inputElement.id.slice(7).toLowerCase()] =
DeviceInformation.checkOrientationBound(Number(inputElement.value));
}
}
});
this.selectedDevice.name = obj.name;
this.selectedDevice.position = obj.position;
this.selectedDevice.orientation = obj.orientation;
this.handleEditForm();
simulationState.patchDevice({
device: obj,
});
}
private handleGetChips() {
const disabledCheckbox = html`
<input type="checkbox" disabled />
<span
class="slider round"
style=${styleMap({ opacity: '0.7' })}
></span>
`;
let lowEnergyCheckbox = disabledCheckbox;
let classicCheckbox = disabledCheckbox;
let wifiCheckbox = disabledCheckbox;
let uwbCheckbox = disabledCheckbox;
if (this.selectedDevice) {
if ('chips' in this.selectedDevice && this.selectedDevice.chips) {
for (const chip of this.selectedDevice.chips) {
if ('bt' in chip && chip.bt) {
if ('lowEnergy' in chip.bt && chip.bt.lowEnergy && 'state' in chip.bt.lowEnergy) {
lowEnergyCheckbox = html `
<input
id="lowEnergy"
type="checkbox"
.checked=${live(chip.bt.lowEnergy.state === State.ON)}
@click=${() => {
// eslint-disable-next-line
this.selectedDevice?.toggleChipState(chip, 'lowEnergy');
this.patchRadio();
}}
/>
<span class="slider round"></span>
`;
}
if ('classic' in chip.bt && chip.bt.classic && 'state' in chip.bt.classic) {
classicCheckbox = html`
<input
id="classic"
type="checkbox"
.checked=${live(chip.bt.classic.state === State.ON)}
@click=${() => {
// eslint-disable-next-line
this.selectedDevice?.toggleChipState(chip, 'classic');
this.patchRadio();
}}
/>
<span class="slider round"></span>
`;
}
}
if ('wifi' in chip && chip.wifi) {
wifiCheckbox = html`
<input
id="wifi"
type="checkbox"
.checked=${live(chip.wifi.state === State.ON)}
@click=${() => {
// eslint-disable-next-line
this.selectedDevice?.toggleChipState(chip);
this.patchRadio();
}}
/>
<span class="slider round"></span>
`;
}
if ('uwb' in chip && chip.uwb) {
uwbCheckbox = html`
<input
id="uwb"
type="checkbox"
.checked=${live(chip.uwb.state === State.ON)}
@click=${() => {
// eslint-disable-next-line
this.selectedDevice?.toggleChipState(chip);
this.patchRadio();
}}
/>
<span class="slider round"></span>
`;
}
}
}
}
return html`
<div class="label">BLE</div>
<div class="info">
<label class="switch">
${lowEnergyCheckbox}
</label>
</div>
<div class="label">Classic</div>
<div class="info">
<label class="switch">
${classicCheckbox}
</label>
</div>
<div class="label">WIFI</div>
<div class="info">
<label class="switch">
${wifiCheckbox}
</label>
</div>
<div class="label">UWB</div>
<div class="info">
<label class="switch">
${uwbCheckbox}
</label>
</div>
`;
}
render() {
return html`${this.selectedDevice
? html`
<div class="title">Device Info</div>
<div class="setting">
<div class="name">Name</div>
<div class="info">${this.selectedDevice.name}</div>
</div>
<div class="setting">
<div class="name">Position</div>
<div class="label">X</div>
<div class="info" style=${styleMap({ color: 'red' })}>
${this.editMode
? html`<input
type="text"
id="editPosX"
.value=${this.posX.toString()}
/>`
: html`${this.posX}`}
</div>
<div class="label">Y</div>
<div class="info" style=${styleMap({ color: 'green' })}>
${this.editMode
? html`<input
type="text"
id="editPosY"
.value=${this.posY.toString()}
/>`
: html`${this.posY}`}
</div>
<div class="label">Z</div>
<div class="info" style=${styleMap({ color: 'blue' })}>
${this.editMode
? html`<input
type="text"
id="editPosZ"
.value=${this.posZ.toString()}
/>`
: html`${this.posZ}`}
</div>
</div>
<div class="setting">
<div class="name">Orientation</div>
<div class="label">Yaw</div>
<div class="info">
<input
id="yaw"
type="range"
min="-90"
max="90"
.value=${this.yaw.toString()}
.disabled=${this.editMode}
@input=${this.changeRange}
@change=${this.patchOrientation}
/>
${this.editMode
? html`<input
type="text"
id="editOriYaw"
class="orientation"
.value=${this.yaw.toString()}
/>`
: html`<div class="text">(${this.yaw})</div>`}
</div>
<div class="label">Pitch</div>
<div class="info">
<input
id="pitch"
type="range"
min="-90"
max="90"
.value=${this.pitch.toString()}
.disabled=${this.editMode}
@input=${this.changeRange}
@change=${this.patchOrientation}
/>
${this.editMode
? html`<input
type="text"
id="editOriPitch"
class="orientation"
.value=${this.pitch.toString()}
/>`
: html`<div class="text">(${this.pitch})</div>`}
</div>
<div class="label">Roll</div>
<div class="info">
<input
id="roll"
type="range"
min="-90"
max="90"
.value=${this.roll.toString()}
.disabled=${this.editMode}
@input=${this.changeRange}
@change=${this.patchOrientation}
/>
${this.editMode
? html`<input
type="text"
id="editOriRoll"
class="orientation"
.value=${this.roll.toString()}
/>`
: html`<div class="text">(${this.roll})</div>`}
</div>
</div>
<div class="setting">
${this.editMode
? html`
<input type="button" value="Save" @click=${this.handleSave} />
<input
type="button"
value="Cancel"
@click=${this.handleEditForm}
/>
`
: html`<input
type="button"
value="Edit"
@click=${this.handleEditForm}
/>`}
</div>
<div class="setting">
<div class="name">Radio States</div>
${this.handleGetChips()}
</div>
`
: html`<div class="title">Device Info</div>`}`;
}}