UNPKG

pareto-anywhere

Version:

Open source IoT middleware suite that makes the data from just about anything usable. We believe in an open Internet of Things.

226 lines (189 loc) 7.07 kB
/** * Copyright reelyActive 2021-2024 * We believe in an open Internet of Things */ // Constants const DEMO_SEARCH_PARAMETER = 'demo'; const ACCEPTED_STORY_TYPES = [ 'schema:Person' ]; const ACTIVE_TIMEOUT_MILLISECONDS = 60000; // DOM elements let connectIcon = document.querySelector('#connectIcon'); let demoalert = document.querySelector('#demoalert'); let activestorycolumn = document.querySelector('#activestorycolumn'); let recentstorycolumn = document.querySelector('#recentstorycolumn'); let time = document.querySelector('#time'); let deviceCount = document.querySelector('#deviceCount'); let raddecRate = document.querySelector('#raddecRate'); let dynambRate = document.querySelector('#dynambRate'); // Other variables let baseUrl = window.location.protocol + '//' + window.location.hostname + ':' + window.location.port; let cards = new Map(); let cormorantOptions; // Initialise based on URL search parameters, if any let searchParams = new URLSearchParams(location.search); let isDemo = searchParams.has(DEMO_SEARCH_PARAMETER); let discreteDataTableOptions = { isClockDisplayed: true, digitalTwins: cormorant.digitalTwins, propertiesToDisplay: [ 'unicodeCodePoints' ], maxRows: 10 }; let discreteDataTable = new DiscreteDataTable('#discreteData', discreteDataTableOptions); // Handle beaver events beaver.on('dynamb', handleDynamb); beaver.on('connect', () => { connectIcon.replaceChildren(createElement('i', 'fas fa-cloud text-success')); demoalert.hidden = true; }); beaver.on('stats', (stats) => { deviceCount.textContent = stats.numberOfDevices; raddecRate.textContent = stats.eventsPerSecond.raddec.toFixed(1); dynambRate.textContent = stats.eventsPerSecond.dynamb.toFixed(1); }); beaver.on('error', (error) => { connectIcon.replaceChildren(createElement('i', 'fas fa-cloud text-danger')); demoalert.hidden = false; }); beaver.on('disconnect', () => { connectIcon.replaceChildren(createElement('i', 'fas fa-cloud text-warning')); }); // Demo mode: connect to starling.js if(isDemo) { let demoIcon = createElement('b', 'animate-breathing text-success', 'DEMO'); let context = starling.getContext(); connectIcon.replaceChildren(demoIcon); beaver.stream(null, { io: starling, ioUrl: "http://pareto.local" }); for(let deviceSignature in context.devices) { let device = context.devices[deviceSignature]; beaver.devices.set(deviceSignature, device); } } // Normal mode: connect to socket.io else { beaver.stream(baseUrl, { io: io }); cormorantOptions = { associationsServerUrl: baseUrl }; } updateDisplay(); // Handle a dynamb event function handleDynamb(dynamb) { let deviceSignature = dynamb.deviceId + '/' + dynamb.deviceIdType; if(cards.has(deviceSignature)) { cormorant.retrieveDigitalTwin(deviceSignature, null, cormorantOptions, (digitalTwin, isRetrievedFromMemory) => { if(digitalTwin && !isRetrievedFromMemory) { discreteDataTable.updateDigitalTwin(deviceSignature, digitalTwin); } }); discreteDataTable.handleDynamb(dynamb); } } // Update the display based on the latest devices function updateDisplay() { beaver.devices.forEach((device, deviceSignature) => { let lastSeenTimestamp = Date.now(); if(device.hasOwnProperty('raddec')) { lastSeenTimestamp = device.raddec.timestamp; } if(cards.has(deviceSignature)) { let card = cards.get(deviceSignature); card.lastSeenTimestamp = lastSeenTimestamp; } else { cormorant.retrieveDigitalTwin(deviceSignature, device, cormorantOptions, (digitalTwin, isRetrievedFromMemory) => { if(digitalTwin && isAcceptedStoryType(digitalTwin.story, ACCEPTED_STORY_TYPES)) { let card = prepareCard(digitalTwin.story, deviceSignature); card.lastSeenTimestamp = lastSeenTimestamp; card.isActiveStory = true; cards.set(deviceSignature, card); activestorycolumn.appendChild(card.node); } }); } }); cards.forEach((card) => { let activeThresholdTime = Date.now() - ACTIVE_TIMEOUT_MILLISECONDS; let isActive = (card.lastSeenTimestamp >= activeThresholdTime); if(isActive && !card.isActiveStory) { prependChild(activestorycolumn, card.node); card.isActiveStory = true; card.footer.hidden = true; } else if(!isActive && card.isActiveStory) { let lastSeenTime = new Date(card.lastSeenTimestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false }); prependChild(recentstorycolumn, card.node); card.lastSeen.textContent = lastSeenTime; card.isActiveStory = false; card.footer.hidden = false; } // TODO: limit number of displayed cards? }); setTimeout(updateDisplay, 5000); } // Prepare a card function prepareCard(story, deviceSignature) { let card = cuttlefishStory.render(story); let disappearanceIcon = createElement('i', 'fas fa-sign-out-alt'); let lastSeen = createElement('span', null); let footer = createElement('div', 'card-footer text-center text-body-secondary bg-transparent', [ disappearanceIcon, '\u00a0', lastSeen ]); let col = createElement('div', 'col', card); card.appendChild(footer); card.setAttribute('class', 'card hover-shadow px-0'); footer.setAttribute('id', deviceSignature + '-footer'); footer.hidden = true; return { story: story, node: card, footer: footer, lastSeen: lastSeen }; } // Determine if the given story is a member of one of the accepted types function isAcceptedStoryType(story, acceptedTypes) { if(story && Array.isArray(story['@graph']) && Array.isArray(acceptedTypes)) { for(const element of story['@graph']) { if(element.hasOwnProperty('@type') && acceptedTypes.includes(element['@type'])) { return true; } } } return false; } // Prepend as the first child function prependChild(parent, child) { if(parent.firstChild) { parent.insertBefore(child, parent.firstChild); } else { parent.appendChild(); } } // Create an element as specified function createElement(elementName, classNames, content) { let element = document.createElement(elementName); if(classNames) { element.setAttribute('class', classNames); } if((content instanceof Element) || (content instanceof DocumentFragment)) { element.appendChild(content); } else if(Array.isArray(content)) { content.forEach(function(item) { if((item instanceof Element) || (item instanceof DocumentFragment)) { element.appendChild(item); } else { element.appendChild(document.createTextNode(item)); } }); } else if(content) { element.appendChild(document.createTextNode(content)); } return element; }