UNPKG

bt-seneca-msc

Version:

A pure Javascript API for the Seneca Multi Smart Calibrator (MSC) device, using web bluetooth.

1 lines 108 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MSC=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r}()({1:[function(require,module,exports){"use strict";var APIState=require("./classes/APIState");var log=require("loglevel");var constants=require("./constants");var utils=require("./utils");var senecaModule=require("./classes/SenecaMSC");var modbus=require("./modbusRtu");var testData=require("./modbusTestData");var btState=APIState.btState;var State=constants.State;var CommandType=constants.CommandType;var ResultCode=constants.ResultCode;var simulation=false;var logging=false;const BlueToothMSC={ServiceUuid:"0003cdd0-0000-1000-8000-00805f9b0131",ModbusAnswerUuid:"0003cdd1-0000-1000-8000-00805f9b0131",ModbusRequestUuid:"0003cdd2-0000-1000-8000-00805f9b0131"};async function SendAndResponse(command){if(command==null)return null;log.debug(">> "+utils.buf2hex(command));btState.response=null;btState.stats["requests"]++;var startTime=(new Date).getTime();if(simulation){btState.response=fakeResponse(command);await utils.sleep(5)}else{await btState.charWrite.writeValueWithoutResponse(command);while(btState.state==State.METER_INITIALIZING||btState.state==State.BUSY){if(btState.response!=null)break;await new Promise(resolve=>setTimeout(resolve,35))}}var endTime=(new Date).getTime();var answer=btState.response?.slice();btState.response=null;if(logging){var packet={request:utils.buf2hex(command),answer:utils.buf2hex(answer)};var packets=window.localStorage.getItem("ModbusRTUtrace");if(packets==null){packets=[]}else{packets=JSON.parse(packets)}packets.push(packet);window.localStorage.setItem("ModbusRTUtrace",JSON.stringify(packets))}btState.stats["responseTime"]=Math.round((1*btState.stats["responseTime"]*(btState.stats["responses"]%500)+(endTime-startTime))/(btState.stats["responses"]%500+1));btState.stats["lastResponseTime"]=Math.round(endTime-startTime)+" ms";btState.stats["responses"]++;return answer}let senecaMSC=new senecaModule.SenecaMSC(SendAndResponse);async function stateMachine(){var nextAction;var DELAY_MS=simulation?20:750;var TIMEOUT_MS=simulation?1e3:3e4;btState.started=true;log.debug("Current state:"+btState.state);if(btState.state==btState.prev_state){btState.state_cpt++}else{btState.state_cpt=0}if(btState.stopRequest){btState.state=State.STOPPING}log.debug("State:"+btState.state);switch(btState.state){case State.NOT_CONNECTED:if(simulation){nextAction=fakePairDevice}else{nextAction=btPairDevice}break;case State.CONNECTING:nextAction=undefined;break;case State.DEVICE_PAIRED:if(simulation){nextAction=fakeSubscribe}else{nextAction=btSubscribe}break;case State.SUBSCRIBING:nextAction=undefined;if(btState.state_cpt>Math.floor(TIMEOUT_MS/DELAY_MS)){log.warn("Timeout in SUBSCRIBING");btState.state=State.DEVICE_PAIRED;btState.state_cpt=0}break;case State.METER_INIT:nextAction=meterInit;break;case State.METER_INITIALIZING:if(btState.state_cpt>Math.floor(TIMEOUT_MS/DELAY_MS)){log.warn("Timeout in METER_INITIALIZING");if(simulation){nextAction=fakeSubscribe}else{nextAction=btSubscribe}btState.state_cpt=0}nextAction=undefined;break;case State.IDLE:if(btState.command!=null)nextAction=processCommand;else{nextAction=refresh}break;case State.ERROR:nextAction=disconnect;break;case State.BUSY:if(btState.state_cpt>Math.floor(TIMEOUT_MS/DELAY_MS)){log.warn("Timeout in BUSY");if(simulation){nextAction=fakeSubscribe}else{nextAction=btSubscribe}btState.state_cpt=0}nextAction=undefined;break;case State.STOPPING:nextAction=disconnect;break;case State.STOPPED:nextAction=undefined;break;default:break}btState.prev_state=btState.state;if(nextAction!=undefined){log.debug("\tExecuting:"+nextAction.name);try{await nextAction()}catch(e){log.error("Exception in state machine",e)}}if(btState.state!=State.STOPPED){utils.sleep(DELAY_MS).then(()=>stateMachine()).catch(err=>{log.error("State machine error:",err);btState.state=State.ERROR})}else{log.debug("\tTerminating State machine");btState.started=false}}async function processCommand(){try{var command=btState.command;var result=ResultCode.SUCCESS;if(command==null){return}btState.state=State.BUSY;btState.stats["commands"]++;log.info("\t\tExecuting command :"+command);result=await senecaMSC.switchOff();if(result!=ResultCode.SUCCESS){throw new Error("Cannot switch meter off before command write!")}if(utils.isGeneration(command.type)||utils.isSetting(command.type)&&command.type!=CommandType.OFF){result=await senecaMSC.writeSetpoints(command.type,command.setpoint,command.setpoint2);if(result!=ResultCode.SUCCESS){throw new Error("Failure to write setpoints!")}}if(!utils.isSetting(command.type)&&utils.isValid(command.type)&&command.type!=CommandType.OFF){result=await senecaMSC.changeMode(command.type);if(result!=ResultCode.SUCCESS){throw new Error("Failure to change meter mode!")}}log.debug("\t\tRefreshing current state");await refresh();command.error=false;command.pending=false;btState.command=null;btState.state=State.IDLE;log.debug("\t\tCompleted command executed")}catch(err){log.error("** error while executing command: "+err);btState.state=State.METER_INIT;btState.stats["exceptions"]++;if(err instanceof modbus.ModbusError)btState.stats["modbus_errors"]++;return}}function getExpectedStateHex(){var stateHex=CommandType.OFF.toString(16);if(btState.command?.type!=null){stateHex=btState.command.type.toString(16)}while(stateHex.length<2)stateHex="0"+stateHex;return stateHex}function fakeResponse(command){var commandHex=utils.buf2hex(command);var forgedAnswers={"19 03 00 64 00 01 c6 0d":"19 03 02 00"+getExpectedStateHex()+" $$$$","default 03":"19 03 06 0001 0001 0001 $$$$","default 10":"19 10 00 d4 00 02 0001 0001 $$$$"};var responseHex=forgedAnswers["default "+commandHex.split(" ")[1]];if(forgedAnswers[commandHex]!=undefined){responseHex=forgedAnswers[commandHex]}else{var found=[];for(const trace of testData.testTraces){if(trace["request"]===commandHex){found.push(trace["answer"])}}if(found.length>0){responseHex=found[Math.floor(Math.random()*found.length)]}else{console.info(commandHex+" not found in test traces")}}if(responseHex.includes("$$$$")){responseHex=responseHex.replace("$$$$","");var crc=modbus.crc16(new Uint8Array(utils.hex2buf(responseHex))).toString(16);while(crc.length<4)crc="0"+crc;responseHex=responseHex+crc.substring(2,4)+crc.substring(0,2)}log.debug("<< "+responseHex);return utils.hex2buf(responseHex)}async function meterInit(){try{btState.state=State.METER_INITIALIZING;btState.meter.serial=await senecaMSC.getSerialNumber();log.info("\t\tSerial number:"+btState.meter.serial);btState.meter.mode=await senecaMSC.getCurrentMode();log.debug("\t\tCurrent mode:"+btState.meter.mode);btState.meter.battery=await senecaMSC.getBatteryVoltage();log.debug("\t\tBattery (V):"+btState.meter.battery);btState.state=State.IDLE}catch(err){log.warn("Error while initializing meter :"+err);btState.stats["exceptions"]++;btState.state=State.DEVICE_PAIRED;if(err instanceof modbus.ModbusError)btState.stats["modbus_errors"]++}}async function disconnect(){btState.command=null;try{if(btState.btDevice!=null){if(btState.btDevice?.gatt?.connected){log.warn("* Calling disconnect on btdevice");btState.btDevice.removeEventListener("gattserverdisconnected",onDisconnected);btState.btDevice.gatt.disconnect()}}btState.btService=null}catch{}btState.state=State.STOPPED}async function onDisconnected(){log.warn("* GATT Server disconnected event, will try to reconnect *");btState.btService=null;btState.stats["GATT disconnects"]++;btState.state=State.DEVICE_PAIRED}function arrayBufferConcat(){var length=0;var buffer=null;for(var i in arguments){buffer=arguments[i];length+=buffer.byteLength}var joined=new Uint8Array(length);var offset=0;for(i in arguments){buffer=arguments[i];joined.set(new Uint8Array(buffer),offset);offset+=buffer.byteLength}return joined.buffer}function handleNotifications(event){let value=event.target.value;if(value!=null){log.debug("<< "+utils.buf2hex(value.buffer));if(btState.response!=null){const MAX_RESPONSE_SIZE=1024;const newSize=btState.response.byteLength+value.buffer.byteLength;if(newSize>MAX_RESPONSE_SIZE){log.warn("Response buffer too large, resetting");btState.response=value.buffer.slice()}else{btState.response=arrayBufferConcat(btState.response,value.buffer)}}else{btState.response=value.buffer.slice()}}}async function btPairDevice(){btState.state=State.CONNECTING;var forceSelection=btState.options["forceDeviceSelection"];log.debug("btPairDevice("+forceSelection+")");try{if(typeof navigator.bluetooth?.getAvailability=="function"){const availability=await navigator.bluetooth.getAvailability();if(!availability){log.error("Bluetooth not available in browser.");throw new Error("Browser does not provide bluetooth")}}var device=null;if(typeof navigator.bluetooth?.getDevices=="function"&&!forceSelection){const availableDevices=await navigator.bluetooth.getDevices();availableDevices.forEach(function(dev,index){log.debug("Found authorized device :"+dev.name);if(dev.name.startsWith("MSC"))device=dev});log.debug("navigator.bluetooth.getDevices()="+device)}if(device==null){device=await navigator.bluetooth.requestDevice({acceptAllDevices:false,filters:[{namePrefix:"MSC"}],optionalServices:[BlueToothMSC.ServiceUuid]})}btState.btDevice=device;btState.state=State.DEVICE_PAIRED;log.info("Bluetooth device "+device.name+" connected.");await utils.sleep(500)}catch(err){log.warn("** error while connecting: "+err.message);btState.btService=null;if(btState.charRead!=null){try{btState.charRead.stopNotifications()}catch(error){}}btState.charRead=null;btState.charWrite=null;btState.state=State.ERROR;btState.stats["exceptions"]++}}async function fakePairDevice(){btState.state=State.CONNECTING;var forceSelection=btState.options["forceDeviceSelection"];log.debug("fakePairDevice("+forceSelection+")");try{var device={name:"FakeBTDevice",gatt:{connected:true}};btState.btDevice=device;btState.state=State.DEVICE_PAIRED;log.info("Bluetooth device "+device.name+" connected.");await utils.sleep(50)}catch(err){log.warn("** error while connecting: "+err.message);btState.btService=null;btState.charRead=null;btState.charWrite=null;btState.state=State.ERROR;btState.stats["exceptions"]++}}async function btSubscribe(){try{btState.state=State.SUBSCRIBING;btState.stats["subcribes"]++;let device=btState.btDevice;let server=null;if(!device?.gatt?.connected){log.debug(`Connecting to GATT Server on ${device.name}...`);device.addEventListener("gattserverdisconnected",onDisconnected);try{if(btState.btService?.connected){btState.btService.disconnect();btState.btService=null;await utils.sleep(100)}}catch(err){}server=await device.gatt.connect();log.debug("> Found GATT server")}else{log.debug("GATT already connected");server=device.gatt}btState.btService=await server.getPrimaryService(BlueToothMSC.ServiceUuid);if(btState.btService==null)throw new Error("GATT Service request failed");log.debug("> Found Serial service");btState.charWrite=await btState.btService.getCharacteristic(BlueToothMSC.ModbusRequestUuid);log.debug("> Found write characteristic");btState.charRead=await btState.btService.getCharacteristic(BlueToothMSC.ModbusAnswerUuid);log.debug("> Found read characteristic");btState.response=null;btState.charRead.addEventListener("characteristicvaluechanged",handleNotifications);btState.charRead.startNotifications();log.info("> Bluetooth interfaces ready.");btState.stats["last_connect"]=(new Date).toISOString();await utils.sleep(50);btState.state=State.METER_INIT}catch(err){log.warn("** error while subscribing: "+err.message);if(btState.charRead!=null){try{if(btState.btDevice?.gatt?.connected){btState.charRead.stopNotifications()}btState.btDevice?.gatt.disconnect()}catch(error){}}btState.charRead=null;btState.charWrite=null;btState.state=State.DEVICE_PAIRED;btState.stats["exceptions"]++}}async function fakeSubscribe(){try{btState.state=State.SUBSCRIBING;btState.stats["subcribes"]++;let device=btState.btDevice;let server=null;if(!device?.gatt?.connected){log.debug(`Connecting to GATT Server on ${device.name}...`);device["gatt"]["connected"]=true;log.debug("> Found GATT server")}else{log.debug("GATT already connected");server=device.gatt}btState.btService={};log.debug("> Found Serial service");btState.charWrite={};log.debug("> Found write characteristic");btState.charRead={};log.debug("> Found read characteristic");btState.response=null;log.info("> Bluetooth interfaces ready.");btState.stats["last_connect"]=(new Date).toISOString();await utils.sleep(10);btState.state=State.METER_INIT}catch(err){log.warn("** error while subscribing: "+err.message);btState.charRead=null;btState.charWrite=null;btState.state=State.DEVICE_PAIRED;btState.stats["exceptions"]++}}async function refresh(){btState.state=State.BUSY;try{var mode=await senecaMSC.getCurrentMode();if(mode!=CommandType.NONE_UNKNOWN){btState.meter.mode=mode;if(btState.meter.isGeneration()){var setpoints=await senecaMSC.getSetpoints(btState.meter.mode);btState.lastSetpoint=setpoints}if(btState.meter.isMeasurement()){var meas=await senecaMSC.getMeasures(btState.meter.mode);btState.lastMeasure=meas}}if(!btState.batteryRefreshCounter){btState.batteryRefreshCounter=0}btState.batteryRefreshCounter++;if(btState.batteryRefreshCounter>=10){btState.meter.battery=await senecaMSC.getBatteryVoltage();log.debug("\t\tBattery refreshed: "+btState.meter.battery+"V");btState.batteryRefreshCounter=0}log.debug("\t\tFinished refreshing current state");btState.state=State.IDLE}catch(err){log.warn("Error while refreshing measure"+err);btState.state=State.DEVICE_PAIRED;btState.stats["exceptions"]++;if(err instanceof modbus.ModbusError)btState.stats["modbus_errors"]++}}function SetSimulation(value){simulation=value}module.exports={stateMachine:stateMachine,SendAndResponse:SendAndResponse,SetSimulation:SetSimulation}},{"./classes/APIState":2,"./classes/SenecaMSC":6,"./constants":7,"./modbusRtu":10,"./modbusTestData":11,"./utils":14,loglevel:12}],2:[function(require,module,exports){var constants=require("../constants");var MeterState=require("./MeterState");class APIState{constructor(){this.state=constants.State.NOT_CONNECTED;this.prev_state=constants.State.NOT_CONNECTED;this.state_cpt=0;this.started=false;this.stopRequest=false;this.lastMeasure={};this.lastSetpoint={};this.meter=new MeterState;this.command=null;this.response=null;this.charRead=null;this.charWrite=null;this.btService=null;this.btDevice=null;this.continuity=false;this.batteryRefreshCounter=0;this.stats={requests:0,responses:0,modbus_errors:0,"GATT disconnects":0,exceptions:0,subcribes:0,commands:0,responseTime:0,lastResponseTime:0,last_connect:new Date(2020,1,1).toISOString()};this.options={forceDeviceSelection:true}}}let btState=new APIState;module.exports={APIState:APIState,btState:btState}},{"../constants":7,"./MeterState":5}],3:[function(require,module,exports){var constants=require("../constants");var utils=require("../utils");var CommandType=constants.CommandType;class Command{constructor(ctype=CommandType.NONE_UNKNOWN){this.type=parseInt(ctype);this.setpoint=null;this.setpoint2=null;this.error=false;this.pending=true;this.request=null;this.response=null}static CreateNoSP(ctype){var cmd=new Command(ctype);return cmd}static CreateOneSP(ctype,setpoint){var cmd=new Command(ctype);cmd.setpoint=parseFloat(setpoint);return cmd}static CreateTwoSP(ctype,set1,set2){var cmd=new Command(ctype);cmd.setpoint=parseFloat(set1);cmd.setpoint2=parseFloat(set2);return cmd}toString(){return"Type: "+utils.Parse(CommandType,this.type)+", setpoint:"+this.setpoint+", setpoint2: "+this.setpoint2+", pending:"+this.pending+", error:"+this.error}defaultSetpoint(){switch(this.type){case CommandType.GEN_THERMO_B:case CommandType.GEN_THERMO_E:case CommandType.GEN_THERMO_J:case CommandType.GEN_THERMO_K:case CommandType.GEN_THERMO_L:case CommandType.GEN_THERMO_N:case CommandType.GEN_THERMO_R:case CommandType.GEN_THERMO_S:case CommandType.GEN_THERMO_T:case CommandType.GEN_Cu50_3W:case CommandType.GEN_Cu50_2W:case CommandType.GEN_Cu100_2W:case CommandType.GEN_Ni100_2W:case CommandType.GEN_Ni120_2W:case CommandType.GEN_PT100_2W:case CommandType.GEN_PT500_2W:case CommandType.GEN_PT1000_2W:return{"Temperature (°C)":0};case CommandType.GEN_V:return{"Voltage (V)":0};case CommandType.GEN_mV:return{"Voltage (mV)":0};case CommandType.GEN_mA_active:case CommandType.GEN_mA_passive:return{"Current (mA)":0};case CommandType.GEN_LoadCell:return{"Imbalance (mV/V)":0};case CommandType.GEN_Frequency:return{"Frequency (Hz)":0};case CommandType.GEN_PulseTrain:return{"Pulses count":0,"Frequency (Hz)":0};case CommandType.SET_UThreshold_F:return{"Uthreshold (V)":2};case CommandType.SET_Sensitivity_uS:return{"Sensibility (uS)":2};case CommandType.SET_ColdJunction:return{"Cold junction compensation":0};case CommandType.SET_Ulow:return{"U low (V)":0/constants.MAX_U_GEN};case CommandType.SET_Uhigh:return{"U high (V)":5/constants.MAX_U_GEN};case CommandType.SET_ShutdownDelay:return{"Delay (s)":60*5};default:return{}}}isGeneration(){return utils.isGeneration(this.type)}isMeasurement(){return utils.isMeasurement(this.type)}isSetting(){return utils.isSetting(this.type)}isValid(){return utils.isMeasurement(this.type)||utils.isGeneration(this.type)||utils.isSetting(this.type)}}module.exports=Command},{"../constants":7,"../utils":14}],4:[function(require,module,exports){class CommandResult{value=0;success=false;message="";unit="";secondary_value=0;secondary_unit=""}module.exports=CommandResult},{}],5:[function(require,module,exports){var constants=require("../constants");class MeterState{constructor(){this.firmware="";this.serial="";this.mode=constants.CommandType.NONE_UNKNOWN;this.battery=0}isMeasurement(){return this.mode>constants.CommandType.NONE_UNKNOWN&&this.mode<constants.CommandType.OFF}isGeneration(){return this.mode>constants.CommandType.OFF&&this.mode<constants.CommandType.GEN_RESERVED}}module.exports=MeterState},{"../constants":7}],6:[function(require,module,exports){"use strict";var log=require("loglevel");var utils=require("../utils");var senecaMB=require("../senecaModbus");var modbus=require("../modbusRtu");var constants=require("../constants");const{btState}=require("./APIState");var CommandType=constants.CommandType;var ResultCode=constants.ResultCode;const RESET_POWER_OFF=6;const SET_POWER_OFF=7;const CLEAR_AVG_MIN_MAX=5;const PULSE_CMD=9;class SenecaMSC{constructor(fnSendAndResponse){this.SendAndResponse=fnSendAndResponse}async getSerialNumber(){log.debug("\t\tReading serial number");var response=await this.SendAndResponse(senecaMB.makeSerialNumber());return senecaMB.parseSerialNumber(response)}async getCurrentMode(){log.debug("\t\tReading current mode");var response=await this.SendAndResponse(senecaMB.makeCurrentMode());return senecaMB.parseCurrentMode(response,CommandType.NONE_UNKNOWN)}async getBatteryVoltage(){log.debug("\t\tReading battery voltage");var response=await this.SendAndResponse(senecaMB.makeBatteryLevel());return Math.round(senecaMB.parseBattery(response)*100)/100}async getQualityValid(){log.debug("\t\tReading measure quality bit");var response=await this.SendAndResponse(senecaMB.makeQualityBitRequest());return senecaMB.isQualityValid(response)}async getGenQualityValid(current_mode){log.debug("\t\tReading generation quality bit");var response=await this.SendAndResponse(senecaMB.makeGenStatusRead());return senecaMB.parseGenStatus(response,current_mode)}async getMeasures(mode){log.debug("\t\tReading measures");var valid=await this.getQualityValid();var response=await this.SendAndResponse(senecaMB.makeMeasureRequest(mode));if(response!=null){var meas=senecaMB.parseMeasure(response,mode);meas["error"]=!valid;return meas}return null}async getSetpoints(mode){log.debug("\t\tReading setpoints");var valid=await this.getGenQualityValid(mode);var response=await this.SendAndResponse(senecaMB.makeSetpointRead(mode));if(response!=null){var results=senecaMB.parseSetpointRead(response,mode);results["error"]=!valid;return results}return null}async switchOff(){log.debug("\t\tSetting meter to OFF");var packet=senecaMB.makeModeRequest(CommandType.OFF);if(packet==null)return ResultCode.FAILED_NO_RETRY;await this.SendAndResponse(packet);await utils.sleep(100);return ResultCode.SUCCESS}async writeSetpoints(command_type,setpoint,setpoint2){var startGen;log.debug("\t\tSetting command:"+command_type+", setpoint: "+setpoint+", setpoint 2: "+setpoint2);var packets=senecaMB.makeSetpointRequest(command_type,setpoint,setpoint2);for(const p of packets){var response=await this.SendAndResponse(p);if(response!=null&&!modbus.parseFC16checked(response,0)){return ResultCode.FAILED_SHOULD_RETRY}}switch(command_type){case CommandType.SET_ShutdownDelay:startGen=modbus.makeFC16(modbus.SENECA_MB_SLAVE_ID,senecaMB.MSCRegisters.CMD,[RESET_POWER_OFF]);response=await this.SendAndResponse(startGen);if(!modbus.parseFC16checked(response,1)){return ResultCode.FAILED_NO_RETRY}break;default:break}return ResultCode.SUCCESS}async clearStatistics(){log.debug("\t\tResetting statistics");var startGen=modbus.makeFC16(modbus.SENECA_MB_SLAVE_ID,senecaMB.MSCRegisters.CMD,[CLEAR_AVG_MIN_MAX]);var response=await this.SendAndResponse(startGen);if(!modbus.parseFC16checked(response,1)){return ResultCode.FAILED_NO_RETRY}return ResultCode.SUCCESS}async startPulseGen(){log.debug("\t\tStarting pulse generation");var startGen=modbus.makeFC16(modbus.SENECA_MB_SLAVE_ID,senecaMB.MSCRegisters.GEN_CMD,[PULSE_CMD,2]);var response=await this.SendAndResponse(startGen);if(!modbus.parseFC16checked(response,2)){return ResultCode.FAILED_NO_RETRY}return ResultCode.SUCCESS}async startFreqGen(){log.debug("\t\tStarting freq gen");var startGen=modbus.makeFC16(modbus.SENECA_MB_SLAVE_ID,senecaMB.MSCRegisters.GEN_CMD,[PULSE_CMD,1]);var response=await this.SendAndResponse(startGen);if(!modbus.parseFC16checked(response,2)){return ResultCode.FAILED_NO_RETRY}return ResultCode.SUCCESS}async disablePowerOff(){log.debug("\t\tDisabling power off");var startGen=modbus.makeFC16(modbus.SENECA_MB_SLAVE_ID,senecaMB.MSCRegisters.CMD,[RESET_POWER_OFF]);await this.SendAndResponse(startGen);return ResultCode.SUCCESS}async changeMode(command_type){log.debug("\t\tSetting meter mode to :"+command_type);var packet=senecaMB.makeModeRequest(command_type);if(packet==null){log.error("Could not generate modbus packet for command type",command_type);return ResultCode.FAILED_NO_RETRY}var response=await this.SendAndResponse(packet);if(!modbus.parseFC16checked(response,0)){log.error("Could not generate modbus packet for command type",command_type);return ResultCode.FAILED_NO_RETRY}var result=ResultCode.SUCCESS;switch(command_type){case CommandType.Continuity:btState.continuity=true;break;case CommandType.V:case CommandType.mV:case CommandType.mA_active:case CommandType.mA_passive:case CommandType.PulseTrain:await utils.sleep(1e3);result=await this.clearStatistics();break;case CommandType.GEN_PulseTrain:await utils.sleep(1e3);result=await this.startPulseGen();break;case CommandType.GEN_Frequency:await utils.sleep(1e3);result=await this.startFreqGen();break}if(result==ResultCode.SUCCESS){result=await this.disablePowerOff()}return result}}module.exports={SenecaMSC:SenecaMSC}},{"../constants":7,"../modbusRtu":10,"../senecaModbus":13,"../utils":14,"./APIState":2,loglevel:12}],7:[function(require,module,exports){const CommandType={NONE_UNKNOWN:0,mA_passive:1,mA_active:2,V:3,mV:4,THERMO_J:5,THERMO_K:6,THERMO_T:7,THERMO_E:8,THERMO_L:9,THERMO_N:10,THERMO_R:11,THERMO_S:12,THERMO_B:13,PT100_2W:14,PT100_3W:15,PT100_4W:16,PT500_2W:17,PT500_3W:18,PT500_4W:19,PT1000_2W:20,PT1000_3W:21,PT1000_4W:22,Cu50_2W:23,Cu50_3W:24,Cu50_4W:25,Cu100_2W:26,Cu100_3W:27,Cu100_4W:28,Ni100_2W:29,Ni100_3W:30,Ni100_4W:31,Ni120_2W:32,Ni120_3W:33,Ni120_4W:34,LoadCell:35,Frequency:36,PulseTrain:37,RESERVED:38,RESERVED_2:40,Continuity:41,OFF:100,GEN_mA_passive:101,GEN_mA_active:102,GEN_V:103,GEN_mV:104,GEN_THERMO_J:105,GEN_THERMO_K:106,GEN_THERMO_T:107,GEN_THERMO_E:108,GEN_THERMO_L:109,GEN_THERMO_N:110,GEN_THERMO_R:111,GEN_THERMO_S:112,GEN_THERMO_B:113,GEN_PT100_2W:114,GEN_PT500_2W:117,GEN_PT1000_2W:120,GEN_Cu50_2W:123,GEN_Cu100_2W:126,GEN_Ni100_2W:129,GEN_Ni120_2W:132,GEN_LoadCell:135,GEN_Frequency:136,GEN_PulseTrain:137,GEN_RESERVED:138,SETTING_RESERVED:1e3,SET_UThreshold_F:1001,SET_Sensitivity_uS:1002,SET_ColdJunction:1003,SET_Ulow:1004,SET_Uhigh:1005,SET_ShutdownDelay:1006};const ContinuityImpl=CommandType.Cu50_2W;const ContinuityThresholdOhms=75;const State={NOT_CONNECTED:"Not connected",CONNECTING:"Bluetooth device pairing...",DEVICE_PAIRED:"Device paired",SUBSCRIBING:"Bluetooth interfaces connecting...",IDLE:"Idle",BUSY:"Busy",ERROR:"Error",STOPPING:"Closing BT interfaces...",STOPPED:"Stopped",METER_INIT:"Meter connected",METER_INITIALIZING:"Reading meter state..."};const ResultCode={FAILED_NO_RETRY:1,FAILED_SHOULD_RETRY:2,SUCCESS:0};const MAX_U_GEN=27;module.exports={State:State,CommandType:CommandType,ResultCode:ResultCode,MAX_U_GEN:MAX_U_GEN,ContinuityImpl:ContinuityImpl,ContinuityThresholdOhms:ContinuityThresholdOhms}},{}],8:[function(require,module,exports){"use strict";const log=require("loglevel");const constants=require("./constants");const APIState=require("./classes/APIState");const Command=require("./classes/Command");const PublicAPI=require("./meterPublicAPI");const TestData=require("./modbusTestData");log.setLevel(log.levels.ERROR,true);exports.Stop=PublicAPI.Stop;exports.Pair=PublicAPI.Pair;exports.Execute=PublicAPI.Execute;exports.SimpleExecute=PublicAPI.SimpleExecute;exports.GetState=PublicAPI.GetState;exports.State=constants.State;exports.CommandType=constants.CommandType;exports.Command=Command;exports.Parse=PublicAPI.Parse;exports.log=log;exports.GetStateJSON=PublicAPI.GetStateJSON;exports.ExecuteJSON=PublicAPI.ExecuteJSON;exports.SimpleExecuteJSON=PublicAPI.SimpleExecuteJSON;exports.GetJsonTraces=TestData.GetJsonTraces},{"./classes/APIState":2,"./classes/Command":3,"./constants":7,"./meterPublicAPI":9,"./modbusTestData":11,loglevel:12}],9:[function(require,module,exports){var CommandResult=require("./classes/CommandResult");var APIState=require("./classes/APIState");var constants=require("./constants");var bluetooth=require("./bluetooth");var utils=require("./utils");var log=require("loglevel");var meterApi=require("./meterApi");var btState=APIState.btState;var State=constants.State;async function GetState(){let ready=false;let initializing=false;switch(btState.state){case State.ERROR:case State.STOPPED:case State.NOT_CONNECTED:ready=false;initializing=false;break;case State.BUSY:case State.IDLE:ready=true;initializing=false;break;case State.CONNECTING:case State.DEVICE_PAIRED:case State.METER_INIT:case State.METER_INITIALIZING:case State.SUBSCRIBING:initializing=true;ready=false;break;default:ready=false;initializing=false}return{lastSetpoint:btState.lastSetpoint,lastMeasure:btState.lastMeasure,deviceName:btState.btDevice?btState.btDevice.name:"",deviceSerial:btState.meter?.serial,stats:btState.stats,deviceMode:btState.meter?.mode,status:btState.state,batteryLevel:btState.meter?.battery,ready:ready,initializing:initializing}}async function GetStateJSON(){return JSON.stringify(await GetState())}async function ExecuteJSON(jsonCommand){let command=JSON.parse(jsonCommand);let command2=meterApi.Command.CreateTwoSP(command.type,command.setpoint,command.setpoint2);return JSON.stringify(await Execute(command2))}async function SimpleExecuteJSON(jsonCommand){let command=JSON.parse(jsonCommand);let command2=meterApi.Command.CreateTwoSP(command.type,command.setpoint,command.setpoint2);return JSON.stringify(await SimpleExecute(command2))}async function SimpleExecute(command){const SIMPLE_EXECUTE_TIMEOUT_S=5;var cr=new CommandResult;log.info("SimpleExecute called...");if(command==null){cr.success=false;cr.message="Invalid command";return cr}command.pending=true;if(!btState.started){cr.success=false;cr.message="Device is not paired";log.warn(cr.message);return cr}if(btState.command!=null&&btState.command.pending){cr.success=false;cr.message="Another command is pending";log.warn(cr.message);return cr}btState.command=command;if(command!=null){await utils.waitForTimeout(()=>!command.pending||btState.state==State.STOPPED,SIMPLE_EXECUTE_TIMEOUT_S)}if(command.error||command.pending){cr.success=false;cr.message="Error while executing the command.";log.warn(cr.message);btState.command=null;return cr}if(utils.isGeneration(command.type)){cr.value=btState.lastSetpoint["Value"];cr.unit=btState.lastSetpoint["Unit"]}else if(utils.isMeasurement(command.type)){cr.value=btState.lastMeasure["Value"];cr.unit=btState.lastMeasure["Unit"];cr.secondary_value=btState.lastMeasure["SecondaryValue"];cr.secondary_unit=btState.lastMeasure["SecondaryUnit"]}else{cr.value=0}cr.success=true;cr.message="Command executed successfully";return cr}async function Execute(command){log.info("Execute called...");if(command==null)return null;command.pending=true;var cpt=0;while(btState.command!=null&&btState.command.pending&&cpt<300){log.debug("Waiting for current command to complete...");await utils.sleep(100);cpt++}log.info("Setting new command :"+command);btState.command=command;if(!btState.started){btState.state=State.NOT_CONNECTED;try{await bluetooth.stateMachine()}catch(err){log.error("Failed to start state machine:",err);command.error=true;command.pending=false;return command}}if(command!=null){await utils.waitFor(()=>!command.pending||btState.state==State.STOPPED)}return command}async function Pair(forceSelection=false){log.info("Pair("+forceSelection+") called...");btState.options["forceDeviceSelection"]=forceSelection;if(!btState.started){btState.state=State.NOT_CONNECTED;bluetooth.stateMachine().catch(err=>{log.error("State machine failed during pairing:",err);btState.state=State.ERROR})}else if(btState.state==State.ERROR){btState.state=State.NOT_CONNECTED}await utils.waitFor(()=>btState.state==State.IDLE||btState.state==State.STOPPED);log.info("Pairing completed, state :",btState.state);return btState.state!=State.STOPPED}async function Stop(){log.info("Stop request received");btState.stopRequest=true;await utils.sleep(100);while(btState.started||btState.state!=State.STOPPED&&btState.state!=State.NOT_CONNECTED){btState.stopRequest=true;await utils.sleep(100)}btState.command=null;btState.stopRequest=false;log.warn("Stopped on request.");return true}module.exports={Stop:Stop,Pair:Pair,Execute:Execute,ExecuteJSON:ExecuteJSON,SimpleExecute:SimpleExecute,SimpleExecuteJSON:SimpleExecuteJSON,GetState:GetState,GetStateJSON:GetStateJSON,log:log}},{"./bluetooth":1,"./classes/APIState":2,"./classes/CommandResult":4,"./constants":7,"./meterApi":8,"./utils":14,loglevel:12}],10:[function(require,module,exports){"use strict";var log=require("loglevel");const SENECA_MB_SLAVE_ID=25;class ModbusError extends Error{constructor(message,fc){super(message);this.message=message;this.fc=fc}}function crc16(buffer){var crc=65535;var odd;for(var i=0;i<buffer.length;i++){crc=crc^buffer[i];for(var j=0;j<8;j++){odd=crc&1;crc=crc>>1;if(odd){crc=crc^40961}}}return crc}function makeFC3(ID,count,register){const buffer=new ArrayBuffer(8);const view=new DataView(buffer);view.setUint8(0,ID);view.setUint8(1,3);view.setUint16(2,register,false);view.setUint16(4,count,false);var crc=crc16(new Uint8Array(buffer.slice(0,-2)));view.setUint16(6,crc,true);return buffer}function makeFC16(address,dataAddress,array){const code=16;if(typeof address==="undefined"||typeof dataAddress==="undefined"){return}let dataLength=array.length;const codeLength=7+2*dataLength;const buf=new ArrayBuffer(codeLength+2);const dv=new DataView(buf);dv.setUint8(0,address);dv.setUint8(1,code);dv.setUint16(2,dataAddress,false);dv.setUint16(4,dataLength,false);dv.setUint8(6,dataLength*2);for(let i=0;i<dataLength;i++){dv.setUint16(7+2*i,array[i],false)}const crc=crc16(new Uint8Array(buf.slice(0,-2)));dv.setUint16(codeLength,crc,true);return buf}function parseFC3(response){if(!(response instanceof ArrayBuffer)){return null}const view=new DataView(response);if(response.length<5)return;var computed_crc=crc16(new Uint8Array(response.slice(0,-2)));var actual_crc=view.getUint16(view.byteLength-2,true);if(computed_crc!=actual_crc){throw new ModbusError("Wrong CRC (expected:"+computed_crc+",got:"+actual_crc+")",3)}var address=view.getUint8(0);if(address!=SENECA_MB_SLAVE_ID){throw new ModbusError("Wrong slave ID :"+address,3)}var fc=view.getUint8(1);if(fc>128){var exp=view.getUint8(2);throw new ModbusError("Exception by slave:"+exp,3)}if(fc!=3){throw new ModbusError("Wrong FC :"+fc,fc)}var length=view.getUint8(2);const buffer=new ArrayBuffer(length);const registers=new DataView(buffer);for(var i=3;i<view.byteLength-2;i+=2){var reg=view.getInt16(i,false);registers.setInt16(i-3,reg,false);var idx=(i-3)/2+1;log.debug("\t\tRegister "+idx+"/"+length/2+" = "+reg)}return registers}function parseFC16checked(response,expected){try{const result=parseFC16(response);return expected<=0||result[1]===expected}catch(err){log.error("FC16 answer error",err);return false}}function parseFC16(response){const view=new DataView(response);if(response.length<3)return;var slave=view.getUint8(0);if(slave!=SENECA_MB_SLAVE_ID){return}var fc=view.getUint8(1);if(fc>128){var exp=view.getUint8(2);throw new ModbusError("Exception :"+exp,16)}if(fc!=16){throw new ModbusError("Wrong FC :"+fc,fc)}var computed_crc=crc16(new Uint8Array(response.slice(0,-2)));var actual_crc=view.getUint16(view.byteLength-2,true);if(computed_crc!=actual_crc){throw new ModbusError("Wrong CRC (expected:"+computed_crc+",got:"+actual_crc+")",16)}var address=view.getUint16(2,false);var length=view.getUint16(4,false);return[address,length]}function getFloat32LEBS(dataView,offset){const buff=new ArrayBuffer(4);const dv=new DataView(buff);dv.setInt16(0,dataView.getInt16(offset+2,false),false);dv.setInt16(2,dataView.getInt16(offset,false),false);return dv.getFloat32(0,false)}function getUint32LEBS(dataView,offset){const buff=new ArrayBuffer(4);const dv=new DataView(buff);dv.setInt16(0,dataView.getInt16(offset+2,false),false);dv.setInt16(2,dataView.getInt16(offset,false),false);return dv.getUint32(0,false)}function setFloat32LEBS(dataView,offset,value){const buff=new ArrayBuffer(4);const dv=new DataView(buff);dv.setFloat32(0,value,false);dataView.setInt16(offset,dv.getInt16(2,false),false);dataView.setInt16(offset+2,dv.getInt16(0,false),false)}function setUint32LEBS(dataView,offset,value){const buff=new ArrayBuffer(4);const dv=new DataView(buff);dv.setUint32(0,value,false);dataView.setInt16(offset,dv.getInt16(2,false),false);dataView.setInt16(offset+2,dv.getInt16(0,false),false)}module.exports={makeFC3:makeFC3,getFloat32LEBS:getFloat32LEBS,makeFC16:makeFC16,setFloat32LEBS:setFloat32LEBS,setUint32LEBS:setUint32LEBS,parseFC3:parseFC3,parseFC16:parseFC16,parseFC16checked:parseFC16checked,ModbusError:ModbusError,SENECA_MB_SLAVE_ID:SENECA_MB_SLAVE_ID,getUint32LEBS:getUint32LEBS,crc16:crc16}},{loglevel:12}],11:[function(require,module,exports){"use strict";const testTraces=[{request:"19 03 00 0a 00 02 e7 d1",answer:"19 03 04 5f 43 3a 90 93 3e"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 64 99 ad"},{request:"19 03 00 ae 00 02 a6 32",answer:"19 03 04 d9 3e 40 80 08 c2"},{request:"19 10 00 6b 00 02 04 00 01 00 64 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 10 00 6b 00 02 04 00 01 00 01 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 10 00 6b 00 01 02 00 05 ff ff",answer:"19 10 00 6b 00 01 73 cd"},{request:"19 10 00 6b 00 01 02 00 06 ff ff",answer:"19 10 00 6b 00 01 73 cd"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 01 59 86"},{request:"19 03 00 66 00 01 67 cd",answer:"19 03 02 00 00 98 46"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c c0 00 3a 2f c0 00 3a 2f c0 00 3a 2f c3 65"},{request:"19 10 00 6b 00 02 04 00 01 00 02 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 02 19 87"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c 80 00 39 76 c0 00 3a 2f 60 00 39 ed 07 67"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c 80 00 39 76 c0 00 3a 2f c0 00 3a 2f a4 06"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c 80 00 39 76 c0 00 3a 2f 80 00 39 76 71 0c"},{request:"19 10 00 6b 00 02 04 00 01 00 03 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 03 d8 47"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c 2d 5c 3c 86 2d 5c 3c 86 b6 d8 3c 4a b6 03"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c 47 74 3c 11 2d 5c 3c 86 47 74 3c 11 96 2b"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c 88 7c 3b f9 2d 5c 3c 86 88 7c 3b f9 08 68"},{request:"19 10 00 6b 00 02 04 00 01 00 04 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 04 99 85"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c f4 e3 c0 ea f4 e3 c0 ea f4 e3 c0 ea 15 8c"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c f4 e3 c0 ea ec e4 c0 ea f4 e3 c0 ea 63 e6"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c f4 e3 c0 ea ec e4 c0 ea ec e4 c0 ea d4 87"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c fc e3 c0 ea ec e4 c0 ea fc e3 c0 ea 80 59"},{request:"19 03 00 84 00 06 86 39",answer:"19 03 0c fc e3 c0 ea ec e4 c0 ea f4 e3 c0 ea 82 39"},{request:"19 10 00 6b 00 02 04 00 01 00 05 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 26 19 9c"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 05 58 45"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 7f d2 c3 0d 4a ea"},{request:"19 10 00 6b 00 02 04 00 01 00 06 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 06 18 44"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 d1 00 c3 75 ca 19"},{request:"19 03 00 66 00 01 67 cd",answer:"19 03 02 20 00 81 86"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 33 d3 c3 76 4d 99"},{request:"19 10 00 6b 00 02 04 00 01 00 07 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 07 d9 84"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 00 90 c3 87 72 8d"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 fe b7 c3 86 32 ae"},{request:"19 10 00 6b 00 02 04 00 01 00 08 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 08 99 80"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 be 27 c2 eb e7 3e"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 bb ad c2 eb c6 18"},{request:"19 10 00 6b 00 02 04 00 01 00 09 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 09 58 40"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 1f b7 c2 d3 c5 3d"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 47 63 c2 d3 96 65"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 1d 55 c2 d3 64 b3"},{request:"19 10 00 6b 00 02 04 00 01 00 0a ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 0a 18 41"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 6b 5e c6 3e cd b4"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 63 7d c6 3e 3e 1e"},{request:"19 10 00 6b 00 02 04 00 01 00 0b ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 0b d9 81"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 77 29 cf 7c fc 5f"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 60 ef cf 7d d8 16"},{request:"19 10 00 6b 00 02 04 00 01 00 0c ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 0c 98 43"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 34 51 cd ce e8 d7"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 a6 ea cd ce b4 4a"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 f9 ee cd cd a7 9e"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 a5 bc cd ce 54 1e"},{request:"19 10 00 6b 00 02 04 00 01 00 0d ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 0d 59 83"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 54 76 cc b0 c7 6c"},{request:"19 03 00 78 00 02 47 ca",answer:"19 03 04 7c 6e cc b0 4e cb"},{request:"19 10 00 6b 00 02 04 00 01 00 0e ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 0e 19 82"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 4f 44 44 5b 36 b6 43 c7 5f 46"},{request:"19 10 00 6b 00 02 04 00 01 00 0f ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 0f d8 42"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 f0 75 c3 b3 1c 4e c3 c7 a2 f8"},{request:"19 10 00 6b 00 02 04 00 01 00 10 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 10 99 8a"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 5d 6f 44 5b 3e ed 43 c7 37 22"},{request:"19 10 00 6b 00 02 04 00 01 00 11 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 11 58 4a"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 fb b1 45 2f 4f 9a 45 7d 1b 92"},{request:"19 10 00 6b 00 02 04 00 01 00 12 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 12 18 4b"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 c6 b0 45 2a 6d 00 c5 7d 4e 48"},{request:"19 10 00 6b 00 02 04 00 01 00 13 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 13 d9 8b"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 fa ed 45 2f 4e fe 45 7d 06 78"},{request:"19 10 00 6b 00 02 04 00 01 00 14 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 14 98 49"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 42 7c 44 61 4f 9a 45 7d a5 9f"},{request:"19 10 00 6b 00 02 04 00 01 00 15 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 15 59 89"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 7f c0 c3 c0 87 98 c5 72 07 13"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 12 77 c3 cd 9b c1 c5 6b 3c 21"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 9d e8 c3 b7 13 a9 c5 77 69 77"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 82 d0 c3 ad f6 d6 c5 7b ce eb"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 57 89 c3 d4 4b 14 c5 67 d3 1e"},{request:"19 10 00 6b 00 02 04 00 01 00 17 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 17 d8 48"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 41 06 44 2e 29 53 43 47 26 86"},{request:"19 10 00 6b 00 02 04 00 01 00 18 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 18 98 4c"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 ac 2f c4 45 25 a5 c3 47 e9 3e"},{request:"19 10 00 6b 00 02 04 00 01 00 19 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 19 59 8c"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 4f 92 44 2e 35 c6 43 47 65 7f"},{request:"19 10 00 6b 00 02 04 00 01 00 1a ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 1a 19 8d"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 af 82 43 67 29 53 43 47 b1 33"},{request:"19 10 00 6b 00 02 04 00 01 00 1b ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 1b d8 4d"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 46 a7 c4 13 25 a5 c3 47 27 0d"},{request:"19 10 00 6b 00 02 04 00 01 00 1c ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 1c 99 8f"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 cc 98 43 67 35 c6 43 47 5b 73"},{request:"19 10 00 6b 00 02 04 00 01 00 1d ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 1d 58 4f"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 70 e5 43 9a 36 b6 43 c7 90 be"},{request:"19 10 00 6b 00 02 04 00 01 00 1e ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 1e 18 4e"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 04 34 c7 06 1c 4e c3 c7 71 15"},{request:"19 10 00 6b 00 02 04 00 01 00 1f ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 1f d9 8e"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 6e df 43 9a 3e ed 43 c7 f9 8e"},{request:"19 10 00 6b 00 02 04 00 01 00 20 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 20 99 9e"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 df ef 43 89 36 b6 43 c7 f5 45"},{request:"19 10 00 6b 00 02 04 00 01 00 21 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 21 58 5e"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 6a 1e c5 dd 1c 4e c3 c7 18 82"},{request:"19 10 00 6b 00 02 04 00 01 00 22 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 22 18 5f"},{request:"19 03 00 80 00 04 46 39",answer:"19 03 08 e5 ed 43 89 3e ed 43 c7 26 5d"},{request:"19 10 00 6b 00 02 04 00 01 00 23 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 23 d9 9f"},{request:"19 03 00 00 00 04 47 d1",answer:"19 03 08 7f 00 01 00 00 2c 00 01 ad cb"},{request:"19 10 00 6b 00 02 04 00 01 00 24 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 24 98 5d"},{request:"19 03 00 a4 00 02 86 30",answer:"19 03 04 6a 48 3d d5 2e f3"},{request:"19 10 00 6b 00 02 04 00 01 00 25 ff ff",answer:"19 10 00 6b 00 02 33 cc"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 25 59 9d"},{request:"19 03 00 96 00 04 a7 fd",answer:"19 03 08 00 00 00 00 00 00 00 00 eb 77"},{request:"19 10 00 d2 00 02 04 00 00 40 80 ff ff",answer:"19 10 00 d2 00 02 e2 29"},{request:"19 10 00 cf 00 02 04 00 01 00 65 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 65 58 6d"},{request:"19 03 00 ca 00 01 a7 ec",answer:"19 03 02 00 00 98 46"},{request:"19 03 00 d2 00 02 67 ea",answer:"19 03 04 00 00 40 80 52 52"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 28 98 58"},{request:"19 10 00 d2 00 02 04 00 00 41 20 ff ff",answer:"19 10 00 d2 00 02 e2 29"},{request:"19 10 00 cf 00 02 04 00 01 00 66 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 66 18 6c"},{request:"19 03 00 d2 00 02 67 ea",answer:"19 03 04 00 00 41 20 53 ba"},{request:"19 03 00 ca 00 01 a7 ec",answer:"19 03 02 80 00 f9 86"},{request:"19 10 00 d4 00 02 04 00 00 40 a0 b0 18",answer:"19 10 00 d4 00 02 02 28"},{request:"19 10 00 cf 00 02 04 00 01 00 67 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 67 d9 ac"},{request:"19 03 00 d4 00 02 87 eb",answer:"19 03 04 00 00 41 20 53 ba"},{request:"19 10 00 d4 00 02 04 70 a4 3f 9d 0a da",answer:"19 10 00 d4 00 02 02 28"},{request:"19 10 00 cf 00 02 04 00 01 00 68 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 68 99 a8"},{request:"19 03 00 d4 00 02 87 eb",answer:"19 03 04 66 66 40 86 2c c7"},{request:"19 10 00 dc 00 02 04 66 66 40 86 ff ff",answer:"19 10 00 dc 00 02 83 ea"},{request:"19 10 00 cf 00 02 04 00 01 00 69 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 69 58 68"},{request:"19 03 00 dc 00 02 06 29",answer:"19 03 04 66 66 40 86 2c c7"},{request:"19 10 00 cf 00 02 04 00 01 00 6a ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 6a 18 69"},{request:"19 10 00 cf 00 02 04 00 01 00 6b ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 6b d9 a9"},{request:"19 10 00 cf 00 02 04 00 01 00 6c ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 6c 98 6b"},{request:"19 10 00 cf 00 02 04 00 01 00 6e ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 6e 19 aa"},{request:"19 10 00 cf 00 02 04 00 01 00 6d ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 6d 59 ab"},{request:"19 10 00 cf 00 02 04 00 01 00 6f ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 6f d8 6a"},{request:"19 10 00 cf 00 02 04 00 01 00 70 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 70 99 a2"},{request:"19 10 00 cf 00 02 04 00 01 00 71 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 71 58 62"},{request:"19 10 00 e4 00 02 04 00 00 41 c8 ff ff",answer:"19 10 00 e4 00 02 02 27"},{request:"19 10 00 cf 00 02 04 00 01 00 72 ff ff",answer:"19 10 00 cf 00 02 72 2f"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 72 18 63"},{request:"19 03 00 e4 00 02 87 e4",answer:"19 03 04 00 00 41 c8 53 f4"},{request:"19 03 00 64 00 01 c6 0d",answer:"19 03 02 00 27 d8 5c"},{request:"19 03 00 ae 00 02 a6 32",answer:"19 03 04 cc e7 40 80 dd 35"},{request:"19 10 00 cf 00 02 04 00 01 00 75 ff ff",answer:"19 10 00 cf 00 02 72