create branch dev and commit code

This commit is contained in:
thanawat saiyota 2026-06-09 10:50:59 +07:00
parent 3b70cc9fe8
commit ea68fa5cc4
44 changed files with 12421 additions and 214 deletions

View file

@ -1,42 +1,73 @@
import { updateMachineStatus } from '../stores/machineInfoStore';
import { addNotification } from '../stores/noti';
import {
loadAndroidRecipeExportFromDevice,
saveAndroidRecipeExportPayload
} from '../services/androidRecipeExportService';
import { handleIncomingMessages } from './messageHandler';
import { setMenuSaved, setMenuSaveError } from '../stores/menuSaveStore';
type AdbPayload = { type: string; payload: any };
async function handleAdbPayload(raw_payload: string) {
console.log('get payload', raw_payload);
console.log('[ADB] Received payload:', raw_payload.slice(0, 300));
try {
const payload: AdbPayload = JSON.parse(raw_payload);
console.log('[ADB] Parsed type:', payload.type, 'payload:', payload.payload);
switch (payload.type) {
case 'log':
let log_level = payload.payload['level'] ?? 'INFO';
let log_message = payload.payload['msg'] ?? '';
if (log_message !== '') addNotification(`${log_level}`);
if (log_message !== '') {
console.log('[ADB LOG]', log_level, log_message);
addNotification(`${log_level}:${log_message}`);
}
break;
case 'response':
if (payload.payload instanceof String) {
if (typeof payload.payload === 'string' || payload.payload instanceof String) {
// single message response
let raw_payload = payload.payload.toString();
if (raw_payload.startsWith('save_recipe_machine')) {
if (
raw_payload.startsWith('save_recipe_machine') ||
raw_payload.startsWith('save_recipe_menu_file')
) {
let res = raw_payload.split('/');
let pd = res[1] ?? '';
let action = res[2] ?? '';
let uiAction = res[3] ?? '';
handleIncomingMessages(
JSON.stringify({
type: 'ui_action',
payload: {
action: uiAction,
from: 'brew',
ref: `${pd}.${action}`
}
})
);
console.log('[ADB] Save response parsed:', { pd, action, uiAction, raw_payload });
// Track menu save status
if (raw_payload.startsWith('save_recipe_menu_file') && pd) {
if (action === 'success' || action === 'ok' || uiAction === 'refreshNow') {
setMenuSaved(pd);
addNotification(`INFO:Menu saved: ${pd}`);
} else if (action === 'error' || action === 'fail') {
setMenuSaveError(pd, 'Save failed');
addNotification(`ERR:Failed to save menu: ${pd}`);
} else {
// Assume success if we get a response
setMenuSaved(pd);
addNotification(`INFO:Menu saved: ${pd}`);
}
}
if (raw_payload.startsWith('save_recipe_machine')) {
handleIncomingMessages(
JSON.stringify({
type: 'ui_action',
payload: {
action: uiAction,
from: 'brew',
ref: `${pd}.${action}`
}
})
);
}
} else if (raw_payload.startsWith('state')) {
let res = raw_payload.split('/');
let new_machine_state = res[1] ?? '';
@ -84,6 +115,39 @@ async function handleAdbPayload(raw_payload: string) {
addNotification(`ERR:${payload.payload}`);
// send message to server if needed
break;
case 'recipe-export':
if (payload.payload?.content) {
saveAndroidRecipeExportPayload({
content: payload.payload.content,
exportedAt: payload.payload.exportedAt,
source: payload.payload.source,
fileSizeBytes: payload.payload.fileSizeBytes,
lineCount: payload.payload.lineCount,
message: payload.payload.message
});
addNotification('INFO:Recipe export received from Android');
} else if (payload.payload?.message) {
addNotification(`ERR:${payload.payload.message}`);
}
break;
case 'recipe-export-ready':
if (payload.payload?.message && payload.payload?.fileSizeBytes === 0) {
addNotification(`ERR:${payload.payload.message}`);
break;
}
void loadAndroidRecipeExportFromDevice({
exportedAt: payload.payload?.exportedAt,
source: payload.payload?.source,
fileSizeBytes: payload.payload?.fileSizeBytes,
lineCount: payload.payload?.lineCount,
message: payload.payload?.message
})
.then(() => addNotification('INFO:Recipe export loaded from Android'))
.catch((error: any) =>
addNotification(`ERR:${error?.message ?? 'Unable to load recipe export from Android'}`)
);
break;
default:
}
} catch (error: any) {

View file

@ -12,6 +12,24 @@ import {
toppingGroupFromServerQuery,
toppingListFromServerQuery
} from '../stores/recipeStore';
import {
handleSheetStreamStart,
handleSheetStreamChunk,
handleSheetStreamEnd,
handleSheetStreamError,
handleCatalogsResponse,
handleListMenuResponse,
sheetCatalogsLoading,
handleRawStreamHeader,
handleRawStreamChunk,
handleRawStreamEnd
} from '../stores/sheetStore';
import {
handleGenLayoutBatchStart,
handleGenLayoutFile,
handleGenLayoutBatchEnd,
handleGenLayoutError
} from '../stores/genLayoutStore';
import { buildOverviewFromServer } from '$lib/data/recipeService';
import { auth } from '../client/firebase';
import { type RecipeVersion } from '$lib/models/recipe_version.model';
@ -202,19 +220,105 @@ const handlers: Record<string, (payload: any) => void> = {
},
stream_patch_update: (p) => {},
notify: (p) => {
let noti_level = p.level ?? 'INFO';
let msg = p.msg ?? `Notify from ${p.from}`;
let target = p.to;
const from = p.from;
const level = p.level ?? 'INFO';
const msg = p.msg;
const target = p.to;
// Handle list-menu response
if (from === 'list-menu') {
const currentUid = auth.currentUser?.uid;
if (target && currentUid && target === currentUid && p.value) {
handleListMenuResponse({ codes: p.value });
}
return;
}
// Handle gen-service responses
if (from === 'gen-service') {
switch (level) {
case 'batch_start':
handleGenLayoutBatchStart({
batch_id: p.batch_id,
total_files: p.total_files,
total_size_bytes: p.total_size_bytes
});
addNotification(`INFO:Gen Layout started (${p.total_files} files)`);
break;
case 'file':
handleGenLayoutFile({
batch_id: p.batch_id,
file_index: p.file_index,
total_files: p.total_files,
file: p.file,
content: p.content,
is_chunked: p.is_chunked,
part_index: p.part_index,
total_parts: p.total_parts,
is_last_part: p.is_last_part
});
break;
case 'batch_end':
handleGenLayoutBatchEnd({
batch_id: p.batch_id,
total_files: p.total_files
});
addNotification('INFO:Gen Layout complete');
break;
case 'ERROR':
handleGenLayoutError(msg);
addNotification(`ERR:Gen Layout error: ${msg}`);
break;
default:
console.log('[GenService] Received:', level, msg);
}
return;
}
if (from === 'sheet-service' && level === 'content') {
const currentUid = auth.currentUser?.uid;
if (target && currentUid && target === currentUid) {
if (!msg && p.content?.catalogs) {
handleCatalogsResponse(p.content);
addNotification(`INFO:Loaded ${p.content.catalogs?.length || 0} catalogs`);
return;
}
// Handle streaming messages (with msg field)
switch (msg) {
case 'start':
handleSheetStreamStart(p);
addNotification('INFO:Sheet data streaming started');
break;
case 'chunk':
handleSheetStreamChunk(p);
break;
case 'end':
handleSheetStreamEnd(p);
addNotification('INFO:Sheet data streaming complete');
break;
case 'error':
handleSheetStreamError(p);
addNotification(`ERR:Sheet streaming error: ${p.content?.error_detail}`);
break;
default:
// Handle other content notifications from sheet-service
console.log('[Sheet] Received content:', p.content);
}
}
return;
}
// Default notification handling
if (target) {
//
let currentUsername = auth.currentUser?.displayName;
if (currentUsername && currentUsername === target) {
addNotification(`${noti_level}:${msg}`);
addNotification(`${level}:${msg}`);
}
} else {
// broadcast to all
addNotification(`${noti_level}:${msg}`);
addNotification(`${level}:${msg}`);
}
},
ui_action: (p) => {
@ -259,12 +363,33 @@ const handlers: Record<string, (payload: any) => void> = {
socketConnectionOfflineCount.set(0);
socketAlreadySendHeartbeat.set(0);
console.log('heartbeat reset offline count');
},
// Raw stream handlers for sheet data (e.g., price)
raw_stream: (p) => {
// Format: raw_stream with subtype in payload
// Header: { subtype: 'price', request_id, header?, country? }
const subtype = p.subtype;
if (subtype) {
handleRawStreamHeader(subtype, p);
}
},
raw_stream_price: (p) => {
// Header for price stream
handleRawStreamHeader('price', p);
},
raw_stream_chunk_price: (p) => {
// Chunk for price stream
handleRawStreamChunk('price', p);
},
raw_stream_end_price: (p) => {
// End for price stream
handleRawStreamEnd('price', p);
}
};
export function handleIncomingMessages(raw: string) {
const msg: WSMessage = JSON.parse(raw);
// console.log(`${new Date().toLocaleTimeString()}:ws msg`, msg);
// console.log(`[WS MSG] type=${msg.type}`, msg.payload);
if (msg == null) {
// error response
addNotification('ERR:No response from server');

View file

@ -18,7 +18,7 @@ function getServiceName(cmdReq: CommandRequest) {
}
// Websocket message wrapper for commands like `sheet`, `command`
export function sendCommandRequest(target: CommandRequest, values: any) {
export function sendCommandRequest(target: CommandRequest, values: any): boolean {
let srv_name = getServiceName(target);
let curr_user = get(auth);
@ -31,7 +31,7 @@ export function sendCommandRequest(target: CommandRequest, values: any) {
};
}
sendMessage({
return sendMessage({
type: target,
payload: {
user_info: user_info ?? {},