Skip to content

Commit

Permalink
增加蓝牙支持和2.0支持,以及更新检测功能
Browse files Browse the repository at this point in the history
  • Loading branch information
hyperzlib committed Oct 18, 2024
1 parent 6ce2236 commit 8e6caae
Show file tree
Hide file tree
Showing 28 changed files with 2,441 additions and 198 deletions.
1,391 changes: 1,335 additions & 56 deletions frontend/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"js-md5": "^0.8.3",
"pako": "^2.1.0",
"pinia": "^2.2.2",
"pinia-plugin-persistedstate": "^4.1.1",
"primeicons": "^7.0.0",
"primevue": "4.0.0-rc.2",
"unplugin-auto-import": "^0.17.6",
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/apis/socketApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ export enum GameConfigType {

export type PulsePlayMode = 'single' | 'sequence' | 'random';

export type RemoteNotificationInfo = {
title?: string;
message: string;
icon?: string;
severity?: string;
ignoreId?: string;
sticky?: boolean;
url?: string;
urlLabel?: string;
};

export interface GameStrengthConfig {
strength: number;
randomStrength: number;
Expand All @@ -64,6 +75,7 @@ export interface GameCustomPulseConfig {
export interface SocketApiEventListeners extends EventDef {
error: [error: any];
open: [];
remoteNotification: [notification: RemoteNotificationInfo];
pulseListUpdated: [pulseList: PulseItemResponse[]];
clientConnected: [];
clientDisconnected: [];
Expand Down Expand Up @@ -94,6 +106,10 @@ export class SocketApi {
this.socketUrl = wsUrl;
}

public get isConnected() {
return this.socket.readyState === WebSocket.OPEN;
}

public connect() {
this.socket = new WebSocket(this.socketUrl);
this.init();
Expand Down Expand Up @@ -275,6 +291,9 @@ export class SocketApi {
console.warn("Unknown game config type:", message.data.type);
}
break;
case "remoteNotification":
this.events.emit("remoteNotification", message.data);
break;
case "heartbeat":
this.send({
action: "heartbeat",
Expand Down
69 changes: 69 additions & 0 deletions frontend/src/components/dialogs/ClientInfoDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script lang="ts" setup>
import { useClientsStore } from '../../stores/ClientsStore';
import { ConnectorType } from '../../type/common';
defineOptions({
name: 'ClientInfoDialog',
});
const clientsStore = useClientsStore();
const visible = defineModel<boolean>('visible');
const props = defineProps<{
clientId: string;
connectorType: string;
}>();
const state = reactive({
inputClientName: '',
});
const connectorTypeStr = computed(() => {
switch (props.connectorType) {
case ConnectorType.DGLAB:
return 'DGLab';
case ConnectorType.COYOTE_BLE_V2:
case ConnectorType.COYOTE_BLE_V3:
return '蓝牙直连';
}
});
const setClientName = async (name: string) => {
clientsStore.updateClientName(props.clientId, name);
};
watch(() => visible.value, (value) => {
if (value) {
const clientInfo = clientsStore.getClientInfo(props.clientId);
console.log(clientInfo);
if (clientInfo) {
state.inputClientName = clientInfo.name;
}
}
});
watch(() => state.inputClientName, (value) => {
setClientName(value);
});
</script>

<template>
<Dialog v-model:visible="visible" modal header="连接信息" class="mx-4 w-full md:w-[40rem]">
<div class="flex flex-col gap-2 mb-4">
<div class="flex items-center gap-2">
<label class="font-semibold w-30">客户端备注名</label>
<InputText v-model="state.inputClientName" class="w-full" />
</div>
<span class="text-gray-500 ml-28">客户端备注名会在每次打开页面的恢复连接窗口中显示</span>
</div>
<div class="flex items-center gap-2 mb-4">
<label class="font-semibold w-30">客户端ID</label>
<InputText :value="props.clientId" class="w-full" readonly />
</div>
<div class="flex items-center gap-2">
<label class="font-semibold w-30">连接方式</label>
<InputText :value="connectorTypeStr" class="w-full" readonly />
</div>
</Dialog>
</template>
5 changes: 3 additions & 2 deletions frontend/src/components/dialogs/ConnectToClientDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const props = defineProps<{
clientId?: string;
}>();
const visible = defineModel('visible');
const visible = defineModel<boolean>('visible');
const emit = defineEmits<{
(name: 'resetClientId'): void;
Expand All @@ -37,6 +37,7 @@ const state = reactive({
formClientId: '',
});
const isSupportBluetooth = computed(() => {
return 'bluetooth' in navigator;
});
Expand Down Expand Up @@ -128,7 +129,7 @@ watch(() => props.clientId, (newVal) => {
</i>
</template>
</Button>
<Button label="连接郊狼 2.0 (开发中)" class="w-full" size="large" disabled title="请期待后续版本支持"
<Button label="连接郊狼 2.0" class="w-full" size="large"
@click="handleStartBluetoothConnect(CoyoteDeviceVersion.V2)">
<template #icon>
<i class="pi">
Expand Down
41 changes: 41 additions & 0 deletions frontend/src/components/dialogs/ConnectToSavedClientsDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts" setup>
import ConnectToSavedClientsList from '../partials/ConnectToSavedClientsList.vue';
defineOptions({
name: 'ConnectToSavedClientsDialog',
});
const visible = defineModel<boolean>('visible');
const emit = defineEmits<{
cancel: [];
confirm: [clientId: string];
}>();
const onCancel = () => {
visible.value = false;
};
const onConnectToClient = (clientId: string) => {
emit('confirm', clientId);
visible.value = false;
};
</script>

<template>
<Dialog v-model:visible="visible" modal header="恢复连接" class="connectedToSavedClients-dialog mx-4 w-full md:w-[40rem]">
<div class="flex flex-col gap-4">
<ConnectToSavedClientsList @connect-to-client="onConnectToClient" />

<div class="flex justify-end mt-4 gap-4">
<Button label="连接新设备" @click="onCancel"></Button>
</div>
</div>
</Dialog>
</template>

<style lang="scss">
.connectedToSavedClients-dialog {
--p-dialog-background: var(--p-surface-50);
}
</style>
82 changes: 82 additions & 0 deletions frontend/src/components/partials/ConnectToSavedClientsList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<script lang="ts" setup>
import { useClientsStore } from '../../stores/ClientsStore';
defineOptions({
name: 'ConnectToSavedClientsList',
});
const clientsStore = useClientsStore();
const emit = defineEmits<{
connectToClient: [clientId: string];
}>();
const sortedClientList = computed(() => {
return clientsStore.clientList.sort((a, b) => b.lastConnectTime - a.lastConnectTime);
});
const connectToClient = (clientId: string) => {
emit('connectToClient', clientId);
};
</script>

<template>
<DataView class="clientsList-container" :value="sortedClientList">
<template #list="slotProps">
<div class="flex flex-col">
<div v-for="(item, index) in slotProps.items" :key="index">
<Card class="clientCard m-1">
<template #content>
<div class="flex flex-col sm:flex-row sm:items-center gap-4">
<div
class="bg-primary text-primary-contrast rounded-full w-10 h-10 flex-shrink-0 flex items-center justify-center">
<i class="pi pi-history"></i>
</div>
<div class="flex flex-col md:flex-row justify-between md:items-center flex-1 gap-6">
<div class="flex flex-row md:flex-col justify-between items-start gap-2">
<div>
<span class="font-medium text-surface-500 dark:text-surface-400 text-sm">ID: {{ item.id }}</span>
<div class="text-lg font-medium">{{ item.name }}</div>
</div>
</div>
<div class="flex flex-col md:items-end gap-8">
<div class="flex flex-row-reverse md:flex-row gap-2">
<Button icon="pi pi-play" label="连接" class="flex-auto md:flex-initial whitespace-nowrap mr-2"
@click="connectToClient(item.id)"></Button>
</div>
</div>
</div>
</div>
</template>
</Card>
</div>
</div>
</template>
</DataView>
</template>

<style scoped>
.clientsList-container {
--p-dataview-content-background: var(--p-surface-50);
height: 50vh;
overflow-y: auto;
scrollbar-width: thin;
&::-webkit-scrollbar {
width: 6px;
}
}
.clientCard {
--p-card-body-padding: 1rem;
}
.bg-primary {
background-color: var(--p-primary-color);
}
.text-primary-contrast {
color: var(--p-primary-contrast-color);
}
</style>
60 changes: 57 additions & 3 deletions frontend/src/components/partials/CoyoteBluetoothPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ defineOptions({
name: 'CoyoteBluetoothPanel',
});
const BT_CONFIG_STORAGE_KEY = 'CGH_BTClientConfig';
const props = defineProps<{
state: any;
}>();
Expand All @@ -32,6 +34,7 @@ const state = reactive({
deviceStrengthA: 0,
deviceStrengthB: 0,
freqBalance: 150,
inputLimitA: 20,
inputLimitB: 20,
});
Expand Down Expand Up @@ -134,6 +137,38 @@ const handleStopBluetoothConnect = () => {
});
};
const saveLocalConfig = () => {
localStorage.setItem(BT_CONFIG_STORAGE_KEY, JSON.stringify({
freqBalance: state.freqBalance,
inputLimitA: state.inputLimitA,
inputLimitB: state.inputLimitB,
}));
};
const loadLocalConfig = () => {
const config = localStorage.getItem(BT_CONFIG_STORAGE_KEY);
if (!config) {
return;
}
const savedConfig = JSON.parse(config);
if (!savedConfig) {
return;
}
if (typeof savedConfig.freqBalance === 'number') {
state.freqBalance = Math.min(200, Math.max(0, savedConfig.freqBalance));
}
if (typeof savedConfig.inputLimitA === 'number') {
state.inputLimitA = Math.min(200, Math.max(1, savedConfig.inputLimitA));
}
if (typeof savedConfig.inputLimitB === 'number') {
state.inputLimitB = Math.min(200, Math.max(1, savedConfig.inputLimitB));
}
};
watch(() => parentState.clientId, async (newVal) => {
if (newVal) {
// 刷新客户端ID时断开蓝牙连接
Expand All @@ -144,8 +179,21 @@ watch(() => parentState.clientId, async (newVal) => {
watch(() => [state.inputLimitA, state.inputLimitB], (newVal) => {
if (bluetoothController) {
bluetoothController.setStrengthLimit(newVal[0], newVal[1]);
saveLocalConfig();
toast?.add({ severity: 'success', summary: '设置成功', detail: '已更新强度上限', life: 3000 });
}
}, { immediate: true });
watch(() => state.freqBalance, (newVal) => {
if (bluetoothController && parentState.connectorType === ConnectorType.COYOTE_BLE_V2) {
bluetoothController.setFreqBalance(newVal);
saveLocalConfig();
toast?.add({ severity: 'success', summary: '设置成功', detail: '已更新频率平衡参数', life: 3000 });
}
}, { immediate: true });
onMounted(() => {
loadLocalConfig();
});
defineExpose({
Expand Down Expand Up @@ -187,16 +235,22 @@ defineExpose({
</Chip>
</div>

<div class="w-full flex flex-col md:flex-row items-top lg:items-center gap-2 lg:gap-8 mb-8 lg:mb-4"
v-if="parentState.connectorType === ConnectorType.COYOTE_BLE_V2">
<label class="font-semibold w-30">频率平衡参数</label>
<InputNumber class="input-small" v-model="state.freqBalance" :min="0" :max="200" />
</div>

<div class="w-full flex flex-col md:flex-row items-top lg:items-center gap-2 lg:gap-8 mb-8 lg:mb-4">
<label class="font-semibold w-30">强度上限</label>
<label class="font-semibold w-30 flex-shrink-0">强度上限</label>
<div class="w-full flex flex-row gap-4">
<InputGroup>
<InputGroupAddon>A</InputGroupAddon>
<InputNumber v-model="state.inputLimitA" :min="0" :max="200" />
<InputNumber v-model="state.inputLimitA" :min="1" :max="200" />
</InputGroup>
<InputGroup>
<InputGroupAddon>B</InputGroupAddon>
<InputNumber v-model="state.inputLimitB" :min="0" :max="200" />
<InputNumber v-model="state.inputLimitB" :min="1" :max="200" />
</InputGroup>
</div>
</div>
Expand Down
Loading

0 comments on commit 8e6caae

Please sign in to comment.