UNPKG

@ncd-io/node-red-enterprise-sensors

Version:

You can install this library through the Palette Manager in Node-Red's UI.

1,638 lines (1,614 loc) 46 kB
const { toMac, signInt, msbLsb } = require('../utils'); module.exports = (globalDevices, emitter) => { const clear_globalDevices_stream = (deviceAddr, probe) => { if (Object.hasOwn(globalDevices, deviceAddr) && Object.hasOwn(globalDevices[deviceAddr], probe)){ if (Object.hasOwn(globalDevices[deviceAddr][probe], 'packet_stream_timeout')) { clearTimeout(globalDevices[deviceAddr][probe].packet_stream_timeout); } delete globalDevices[deviceAddr][probe]; } }; const init_globalDevices_stream = (deviceAddr, payload, expected_packets, parsed, msg_type, probe) => { globalDevices[deviceAddr][probe] = { data: {}, odr: msbLsb(payload[9], payload[10]), mo: payload[8], fsr: payload[11] >> 5, hour: payload[12], minute: payload[13], temperature: msbLsb(payload[14], payload[15]) / 100, expected_packets: expected_packets }; globalDevices[deviceAddr][probe].packet_stream_timeout = setTimeout(() => { // Calling sibling function directly parsed.sensor_data = concat_fft_data(deviceAddr, payload[8], msg_type, probe); parsed.sensor_data.error = 'Time Series Data Stream Timeout - incomplete data received'; emitter.emit('sensor_data', parsed); emitter.emit('sensor_data-111', parsed); emitter.emit('sensor_data' + '-' + deviceAddr, parsed); }, 60000); }; const concat_fft_data = (deviceAddr, mode, msg_type, probe) => { var raw_data = new Array(); for (const packet in globalDevices[deviceAddr][probe].data) { raw_data = raw_data.concat(globalDevices[deviceAddr][probe].data[packet]); } var label = 0; var fft_concat = { x: [], y: [], z: [] }; var en_axis_data = {}; en_axis_data.x_offset = 0; en_axis_data.y_offset = 2; en_axis_data.z_offset = 4; en_axis_data.increment = 6; var fsr_mult = .00006; var fsr_text = ""; switch (globalDevices[deviceAddr][probe].fsr) { case 0: fsr_mult = 0.00006; break; case 1: fsr_mult = 0.00012; break; case 2: fsr_mult = 0.00024; break; case 3: fsr_mult = 0.00049; break; } switch (globalDevices[deviceAddr][probe].fsr) { case 0: fsr_text = "2g"; break; case 1: fsr_text = "4g"; break; case 2: fsr_text = "8g"; break; case 3: fsr_text = "16g"; break; } for (var i = 0; i < raw_data.length; i += en_axis_data.increment) { label++; if ('x_offset' in en_axis_data) { fft_concat.x.push(parseFloat((signInt(((raw_data[i + en_axis_data.x_offset] << 8) + (raw_data[i + en_axis_data.x_offset + 1])), 16) * fsr_mult).toFixed(3))); } if ('y_offset' in en_axis_data) { fft_concat.y.push(parseFloat((signInt(((raw_data[i + en_axis_data.y_offset] << 8) + (raw_data[i + en_axis_data.y_offset + 1])), 16) * fsr_mult).toFixed(3))); } if ('z_offset' in en_axis_data) { fft_concat.z.push(parseFloat((signInt(((raw_data[i + en_axis_data.z_offset] << 8) + (raw_data[i + en_axis_data.z_offset + 1])), 16) * fsr_mult).toFixed(3))); } } var fft_concat_obj = { mode: mode, msg_type: msg_type, probe: probe, time_id: [ String(globalDevices[deviceAddr][probe].hour).padStart(2, '0'), String(globalDevices[deviceAddr][probe].minute).padStart(2, '0'), ].join(':'), mac_address: deviceAddr, fsr: fsr_text, odr: globalDevices[deviceAddr][probe].odr, temperature: globalDevices[deviceAddr][probe].temperature, total_samples: label, fft_confidence: ((Object.keys(globalDevices[deviceAddr][probe].data).length / globalDevices[deviceAddr][probe].expected_packets) * 100).toFixed(2) + '%', data: fft_concat }; return fft_concat_obj; }; const get_write_buffer_size = (firmware) => { return 49; }; const get_config_map = (firmware) => { console.log('Generating sync map for firmware version', firmware); return { "core_version": { "read_index": 3, "descriptions": { "title": "Core Version", "main_caption": "The version of the core communication stack." }, "validator": { "type": "uint8" }, "tags": [ "system" ] }, "firmware_version": { "read_index": 4, "descriptions": { "title": "Firmware Version", "main_caption": "The application-specific firmware version." }, "validator": { "type": "uint8" }, "tags": [ "system" ], "old_fly_id": "firmware" }, "sensor_type": { "read_index": 5, "descriptions": { "title": "Sensor Type", "main_caption": "The hardware identifier for the specific sensor model." }, "validator": { "type": "uint16be" }, "tags": [ "system" ] }, "tx_lifetime_counter": { "read_index": 7, "descriptions": { "title": "Transmission Lifetime Counter", "main_caption": "Total number of transmissions since the device was manufactured." }, "validator": { "type": "uint32be" }, "tags": [ "diagnostics" ] }, "hardware_id": { "read_index": 11, "length": 3, "descriptions": { "title": "Hardware ID", "main_caption": "A unique 3-byte hardware identifier." }, "validator": { "type": "buffer" }, "tags": [ "system" ] }, "network_id": { "read_index": 14, "write_index": 3, "length": 2, "descriptions": { "title": "Network ID", "main_caption": "" }, "default_value": "7fff", "validator": { "type": "hex", "length": 4 }, "html_id": "pan_id", "tags": [ "Communications" ] }, "destination_address": { "read_index": 16, "write_index": 5, "length": 4, "descriptions": { "title": "Destination Address", "main_caption": "" }, "default_value": "0000ffff", "validator": { "type": "mac", "length": 8 }, "html_id": "destination", "tags": [ "Communications" ] }, "node_id": { "read_index": 20, "write_index": 9, "descriptions": { "title": "Node ID", "main_caption": "" }, "default_value": "0", "validator": { "type": "uint8", "min": 0, "max": 255, "generated": true }, "html_id": "node_id", "tags": [ "generic" ], "tags": [ "Communications" ] }, "odr_1": { "read_index": 21, "write_index": 10, "descriptions": { "title": "Probe 1: Output Data Rate", "main_caption": "<p>This would determine how many samples the output data has...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 7, "max": 15, "generated": true }, "options": { "7": "100Hz", "8": "200Hz", "9": "400Hz", "10": "800Hz", "11": "1600Hz", "12": "3200Hz", "13": "6400Hz", "14": "12800Hz", "15": "25600Hz" }, "tags": [ "Vibration Sampling" ], "html_id": "odr_p1_110" }, "odr_2": { "read_index": 22, "write_index": 11, "descriptions": { "title": "Probe 2: Output Data Rate", "main_caption": "<p>This would determine how many samples the output data has...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 7, "max": 15, "generated": true }, "options": { "7": "100Hz", "8": "200Hz", "9": "400Hz", "10": "800Hz", "11": "1600Hz", "12": "3200Hz", "13": "6400Hz", "14": "12800Hz", "15": "25600Hz" }, "tags": [ "Vibration Sampling" ], "html_id": "odr_p2_110" }, "sampling_duration_1": { "read_index": 23, "write_index": 12, "descriptions": { "title": "Probe 1: Sampling Duration", "main_caption": "<p>Set the amount of time which the samples are taken...</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 1, "max": 100, "generated": true }, "tags": [ "Vibration Sampling" ], "html_id": "sampling_duration_p1_110" }, "sampling_duration_2": { "read_index": 24, "write_index": 13, "descriptions": { "title": "Probe 2: Sampling Duration", "main_caption": "<p>Set the amount of time which the samples are taken...</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 1, "max": 100, "generated": true }, "tags": [ "Vibration Sampling" ], "html_id": "sampling_duration_p2_110" }, "lpf_coefficient_1": { "read_index": 25, "write_index": 14, "descriptions": { "title": "Probe 1: Set Low Pass Filter", "main_caption": "<p>This setting will set the LPF freq to ODR divided by Selected Value...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 9, "generated": true }, "options": { "10": "2", "0": "4", "1": "8", "2": "16", "3": "32", "4": "64", "5": "128", "6": "256", "7": "512", "8": "1024", "9": "2048" }, "html_id": "low_pass_filter_p1_110", "old_fly_id": "lpf_coeff_1" }, "lpf_coefficient_2": { "read_index": 26, "write_index": 15, "descriptions": { "title": "Probe 2: Set Low Pass Filter", "main_caption": "<p>This setting will set the LPF freq to ODR divided by Selected Value...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 9, "generated": true }, "options": { "10": "2", "0": "4", "1": "8", "2": "16", "3": "32", "4": "64", "5": "128", "6": "256", "7": "512", "8": "1024", "9": "2048" }, "html_id": "low_pass_filter_p2_110", "old_fly_id": "lpf_coeff_2" }, "hpf_coefficient_1": { "read_index": 27, "write_index": 16, "descriptions": { "title": "Probe 1: Set High Pass Filter", "main_caption": "<p>This setting will set the HPF freq to ODR divided by Selected Value...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 9, "generated": true }, "options": { "0": "4", "1": "8", "2": "16", "3": "32", "4": "64", "5": "128", "6": "256", "7": "512", "8": "1024", "9": "2048" }, "html_id": "high_pass_filter_p1_110", "old_fly_id": "hpf_coeff_1" }, "hpf_coefficient_2": { "read_index": 28, "write_index": 17, "descriptions": { "title": "Probe 2: Set High Pass Filter", "main_caption": "<p>This setting will set the HPF freq to ODR divided by Selected Value...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 9, "generated": true }, "options": { "0": "4", "1": "8", "2": "16", "3": "32", "4": "64", "5": "128", "6": "256", "7": "512", "8": "1024", "9": "2048" }, "html_id": "high_pass_filter_p2_110", "old_fly_id": "hpf_coeff_2" }, "full_scale_range": { "read_index": 29, "write_index": 18, "descriptions": { "title": "Full Scale Range", "main_caption": "<p>Set how large of a range the device can measure acceleration in.</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 0, "max": 5, "generated": true }, "options": { "0": "+/- 2g", "1": "+/- 4g", "2": "+/- 8g", "3": "+/- 16g", "4": "+/- 32g", "5": "+/- 64g" }, "tags": [ "Vibration Sampling" ], "html_id": "full_scale_range_101", "old_fly_id": "fsr" }, "axes_enabled": { "read_index": 30, "write_index": 19, "descriptions": { "title": "Axes Enabled", "main_caption": "New Command" }, "validator": { "type": "uint8" }, "read_only": true, }, "sampling_interval": { "read_index": 31, "write_index": 20, "descriptions": { "title": "Sampling Interval", "main_caption": "<p>Set how often will the sensor transmit measurement data.</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 0, "max": 8, "generated": true }, "options": { "0": "5 Minutes", "1": "10 Minutes", "2": "15 Minutes", "3": "20 Minutes", "4": "30 Minutes", "5": "60 Minutes", "6": "120 Minutes", "7": "180 Minutes", "8": "1 Minute" }, "tags": [ "Communications" ], "html_id": "sampling_interval_110" }, "filter_status": { "read_index": 32, "write_index": 21, "descriptions": { "title": "Set Filtering", "main_caption": "<p>Enable/Disable built-in filters</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 1, "generated": true }, "options": { "0": "Enabled", "1": "Disabled" }, "html_id": "enable_filtering_110" }, "operation_mode": { "read_index": 33, "write_index": 22, "descriptions": { "title": "Mode", "main_caption": "<p>• <strong>Processed:</strong> FFT is performed...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 3, "generated": true }, "options": { "0": "Processed", "1": "Raw", "2": "Processed + Raw on demand", "3": "Smart" }, "html_id": "mode_110", "old_fly_id": "mode" }, "measurement_mode": { "read_index": 34, "write_index": 23, "descriptions": { "title": "Measurement Mode", "main_caption": "Changing this value does not do anything. Only give one option." }, "validator": { "type": "uint8" }, "read_only": true }, "on_request_timeout": { "read_index": 35, "write_index": 24, "descriptions": { "title": "Set On Request Timeout", "main_caption": "<p>Set how long device will stay awake...</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 1, "max": 10, "generated": true }, "depends_on": { "operation_mode": [ 2, 3 ] }, "html_id": "on_request_timeout_80" }, "deadband": { "read_index": 36, "write_index": 25, "descriptions": { "title": "Set Dead Band in mg", "main_caption": "<p>Filters out acceleration values below the dead band threshold...</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 255, "generated": true }, "html_id": "deadband_80" }, "motion_detection_threshold_probe_1": { "read_index": 37, "write_index": 26, "descriptions": { "title": "Probe 1: Set Acceleration Wake/Interrupt Threshold", "main_caption": "<div><p>Set a breakpoint for sensor to wake up...</p></div>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 40, "generated": true }, "html_id": "motion_detect_threshold_p1_110" }, "smart_mode_acc_threshold_probe_1": { "read_index": 40, "write_index": 29, "descriptions": { "title": "Probe 1: Set Smart Mode Threshold", "main_caption": "<p>If RMS acceleration is above this in any axis...</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 1, "max": 40 }, "depends_on": { "operation_mode": 3 }, "html_id": "smart_threshold_110" }, "motion_detection_threshold_probe_2": { "read_index": 41, "write_index": 30, "descriptions": { "title": "Probe 2: Set Acceleration Wake/Interrupt Threshold", "main_caption": "<div><p>Set a breakpoint for sensor to wake up...</p></div>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 40, "generated": true }, "html_id": "motion_detect_threshold_p2_110", }, "smart_mode_acc_threshold_probe_2": { "read_index": 44, "write_index": 33, "descriptions": { "title": "Probe 2: Set Smart Mode Threshold", "main_caption": "<p>If RMS acceleration is above this in any axis...</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 1, "max": 40 }, "depends_on": { "operation_mode": 3 }, "html_id": "smart_threshold_p2_110" }, "raw_packet_length": { "read_index": 46, "write_index": 35, "descriptions": { "title": "Payload Length", "main_caption": "<p>Set the size of the data payload...</p>", "sub_caption": "<p class=\"caption\"><i>Note: For the 2.4GHz version you need to operate with a 55 Byte payload.</i></p>" }, "default_value": 3, "validator": { "type": "uint8", "min": 0, "max": 3, "generated": true }, "options": { "0": "55 Bytes", "1": "100 Bytes", "2": "150 Bytes", "3": "180 Bytes" }, "tags": [ "Communications" ], "html_id": "payload_length_80", "old_fly_id": "payload_length" }, "auto_raw_interval": { "read_index": 47, "write_index": 36, "descriptions": { "title": "Set Auto Raw Interval", "main_caption": "<p>Set the Auto Time Domain (Raw) data transmission Interval...</p>", "sub_caption": "<p class=\"caption\"><i>Note: Auto Raw Transmission is disabled by default.</i></p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 255, "generated": true }, "depends_on": { "operation_mode": 3 }, "tags": [ "Communications" ], "html_id": "auto_raw_interval_110" }, "auto_raw_destination_address": { "read_index": 48, "write_index": 37, "length": 4, "descriptions": { "title": "Set Auto Raw Destination Address", "main_caption": "<p>Set the address where the Auto Time Domain (Raw) data will be transmitted...</p>", "sub_caption": "<p class=\"caption\">Default value: 0000FFFF for Broadcast Mode</p>" }, "default_value": "0000FFFF", "validator": { "type": "mac", "length": 8, "generated": true }, "depends_on": { "operation_mode": 3 }, "html_id": "auto_raw_destination_110" }, "smart_mode_skip_count": { "read_index": 52, "write_index": 41, "descriptions": { "title": "Set Smart Mode Skip Interval", "main_caption": "<p>Sensor will skip sending data this many times if vibration is below the smart threshold.</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 255, "generated": true }, "depends_on": { "operation_mode": 3 }, "html_id": "smart_interval_110" }, "sync_interval": { "read_index": 53, "write_index": 42, "descriptions": { "title": "Set FLY Interval", "main_caption": "<p>Set the interval at which the sensor will transmit FLY packets...</p>" }, "default_value": 60, "validator": { "type": "uint16be", "min": 0, "max": 1440, "generated": true }, "options": { "60": "1 Hour", "120": "2 Hours", "240": "4 Hours", "480": "8 Hours", "720": "12 Hours", "1080": "18 Hours", "1440": "24 Hours" }, "tags": [ "Communications" ], "html_id": "fly_interval_110" }, "rpm_compute_status": { "read_index": 55, "write_index": 44, "descriptions": { "title": "RPM Calculate Status", "main_caption": "<p>Enable/Disable Revolutions Per Minute Calculate Status</p>" }, "default_value": 0, "validator": { "type": "uint8", "min": 0, "max": 1, "generated": true }, "options": { "0": "Disabled", "1": "Enabled" }, "html_id": "enable_rpm_calculate_status_110" }, "max_raw_samples": { "read_index": 56, "write_index": 45, "descriptions": { "title": "Set Max Raw Sample", "main_caption": "<p>Set the maximum number of samples...</p>" }, "default_value": 0, "validator": { "type": "uint16be", "min": 1024, "max": 8100 }, "options": { "1024": "1024 Samples", "2048": "2048 Samples", "4096": "4096 Samples", "6400": "6400 Samples", "8100": "8100 Samples" }, "html_id": "max_raw_sample_110", "old_fly_id": "max_tx_raw_samples" }, "motion_to_sampling_delay": { "read_index": 58, "write_index": 47, "descriptions": { "title": "Set Motion to Sampling Delay", "main_caption": "<p>Once motion is detected, the sensor will wait...</p>" }, "default_value": 100, "validator": { "type": "uint8", "min": 0, "max": 255, "generated": true }, "html_id": "motion_to_sampling_delay_110" }, "max_motion_tx_per_interval": { "read_index": 59, "write_index": 48, "descriptions": { "title": "Set Max Number Motion Tx Per Interval", "main_caption": "<p>Set Number of times it will send data due to motion triggers.</p>" }, "default_value": 1, "validator": { "type": "uint8", "min": 1, "max": 255, "generated": true }, "html_id": "max_num_motion_tx_delay_110", "old_fly_id": "max_num_of_motion_tx_per_interval" }, "uptime_counter_probe_1": { "read_index": 60, "descriptions": { "title": "Probe Uptime 2", "main_caption": "" }, "validator": { "type": "uint32be" }, "tags": [ "diagnostics" ] }, "uptime_counter_probe_2": { "read_index": 64, "descriptions": { "title": "Probe Uptime 1", "main_caption": "" }, "validator": { "type": "uint32be" }, "tags": [ "diagnostics" ] } }; }; const sync_parse = (rep_buffer) => { let response = { 'human_readable': {}, 'machine_values': {} }; // Get the map based on the sensor type byte const sync_map = get_config_map(rep_buffer[4]); for (const [key, config] of Object.entries(sync_map)) { // Destructure 'type' from inside 'validator' and rename 'read_index' to 'idx' const { read_index: idx, length, validator: { type } = {}, converter, options } = config; // If for some reason a config doesn't have a validator/type, skip it if (!type) continue; switch (type) { case 'uint8': response.machine_values[key] = rep_buffer[idx]; break; case 'uint16be': response.machine_values[key] = rep_buffer.readUInt16BE(idx); break; case 'uint32be': response.machine_values[key] = rep_buffer.readUInt32BE(idx); break; case 'buffer': response.machine_values[key] = rep_buffer.subarray(idx, idx + length); break; case 'hex': response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex'); break; case 'mac': response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex'); break; } let human_value = response.machine_values[key]; if(options && options[response.machine_values[key]]){ human_value = options[response.machine_values[key]]; }else{ if(converter && converter.multiplier){ human_value = human_value * converter.multiplier; } if(converter && converter.units){ human_value = human_value + converter.units; } } response.human_readable[key] = human_value; } if (Object.hasOwn(response.machine_values, 'destination_address') && response.machine_values.destination_address.toLowerCase() === '00000000') { console.log('##############################'); console.log('#########Dest Override########'); console.log('##############################'); response.destination_address = "0000ffff"; response.auto_raw_destination_address = "0000ffff"; }; return response; }; const parse_fly = (frame) => { let frame_data = {}; switch(frame[16]){ case 0: frame_data.mode = "Processed"; break; case 1: frame_data.mode = "Raw"; break; case 2: frame_data.mode = "Processed + Raw on demand"; break; case 3: frame_data.mode = "Smart"; break; } switch(frame[17]){ case 6: frame_data.odr_1 = 50; break; case 7: frame_data.odr_1 = 100; break; case 8: frame_data.odr_1 = 200; break; case 9: frame_data.odr_1 = 400; break; case 10: frame_data.odr_1 = 800; break; case 11: frame_data.odr_1 = 1600; break; case 12: frame_data.odr_1 = 3200; break; case 13: frame_data.odr_1 = 6400; break; case 14: frame_data.odr_1 = 12800; break; case 15: frame_data.odr_1 = 25600; break; } switch(frame[18]){ case 6: frame_data.odr_2 = 50; break; case 7: frame_data.odr_2 = 100; break; case 8: frame_data.odr_2 = 200; break; case 9: frame_data.odr_2 = 400; break; case 10: frame_data.odr_2 = 800; break; case 11: frame_data.odr_2 = 1600; break; case 12: frame_data.odr_2 = 3200; break; case 13: frame_data.odr_2 = 6400; break; case 14: frame_data.odr_2 = 12800; break; case 15: frame_data.odr_2 = 25600; break; } frame_data.sampling_duration_1 = frame[19]*50 + "ms"; frame_data.sampling_duration_2 = frame[20]*50 + "ms"; switch(frame[21]){ case 0: frame_data.filter_status = "Disabled"; break; case 1: frame_data.filter_status = "Enabled"; break; } switch(frame[22]){ case 0: frame_data.lpf_coeff_1 = 4; break; case 1: frame_data.lpf_coeff_1 = 8; break; case 2: frame_data.lpf_coeff_1 = 16; break; case 2: frame_data.lpf_coeff_1 = 32; break; case 4: frame_data.lpf_coeff_1 = 64; break; case 5: frame_data.lpf_coeff_1 = 128; break; case 6: frame_data.lpf_coeff_1 = 256; break; case 7: frame_data.lpf_coeff_1 = 512; break; case 8: frame_data.lpf_coeff_1 = 1024; break; case 9: frame_data.lpf_coeff_1 = 2048; break; } frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1; switch(frame[23]){ case 0: frame_data.lpf_coeff_2 = 4; break; case 1: frame_data.lpf_coeff_2 = 8; break; case 2: frame_data.lpf_coeff_2 = 16; break; case 2: frame_data.lpf_coeff_2 = 32; break; case 4: frame_data.lpf_coeff_2 = 64; break; case 5: frame_data.lpf_coeff_2 = 128; break; case 6: frame_data.lpf_coeff_2 = 256; break; case 7: frame_data.lpf_coeff_2 = 512; break; case 8: frame_data.lpf_coeff_2 = 1024; break; case 9: frame_data.lpf_coeff_2 = 2048; break; } frame_data.lpf_freq_2 = frame_data.odr_2 / frame_data.lpf_coeff_2; switch(frame[24]){ case 0: frame_data.hpf_coeff_1 = 4; break; case 1: frame_data.hpf_coeff_1 = 8; break; case 2: frame_data.hpf_coeff_1 = 16; break; case 2: frame_data.hpf_coeff_1 = 32; break; case 4: frame_data.hpf_coeff_1 = 64; break; case 5: frame_data.hpf_coeff_1 = 128; break; case 6: frame_data.hpf_coeff_1 = 256; break; case 7: frame_data.hpf_coeff_1 = 512; break; case 8: frame_data.hpf_coeff_1 = 1024; break; case 9: frame_data.hpf_coeff_1 = 2048; break; } frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1; switch(frame[25]){ case 0: frame_data.hpf_coeff_2 = 4; break; case 1: frame_data.hpf_coeff_2 = 8; break; case 2: frame_data.hpf_coeff_2 = 16; break; case 2: frame_data.hpf_coeff_2 = 32; break; case 4: frame_data.hpf_coeff_2 = 64; break; case 5: frame_data.hpf_coeff_2 = 128; break; case 6: frame_data.hpf_coeff_2 = 256; break; case 7: frame_data.hpf_coeff_2 = 512; break; case 8: frame_data.hpf_coeff_2 = 1024; break; case 9: frame_data.hpf_coeff_2 = 2048; break; } frame_data.hpf_freq_2 = frame_data.odr_2 / frame_data.hpf_coeff_2; switch(frame[26]){ case 0: frame_data.sampling_interval = "5 Minutes"; frame_data.sampling_interval_number = 5; break; case 1: frame_data.sampling_interval = "10 Minutes"; frame_data.sampling_interval_number = 10; break; case 2: frame_data.sampling_interval = "15 Minutes"; frame_data.sampling_interval_number = 15; break; case 2: frame_data.sampling_interval = "20 Minutes"; frame_data.sampling_interval_number = 20; break; case 4: frame_data.sampling_interval = "30 Minutes"; frame_data.sampling_interval_number = 30; break; case 5: frame_data.sampling_interval = "60 Minutes"; frame_data.sampling_interval_number = 60; break; case 6: frame_data.sampling_interval = "120 Minutes"; frame_data.sampling_interval_number = 120; break; case 7: frame_data.sampling_interval = "180 Minutes"; frame_data.sampling_interval_number = 180; break; case 8: frame_data.sampling_interval = "1 Minute"; frame_data.sampling_interval_number = 1; break; } frame_data.on_request_timeout = frame[27] + " Seconds"; frame_data.deadband = frame[28] + "mg"; switch(frame[29]){ case 0: frame_data.payload_length = "50 Bytes"; break; case 1: frame_data.payload_length = "100 Bytes"; break; case 2: frame_data.payload_length = "150 Bytes"; break; case 3: frame_data.payload_length = "180 Bytes"; break; } switch(frame[30]){ case 0: frame_data.fsr_text = "2g"; break; case 1: frame_data.fsr_text = "4g"; break; case 2: frame_data.fsr_text = "8g"; break; case 3: frame_data.fsr_text = "16g"; break; } frame_data.rpm_status = frame[31]? 'Enabled': 'Disabled'; frame_data.auto_raw_interval = frame[36] * frame_data.sampling_interval_number || 'disabled'; frame_data.auto_raw_interval = typeof frame_data.auto_raw_interval === 'number' ? frame_data.auto_raw_interval+'min' : frame_data.auto_raw_interval; frame_data.p1_smart_mode_threshold = frame[38] * 50; frame_data.p2_smart_mode_threshold = frame[39] * 50; if(frame[2] > 5){ // for Firmware v6 and above frame_data.motion_to_delay = frame[50] * 50; return { 'firmware': frame[2], 'destination_address': toMac(frame.slice(12, 16)), 'mode': frame_data.mode, 'odr_1': frame_data.odr_1+'Hz', 'odr_2': frame_data.odr_2+'Hz', 'sampling_duration_1': frame_data.sampling_duration_1, 'sampling_duration_2': frame_data.sampling_duration_2, 'filter_status': frame_data.filter_status, 'lpf_coeff_1': frame_data.lpf_coeff_1, 'lpf_freq_1': frame_data.lpf_freq_1+'Hz', 'lpf_coeff_2': frame_data.lpf_coeff_2, 'lpf_freq_2': frame_data.lpf_freq_2+'Hz', 'hpf_coeff_1': frame_data.hpf_coeff_1, 'hpf_freq_1': frame_data.hpf_freq_1+'Hz', 'hpf_coeff_2': frame_data.hpf_coeff_2, 'hpf_freq_2': frame_data.hpf_freq_2+'Hz', 'sampling_interval': frame_data.sampling_interval, 'on_request_timeout': frame_data.on_request_timeout, 'deadband': frame_data.deadband, 'payload_length': frame_data.payload_length, 'fsr': frame_data.fsr_text, 'rpm_compute_status': frame_data.rpm_status, 'auto_raw_destination_address': toMac(frame.slice(32 , 36)), 'auto_raw_interval': frame_data.auto_raw_interval, 'smart_mode_skip_count': frame[37], 'smart_mode_acc_threshold_probe_1': frame_data.p1_smart_mode_threshold+'mg', 'smart_mode_acc_threshold_probe_2': frame_data.p2_smart_mode_threshold+'mg', 'uptime_counter_probe_1': frame.slice(40, 44).reduce(msbLsb)+'sec', 'uptime_counter_probe_2': frame.slice(44, 48).reduce(msbLsb)+'sec', 'max_tx_raw_samples': frame.slice(48, 50).reduce(msbLsb), 'motion_to_sampling_delay': frame_data.motion_to_delay +'msec', 'max_num_of_motion_tx_per_interval': frame[51], 'hardware_id': frame.slice(52, 55), 'reserved': frame.slice(55, 59), 'tx_lifetime_counter': frame.slice(59, 63).reduce(msbLsb), 'machine_values': { 'firmware': frame[2], 'destination_address': toMac(frame.slice(12, 16), false), 'mode': frame[16], 'odr_1': frame[17], 'odr_2': frame[18], 'sampling_duration_1': frame[19], 'sampling_duration_2': frame[20], 'filter_status': frame[21], 'lpf_coeff_1': frame[22], 'lpf_coeff_2': frame[23], 'hpf_coeff_1': frame[24], 'hpf_coeff_2': frame[25], 'sampling_interval': frame[26], 'on_request_timeout': frame[27], 'deadband': frame[28], 'payload_length': frame[29], 'fsm': frame[30], 'rpm_compute_status': frame[31], 'auto_raw_destination_address': toMac(frame.slice(32 , 36), false), 'auto_raw_interval': frame[36], 'smart_mode_skip_count': frame[37], 'smart_mode_acc_threshold_probe_1':frame[38], 'smart_mode_acc_threshold_probe_2':frame[39], 'uptime_counter_probe_1': frame.slice(40, 44), 'uptime_counter_probe_2': frame.slice(44, 48), 'max_tx_raw_samples': frame.slice(48, 50), 'motion_to_sampling_delay': frame[50], 'max_num_of_motion_tx_per_interval': frame[51], 'hardware_id': frame.slice(52, 55), 'reserved': frame.slice(55, 59), 'tx_lifetime_counter': frame.slice(59, 63) } } } else if (frame[2] > 4){ // for Firmware v5 and above return { 'firmware': frame[2], 'destination_address': toMac(frame.slice(12, 16)), 'mode': frame_data.mode, 'odr_1': frame_data.odr_1+'Hz', 'odr_2': frame_data.odr_2+'Hz', 'sampling_duration_1': frame_data.sampling_duration_1, 'sampling_duration_2': frame_data.sampling_duration_2, 'filter_status': frame_data.filter_status, 'lpf_coeff_1': frame_data.lpf_coeff_1, 'lpf_freq_1': frame_data.lpf_freq_1+'Hz', 'lpf_coeff_2': frame_data.lpf_coeff_2, 'lpf_freq_2': frame_data.lpf_freq_2+'Hz', 'hpf_coeff_1': frame_data.hpf_coeff_1, 'hpf_freq_1': frame_data.hpf_freq_1+'Hz', 'hpf_coeff_2': frame_data.hpf_coeff_2, 'hpf_freq_2': frame_data.hpf_freq_2+'Hz', 'sampling_interval': frame_data.sampling_interval, 'on_request_timeout': frame_data.on_request_timeout, 'deadband': frame_data.deadband, 'payload_length': frame_data.payload_length, 'fsr': frame_data.fsr_text, 'rpm_compute_status': frame_data.rpm_status, 'auto_raw_destination_address': toMac(frame.slice(32 , 36)), 'auto_raw_interval': frame_data.auto_raw_interval, 'smart_mode_skip_count': frame[37], 'smart_mode_acc_threshold_probe_1': frame_data.p1_smart_mode_threshold+'mg', 'smart_mode_acc_threshold_probe_2': frame_data.p2_smart_mode_threshold+'mg', 'uptime_counter_probe_1': frame.slice(40, 44).reduce(msbLsb)+'sec', 'uptime_counter_probe_2': frame.slice(44, 48).reduce(msbLsb)+'sec', 'max_tx_raw_samples': frame.slice(48, 50).reduce(msbLsb), 'hardware_id': frame.slice(50, 53), 'reserved': frame.slice(53, 57), 'tx_lifetime_counter': frame.slice(57, 61).reduce(msbLsb), 'machine_values': { 'firmware': frame[2], 'destination_address': toMac(frame.slice(12, 16), false), 'mode': frame[16], 'odr_1': frame[17], 'odr_2': frame[18], 'sampling_duration_1': frame[19], 'sampling_duration_2': frame[20], 'filter_status': frame[21], 'lpf_coeff_1': frame[22], 'lpf_coeff_2': frame[23], 'hpf_coeff_1': frame[24], 'hpf_coeff_2': frame[25], 'sampling_interval': frame[26], 'on_request_timeout': frame[27], 'deadband': frame[28], 'payload_length': frame[29], 'fsm': frame[30], 'rpm_compute_status': frame[31], 'auto_raw_destination_address': toMac(frame.slice(32 , 36), false), 'auto_raw_interval': frame[36], 'smart_mode_skip_count': frame[37], 'smart_mode_acc_threshold_probe_1':frame[38], 'smart_mode_acc_threshold_probe_2':frame[39], 'uptime_counter_probe_1': frame.slice(40, 44), 'uptime_counter_probe_2': frame.slice(44, 48), 'max_tx_raw_samples': frame.slice(48, 50), 'hardware_id': frame.slice(50, 53), 'reserved': frame.slice(53, 57), 'tx_lifetime_counter': frame.slice(57, 61) } } }else{ return { 'firmware': frame[2], 'destination_address': toMac(frame.slice(12, 16)), 'mode': frame_data.mode, 'odr_1': frame_data.odr_1+'Hz', 'odr_2': frame_data.odr_2+'Hz', 'sampling_duration_1': frame_data.sampling_duration_1, 'sampling_duration_2': frame_data.sampling_duration_2, 'filter_status': frame_data.filter_status, 'lpf_coeff_1': frame_data.lpf_coeff_1, 'lpf_freq_1': frame_data.lpf_freq_1+'Hz', 'lpf_coeff_2': frame_data.lpf_coeff_2, 'lpf_freq_2': frame_data.lpf_freq_2+'Hz', 'hpf_coeff_1': frame_data.hpf_coeff_1, 'hpf_freq_1': frame_data.hpf_freq_1+'Hz', 'hpf_coeff_2': frame_data.hpf_coeff_2, 'hpf_freq_2': frame_data.hpf_freq_2+'Hz', 'sampling_interval': frame_data.sampling_interval, 'on_request_timeout': frame_data.on_request_timeout, 'deadband': frame_data.deadband, 'payload_length': frame_data.payload_length, 'fsr': frame_data.fsr_text, 'rpm_compute_status': frame_data.rpm_status, 'auto_raw_destination_address': toMac(frame.slice(32 , 36)), 'auto_raw_interval': frame_data.auto_raw_interval, 'smart_mode_skip_count': frame[37], 'smart_mode_acc_threshold_probe_1': frame_data.p1_smart_mode_threshold+'mg', 'smart_mode_acc_threshold_probe_2': frame_data.p2_smart_mode_threshold+'mg', 'uptime_counter_probe_1': frame.slice(40, 44).reduce(msbLsb)+'sec', 'uptime_counter_probe_2': frame.slice(44, 48).reduce(msbLsb)+'sec', 'hardware_id': frame.slice(48, 51), 'reserved': frame.slice(51, 55), 'tx_lifetime_counter': frame.slice(55, 59).reduce(msbLsb), 'machine_values': { 'firmware': frame[2], 'destination_address': toMac(frame.slice(12, 16), false), 'mode': frame[16], 'odr_1': frame[17], 'odr_2': frame[18], 'sampling_duration_1': frame[19], 'sampling_duration_2': frame[20], 'filter_status': frame[21], 'lpf_coeff_1': frame[22], 'lpf_coeff_2': frame[23], 'hpf_coeff_1': frame[24], 'hpf_coeff_2': frame[25], 'sampling_interval': frame[26], 'on_request_timeout': frame[27], 'deadband': frame[28], 'payload_length': frame[29], 'fsm': frame[30], 'rpm_compute_status': frame[31], 'auto_raw_destination_address': toMac(frame.slice(32 , 36), false), 'auto_raw_interval': frame[36], 'smart_mode_skip_count': frame[37], 'smart_mode_acc_threshold_probe_1':frame[38], 'smart_mode_acc_threshold_probe_2':frame[39], 'uptime_counter_probe_1': frame.slice(40, 44), 'uptime_counter_probe_2': frame.slice(44, 48), 'hardware_id': frame.slice(48, 51), 'reserved': frame.slice(51, 55), 'tx_lifetime_counter': frame.slice(55, 59) } } } }; const parse = (payload, parsed, mac) => { parsed.data = {}; if(payload[7] & 2){ parsed.data['probe_1_error'] = true; } if(payload[7] & 4){ parsed.data['probe_2_error'] = true; } if(payload[7] & 2 && payload[7] & 4){ return parsed; } let msg_type = (payload[7] & 16) ? 'motion' : 'regular'; if (payload[8] === 1) { var deviceAddr = mac; var expected_packets = msbLsb(payload[16], payload[17]); var current_packet = msbLsb(payload[18], payload[19]); var sdata_start = 20; var probe; // Sensor sends a 33 byte length packet when it fails to get sensor data for a probe. if(expected_packets == 0){ if(payload[7] & 2){ return { 'error': 'Probe is 1 unattached, unable to collect sensor data. Check probe connection.', 'probe': 1 }; }else{ return { 'error': 'Probe is 2 unattached, unable to collect sensor data. Check probe connection.', 'probe': 2 }; } } if (payload[7] & 8) { probe = 2; } else { probe = 1; } if(!Object.hasOwn(globalDevices, deviceAddr)){ globalDevices[deviceAddr] = {}; } if (globalDevices[deviceAddr].hasOwnProperty(probe) || expected_packets == 1) { if (expected_packets != 1) { if (globalDevices[deviceAddr][probe].last_packet_counter == current_packet) { console.log('Duplicated message'); return; } if (current_packet == 1 || (globalDevices[deviceAddr][probe].last_packet_counter > current_packet)) { console.log('Recovering bad packet'); clear_globalDevices_stream(deviceAddr, probe); init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed, msg_type, probe); globalDevices[deviceAddr][probe].last_packet_counter = current_packet; globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start); return; } else { globalDevices[deviceAddr][probe].last_packet_counter = current_packet; globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start); } } else { clear_globalDevices_stream(deviceAddr, probe); init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed, msg_type, probe); globalDevices[deviceAddr][probe].last_packet_counter = current_packet; globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start); } } else { clear_globalDevices_stream(deviceAddr, probe); init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed, msg_type, probe); globalDevices[deviceAddr][probe].last_packet_counter = current_packet; globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start); } if (current_packet == expected_packets) { sensor_data = concat_fft_data(deviceAddr, payload[8], msg_type, probe); clear_globalDevices_stream(deviceAddr, probe); return sensor_data; } else { return; } } else if (payload[8] === 0 || payload[8] === 2 || payload[8] === 3) { var odr1; switch (payload[9]) { case 6: odr1 = "50Hz"; break; case 7: odr1 = "100Hz"; break; case 8: odr1 = "200Hz"; break; case 9: odr1 = "400Hz"; break; case 10: odr1 = "800Hz"; break; case 11: odr1 = "1600Hz"; break; case 12: odr1 = "3200Hz"; break; case 13: odr1 = "6400Hz"; break; case 14: odr1 = "12800Hz"; break; case 15: odr1 = "25600Hz"; break; } var odr2; switch (payload[56]) { case 6: odr2 = "50Hz"; break; case 7: odr2 = "100Hz"; break; case 8: odr2 = "200Hz"; break; case 9: odr2 = "400Hz"; break; case 10: odr2 = "800Hz"; break; case 11: odr2 = "1600Hz"; break; case 12: odr2 = "3200Hz"; break; case 13: odr2 = "6400Hz"; break; case 14: odr2 = "12800Hz"; break; case 15: odr2 = "25600Hz"; break; } return { mode: payload[8], msg_type: msg_type, s1_odr: odr1, s1_temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100, x1_rms_ACC_G: payload.slice(12, 14).reduce(msbLsb)/1000, x1_max_ACC_G: payload.slice(14, 16).reduce(msbLsb)/1000, x1_velocity_mm_sec: payload.slice(16, 18).reduce(msbLsb) / 100, x1_displacement_mm: payload.slice(18, 20).reduce(msbLsb) / 100, x1_peak_one_Hz: payload.slice(20, 22).reduce(msbLsb), x1_peak_two_Hz: payload.slice(22, 24).reduce(msbLsb), x1_peak_three_Hz: payload.slice(24, 26).reduce(msbLsb), y1_rms_ACC_G: payload.slice(26, 28).reduce(msbLsb)/1000, y1_max_ACC_G: payload.slice(28, 30).reduce(msbLsb)/1000, y1_velocity_mm_sec: payload.slice(30, 32).reduce(msbLsb) / 100, y1_displacement_mm: payload.slice(32, 34).reduce(msbLsb) / 100, y1_peak_one_Hz: payload.slice(34, 36).reduce(msbLsb), y1_peak_two_Hz: payload.slice(36, 38).reduce(msbLsb), y1_peak_three_Hz: payload.slice(38, 40).reduce(msbLsb), z1_rms_ACC_G: payload.slice(40, 42).reduce(msbLsb)/1000, z1_max_ACC_G: payload.slice(42, 44).reduce(msbLsb)/1000, z1_velocity_mm_sec: payload.slice(44, 46).reduce(msbLsb) / 100, z1_displacement_mm: payload.slice(46, 48).reduce(msbLsb) / 100, z1_peak_one_Hz: payload.slice(48, 50).reduce(msbLsb), z1_peak_two_Hz: payload.slice(50, 52).reduce(msbLsb), z1_peak_three_Hz: payload.slice(52, 54).reduce(msbLsb), rpm_1: payload.slice(54, 56).reduce(msbLsb), s2_odr: odr2, s2_temperature: signInt(payload.slice(57, 59).reduce(msbLsb), 16) / 100, x2_rms_ACC_G: payload.slice(59, 61).reduce(msbLsb)/1000, x2_max_ACC_G: payload.slice(61, 63).reduce(msbLsb)/1000, x2_velocity_mm_sec: payload.slice(63, 65).reduce(msbLsb) / 100, x2_displacement_mm: payload.slice(65, 67).reduce(msbLsb) / 100, x2_peak_one_Hz: payload.slice(67, 69).reduce(msbLsb), x2_peak_two_Hz: payload.slice(69, 71).reduce(msbLsb), x2_peak_three_Hz: payload.slice(71, 73).reduce(msbLsb), y2_rms_ACC_G: payload.slice(73, 75).reduce(msbLsb)/1000, y2_max_ACC_G: payload.slice(75, 77).reduce(msbLsb)/1000, y2_velocity_mm_sec: payload.slice(77, 79).reduce(msbLsb) / 100, y2_displacement_mm: payload.slice(79, 81).reduce(msbLsb) / 100, y2_peak_one_Hz: payload.slice(81, 83).reduce(msbLsb), y2_peak_two_Hz: payload.slice(83, 85).reduce(msbLsb), y2_peak_three_Hz: payload.slice(85, 87).reduce(msbLsb), z2_rms_ACC_G: payload.slice(87, 89).reduce(msbLsb)/1000, z2_max_ACC_G: payload.slice(89, 91).reduce(msbLsb)/1000, z2_velocity_mm_sec: payload.slice(91, 93).reduce(msbLsb) / 100, z2_displacement_mm: payload.slice(93, 95).reduce(msbLsb) / 100, z2_peak_one_Hz: payload.slice(95, 97).reduce(msbLsb), z2_peak_two_Hz: payload.slice(97, 99).reduce(msbLsb), z2_peak_three_Hz: payload.slice(99, 101).reduce(msbLsb), rpm_2: payload.slice(101, 103).reduce(msbLsb) }; } }; // Export the module with all the necessary functions and properties // that need to be called from outside the scrip return { type: 111, name: 'Two Channel Vibration Plus v4', parse, get_write_buffer_size, get_config_map, sync_parse, parse_fly, }; };