let skApp = null; /** * Inizializza il modulo con l'istanza dell'app Signal K. * Da chiamare una sola volta nel plugin.start() * @param {Object} app - l'istanza dell'applicazione Signal K */ function init(app) { skApp = app; } /** * Pubblica un set di dati nel data browser di Signal K tramite i delta. * @param {Object} data - Oggetto JSON dove le chiavi sono i percorsi e i valori sono i dati da pubblicare */ function publish(data) { //TODO: Controlla se serve aggiungere typeof skApp.handleMessage !== 'function' (controlla che esista la funzione handleMessage, ma in teoria esiste sempre) if (!skApp) { console.error('[SKFLOW] skApp non inizializzato') return; } if (!data || typeof data !== 'object') { console.error('[SKFLOW] Dati non validi') return; } const values = Object.entries(data).map(([path, value]) => { return { path: path, value: value }; }); //La funzione non continua se non ci sono dati //TODO: Controllare se serve davvero, non dovrebbe interrompersi già al check di data? if (values.length === 0) return; // Viene creato un "Delta Update" con l'ID del plugin 'meb.plugin' e l'array di valori. skApp.handleMessage('meb.plugin', { updates: [ { values: values } ] }); } /** * Ottieni i dati dal Data-Browser di Signal K * @param {String} path - Il path Signal K * @returns {*} Il dato */ function get(path) { if (!skApp) { return null; } const valObj = skApp.getSelfPath(path); return valObj ? valObj.value : null; } /** * Ottieni tutti i dati nel databrowser di Signal K che corrispondono ad un ID o ad una sorgente specifica. * @param {String} source - Il parametro da confrontare con il sorgente ($source o source) o ID del dato. * @returns {Object} Un oggetto contenente path e valori trovati. */ function getBySource(source) { if (!skApp) return {}; const results = {}; const self = skApp.signalk?.self || skApp.signalk?.retrieve()?.vessels?.[skApp.selfId] || {}; if (!self || Object.keys(self).length === 0) { console.log('[SKFLOW] Nessun dato trovato nel databrowser'); return results; } const traverse = (obj, path = '') => { if (!obj || typeof obj !== 'object') return; // Se l'oggetto ha una proprietà 'value', verifichiamo la sorgente if (Object.prototype.hasOwnProperty.call(obj, 'value')) { const hasSource = obj.$source === source || obj.source === source || obj.id === source; if (hasSource) { results[path] = obj.value; } } // Esplora i sotto-oggetti escludendo chiavi di sistema che non sono percorsi SK const skip = ['value', 'timestamp', '$source', 'source', 'meta', 'sentence', 'talker']; for (const key in obj) { if (skip.includes(key)) continue; const subPath = path ? `${path}.${key}` : key; traverse(obj[key], subPath); } }; traverse(self); return results; } /** * Ottieni tutti i dati nel databrowser di Signal K il cui path inizia con la stringa specificata. * @param {String} filterPath - La stringa con cui deve iniziare il path (es. "custom.plugin"). * @returns {Object} Un oggetto contenente path e valori trovati. */ function getWithFilter(filterPath) { if (!skApp) return {}; const results = {}; const self = skApp.signalk?.self || skApp.signalk?.retrieve()?.vessels?.[skApp.selfId] || {}; if (!self || Object.keys(self).length === 0) { return results; } const traverse = (obj, path = '') => { if (!obj || typeof obj !== 'object') return; // Se l'oggetto ha una proprietà 'value', verifichiamo se il path corrisponde al filtro if (Object.prototype.hasOwnProperty.call(obj, 'value')) { if (path.startsWith(filterPath)) { results[path] = obj.value; } } // Esplora i sotto-oggetti escludendo chiavi di sistema const skip = ['value', 'timestamp', '$source', 'source', 'meta', 'sentence', 'talker']; for (const key in obj) { if (skip.includes(key)) continue; const subPath = path ? `${path}.${key}` : key; // Ottimizzazione: se `subPath` non inizia con `filterPath` E `filterPath` non inizia con `subPath`, // possiamo evitare di scendere in rami completamente irrilevanti. // (es. filter = "environment." e subPath = "navigation." -> skippa) if (!subPath.startsWith(filterPath) && !filterPath.startsWith(subPath)) continue; traverse(obj[key], subPath); } }; traverse(self); return results; } /** * Ottieni un oggetto con path e valori per una lista specifica di path. * @param {Array} data - Un array contenente i path di Signal K da recuperare. * @returns {Object} Un oggetto JSON con elementi path: value. */ function getFrom(data) { if (!Array.isArray(data)) return {}; const results = {}; for (const path of data) { results[path] = get(path); } return results; } module.exports = { init, publish, get, getBySource, getWithFilter, getFrom };