a386986c17
Implementazione completa del flusso operativo per il ruolo MeasurementTec:
Blueprint measure.py:
- select_recipe: selezione ricetta con ricerca e barcode
- task_list: lista task con conteggi subtask e allegati
- task_execute: esecuzione misure con numpad, calibro USB, feedback real-time
- task_complete: riepilogo con statistiche pass/fail e export CSV
- API AJAX: lookup-barcode, save-traceability, save-measurement
- Autorizzazione role_required("MeasurementTec") su tutte le route
Componenti riutilizzabili:
- numpad.html/js/css: tastierino numerico touch-friendly con keyboard support
- caliper_status.html + caliper.js: integrazione calibro USB via Web Serial API
- barcode_scanner.html + barcode.js: scansione barcode con html5-qrcode
- measurement_feedback.html: feedback visivo pass/warning/fail in tempo reale
- next_measurement.html: indicatore prossima misurazione
- annotation-viewer.js: visualizzatore canvas con marker su disegni tecnici
- csv-export.js: export CSV con locale italiano (delimitatore ;, decimale ,)
Sicurezza:
- Decoratore role_required(*roles) per autorizzazione basata su ruoli
- CSRF token su tutti i POST AJAX
- |tojson per prevenire XSS su annotations_json
- Validazione input lato client e server
i18n: 23+ nuove chiavi tradotte IT/EN per tutti i template FASE 3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
143 lines
3.3 KiB
JavaScript
143 lines
3.3 KiB
JavaScript
/**
|
|
* Caliper Connection Manager
|
|
* Web Serial API integration for USB digital calipers
|
|
* Supports standard SPC/Digimatic protocol
|
|
*/
|
|
|
|
function caliperConnection() {
|
|
return {
|
|
connected: false,
|
|
status: 'disconnected', // disconnected, connecting, connected, error
|
|
lastReading: null,
|
|
port: null,
|
|
reader: null,
|
|
readController: null,
|
|
|
|
get isSupported() {
|
|
return 'serial' in navigator;
|
|
},
|
|
|
|
async connect() {
|
|
if (!this.isSupported) {
|
|
this.status = 'error';
|
|
console.error('Web Serial API not supported');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
this.status = 'connecting';
|
|
|
|
// Request port selection
|
|
this.port = await navigator.serial.requestPort();
|
|
|
|
// Open port with standard caliper settings
|
|
await this.port.open({
|
|
baudRate: 9600,
|
|
dataBits: 8,
|
|
stopBits: 1,
|
|
parity: 'none',
|
|
flowControl: 'none'
|
|
});
|
|
|
|
this.connected = true;
|
|
this.status = 'connected';
|
|
|
|
// Start reading loop
|
|
this.readLoop();
|
|
|
|
} catch (err) {
|
|
this.status = 'error';
|
|
this.connected = false;
|
|
console.error('Caliper connection error:', err);
|
|
}
|
|
},
|
|
|
|
async readLoop() {
|
|
if (!this.port || !this.port.readable) return;
|
|
|
|
const reader = this.port.readable.getReader();
|
|
this.reader = reader;
|
|
|
|
try {
|
|
while (true) {
|
|
const { value, done } = await reader.read();
|
|
if (done) {
|
|
console.log('Reader closed');
|
|
break;
|
|
}
|
|
this.parseData(value);
|
|
}
|
|
} catch (err) {
|
|
if (err.name !== 'NetworkError') {
|
|
console.error('Read error:', err);
|
|
}
|
|
} finally {
|
|
reader.releaseLock();
|
|
}
|
|
},
|
|
|
|
parseData(data) {
|
|
try {
|
|
// Decode buffer to text
|
|
const text = new TextDecoder().decode(data);
|
|
|
|
// Standard digital caliper protocol patterns
|
|
// Format: typically " XX.XXX mm\r\n" or similar
|
|
// Try multiple patterns for compatibility
|
|
|
|
// Pattern 1: Standard with units
|
|
let match = text.match(/([+-]?\s*\d+\.?\d*)\s*mm/i);
|
|
|
|
// Pattern 2: Just number with optional sign
|
|
if (!match) {
|
|
match = text.match(/([+-]?\s*\d+\.?\d*)/);
|
|
}
|
|
|
|
if (match) {
|
|
// Clean whitespace and parse
|
|
const valueStr = match[1].replace(/\s+/g, '');
|
|
const value = parseFloat(valueStr);
|
|
|
|
if (!isNaN(value)) {
|
|
this.lastReading = value;
|
|
|
|
// Dispatch event for other components
|
|
this.$dispatch('caliper-reading', {
|
|
value: value,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('Parse error:', err);
|
|
}
|
|
},
|
|
|
|
async disconnect() {
|
|
try {
|
|
// Cancel reader
|
|
if (this.reader) {
|
|
await this.reader.cancel();
|
|
this.reader = null;
|
|
}
|
|
|
|
// Close port
|
|
if (this.port) {
|
|
await this.port.close();
|
|
this.port = null;
|
|
}
|
|
} catch (err) {
|
|
console.error('Disconnect error:', err);
|
|
} finally {
|
|
this.connected = false;
|
|
this.status = 'disconnected';
|
|
}
|
|
},
|
|
|
|
// Cleanup on component destroy
|
|
destroy() {
|
|
this.disconnect();
|
|
}
|
|
};
|
|
}
|