feat: show price (WIP edit)

- fix: adb tcp connection unstable retry
- fix: recipe not show by undefined machine status

Signed-off-by: pakintada@gmail.com <Pakin>
This commit is contained in:
pakintada@gmail.com 2026-05-18 09:49:09 +07:00
parent 60424ebe5a
commit 3b70cc9fe8
8 changed files with 269 additions and 48 deletions

View file

@ -14,9 +14,83 @@ import { handleAdbPayload } from '../handlers/adbPayloadHandler';
import { adbWriter } from '../stores/adbWriter';
import { WritableStream } from '@yume-chan/stream-extra';
import { env } from '$env/dynamic/public';
import type Dice_2 from '@lucide/svelte/icons/dice-2';
let syncConnection: any = null;
function isRecoverableError(error: any): boolean {
if (!error) return false;
const errorMessage = error.message ? String(error.message).toLowerCase() : '';
const errorName = error.name ? String(error.name).toLowerCase() : '';
// Network-related errors that are typically recoverable
const recoverablePatterns = [
'connection refused',
'connection reset',
'connection timeout',
'network is unreachable',
'host is unreachable',
'temporary failure',
'operation timed out',
'failed to connect',
'connection lost',
'broken pipe',
'socket closed',
'eof',
'end of file',
'disconnected'
];
for (const pattern of recoverablePatterns) {
if (errorMessage.includes(pattern) || errorName.includes(pattern)) {
return true;
}
}
if (
(error.name && error.name.includes('Error')) ||
error.name.includes('Exception') ||
error.name === 'IOError' ||
error.name === 'NetworkError'
) {
return true;
}
return false;
}
async function connectWithRetry<T>(
connectionFn: () => Promise<T>,
description: string,
maxRetries: number = 5,
baseDelayMs: number = 1000
): Promise<T> {
let lastError: any;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error(`Connection timeout for ${description}`)), 10000);
});
const result = await Promise.race([connectionFn(), timeoutPromise]);
return result;
} catch (e) {
lastError = e;
if (attempt === maxRetries - 1) {
break;
}
if (!isRecoverableError(e)) {
break;
}
const delay = Math.min(baseDelayMs * Math.pow(2, attempt) + Math.random() * 1000, 10000);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw new Error(
`failed to ${description} after ${maxRetries} attempts. Last error: ${lastError.message}`
);
}
export async function connnectViaWebUSB() {
const device = await AdbDaemonWebUsbDeviceManager.BROWSER?.requestDevice();
console.log('usb ok', globalThis.navigator.usb);
@ -216,50 +290,79 @@ export async function push(path: string, obj: string) {
}
// NOTE: adb reverse is not work by unavailable features support
async function connectToAndroidServer() {
try {
let inst = getAdbInstance();
if (!inst) {
console.warn('adb instance not found');
return;
}
// await push('/sdcard/coffeevending/enable_adb_block_watch', '1');
const stream = await inst.transport.connect(env.PUBLIC_BREW_CONN_PORT);
const writer = stream.writable.getWriter();
const reader = stream.readable.getReader();
console.log('checking on writer ', writer);
adbWriter.set(writer);
if (writer) {
addNotification('INFO:Enable Brewing Mode T on machine');
} else {
addNotification('WARN:Brewing Mode T unavailable');
setTimeout(async () => {
console.log('reconnecting android server');
await connectToAndroidServer();
}, 5000);
}
(async () => {
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
handleAdbPayload(new TextDecoder().decode(value));
}
} catch (e) {
console.error('read error', e);
} finally {
adbWriter.set(null);
addNotification('WARN:Brewing Mode T Offline ...');
async function connectToAndroidServer(maxRetries = 5) {
let lastError: any;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
let inst = getAdbInstance();
if (!inst) {
console.warn('adb instance not found');
return;
}
})();
} catch (err) {
console.error('Connection failed. Suspect java running or not', err);
addNotification('ERR:Fail to enable brewing mode T');
// add retry mechanism
const stream = await connectWithRetry(
async () => inst.transport.connect(env.PUBLIC_BREW_CONN_PORT),
`connect to Android server port ${env.PUBLIC_BREW_CONN_PORT}`,
3,
500
);
const writer = stream.writable.getWriter();
const reader = stream.readable.getReader();
console.log('checking on writer ', writer);
adbWriter.set(writer);
if (writer) {
addNotification('INFO:Enable Brewing Mode T on machine');
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
handleAdbPayload(new TextDecoder().decode(value));
}
} catch (e) {
console.error('read error', e);
if (isRecoverableError(e)) {
throw e;
}
throw e;
} finally {
adbWriter.set(null);
addNotification('WARN:Brewing Mode T Offline ...');
}
} else {
addNotification('WARN:Brewing Mode T unavailable');
if (attempt < maxRetries - 1) {
const delay = Math.min(500 * Math.pow(2, attempt) + Math.random() * 500, 5000);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
} else {
throw new Error('Brewing Mode T unavailable after all retries');
}
}
} catch (err) {
lastError = err;
if (attempt == maxRetries - 1) {
break;
}
if (!isRecoverableError(err)) {
break;
}
const delay = Math.min(1000 * Math.pow(2, attempt) + Math.random() * 1000, 10000);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
if (lastError) {
console.error('Connection failed. Suspect java running or not', lastError);
addNotification(`ERR:Fail to enable brewing mode T\n${lastError.message ?? ''}`);
}
}