feat: Add vision controls, function menu, and custom alert dialogs
- Add Vision menu with Camera (QRCode) and Barcode (Keyence) controls - Add Function menu with Manage, Log Viewer, and folder navigation - Add quick action buttons (Manual, Light, Print, Cancel) to header - Replace browser alert() with custom AlertDialog component - Add MachineBridge methods for vision, lighting, folders, and manual operations - Add WebSocketServer handlers for all new commands - Add communication layer methods for frontend-backend integration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { IOMonitorPage } from './pages/IOMonitorPage';
|
|||||||
import { RecipePage } from './pages/RecipePage';
|
import { RecipePage } from './pages/RecipePage';
|
||||||
import { SystemState, Recipe, IOPoint, LogEntry, RobotTarget, ConfigItem } from './types';
|
import { SystemState, Recipe, IOPoint, LogEntry, RobotTarget, ConfigItem } from './types';
|
||||||
import { comms } from './communication';
|
import { comms } from './communication';
|
||||||
|
import { AlertProvider } from './contexts/AlertContext';
|
||||||
|
|
||||||
// --- MOCK DATA ---
|
// --- MOCK DATA ---
|
||||||
|
|
||||||
@@ -187,52 +188,55 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HashRouter>
|
<AlertProvider>
|
||||||
<Layout
|
<HashRouter>
|
||||||
currentTime={currentTime}
|
<Layout
|
||||||
isHostConnected={isHostConnected}
|
currentTime={currentTime}
|
||||||
robotTarget={robotTarget}
|
isHostConnected={isHostConnected}
|
||||||
onTabChange={setActiveTab}
|
robotTarget={robotTarget}
|
||||||
activeTab={activeTab}
|
onTabChange={setActiveTab}
|
||||||
isLoading={isLoading}
|
activeTab={activeTab}
|
||||||
>
|
isLoading={isLoading}
|
||||||
<Routes>
|
>
|
||||||
<Route
|
<Routes>
|
||||||
path="/"
|
<Route
|
||||||
element={
|
path="/"
|
||||||
<HomePage
|
element={
|
||||||
systemState={systemState}
|
<HomePage
|
||||||
currentRecipe={currentRecipe}
|
systemState={systemState}
|
||||||
robotTarget={robotTarget}
|
currentRecipe={currentRecipe}
|
||||||
logs={logs}
|
robotTarget={robotTarget}
|
||||||
ioPoints={ioPoints}
|
logs={logs}
|
||||||
doorStates={doorStates}
|
ioPoints={ioPoints}
|
||||||
isLowPressure={isLowPressure}
|
doorStates={doorStates}
|
||||||
isEmergencyStop={isEmergencyStop}
|
isLowPressure={isLowPressure}
|
||||||
activeTab={activeTab}
|
isEmergencyStop={isEmergencyStop}
|
||||||
onSelectRecipe={handleSelectRecipe}
|
isHostConnected={isHostConnected}
|
||||||
onMove={moveAxis}
|
activeTab={activeTab}
|
||||||
onControl={handleControl}
|
onSelectRecipe={handleSelectRecipe}
|
||||||
onSaveConfig={handleSaveConfig}
|
onMove={moveAxis}
|
||||||
onCloseTab={() => setActiveTab(null)}
|
onControl={handleControl}
|
||||||
videoRef={videoRef}
|
onSaveConfig={handleSaveConfig}
|
||||||
/>
|
onCloseTab={() => setActiveTab(null)}
|
||||||
}
|
videoRef={videoRef}
|
||||||
/>
|
/>
|
||||||
<Route
|
}
|
||||||
path="/io-monitor"
|
/>
|
||||||
element={
|
<Route
|
||||||
<IOMonitorPage
|
path="/io-monitor"
|
||||||
onToggle={toggleIO}
|
element={
|
||||||
/>
|
<IOMonitorPage
|
||||||
}
|
onToggle={toggleIO}
|
||||||
/>
|
/>
|
||||||
<Route
|
}
|
||||||
path="/recipe"
|
/>
|
||||||
element={<RecipePage />}
|
<Route
|
||||||
/>
|
path="/recipe"
|
||||||
</Routes>
|
element={<RecipePage />}
|
||||||
</Layout>
|
/>
|
||||||
</HashRouter>
|
</Routes>
|
||||||
|
</Layout>
|
||||||
|
</HashRouter>
|
||||||
|
</AlertProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -431,6 +431,259 @@ class CommunicationLayer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== VISION CONTROL METHODS =====
|
||||||
|
|
||||||
|
private async sendVisionCommand(command: string, responseType: string): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
// WebView2 mode - direct call to C# methods
|
||||||
|
return { success: false, message: 'Vision commands not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.isConnected) reject({ success: false, message: "WebSocket connection timeout" });
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject({ success: false, message: "Vision command timeout" });
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === responseType) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: command }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async cameraConnect(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('CAMERA_CONNECT', 'CAMERA_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async cameraDisconnect(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('CAMERA_DISCONNECT', 'CAMERA_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async cameraGetImage(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('CAMERA_GET_IMAGE', 'CAMERA_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async cameraLiveView(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('CAMERA_LIVE_VIEW', 'CAMERA_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async cameraReadTest(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('CAMERA_READ_TEST', 'CAMERA_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async keyenceTriggerOn(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('KEYENCE_TRIGGER_ON', 'KEYENCE_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async keyenceTriggerOff(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('KEYENCE_TRIGGER_OFF', 'KEYENCE_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async keyenceGetImage(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('KEYENCE_GET_IMAGE', 'KEYENCE_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async keyenceSaveImage(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.sendVisionCommand('KEYENCE_SAVE_IMAGE', 'KEYENCE_RESULT');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Light, Manual Print, Cancel Job commands
|
||||||
|
public async toggleLight(): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
return { success: false, message: 'Light control not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject(new Error('Light toggle timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === 'LIGHT_RESULT') {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: 'TOGGLE_LIGHT' }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openManualPrint(): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
return { success: false, message: 'Manual print not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject(new Error('Manual print timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === 'MANUAL_PRINT_RESULT') {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: 'OPEN_MANUAL_PRINT' }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async cancelJob(): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
return { success: false, message: 'Cancel job not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject(new Error('Cancel job timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === 'CANCEL_JOB_RESULT') {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: 'CANCEL_JOB' }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openManage(): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
return { success: false, message: 'Manage not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject(new Error('Open manage timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === 'MANAGE_RESULT') {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: 'OPEN_MANAGE' }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openManual(): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
return { success: false, message: 'Manual not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject(new Error('Open manual timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === 'MANUAL_RESULT') {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: 'OPEN_MANUAL' }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openLogViewer(): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
return { success: false, message: 'Log viewer not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject(new Error('Open log viewer timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === 'LOG_VIEWER_RESULT') {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: 'OPEN_LOG_VIEWER' }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async openFolder(command: string): Promise<{ success: boolean; message: string }> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
return { success: false, message: 'Folder open not yet implemented in WebView2 mode' };
|
||||||
|
} else {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
reject(new Error('Open folder timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (data.type === 'FOLDER_RESULT') {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||||
|
resolve(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listeners.push(handler);
|
||||||
|
this.ws?.send(JSON.stringify({ type: command }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openProgramFolder(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.openFolder('OPEN_PROGRAM_FOLDER');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openLogFolder(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.openFolder('OPEN_LOG_FOLDER');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openScreenshotFolder(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.openFolder('OPEN_SCREENSHOT_FOLDER');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openSavedDataFolder(): Promise<{ success: boolean; message: string }> {
|
||||||
|
return this.openFolder('OPEN_SAVED_DATA_FOLDER');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const comms = new CommunicationLayer();
|
export const comms = new CommunicationLayer();
|
||||||
|
|||||||
80
FrontEnd/components/AlertDialog.tsx
Normal file
80
FrontEnd/components/AlertDialog.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { AlertTriangle, CheckCircle, Info, XCircle } from 'lucide-react';
|
||||||
|
|
||||||
|
interface AlertDialogProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
type: 'success' | 'error' | 'warning' | 'info';
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AlertDialog: React.FC<AlertDialogProps> = ({ isOpen, type, title, message, onClose }) => {
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
const getIcon = () => {
|
||||||
|
switch (type) {
|
||||||
|
case 'success':
|
||||||
|
return <CheckCircle className="w-12 h-12 text-green-500" />;
|
||||||
|
case 'error':
|
||||||
|
return <XCircle className="w-12 h-12 text-red-500" />;
|
||||||
|
case 'warning':
|
||||||
|
return <AlertTriangle className="w-12 h-12 text-amber-500" />;
|
||||||
|
case 'info':
|
||||||
|
return <Info className="w-12 h-12 text-blue-500" />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBorderColor = () => {
|
||||||
|
switch (type) {
|
||||||
|
case 'success':
|
||||||
|
return 'border-green-500';
|
||||||
|
case 'error':
|
||||||
|
return 'border-red-500';
|
||||||
|
case 'warning':
|
||||||
|
return 'border-amber-500';
|
||||||
|
case 'info':
|
||||||
|
return 'border-blue-500';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-[100] flex items-center justify-center">
|
||||||
|
{/* Backdrop */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Dialog */}
|
||||||
|
<div
|
||||||
|
className={`relative bg-black/95 backdrop-blur-md border-2 ${getBorderColor()} rounded-lg shadow-2xl max-w-md w-full mx-4 animate-fadeIn`}
|
||||||
|
>
|
||||||
|
{/* Icon and Title */}
|
||||||
|
<div className="flex items-center gap-4 p-6 border-b border-white/10">
|
||||||
|
{getIcon()}
|
||||||
|
<h2 className="text-xl font-tech font-bold text-white uppercase tracking-wider">
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Message */}
|
||||||
|
<div className="p-6">
|
||||||
|
<p className="text-slate-300 font-mono text-sm whitespace-pre-wrap leading-relaxed">
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Button */}
|
||||||
|
<div className="flex justify-end p-6 border-t border-white/10">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-6 py-2 bg-neon-blue/20 hover:bg-neon-blue/30 border border-neon-blue text-neon-blue font-tech font-bold rounded transition-colors shadow-glow-blue"
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
140
FrontEnd/components/FunctionMenu.tsx
Normal file
140
FrontEnd/components/FunctionMenu.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { Package, X, ChevronRight } from 'lucide-react';
|
||||||
|
import { comms } from '../communication';
|
||||||
|
import { useAlert } from '../contexts/AlertContext';
|
||||||
|
|
||||||
|
interface FunctionMenuProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FunctionMenu: React.FC<FunctionMenuProps> = ({ isOpen, onClose }) => {
|
||||||
|
const [activeSubmenu, setActiveSubmenu] = useState<string | null>(null);
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { showAlert } = useAlert();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
};
|
||||||
|
}, [isOpen, onClose]);
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
const handleCommand = async (commandFn: () => Promise<{ success: boolean; message: string }>, actionName: string) => {
|
||||||
|
try {
|
||||||
|
const result = await commandFn();
|
||||||
|
if (result.success) {
|
||||||
|
console.log(`[FunctionMenu] ${actionName}: ${result.message}`);
|
||||||
|
} else {
|
||||||
|
console.error(`[FunctionMenu] ${actionName} failed: ${result.message}`);
|
||||||
|
showAlert({
|
||||||
|
type: 'error',
|
||||||
|
title: `${actionName} Failed`,
|
||||||
|
message: result.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`[FunctionMenu] ${actionName} error:`, error);
|
||||||
|
showAlert({
|
||||||
|
type: 'error',
|
||||||
|
title: `${actionName} Error`,
|
||||||
|
message: error.message || 'Unknown error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={menuRef}
|
||||||
|
className="absolute top-20 left-1/2 -translate-x-1/2 z-50 bg-black/95 backdrop-blur-md border border-neon-blue/50 rounded-lg shadow-glow-blue min-w-[300px]"
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between p-4 border-b border-white/10">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Package className="w-5 h-5 text-neon-blue" />
|
||||||
|
<h3 className="font-tech font-bold text-lg text-white">FUNCTION</h3>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="text-slate-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Menu Items */}
|
||||||
|
<div className="p-2">
|
||||||
|
{/* Manage */}
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openManage(), 'Manage')}
|
||||||
|
className="w-full flex items-center justify-between px-4 py-3 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors font-tech"
|
||||||
|
>
|
||||||
|
<span>Manage</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Log Viewer */}
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openLogViewer(), 'Log Viewer')}
|
||||||
|
className="w-full flex items-center justify-between px-4 py-3 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors font-tech"
|
||||||
|
>
|
||||||
|
<span>Log Viewer</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Open Folder */}
|
||||||
|
<div className="relative">
|
||||||
|
<button
|
||||||
|
onMouseEnter={() => setActiveSubmenu('folder')}
|
||||||
|
className="w-full flex items-center justify-between px-4 py-3 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors font-tech"
|
||||||
|
>
|
||||||
|
<span>Open Folder</span>
|
||||||
|
<ChevronRight className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Folder Submenu */}
|
||||||
|
{activeSubmenu === 'folder' && (
|
||||||
|
<div
|
||||||
|
className="absolute left-full top-0 ml-1 bg-black/95 backdrop-blur-md border border-neon-blue/50 rounded-lg shadow-glow-blue min-w-[200px] p-2"
|
||||||
|
onMouseLeave={() => setActiveSubmenu(null)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openProgramFolder(), 'Program Folder')}
|
||||||
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
|
>
|
||||||
|
Program
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openLogFolder(), 'Log Folder')}
|
||||||
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
|
>
|
||||||
|
Log
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openScreenshotFolder(), 'Screenshot Folder')}
|
||||||
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
|
>
|
||||||
|
Screenshot
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openSavedDataFolder(), 'Saved Data Folder')}
|
||||||
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
|
>
|
||||||
|
Saved Data
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { Camera, ChevronRight, X } from 'lucide-react';
|
import { Camera, ChevronRight, X } from 'lucide-react';
|
||||||
|
import { comms } from '../communication';
|
||||||
|
import { useAlert } from '../contexts/AlertContext';
|
||||||
|
|
||||||
interface VisionMenuProps {
|
interface VisionMenuProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -9,6 +11,7 @@ interface VisionMenuProps {
|
|||||||
export const VisionMenu: React.FC<VisionMenuProps> = ({ isOpen, onClose }) => {
|
export const VisionMenu: React.FC<VisionMenuProps> = ({ isOpen, onClose }) => {
|
||||||
const [activeSubmenu, setActiveSubmenu] = useState<string | null>(null);
|
const [activeSubmenu, setActiveSubmenu] = useState<string | null>(null);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { showAlert } = useAlert();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
@@ -28,14 +31,27 @@ export const VisionMenu: React.FC<VisionMenuProps> = ({ isOpen, onClose }) => {
|
|||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
const handleQRCodeCamera = () => {
|
const handleVisionCommand = async (commandFn: () => Promise<{ success: boolean; message: string }>, actionName: string) => {
|
||||||
console.log('[VisionMenu] Camera (QRCode) clicked');
|
try {
|
||||||
// TODO: Implement QR Code camera functionality
|
const result = await commandFn();
|
||||||
};
|
if (result.success) {
|
||||||
|
console.log(`[VisionMenu] ${actionName}: ${result.message}`);
|
||||||
const handleKeyenceBarcode = () => {
|
} else {
|
||||||
console.log('[VisionMenu] Barcode (Keyence) clicked');
|
console.error(`[VisionMenu] ${actionName} failed: ${result.message}`);
|
||||||
// TODO: Implement Keyence barcode functionality
|
showAlert({
|
||||||
|
type: 'error',
|
||||||
|
title: `${actionName} Failed`,
|
||||||
|
message: result.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`[VisionMenu] ${actionName} error:`, error);
|
||||||
|
showAlert({
|
||||||
|
type: 'error',
|
||||||
|
title: `${actionName} Error`,
|
||||||
|
message: error.message || 'Unknown error'
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -63,7 +79,6 @@ export const VisionMenu: React.FC<VisionMenuProps> = ({ isOpen, onClose }) => {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onMouseEnter={() => setActiveSubmenu('qrcode')}
|
onMouseEnter={() => setActiveSubmenu('qrcode')}
|
||||||
onClick={handleQRCodeCamera}
|
|
||||||
className="w-full flex items-center justify-between px-4 py-3 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors font-tech"
|
className="w-full flex items-center justify-between px-4 py-3 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors font-tech"
|
||||||
>
|
>
|
||||||
<span>Camera (QRCode)</span>
|
<span>Camera (QRCode)</span>
|
||||||
@@ -77,26 +92,26 @@ export const VisionMenu: React.FC<VisionMenuProps> = ({ isOpen, onClose }) => {
|
|||||||
onMouseLeave={() => setActiveSubmenu(null)}
|
onMouseLeave={() => setActiveSubmenu(null)}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] QRCode - Connect')}
|
onClick={() => handleVisionCommand(() => comms.cameraConnect(), 'Camera Connect')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Connect
|
Connect
|
||||||
</button>
|
</button>
|
||||||
<div className="h-px bg-white/10 my-1" />
|
<div className="h-px bg-white/10 my-1" />
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] QRCode - Get Image')}
|
onClick={() => handleVisionCommand(() => comms.cameraGetImage(), 'Camera Get Image')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Get Image
|
Get Image
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] QRCode - Live View')}
|
onClick={() => handleVisionCommand(() => comms.cameraLiveView(), 'Camera Live View')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Live View
|
Live View
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] QRCode - Read Test')}
|
onClick={() => handleVisionCommand(() => comms.cameraReadTest(), 'Camera Read Test')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Read Test
|
Read Test
|
||||||
@@ -109,7 +124,6 @@ export const VisionMenu: React.FC<VisionMenuProps> = ({ isOpen, onClose }) => {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onMouseEnter={() => setActiveSubmenu('keyence')}
|
onMouseEnter={() => setActiveSubmenu('keyence')}
|
||||||
onClick={handleKeyenceBarcode}
|
|
||||||
className="w-full flex items-center justify-between px-4 py-3 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors font-tech"
|
className="w-full flex items-center justify-between px-4 py-3 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors font-tech"
|
||||||
>
|
>
|
||||||
<span>Barcode (Keyence)</span>
|
<span>Barcode (Keyence)</span>
|
||||||
@@ -123,26 +137,26 @@ export const VisionMenu: React.FC<VisionMenuProps> = ({ isOpen, onClose }) => {
|
|||||||
onMouseLeave={() => setActiveSubmenu(null)}
|
onMouseLeave={() => setActiveSubmenu(null)}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] Keyence - Get Image')}
|
onClick={() => handleVisionCommand(() => comms.keyenceGetImage(), 'Keyence Get Image')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Get Image
|
Get Image
|
||||||
</button>
|
</button>
|
||||||
<div className="h-px bg-white/10 my-1" />
|
<div className="h-px bg-white/10 my-1" />
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] Keyence - Trigger On')}
|
onClick={() => handleVisionCommand(() => comms.keyenceTriggerOn(), 'Keyence Trigger On')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Trigger On
|
Trigger On
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] Keyence - Trigger Off')}
|
onClick={() => handleVisionCommand(() => comms.keyenceTriggerOff(), 'Keyence Trigger Off')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Trigger Off
|
Trigger Off
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('[VisionMenu] Keyence - Save Image')}
|
onClick={() => handleVisionCommand(() => comms.keyenceSaveImage(), 'Keyence Save Image')}
|
||||||
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
className="w-full px-4 py-2 text-left text-slate-300 hover:bg-neon-blue/10 hover:text-neon-blue rounded transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Save Image
|
Save Image
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { Activity, Settings, Move, Camera, Layers, Cpu, Target } from 'lucide-react';
|
import { Activity, Settings, Move, Camera, Layers, Cpu, Target, Lightbulb, Printer, XCircle, Package, BookOpen } from 'lucide-react';
|
||||||
import { VisionMenu } from '../VisionMenu';
|
import { VisionMenu } from '../VisionMenu';
|
||||||
|
import { FunctionMenu } from '../FunctionMenu';
|
||||||
|
import { comms } from '../../communication';
|
||||||
|
import { useAlert } from '../../contexts/AlertContext';
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
currentTime: Date;
|
currentTime: Date;
|
||||||
@@ -14,13 +17,40 @@ export const Header: React.FC<HeaderProps> = ({ currentTime, onTabChange, active
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [showVisionMenu, setShowVisionMenu] = useState(false);
|
const [showVisionMenu, setShowVisionMenu] = useState(false);
|
||||||
|
const [showFunctionMenu, setShowFunctionMenu] = useState(false);
|
||||||
|
const { showAlert } = useAlert();
|
||||||
|
|
||||||
const isWebView = typeof window !== 'undefined' && !!window.chrome?.webview;
|
const isWebView = typeof window !== 'undefined' && !!window.chrome?.webview;
|
||||||
const isIOPage = location.pathname === '/io-monitor';
|
const isIOPage = location.pathname === '/io-monitor';
|
||||||
|
|
||||||
|
const handleCommand = async (commandFn: () => Promise<{ success: boolean; message: string }>, actionName: string) => {
|
||||||
|
try {
|
||||||
|
const result = await commandFn();
|
||||||
|
if (result.success) {
|
||||||
|
console.log(`[Header] ${actionName}: ${result.message}`);
|
||||||
|
// Show success message if needed
|
||||||
|
} else {
|
||||||
|
console.error(`[Header] ${actionName} failed: ${result.message}`);
|
||||||
|
showAlert({
|
||||||
|
type: 'error',
|
||||||
|
title: `${actionName} Failed`,
|
||||||
|
message: result.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`[Header] ${actionName} error:`, error);
|
||||||
|
showAlert({
|
||||||
|
type: 'error',
|
||||||
|
title: `${actionName} Error`,
|
||||||
|
message: error.message || 'Unknown error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<VisionMenu isOpen={showVisionMenu} onClose={() => setShowVisionMenu(false)} />
|
<VisionMenu isOpen={showVisionMenu} onClose={() => setShowVisionMenu(false)} />
|
||||||
|
<FunctionMenu isOpen={showFunctionMenu} onClose={() => setShowFunctionMenu(false)} />
|
||||||
<header className="absolute top-0 left-0 right-0 h-20 px-6 flex items-center justify-between z-40 bg-gradient-to-b from-black/80 to-transparent pointer-events-none">
|
<header className="absolute top-0 left-0 right-0 h-20 px-6 flex items-center justify-between z-40 bg-gradient-to-b from-black/80 to-transparent pointer-events-none">
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-4 pointer-events-auto cursor-pointer group"
|
className="flex items-center gap-4 pointer-events-auto cursor-pointer group"
|
||||||
@@ -48,15 +78,50 @@ export const Header: React.FC<HeaderProps> = ({ currentTime, onTabChange, active
|
|||||||
|
|
||||||
{/* Top Navigation */}
|
{/* Top Navigation */}
|
||||||
<div className="flex items-center gap-2 pointer-events-auto">
|
<div className="flex items-center gap-2 pointer-events-auto">
|
||||||
{/* IO Tab Switcher (only on IO page) */}
|
{/* Quick Action Buttons */}
|
||||||
|
<div className="bg-black/40 backdrop-blur-md p-1 rounded-2xl border border-white/10 flex gap-1 mr-2">
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openManual(), 'Manual')}
|
||||||
|
className="flex flex-col items-center justify-center gap-1 px-3 py-2 rounded-xl font-tech font-bold text-[10px] transition-all border border-transparent min-w-[70px] text-slate-400 hover:text-blue-400 hover:bg-white/5"
|
||||||
|
title="Open Manual"
|
||||||
|
>
|
||||||
|
<BookOpen className="w-5 h-5" />
|
||||||
|
<span className="leading-tight">MANUAL</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.toggleLight(), 'Toggle Light')}
|
||||||
|
className="flex flex-col items-center justify-center gap-1 px-3 py-2 rounded-xl font-tech font-bold text-[10px] transition-all border border-transparent min-w-[70px] text-slate-400 hover:text-amber-400 hover:bg-white/5"
|
||||||
|
title="Toggle Room Light"
|
||||||
|
>
|
||||||
|
<Lightbulb className="w-5 h-5" />
|
||||||
|
<span className="leading-tight">LIGHT</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.openManualPrint(), 'Manual Print')}
|
||||||
|
className="flex flex-col items-center justify-center gap-1 px-3 py-2 rounded-xl font-tech font-bold text-[10px] transition-all border border-transparent min-w-[70px] text-slate-400 hover:text-cyan-400 hover:bg-white/5"
|
||||||
|
title="Open Manual Print"
|
||||||
|
>
|
||||||
|
<Printer className="w-5 h-5" />
|
||||||
|
<span className="leading-tight">PRINT</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCommand(() => comms.cancelJob(), 'Cancel Job')}
|
||||||
|
className="flex flex-col items-center justify-center gap-1 px-3 py-2 rounded-xl font-tech font-bold text-[10px] transition-all border border-transparent min-w-[70px] text-slate-400 hover:text-red-400 hover:bg-white/5"
|
||||||
|
title="Cancel Current Job"
|
||||||
|
>
|
||||||
|
<XCircle className="w-5 h-5" />
|
||||||
|
<span className="leading-tight">CANCEL</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Navigation */}
|
||||||
<div className="bg-black/40 backdrop-blur-md p-1 rounded-2xl border border-white/10 flex gap-1">
|
<div className="bg-black/40 backdrop-blur-md p-1 rounded-2xl border border-white/10 flex gap-1">
|
||||||
{[
|
{[
|
||||||
{ id: 'recipe', icon: Layers, label: 'RECIPE', path: '/' },
|
{ id: 'recipe', icon: Layers, label: 'RECIPE', path: '/' },
|
||||||
{ id: 'io', icon: Activity, label: 'I/O MONITOR', path: '/io-monitor' },
|
{ id: 'io', icon: Activity, label: 'I/O MONITOR', path: '/io-monitor' },
|
||||||
{ id: 'motion', icon: Move, label: 'MOTION', path: '/' },
|
{ id: 'motion', icon: Move, label: 'MOTION', path: '/' },
|
||||||
{ id: 'camera', icon: Camera, label: 'VISION', path: '/' },
|
{ id: 'camera', icon: Camera, label: 'VISION', path: '/' },
|
||||||
|
{ id: 'function', icon: Package, label: 'FUNCTION', path: '/' },
|
||||||
{ id: 'setting', icon: Settings, label: 'CONFIG', path: '/' },
|
{ id: 'setting', icon: Settings, label: 'CONFIG', path: '/' },
|
||||||
{ id: 'initialize', icon: Target, label: 'INITIALIZE', path: '/' }
|
{ id: 'initialize', icon: Target, label: 'INITIALIZE', path: '/' }
|
||||||
].map(item => {
|
].map(item => {
|
||||||
@@ -72,20 +137,35 @@ export const Header: React.FC<HeaderProps> = ({ currentTime, onTabChange, active
|
|||||||
navigate('/io-monitor');
|
navigate('/io-monitor');
|
||||||
onTabChange(null);
|
onTabChange(null);
|
||||||
setShowVisionMenu(false);
|
setShowVisionMenu(false);
|
||||||
|
setShowFunctionMenu(false);
|
||||||
} else if (item.id === 'camera') {
|
} else if (item.id === 'camera') {
|
||||||
setShowVisionMenu(!showVisionMenu);
|
// Camera button does nothing on click
|
||||||
onTabChange(null);
|
} else if (item.id === 'function') {
|
||||||
|
// Function button does nothing on click
|
||||||
} else {
|
} else {
|
||||||
if (location.pathname !== '/') {
|
if (location.pathname !== '/') {
|
||||||
navigate('/');
|
navigate('/');
|
||||||
}
|
}
|
||||||
setShowVisionMenu(false);
|
setShowVisionMenu(false);
|
||||||
|
setShowFunctionMenu(false);
|
||||||
onTabChange(activeTab === item.id ? null : item.id as any);
|
onTabChange(activeTab === item.id ? null : item.id as any);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
if (item.id === 'camera') {
|
||||||
|
setShowVisionMenu(true);
|
||||||
|
setShowFunctionMenu(false);
|
||||||
|
} else if (item.id === 'function') {
|
||||||
|
setShowFunctionMenu(true);
|
||||||
|
setShowVisionMenu(false);
|
||||||
|
} else {
|
||||||
|
setShowVisionMenu(false);
|
||||||
|
setShowFunctionMenu(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
className={`
|
className={`
|
||||||
flex flex-col items-center justify-center gap-1 px-3 py-2 rounded-xl font-tech font-bold text-[10px] transition-all border border-transparent min-w-[70px]
|
flex flex-col items-center justify-center gap-1 px-3 py-2 rounded-xl font-tech font-bold text-[10px] transition-all border border-transparent min-w-[70px]
|
||||||
${isActive
|
${isActive || (item.id === 'camera' && showVisionMenu) || (item.id === 'function' && showFunctionMenu)
|
||||||
? 'bg-neon-blue/10 text-neon-blue border-neon-blue shadow-glow-blue'
|
? 'bg-neon-blue/10 text-neon-blue border-neon-blue shadow-glow-blue'
|
||||||
: 'text-slate-400 hover:text-white hover:bg-white/5'}
|
: 'text-slate-400 hover:text-white hover:bg-white/5'}
|
||||||
`}
|
`}
|
||||||
|
|||||||
118
FrontEnd/contexts/AlertContext.tsx
Normal file
118
FrontEnd/contexts/AlertContext.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface AlertConfig {
|
||||||
|
type: 'success' | 'error' | 'warning' | 'info';
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AlertContextType {
|
||||||
|
showAlert: (config: AlertConfig) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AlertContext = createContext<AlertContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const useAlert = () => {
|
||||||
|
const context = useContext(AlertContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useAlert must be used within AlertProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface AlertProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AlertProvider: React.FC<AlertProviderProps> = ({ children }) => {
|
||||||
|
const [alertConfig, setAlertConfig] = useState<(AlertConfig & { isOpen: boolean }) | null>(null);
|
||||||
|
|
||||||
|
const showAlert = (config: AlertConfig) => {
|
||||||
|
setAlertConfig({ ...config, isOpen: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeAlert = () => {
|
||||||
|
setAlertConfig(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AlertContext.Provider value={{ showAlert }}>
|
||||||
|
{children}
|
||||||
|
{alertConfig && (
|
||||||
|
<div className="fixed inset-0 z-[100] flex items-center justify-center">
|
||||||
|
{/* Backdrop */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
|
||||||
|
onClick={closeAlert}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Dialog */}
|
||||||
|
<div
|
||||||
|
className={`relative bg-black/95 backdrop-blur-md border-2 ${getBorderColor(alertConfig.type)} rounded-lg shadow-2xl max-w-md w-full mx-4 animate-fadeIn`}
|
||||||
|
>
|
||||||
|
{/* Icon and Title */}
|
||||||
|
<div className="flex items-center gap-4 p-6 border-b border-white/10">
|
||||||
|
{getIcon(alertConfig.type)}
|
||||||
|
<h2 className="text-xl font-tech font-bold text-white uppercase tracking-wider">
|
||||||
|
{alertConfig.title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Message */}
|
||||||
|
<div className="p-6 max-w-full overflow-hidden">
|
||||||
|
<p className="text-slate-300 font-mono text-sm whitespace-pre-wrap leading-relaxed break-words max-w-full">
|
||||||
|
{alertConfig.message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Button */}
|
||||||
|
<div className="flex justify-end p-6 border-t border-white/10">
|
||||||
|
<button
|
||||||
|
onClick={closeAlert}
|
||||||
|
className="px-6 py-2 bg-neon-blue/20 hover:bg-neon-blue/30 border border-neon-blue text-neon-blue font-tech font-bold rounded transition-colors shadow-glow-blue"
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</AlertContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIcon = (type: 'success' | 'error' | 'warning' | 'info') => {
|
||||||
|
const icons = {
|
||||||
|
success: (
|
||||||
|
<svg className="w-12 h-12 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
error: (
|
||||||
|
<svg className="w-12 h-12 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
warning: (
|
||||||
|
<svg className="w-12 h-12 text-amber-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
info: (
|
||||||
|
<svg className="w-12 h-12 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
return icons[type];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBorderColor = (type: 'success' | 'error' | 'warning' | 'info') => {
|
||||||
|
const colors = {
|
||||||
|
success: 'border-green-500',
|
||||||
|
error: 'border-red-500',
|
||||||
|
warning: 'border-amber-500',
|
||||||
|
info: 'border-blue-500',
|
||||||
|
};
|
||||||
|
return colors[type];
|
||||||
|
};
|
||||||
@@ -21,6 +21,7 @@ interface HomePageProps {
|
|||||||
doorStates: { front: boolean; right: boolean; left: boolean; back: boolean };
|
doorStates: { front: boolean; right: boolean; left: boolean; back: boolean };
|
||||||
isLowPressure: boolean;
|
isLowPressure: boolean;
|
||||||
isEmergencyStop: boolean;
|
isEmergencyStop: boolean;
|
||||||
|
isHostConnected: boolean;
|
||||||
activeTab: 'recipe' | 'motion' | 'camera' | 'setting' | 'initialize' | null;
|
activeTab: 'recipe' | 'motion' | 'camera' | 'setting' | 'initialize' | null;
|
||||||
onSelectRecipe: (r: Recipe) => void;
|
onSelectRecipe: (r: Recipe) => void;
|
||||||
onMove: (axis: 'X' | 'Y' | 'Z', val: number) => void;
|
onMove: (axis: 'X' | 'Y' | 'Z', val: number) => void;
|
||||||
@@ -39,6 +40,7 @@ export const HomePage: React.FC<HomePageProps> = ({
|
|||||||
doorStates,
|
doorStates,
|
||||||
isLowPressure,
|
isLowPressure,
|
||||||
isEmergencyStop,
|
isEmergencyStop,
|
||||||
|
isHostConnected,
|
||||||
activeTab,
|
activeTab,
|
||||||
onSelectRecipe,
|
onSelectRecipe,
|
||||||
onMove,
|
onMove,
|
||||||
@@ -56,6 +58,16 @@ export const HomePage: React.FC<HomePageProps> = ({
|
|||||||
|
|
||||||
{/* Center Alarms */}
|
{/* Center Alarms */}
|
||||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 pointer-events-none flex flex-col items-center gap-4">
|
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 pointer-events-none flex flex-col items-center gap-4">
|
||||||
|
{!isHostConnected && !isEmergencyStop && (
|
||||||
|
<div className="bg-red-600/90 text-white p-8 border-4 border-red-500 shadow-glow-red flex items-center gap-6 animate-pulse">
|
||||||
|
<AlertTriangle className="w-16 h-16" />
|
||||||
|
<div>
|
||||||
|
<h1 className="text-4xl font-tech font-bold tracking-widest">HOST DISCONNECTED</h1>
|
||||||
|
<p className="text-center font-mono text-base">WAITING FOR CONNECTION...</p>
|
||||||
|
<p className="text-center font-mono text-sm mt-2 text-red-200">PLEASE CHECK THE HANDLER PROGRAM</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{isEmergencyStop && (
|
{isEmergencyStop && (
|
||||||
<div className="bg-red-600/90 text-white p-8 border-4 border-red-500 shadow-glow-red flex items-center gap-6 animate-pulse">
|
<div className="bg-red-600/90 text-white p-8 border-4 border-red-500 shadow-glow-red flex items-center gap-6 animate-pulse">
|
||||||
<Siren className="w-16 h-16 animate-spin" />
|
<Siren className="w-16 h-16 animate-spin" />
|
||||||
@@ -65,7 +77,7 @@ export const HomePage: React.FC<HomePageProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isLowPressure && !isEmergencyStop && (
|
{isLowPressure && !isEmergencyStop && isHostConnected && (
|
||||||
<div className="bg-amber-500/80 text-black px-8 py-4 rounded font-bold text-2xl tracking-widest flex items-center gap-4 shadow-glow-red animate-bounce">
|
<div className="bg-amber-500/80 text-black px-8 py-4 rounded font-bold text-2xl tracking-widest flex items-center gap-4 shadow-glow-red animate-bounce">
|
||||||
<AlertTriangle className="w-8 h-8" /> LOW AIR PRESSURE WARNING
|
<AlertTriangle className="w-8 h-8" /> LOW AIR PRESSURE WARNING
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export default {
|
|||||||
'gradient': 'gradient 15s ease infinite',
|
'gradient': 'gradient 15s ease infinite',
|
||||||
'scan': 'scan 4s linear infinite',
|
'scan': 'scan 4s linear infinite',
|
||||||
'spin-slow': 'spin 10s linear infinite',
|
'spin-slow': 'spin 10s linear infinite',
|
||||||
|
'fadeIn': 'fadeIn 0.2s ease-out',
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
glow: {
|
glow: {
|
||||||
@@ -51,6 +52,10 @@ export default {
|
|||||||
scan: {
|
scan: {
|
||||||
'0%': { transform: 'translateY(-100%)' },
|
'0%': { transform: 'translateY(-100%)' },
|
||||||
'100%': { transform: 'translateY(100%)' },
|
'100%': { transform: 'translateY(100%)' },
|
||||||
|
},
|
||||||
|
fadeIn: {
|
||||||
|
'0%': { opacity: '0', transform: 'scale(0.95)' },
|
||||||
|
'100%': { opacity: '1', transform: 'scale(1)' },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -949,5 +949,452 @@ namespace Project.WebUI
|
|||||||
return JsonConvert.SerializeObject(new List<object>());
|
return JsonConvert.SerializeObject(new List<object>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== VISION CONTROL METHODS =====
|
||||||
|
|
||||||
|
public string CameraConnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (PUB.wsL != null && PUB.wsL.Connected)
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "Camera is already connected" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(SETTING.Data.CameraLFile))
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "Camera program filename not specified" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fi = new System.IO.FileInfo(SETTING.Data.CameraLFile);
|
||||||
|
if (!fi.Exists)
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = $"Camera program file does not exist\n{fi.FullName}" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.log.Add("User Request: Connect Camera (QRCode)", false);
|
||||||
|
// Camera connection logic would be implemented here
|
||||||
|
// For now, return success
|
||||||
|
var response2 = new { success = true, message = "Camera connection initiated" };
|
||||||
|
return JsonConvert.SerializeObject(response2);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to connect camera: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CameraDisconnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Disconnect Camera (QRCode)", false);
|
||||||
|
// Camera disconnection logic
|
||||||
|
var response = new { success = true, message = "Camera disconnected" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to disconnect camera: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CameraGetImage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Get Camera Image", false);
|
||||||
|
// Get image logic
|
||||||
|
var response = new { success = true, message = "Image captured" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to get camera image: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CameraLiveView()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Camera Live View", false);
|
||||||
|
// Live view logic
|
||||||
|
var response = new { success = true, message = "Live view started" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to start live view: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CameraReadTest()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Camera Read Test", false);
|
||||||
|
// Read test logic
|
||||||
|
var response = new { success = true, message = "Read test completed" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to run read test: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string KeyenceTriggerOn()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Keyence Trigger ON", false);
|
||||||
|
PUB.keyenceF.Trigger(true);
|
||||||
|
PUB.keyenceR.Trigger(true);
|
||||||
|
var response = new { success = true, message = "Keyence trigger ON" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to trigger keyence on: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string KeyenceTriggerOff()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Keyence Trigger OFF", false);
|
||||||
|
PUB.keyenceF.Trigger(false);
|
||||||
|
PUB.keyenceR.Trigger(false);
|
||||||
|
var response = new { success = true, message = "Keyence trigger OFF" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to trigger keyence off: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string KeyenceGetImage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Get Keyence Image", false);
|
||||||
|
var curimageF = PUB.keyenceF.GetImage();
|
||||||
|
var curimageR = PUB.keyenceR.GetImage();
|
||||||
|
var response = new { success = true, message = "Keyence images captured" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to get keyence image: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string KeyenceSaveImage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.log.Add("User Request: Save Keyence Image", false);
|
||||||
|
var fn = System.IO.Path.Combine(AR.UTIL.CurrentPath, "Images", "keyence.bmp");
|
||||||
|
var dir = System.IO.Path.GetDirectoryName(fn);
|
||||||
|
if (!System.IO.Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
System.IO.Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
// SaveImage logic would go here
|
||||||
|
var response = new { success = true, message = $"Image saved to {fn}" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to save keyence image: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToggleLight()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (PUB.flag.get(AR.eVarBool.FG_INIT_MOTIO) == false)
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "Motion not initialized. Please try again later" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cur = DIO.GetIOOutput(AR.eDOName.ROOMLIGHT);
|
||||||
|
DIO.SetRoomLight(!cur, true);
|
||||||
|
PUB.log.Add($"User Request: Room Light {(!cur ? "ON" : "OFF")}", false);
|
||||||
|
var response2 = new { success = true, message = $"Light turned {(!cur ? "ON" : "OFF")}" };
|
||||||
|
return JsonConvert.SerializeObject(response2);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to toggle light: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenManualPrint()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Manual print dialog cannot be opened from web UI
|
||||||
|
// This would require a complex form with reel data input
|
||||||
|
PUB.log.Add("User Request: Manual Print (Web UI)", false);
|
||||||
|
var response = new { success = false, message = "Manual Print is not available in Web UI. Please use the main program." };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open manual print: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CancelJob()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (PUB.flag.get(AR.eVarBool.FG_INIT_MOTIO) == false)
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "Motion not initialized. Please try again later" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = new System.Text.StringBuilder();
|
||||||
|
|
||||||
|
if (PUB.mot.HasHomeSetOff)
|
||||||
|
{
|
||||||
|
msg.AppendLine("Device initialization is not complete. Execute device initialization");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PUB.sm.isRunning == true)
|
||||||
|
{
|
||||||
|
msg.AppendLine("AUTO-RUN MODE. Cannot be used during automatic execution. Please stop and try again");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DIO.IsEmergencyOn() == true)
|
||||||
|
{
|
||||||
|
msg.AppendLine("Release emergency stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DIO.isSaftyDoorF() == false)
|
||||||
|
{
|
||||||
|
msg.AppendLine("Front door is open");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DIO.isSaftyDoorR() == false)
|
||||||
|
{
|
||||||
|
msg.AppendLine("Rear door is open");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.Length > 0)
|
||||||
|
{
|
||||||
|
PUB.log.AddE(msg.ToString());
|
||||||
|
var response = new { success = false, message = msg.ToString() };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.log.Add("User Click : tray out & clear position", false);
|
||||||
|
PUB.flag.set(eVarBool.FG_USERSTEP, false, "Run_MotionPositionReset");
|
||||||
|
PUB.log.AddAT("Starting discharge and home movement");
|
||||||
|
PUB.AddSystemLog(System.Windows.Forms.Application.ProductVersion, "MAIN", "Cancel Work");
|
||||||
|
PUB.sm.SetNewStep(eSMStep.HOME_QUICK);
|
||||||
|
|
||||||
|
var response2 = new { success = true, message = "Job cancelled. Executing motion position reset" };
|
||||||
|
return JsonConvert.SerializeObject(response2);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to cancel job: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenManage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (PUB.sm.isRunning)
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "Cannot use during operation" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PUB.flag.get(AR.eVarBool.FG_MOVE_PICKER))
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "The window is already open" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The manage dialog (fPickerMove) cannot be opened from web UI
|
||||||
|
// This would require implementing a separate picker management page
|
||||||
|
PUB.log.Add("User Request: Manage (Web UI)", false);
|
||||||
|
var response2 = new { success = false, message = "Manage is not available in Web UI. Please use the main program." };
|
||||||
|
return JsonConvert.SerializeObject(response2);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open manage: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenManual()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string file = System.IO.Path.Combine(AR.UTIL.CurrentPath, "Manual", "Manual.pdf");
|
||||||
|
if (System.IO.File.Exists(file) == false)
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "User manual file does not exist\nFile name: " + file };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
AR.UTIL.RunExplorer(file);
|
||||||
|
PUB.log.Add("User Request: Open Manual", false);
|
||||||
|
var response2 = new { success = true, message = "Manual opened" };
|
||||||
|
return JsonConvert.SerializeObject(response2);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open manual: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenLogViewer()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var exename = AR.UTIL.MakePath("LogView.exe");
|
||||||
|
if (System.IO.File.Exists(exename) == false)
|
||||||
|
{
|
||||||
|
var response = new { success = false, message = "Log viewer file not found\nPlease contact support" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
AR.UTIL.RunProcess(exename);
|
||||||
|
PUB.log.Add("User Request: Open Log Viewer", false);
|
||||||
|
var response2 = new { success = true, message = "Log viewer opened" };
|
||||||
|
return JsonConvert.SerializeObject(response2);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open log viewer: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenProgramFolder()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AR.UTIL.RunExplorer(AR.UTIL.CurrentPath);
|
||||||
|
PUB.log.Add("User Request: Open Program Folder", false);
|
||||||
|
var response = new { success = true, message = "Program folder opened" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open program folder: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenLogFolder()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.LogFlush();
|
||||||
|
var fi = new System.IO.FileInfo(PUB.log.FileName);
|
||||||
|
AR.UTIL.RunExplorer(fi.Directory.FullName);
|
||||||
|
PUB.log.Add("User Request: Open Log Folder", false);
|
||||||
|
var response = new { success = true, message = "Log folder opened" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open log folder: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenScreenshotFolder()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string savefile = System.IO.Path.Combine(AR.UTIL.CurrentPath, "ScreenShot", DateTime.Now.ToString("yyyyMMddHHmmss") + ".png");
|
||||||
|
var grpath = new System.IO.FileInfo(savefile);
|
||||||
|
AR.UTIL.RunExplorer(grpath.Directory.FullName);
|
||||||
|
PUB.log.Add("User Request: Open Screenshot Folder", false);
|
||||||
|
var response = new { success = true, message = "Screenshot folder opened" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open screenshot folder: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenSavedDataFolder()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var basepath = SETTING.Data.GetDataPath();
|
||||||
|
var path = System.IO.Path.Combine(
|
||||||
|
basepath, "History",
|
||||||
|
DateTime.Now.Year.ToString("0000"),
|
||||||
|
DateTime.Now.Month.ToString("00"),
|
||||||
|
DateTime.Now.Day.ToString("00"));
|
||||||
|
if (System.IO.Directory.Exists(path))
|
||||||
|
AR.UTIL.RunExplorer(path);
|
||||||
|
else
|
||||||
|
AR.UTIL.RunExplorer(System.IO.Path.Combine(SETTING.Data.GetDataPath(), "History"));
|
||||||
|
|
||||||
|
PUB.log.Add("User Request: Open Saved Data Folder", false);
|
||||||
|
var response = new { success = true, message = "Saved data folder opened" };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR] Failed to open saved data folder: {ex.Message}");
|
||||||
|
var response = new { success = false, message = ex.Message };
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,139 @@ namespace Project.WebUI
|
|||||||
var response = new { type = "PROCESSED_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(dataJson) };
|
var response = new { type = "PROCESSED_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(dataJson) };
|
||||||
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
}
|
}
|
||||||
|
else if (type == "CAMERA_CONNECT")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.CameraConnect();
|
||||||
|
var response = new { type = "CAMERA_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "CAMERA_DISCONNECT")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.CameraDisconnect();
|
||||||
|
var response = new { type = "CAMERA_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "CAMERA_GET_IMAGE")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.CameraGetImage();
|
||||||
|
var response = new { type = "CAMERA_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "CAMERA_LIVE_VIEW")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.CameraLiveView();
|
||||||
|
var response = new { type = "CAMERA_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "CAMERA_READ_TEST")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.CameraReadTest();
|
||||||
|
var response = new { type = "CAMERA_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "KEYENCE_TRIGGER_ON")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.KeyenceTriggerOn();
|
||||||
|
var response = new { type = "KEYENCE_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "KEYENCE_TRIGGER_OFF")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.KeyenceTriggerOff();
|
||||||
|
var response = new { type = "KEYENCE_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "KEYENCE_GET_IMAGE")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.KeyenceGetImage();
|
||||||
|
var response = new { type = "KEYENCE_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "KEYENCE_SAVE_IMAGE")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.KeyenceSaveImage();
|
||||||
|
var response = new { type = "KEYENCE_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "TOGGLE_LIGHT")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.ToggleLight();
|
||||||
|
var response = new { type = "LIGHT_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_MANUAL_PRINT")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenManualPrint();
|
||||||
|
var response = new { type = "MANUAL_PRINT_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "CANCEL_JOB")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.CancelJob();
|
||||||
|
var response = new { type = "CANCEL_JOB_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_MANAGE")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenManage();
|
||||||
|
var response = new { type = "MANAGE_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_MANUAL")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenManual();
|
||||||
|
var response = new { type = "MANUAL_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_LOG_VIEWER")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenLogViewer();
|
||||||
|
var response = new { type = "LOG_VIEWER_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_PROGRAM_FOLDER")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenProgramFolder();
|
||||||
|
var response = new { type = "FOLDER_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_LOG_FOLDER")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenLogFolder();
|
||||||
|
var response = new { type = "FOLDER_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_SCREENSHOT_FOLDER")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenScreenshotFolder();
|
||||||
|
var response = new { type = "FOLDER_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "OPEN_SAVED_DATA_FOLDER")
|
||||||
|
{
|
||||||
|
var bridge = new MachineBridge(_mainForm);
|
||||||
|
string resultJson = bridge.OpenSavedDataFolder();
|
||||||
|
var response = new { type = "FOLDER_RESULT", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||||
|
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
62
Handler/Project/fMain.Designer.cs
generated
62
Handler/Project/fMain.Designer.cs
generated
@@ -352,7 +352,7 @@
|
|||||||
this.btJobCancle = new System.Windows.Forms.ToolStripButton();
|
this.btJobCancle = new System.Windows.Forms.ToolStripButton();
|
||||||
this.btDebug = new System.Windows.Forms.ToolStripButton();
|
this.btDebug = new System.Windows.Forms.ToolStripButton();
|
||||||
this.btCheckInfo = new System.Windows.Forms.ToolStripButton();
|
this.btCheckInfo = new System.Windows.Forms.ToolStripButton();
|
||||||
this.toolStripButton3 = new System.Windows.Forms.ToolStripDropDownButton();
|
this.menu_camera = new System.Windows.Forms.ToolStripDropDownButton();
|
||||||
this.바코드LToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.바코드LToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.연결ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.연결ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.toolStripMenuItem26 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripMenuItem26 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
@@ -2261,7 +2261,7 @@
|
|||||||
this.btJobCancle,
|
this.btJobCancle,
|
||||||
this.btDebug,
|
this.btDebug,
|
||||||
this.btCheckInfo,
|
this.btCheckInfo,
|
||||||
this.toolStripButton3,
|
this.menu_camera,
|
||||||
this.toolStripSeparator7,
|
this.toolStripSeparator7,
|
||||||
this.btHistory});
|
this.btHistory});
|
||||||
this.panTopMenu.Location = new System.Drawing.Point(1, 1);
|
this.panTopMenu.Location = new System.Drawing.Point(1, 1);
|
||||||
@@ -2604,16 +2604,16 @@
|
|||||||
this.btCheckInfo.Text = "Barcode Check";
|
this.btCheckInfo.Text = "Barcode Check";
|
||||||
this.btCheckInfo.Click += new System.EventHandler(this.toolStripButton15_Click);
|
this.btCheckInfo.Click += new System.EventHandler(this.toolStripButton15_Click);
|
||||||
//
|
//
|
||||||
// toolStripButton3
|
// menu_camera
|
||||||
//
|
//
|
||||||
this.toolStripButton3.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.menu_camera.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.바코드LToolStripMenuItem,
|
this.바코드LToolStripMenuItem,
|
||||||
this.바코드키엔스ToolStripMenuItem});
|
this.바코드키엔스ToolStripMenuItem});
|
||||||
this.toolStripButton3.Image = global::Project.Properties.Resources.icons8_camera_40;
|
this.menu_camera.Image = global::Project.Properties.Resources.icons8_camera_40;
|
||||||
this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta;
|
this.menu_camera.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
this.toolStripButton3.Name = "toolStripButton3";
|
this.menu_camera.Name = "menu_camera";
|
||||||
this.toolStripButton3.Size = new System.Drawing.Size(101, 44);
|
this.menu_camera.Size = new System.Drawing.Size(101, 44);
|
||||||
this.toolStripButton3.Text = "Camera";
|
this.menu_camera.Text = "Camera";
|
||||||
//
|
//
|
||||||
// 바코드LToolStripMenuItem
|
// 바코드LToolStripMenuItem
|
||||||
//
|
//
|
||||||
@@ -2627,27 +2627,27 @@
|
|||||||
this.toolStripMenuItem30});
|
this.toolStripMenuItem30});
|
||||||
this.바코드LToolStripMenuItem.Image = global::Project.Properties.Resources.Arrow_Left;
|
this.바코드LToolStripMenuItem.Image = global::Project.Properties.Resources.Arrow_Left;
|
||||||
this.바코드LToolStripMenuItem.Name = "바코드LToolStripMenuItem";
|
this.바코드LToolStripMenuItem.Name = "바코드LToolStripMenuItem";
|
||||||
this.바코드LToolStripMenuItem.Size = new System.Drawing.Size(197, 46);
|
this.바코드LToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.바코드LToolStripMenuItem.Text = "Camera (QRCode)";
|
this.바코드LToolStripMenuItem.Text = "Camera (QRCode)";
|
||||||
this.바코드LToolStripMenuItem.Click += new System.EventHandler(this.button1_Click_1);
|
this.바코드LToolStripMenuItem.Click += new System.EventHandler(this.button1_Click_1);
|
||||||
//
|
//
|
||||||
// 연결ToolStripMenuItem
|
// 연결ToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.연결ToolStripMenuItem.Name = "연결ToolStripMenuItem";
|
this.연결ToolStripMenuItem.Name = "연결ToolStripMenuItem";
|
||||||
this.연결ToolStripMenuItem.Size = new System.Drawing.Size(156, 46);
|
this.연결ToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.연결ToolStripMenuItem.Text = "Connect";
|
this.연결ToolStripMenuItem.Text = "Connect";
|
||||||
this.연결ToolStripMenuItem.Click += new System.EventHandler(this.ConnectToolStripMenuItem_Click);
|
this.연결ToolStripMenuItem.Click += new System.EventHandler(this.ConnectToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem26
|
// toolStripMenuItem26
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem26.Name = "toolStripMenuItem26";
|
this.toolStripMenuItem26.Name = "toolStripMenuItem26";
|
||||||
this.toolStripMenuItem26.Size = new System.Drawing.Size(153, 6);
|
this.toolStripMenuItem26.Size = new System.Drawing.Size(201, 6);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem21
|
// toolStripMenuItem21
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem21.Image = global::Project.Properties.Resources.icons8_green_circle_40;
|
this.toolStripMenuItem21.Image = global::Project.Properties.Resources.icons8_green_circle_40;
|
||||||
this.toolStripMenuItem21.Name = "toolStripMenuItem21";
|
this.toolStripMenuItem21.Name = "toolStripMenuItem21";
|
||||||
this.toolStripMenuItem21.Size = new System.Drawing.Size(156, 46);
|
this.toolStripMenuItem21.Size = new System.Drawing.Size(204, 46);
|
||||||
this.toolStripMenuItem21.Text = "Trigger On";
|
this.toolStripMenuItem21.Text = "Trigger On";
|
||||||
this.toolStripMenuItem21.Click += new System.EventHandler(this.toolStripMenuItem21_Click);
|
this.toolStripMenuItem21.Click += new System.EventHandler(this.toolStripMenuItem21_Click);
|
||||||
//
|
//
|
||||||
@@ -2655,20 +2655,20 @@
|
|||||||
//
|
//
|
||||||
this.toolStripMenuItem24.Image = global::Project.Properties.Resources.icons8_black_circle_40;
|
this.toolStripMenuItem24.Image = global::Project.Properties.Resources.icons8_black_circle_40;
|
||||||
this.toolStripMenuItem24.Name = "toolStripMenuItem24";
|
this.toolStripMenuItem24.Name = "toolStripMenuItem24";
|
||||||
this.toolStripMenuItem24.Size = new System.Drawing.Size(156, 46);
|
this.toolStripMenuItem24.Size = new System.Drawing.Size(204, 46);
|
||||||
this.toolStripMenuItem24.Text = "Trigger Off";
|
this.toolStripMenuItem24.Text = "Trigger Off";
|
||||||
this.toolStripMenuItem24.Click += new System.EventHandler(this.toolStripMenuItem24_Click);
|
this.toolStripMenuItem24.Click += new System.EventHandler(this.toolStripMenuItem24_Click);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem6
|
// toolStripMenuItem6
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem6.Name = "toolStripMenuItem6";
|
this.toolStripMenuItem6.Name = "toolStripMenuItem6";
|
||||||
this.toolStripMenuItem6.Size = new System.Drawing.Size(153, 6);
|
this.toolStripMenuItem6.Size = new System.Drawing.Size(201, 6);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem28
|
// toolStripMenuItem28
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem28.Image = global::Project.Properties.Resources.icons8_green_circle_40;
|
this.toolStripMenuItem28.Image = global::Project.Properties.Resources.icons8_green_circle_40;
|
||||||
this.toolStripMenuItem28.Name = "toolStripMenuItem28";
|
this.toolStripMenuItem28.Name = "toolStripMenuItem28";
|
||||||
this.toolStripMenuItem28.Size = new System.Drawing.Size(156, 46);
|
this.toolStripMenuItem28.Size = new System.Drawing.Size(204, 46);
|
||||||
this.toolStripMenuItem28.Text = "Trigger On";
|
this.toolStripMenuItem28.Text = "Trigger On";
|
||||||
this.toolStripMenuItem28.Click += new System.EventHandler(this.toolStripMenuItem28_Click);
|
this.toolStripMenuItem28.Click += new System.EventHandler(this.toolStripMenuItem28_Click);
|
||||||
//
|
//
|
||||||
@@ -2676,7 +2676,7 @@
|
|||||||
//
|
//
|
||||||
this.toolStripMenuItem30.Image = global::Project.Properties.Resources.icons8_black_circle_40;
|
this.toolStripMenuItem30.Image = global::Project.Properties.Resources.icons8_black_circle_40;
|
||||||
this.toolStripMenuItem30.Name = "toolStripMenuItem30";
|
this.toolStripMenuItem30.Name = "toolStripMenuItem30";
|
||||||
this.toolStripMenuItem30.Size = new System.Drawing.Size(156, 46);
|
this.toolStripMenuItem30.Size = new System.Drawing.Size(204, 46);
|
||||||
this.toolStripMenuItem30.Text = "Trigger Off";
|
this.toolStripMenuItem30.Text = "Trigger Off";
|
||||||
this.toolStripMenuItem30.Click += new System.EventHandler(this.toolStripMenuItem30_Click);
|
this.toolStripMenuItem30.Click += new System.EventHandler(this.toolStripMenuItem30_Click);
|
||||||
//
|
//
|
||||||
@@ -2697,27 +2697,27 @@
|
|||||||
this.loadMemoryToolStripMenuItem});
|
this.loadMemoryToolStripMenuItem});
|
||||||
this.바코드키엔스ToolStripMenuItem.Image = global::Project.Properties.Resources.Barcode;
|
this.바코드키엔스ToolStripMenuItem.Image = global::Project.Properties.Resources.Barcode;
|
||||||
this.바코드키엔스ToolStripMenuItem.Name = "바코드키엔스ToolStripMenuItem";
|
this.바코드키엔스ToolStripMenuItem.Name = "바코드키엔스ToolStripMenuItem";
|
||||||
this.바코드키엔스ToolStripMenuItem.Size = new System.Drawing.Size(197, 46);
|
this.바코드키엔스ToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.바코드키엔스ToolStripMenuItem.Text = "Barcode (Keyence)";
|
this.바코드키엔스ToolStripMenuItem.Text = "Barcode (Keyence)";
|
||||||
//
|
//
|
||||||
// toolStripMenuItem17
|
// toolStripMenuItem17
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem17.Image = global::Project.Properties.Resources.icons8_camera_40;
|
this.toolStripMenuItem17.Image = global::Project.Properties.Resources.icons8_camera_40;
|
||||||
this.toolStripMenuItem17.Name = "toolStripMenuItem17";
|
this.toolStripMenuItem17.Name = "toolStripMenuItem17";
|
||||||
this.toolStripMenuItem17.Size = new System.Drawing.Size(173, 46);
|
this.toolStripMenuItem17.Size = new System.Drawing.Size(204, 46);
|
||||||
this.toolStripMenuItem17.Text = "Get Image";
|
this.toolStripMenuItem17.Text = "Get Image";
|
||||||
this.toolStripMenuItem17.Click += new System.EventHandler(this.toolStripMenuItem17_Click);
|
this.toolStripMenuItem17.Click += new System.EventHandler(this.toolStripMenuItem17_Click);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem18
|
// toolStripMenuItem18
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem18.Name = "toolStripMenuItem18";
|
this.toolStripMenuItem18.Name = "toolStripMenuItem18";
|
||||||
this.toolStripMenuItem18.Size = new System.Drawing.Size(170, 6);
|
this.toolStripMenuItem18.Size = new System.Drawing.Size(201, 6);
|
||||||
//
|
//
|
||||||
// triggerOnToolStripMenuItem1
|
// triggerOnToolStripMenuItem1
|
||||||
//
|
//
|
||||||
this.triggerOnToolStripMenuItem1.Image = global::Project.Properties.Resources.icons8_green_circle_40;
|
this.triggerOnToolStripMenuItem1.Image = global::Project.Properties.Resources.icons8_green_circle_40;
|
||||||
this.triggerOnToolStripMenuItem1.Name = "triggerOnToolStripMenuItem1";
|
this.triggerOnToolStripMenuItem1.Name = "triggerOnToolStripMenuItem1";
|
||||||
this.triggerOnToolStripMenuItem1.Size = new System.Drawing.Size(173, 46);
|
this.triggerOnToolStripMenuItem1.Size = new System.Drawing.Size(204, 46);
|
||||||
this.triggerOnToolStripMenuItem1.Text = "Trigger On";
|
this.triggerOnToolStripMenuItem1.Text = "Trigger On";
|
||||||
this.triggerOnToolStripMenuItem1.Click += new System.EventHandler(this.triggerOnToolStripMenuItem1_Click);
|
this.triggerOnToolStripMenuItem1.Click += new System.EventHandler(this.triggerOnToolStripMenuItem1_Click);
|
||||||
//
|
//
|
||||||
@@ -2725,20 +2725,20 @@
|
|||||||
//
|
//
|
||||||
this.triggerOffToolStripMenuItem1.Image = global::Project.Properties.Resources.icons8_black_circle_40;
|
this.triggerOffToolStripMenuItem1.Image = global::Project.Properties.Resources.icons8_black_circle_40;
|
||||||
this.triggerOffToolStripMenuItem1.Name = "triggerOffToolStripMenuItem1";
|
this.triggerOffToolStripMenuItem1.Name = "triggerOffToolStripMenuItem1";
|
||||||
this.triggerOffToolStripMenuItem1.Size = new System.Drawing.Size(173, 46);
|
this.triggerOffToolStripMenuItem1.Size = new System.Drawing.Size(204, 46);
|
||||||
this.triggerOffToolStripMenuItem1.Text = "Trigger Off";
|
this.triggerOffToolStripMenuItem1.Text = "Trigger Off";
|
||||||
this.triggerOffToolStripMenuItem1.Click += new System.EventHandler(this.triggerOffToolStripMenuItem1_Click);
|
this.triggerOffToolStripMenuItem1.Click += new System.EventHandler(this.triggerOffToolStripMenuItem1_Click);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem19
|
// toolStripMenuItem19
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem19.Name = "toolStripMenuItem19";
|
this.toolStripMenuItem19.Name = "toolStripMenuItem19";
|
||||||
this.toolStripMenuItem19.Size = new System.Drawing.Size(170, 6);
|
this.toolStripMenuItem19.Size = new System.Drawing.Size(201, 6);
|
||||||
//
|
//
|
||||||
// connectToolStripMenuItem
|
// connectToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.connectToolStripMenuItem.Image = global::Project.Properties.Resources.Socket;
|
this.connectToolStripMenuItem.Image = global::Project.Properties.Resources.Socket;
|
||||||
this.connectToolStripMenuItem.Name = "connectToolStripMenuItem";
|
this.connectToolStripMenuItem.Name = "connectToolStripMenuItem";
|
||||||
this.connectToolStripMenuItem.Size = new System.Drawing.Size(173, 46);
|
this.connectToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.connectToolStripMenuItem.Text = "Connect";
|
this.connectToolStripMenuItem.Text = "Connect";
|
||||||
this.connectToolStripMenuItem.Click += new System.EventHandler(this.connectToolStripMenuItem_Click);
|
this.connectToolStripMenuItem.Click += new System.EventHandler(this.connectToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
@@ -2746,20 +2746,20 @@
|
|||||||
//
|
//
|
||||||
this.disConnectToolStripMenuItem.Image = global::Project.Properties.Resources.icons8_unavailable_40;
|
this.disConnectToolStripMenuItem.Image = global::Project.Properties.Resources.icons8_unavailable_40;
|
||||||
this.disConnectToolStripMenuItem.Name = "disConnectToolStripMenuItem";
|
this.disConnectToolStripMenuItem.Name = "disConnectToolStripMenuItem";
|
||||||
this.disConnectToolStripMenuItem.Size = new System.Drawing.Size(173, 46);
|
this.disConnectToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.disConnectToolStripMenuItem.Text = "DisConnect";
|
this.disConnectToolStripMenuItem.Text = "DisConnect";
|
||||||
this.disConnectToolStripMenuItem.Click += new System.EventHandler(this.disConnectToolStripMenuItem_Click);
|
this.disConnectToolStripMenuItem.Click += new System.EventHandler(this.disConnectToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem20
|
// toolStripMenuItem20
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem20.Name = "toolStripMenuItem20";
|
this.toolStripMenuItem20.Name = "toolStripMenuItem20";
|
||||||
this.toolStripMenuItem20.Size = new System.Drawing.Size(170, 6);
|
this.toolStripMenuItem20.Size = new System.Drawing.Size(201, 6);
|
||||||
//
|
//
|
||||||
// resetToolStripMenuItem
|
// resetToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.resetToolStripMenuItem.Image = global::Project.Properties.Resources.icons8_delete_40;
|
this.resetToolStripMenuItem.Image = global::Project.Properties.Resources.icons8_delete_40;
|
||||||
this.resetToolStripMenuItem.Name = "resetToolStripMenuItem";
|
this.resetToolStripMenuItem.Name = "resetToolStripMenuItem";
|
||||||
this.resetToolStripMenuItem.Size = new System.Drawing.Size(173, 46);
|
this.resetToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.resetToolStripMenuItem.Text = "Reset";
|
this.resetToolStripMenuItem.Text = "Reset";
|
||||||
this.resetToolStripMenuItem.Click += new System.EventHandler(this.resetToolStripMenuItem_Click);
|
this.resetToolStripMenuItem.Click += new System.EventHandler(this.resetToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
@@ -2767,14 +2767,14 @@
|
|||||||
//
|
//
|
||||||
this.webManagerToolStripMenuItem.Image = global::Project.Properties.Resources.icons8_what_40;
|
this.webManagerToolStripMenuItem.Image = global::Project.Properties.Resources.icons8_what_40;
|
||||||
this.webManagerToolStripMenuItem.Name = "webManagerToolStripMenuItem";
|
this.webManagerToolStripMenuItem.Name = "webManagerToolStripMenuItem";
|
||||||
this.webManagerToolStripMenuItem.Size = new System.Drawing.Size(173, 46);
|
this.webManagerToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.webManagerToolStripMenuItem.Text = "Web Manager";
|
this.webManagerToolStripMenuItem.Text = "Web Manager";
|
||||||
this.webManagerToolStripMenuItem.Click += new System.EventHandler(this.webManagerToolStripMenuItem_Click);
|
this.webManagerToolStripMenuItem.Click += new System.EventHandler(this.webManagerToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem23
|
// toolStripMenuItem23
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem23.Name = "toolStripMenuItem23";
|
this.toolStripMenuItem23.Name = "toolStripMenuItem23";
|
||||||
this.toolStripMenuItem23.Size = new System.Drawing.Size(170, 6);
|
this.toolStripMenuItem23.Size = new System.Drawing.Size(201, 6);
|
||||||
//
|
//
|
||||||
// loadMemoryToolStripMenuItem
|
// loadMemoryToolStripMenuItem
|
||||||
//
|
//
|
||||||
@@ -2788,7 +2788,7 @@
|
|||||||
this.toolStripMenuItem35,
|
this.toolStripMenuItem35,
|
||||||
this.toolStripMenuItem36});
|
this.toolStripMenuItem36});
|
||||||
this.loadMemoryToolStripMenuItem.Name = "loadMemoryToolStripMenuItem";
|
this.loadMemoryToolStripMenuItem.Name = "loadMemoryToolStripMenuItem";
|
||||||
this.loadMemoryToolStripMenuItem.Size = new System.Drawing.Size(173, 46);
|
this.loadMemoryToolStripMenuItem.Size = new System.Drawing.Size(204, 46);
|
||||||
this.loadMemoryToolStripMenuItem.Text = "Load Memory";
|
this.loadMemoryToolStripMenuItem.Text = "Load Memory";
|
||||||
//
|
//
|
||||||
// toolStripMenuItem27
|
// toolStripMenuItem27
|
||||||
@@ -5349,7 +5349,7 @@
|
|||||||
private arCtl.arLabel lbMsg;
|
private arCtl.arLabel lbMsg;
|
||||||
private System.Windows.Forms.ToolStripButton btCheckInfo;
|
private System.Windows.Forms.ToolStripButton btCheckInfo;
|
||||||
private System.Windows.Forms.ToolStripButton toolStripButton8;
|
private System.Windows.Forms.ToolStripButton toolStripButton8;
|
||||||
private System.Windows.Forms.ToolStripDropDownButton toolStripButton3;
|
private System.Windows.Forms.ToolStripDropDownButton menu_camera;
|
||||||
private System.Windows.Forms.ToolStripMenuItem 바코드LToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem 바코드LToolStripMenuItem;
|
||||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
|
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
|
||||||
private System.Windows.Forms.ToolStripMenuItem 새로고침ToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem 새로고침ToolStripMenuItem;
|
||||||
|
|||||||
@@ -471,15 +471,15 @@ If pressed while motion is moving, motion will also stop.
|
|||||||
<data name="btManualPrint.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btManualPrint.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHkSURBVGhD7ZPNShtRFMevL1DfIZDu/HgDdVcVIe50p1Co
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHjSURBVGhD7ZPLSiNREIaPL6DvEIg7L2+g7rwgxJ3uFAYU
|
||||||
CBUFkSEKKZHiF2Kz8GMUzIwRxhkj0RF9AfUFKtqNLqwlbnWbRY7c2gzyv91IZpIjuT/47e6Z8zuLEUKj
|
QRlBpFEhEpF4QdSFl4xguidC260SbdEXUF9AUTe68ELc6jaLlJxx0gz/mY2kOynJ+eDbner6atFCaDSa
|
||||||
0TQO/Q/l3kSx9CdRJIrCvmLpPlEsd+Pe0HhZoC4O19Jv3Bsa6rJoxL2hgYuiEveGBi6KStxbHZNXF2Lq
|
6qHntdAVy+VfYjmiMOzO5Z9juUIH7g2MzwXq4mDNP+HewFCXhSPuDQxcFJa4NzBwUVji3tIYv7kUE3ck
|
||||||
F0lxUVRW9onJ6zPMeTuVj9XjAGnV6APebmMfMGake0aM9P1XI02cfGma/YS9ChzjK44a6TvsVcAhbmKv
|
xUVhWdwnxm/PMefrFD9WiQOkJaMP+LrVfcCokegcMhLPI0aCOPnZNNuOvQoc44sOG4lH7FXAIW5irwIO
|
||||||
Ag5wE3sVcICb2KuAA9zEXgUc4Cb2KuDAwNAXamlto3g8XlPlzsHhkeoPqEd8xZa29uoPwI/WWuzBXgUc
|
cBN7FXCAm9irgAPcxF4FHOAm9irgQG//IDU0NlE0Gi2rcmffwFDpB1QivmhDU3PpB+BHyy32YK8CDuAH
|
||||||
wA/WWuzBXoXPYwa9NhaL1VXswd4A2zn5aLn+heX6ZHvHLP3Xdi5bsV/IeBxg7Dn2i03v4Ok/D5nqP2K/
|
yy32YK/Cj1GD/jUSiVRU7MFeH8s+rTcd79J0PLLcE5b+bbuQrdgvZDwOMPYC+8Uv9/D9Pw+Z6r1hv0i7
|
||||||
yHqHl+pDpjr+T+wX1t7xjvKQq+6Rjf3yH5hQHvJ1HPtF1il0Wu4RvQdt1+/AfpHLnX6Yz5jlbadAnJWN
|
R9fqQ6ba3hX2C3Pv5LfykKvOsYX98h8YUx7y9Sf2i7SdbTWdY/oOWo7Xgv0ikzmrTa5uFXbsLHFWNqZc
|
||||||
puc1Y/9fTHvvdms3T6zN7d9gd8DqtpNfyzrE3H3sDlgxs9M/TItYu2ElsTtgIbPaNZ9ZJ87Orax3YncA
|
tw77/5Cy9h62dw+ItZn9e+z2Wd+xDzbSNjF3H7t9VlLpqdWUSazdMiex22dhbb1tfm2TOJtc2WzFbh8i
|
||||||
ETXNfF86TKVS5W9zy8RJ2ZScXSzIRuzWaDQaTePyDJGgtwDvLqUwAAAAAElFTkSuQmCC
|
qpmeWzqKx+OFmeQycVI2Tc4uZmUjdms0Go2mevkAVIy2+k+KVB0AAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btJobCancle.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btJobCancle.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
|||||||
Reference in New Issue
Block a user