From 523cf7a992462e7f35d58b2d024ab84641377554 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 27 Jan 2022 18:28:43 -0500 Subject: [PATCH 01/85] LED Class Improvements led color enum, parsing to String --- software/PeaPodOS-Arduino/src/LED.cpp | 23 ++++++++++++-- software/PeaPodOS-Arduino/src/LED.h | 8 +++++ subsystems/lighting/software/src/LED.cpp | 39 ------------------------ subsystems/lighting/software/src/LED.h | 21 ------------- 4 files changed, 28 insertions(+), 63 deletions(-) delete mode 100644 subsystems/lighting/software/src/LED.cpp delete mode 100644 subsystems/lighting/software/src/LED.h diff --git a/software/PeaPodOS-Arduino/src/LED.cpp b/software/PeaPodOS-Arduino/src/LED.cpp index 6e249f53..e380a266 100644 --- a/software/PeaPodOS-Arduino/src/LED.cpp +++ b/software/PeaPodOS-Arduino/src/LED.cpp @@ -1,9 +1,26 @@ #include "LED.h" -LED::LED(uint8_t pin) : Actuator("LED", "led0", "led"){ +LED::LED(uint8_t pin, led_t color) : Actuator(parseLED(color), String("CREE" + parseLED(color)), "Lighting"){ this->pin = pin; } +String parseLED(led_t led){ + switch(led){ + case LED_BLUE: + return String("Royal Blue LED"); + case LED_COOL: + return String("Cool White LED"); + case LED_WARM: + return String("Warm White LED"); + case LED_RED: + return String("Photo Red LED"); + case LED_FAR: + return String("Far Red LED"); + default: + return String("Unknown LED"); + } +} + bool LED::init(){ this->target = 0; pinMode(this->pin, OUTPUT); @@ -13,6 +30,6 @@ bool LED::init(){ void LED::update(){ // Clamp to 0target = min(max(target, 0), 1); - Serial.println(this->target*255*LED_BRIGHTNESS_FACTOR); - analogWrite(this->pin, this->target*255*LED_BRIGHTNESS_FACTOR); + Serial.println(this->target*255); + analogWrite(this->pin, this->target*255); } \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/LED.h b/software/PeaPodOS-Arduino/src/LED.h index 528fc407..9c093549 100644 --- a/software/PeaPodOS-Arduino/src/LED.h +++ b/software/PeaPodOS-Arduino/src/LED.h @@ -3,6 +3,14 @@ #include "Actuator.h" +enum led_t { + LED_BLUE, + LED_COOL, + LED_WARM, + LED_RED, + LED_FAR +}; + class LED : public Actuator { public: LED(uint8_t pin); diff --git a/subsystems/lighting/software/src/LED.cpp b/subsystems/lighting/software/src/LED.cpp deleted file mode 100644 index 5cd6fdb8..00000000 --- a/subsystems/lighting/software/src/LED.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "LED.h" - -String parseEnum(t_color color); - -LED::LED(uint8_t pin, t_color color) : Actuator(parseEnum(color), "led-"+String(pin), "lighting"){ - this->pin = pin; -} - -String parseEnum(t_color color){ - switch(color){ - case LED_BLUE: - return "Royal Blue LEDs"; - case LED_COOL: - return "Cool White LEDs"; - case LED_WARM: - return "Warm White LEDs"; - case LED_RED: - return "Photo Red LEDs"; - case LED_FAR: - return "Far Red LEDs"; - default: - return "LEDs"; - } -} - -bool LED::init(){ - this->target = 0; - pinMode(this->pin, OUTPUT); - return true; -} - -void LED::update(){ - // Clamp to 0target = constrain(target, 0, 1); - float power = this->target*255; - Serial.print("Power: "); - Serial.println(power); - analogWrite(this->pin, power); -} \ No newline at end of file diff --git a/subsystems/lighting/software/src/LED.h b/subsystems/lighting/software/src/LED.h deleted file mode 100644 index 8fc38f8c..00000000 --- a/subsystems/lighting/software/src/LED.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef LED_H -#define LED_H - -#include "Actuator.h" - -enum t_color { LED_N, LED_BLUE, LED_COOL, LED_WARM, LED_RED, LED_FAR }; - -class LED : public Actuator { - public: - LED(uint8_t pin, t_color color); - LED(uint8_t pin); - private: - void update() override; - bool init() override; - /** - * PWM output pin. - * */ - uint8_t pin; -}; - -#endif \ No newline at end of file From bc44386b014fdb55530876aa247bd8026c3dedc3 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Mon, 21 Feb 2022 02:12:51 -0500 Subject: [PATCH 02/85] Testing Suite, ICSP Flashing Tests: GPIO miniUART serial port functionality (loopback test), compilation and upload test (blink), revision check test Arduino compile and flash utility function (utils) Readme overhaul Removed deprecated Arduino libraries (scale, nau7802, float) --- software/.gitignore | 13 +- software/.npmignore | 3 +- software/PeaPodOS-Arduino/.gitignore | 2 + .../PeaPodOS-Arduino/PeaPodOS-Arduino.ino | 98 +- software/PeaPodOS-Arduino/src/FloatSensor.cpp | 14 - software/PeaPodOS-Arduino/src/FloatSensor.h | 20 - software/PeaPodOS-Arduino/src/LED.cpp | 3 +- software/PeaPodOS-Arduino/src/LED.h | 7 +- software/PeaPodOS-Arduino/src/NAU7802.cpp | 435 ------- software/PeaPodOS-Arduino/src/NAU7802.h | 224 ---- software/PeaPodOS-Arduino/src/Scale.cpp | 47 - software/PeaPodOS-Arduino/src/Scale.h | 25 - software/README.md | 268 +++- software/index.ts | 104 +- software/lib/PeaPodArduino.ts | 63 +- software/lib/PeaPodSimulator.ts | 27 +- software/lib/errors.ts | 17 +- software/lib/utils.ts | 77 +- software/package-lock.json | 1088 +++++------------ software/package.json | 2 +- software/tests/README.md | 13 + software/tests/blink/blink.ino | 10 + software/tests/revision/revision.ino | 99 ++ software/tests/serialTest.py | 18 + 24 files changed, 924 insertions(+), 1753 deletions(-) create mode 100644 software/PeaPodOS-Arduino/.gitignore delete mode 100644 software/PeaPodOS-Arduino/src/FloatSensor.cpp delete mode 100644 software/PeaPodOS-Arduino/src/FloatSensor.h delete mode 100644 software/PeaPodOS-Arduino/src/NAU7802.cpp delete mode 100644 software/PeaPodOS-Arduino/src/NAU7802.h delete mode 100644 software/PeaPodOS-Arduino/src/Scale.cpp delete mode 100644 software/PeaPodOS-Arduino/src/Scale.h create mode 100644 software/tests/README.md create mode 100644 software/tests/blink/blink.ino create mode 100644 software/tests/revision/revision.ino create mode 100644 software/tests/serialTest.py diff --git a/software/.gitignore b/software/.gitignore index 7f9eeed5..c420b566 100644 --- a/software/.gitignore +++ b/software/.gitignore @@ -2,5 +2,16 @@ node_modules/ *.js *.d.ts + +# Runtime files deviceInfo.json -projects/ \ No newline at end of file +projects/ + +# Logs +logs/** + +# Arduino Binaries +*.elf +*.hex +*.eep +*.bin \ No newline at end of file diff --git a/software/.npmignore b/software/.npmignore index a4925d7f..8aab899c 100644 --- a/software/.npmignore +++ b/software/.npmignore @@ -1,11 +1,12 @@ node_modules/ tsconfig.json +**/build/ *.ts !*.d.ts .git/ -.gitignore +**/.gitignore .gitattributes .npmignore package-lock.json diff --git a/software/PeaPodOS-Arduino/.gitignore b/software/PeaPodOS-Arduino/.gitignore new file mode 100644 index 00000000..e9ab1843 --- /dev/null +++ b/software/PeaPodOS-Arduino/.gitignore @@ -0,0 +1,2 @@ +# Build Files +build/ \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino index c983f94f..31e15f1d 100644 --- a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino +++ b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino @@ -1,8 +1,8 @@ #include "./src/Sensor.h" #include "./src/SHT31.h" -#include "./src/Actuator.h" -#include "./src/LED.h" +// #include "./src/Actuator.h" +// #include "./src/LED.h" // #include "Wire.h" // #include "K30.h" // #include "FloatSensor.h" @@ -10,7 +10,7 @@ //MACRO DEFINITIONS #define NUM_SENSORS 2 -#define NUM_ACTUATORS 1 +// #define NUM_ACTUATORS 0 // #define FLOATSENSOR_PIN 5 #define REVISION 0 @@ -31,19 +31,19 @@ Sensor* sensors [NUM_SENSORS] = { }; //Actuators -LED led_blue(3); -LED led_cool(5); -LED led_warm(6); -LED led_red(9); -LED led_far(10); - -Actuator* actuators [NUM_ACTUATORS] = { - &led_blue, - &led_cool, - &led_warm, - &led_red, - &led_far, -} +// LED led_blue(3); +// LED led_cool(5); +// LED led_warm(6); +// LED led_red(9); +// LED led_far(10); + +// Actuator* actuators [NUM_ACTUATORS] = { +// &led_blue, +// &led_cool, +// &led_warm, +// &led_red, +// &led_far, +// } void setup() @@ -106,26 +106,26 @@ bool handleInstruction(String in){ float value = in.substring(split+1).toFloat(); //INSTRUCTION HANDLING IF BLOCKS - EACH RETURNS TRUE - if(var.equals("led_blue")){ - led_blue.target = value; - return true; - } - if(var.equals("led_cool")){ - led_cool.target = value; - return true; - } - if(var.equals("led_warm")){ - led_warm.target = value; - return true; - } - if(var.equals("led_red")){ - led_red.target = value; - return true; - } - if(var.equals("led_far")){ - led_far.target = value; - return true; - } + // if(var.equals("led_blue")){ + // led_blue.target = value; + // return true; + // } + // if(var.equals("led_cool")){ + // led_cool.target = value; + // return true; + // } + // if(var.equals("led_warm")){ + // led_warm.target = value; + // return true; + // } + // if(var.equals("led_red")){ + // led_red.target = value; + // return true; + // } + // if(var.equals("led_far")){ + // led_far.target = value; + // return true; + // } Serial.print("{\"type\":\"error\",\"data\":\"Unknown instruction target '"); Serial.print(var); Serial.print("'\"}\n"); @@ -194,18 +194,18 @@ bool post(){ } // Test actuator protocols - per-actuator tests - for(int i = 0; i < NUM_ACTUATORS; i++){ - bool latest = actuators[i]->begin(); - success &= latest; - if(!latest){ - Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize actuator '"); - Serial.print(actuators[i]->name); - Serial.print("'. Check wiring.\"}\n"); - } else { - Serial.print("{\"type\":\"debug\",\"data\":\"Actuator '"); - Serial.print(actuators[i]->name); - Serial.print("' initialized successfully.\"}\n"); - } - } + // for(int i = 0; i < NUM_ACTUATORS; i++){ + // bool latest = actuators[i]->begin(); + // success &= latest; + // if(!latest){ + // Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize actuator '"); + // Serial.print(actuators[i]->name); + // Serial.print("'. Check wiring.\"}\n"); + // } else { + // Serial.print("{\"type\":\"debug\",\"data\":\"Actuator '"); + // Serial.print(actuators[i]->name); + // Serial.print("' initialized successfully.\"}\n"); + // } + // } return success; } diff --git a/software/PeaPodOS-Arduino/src/FloatSensor.cpp b/software/PeaPodOS-Arduino/src/FloatSensor.cpp deleted file mode 100644 index d553b9cb..00000000 --- a/software/PeaPodOS-Arduino/src/FloatSensor.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "FloatSensor.h" - -FloatSensor::FloatSensor(uint8_t pin) : Sensor("Float Sensor", "Float Switch", "Reservoir Level", 500){ - this->pin = pin; -} - -bool FloatSensor::init(){ - pinMode(pin, INPUT); - return true; -} - -float FloatSensor::read(){ - return digitalRead(pin); -} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/FloatSensor.h b/software/PeaPodOS-Arduino/src/FloatSensor.h deleted file mode 100644 index e2d4f0b7..00000000 --- a/software/PeaPodOS-Arduino/src/FloatSensor.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef FloatSensor_H -#define FloatSensor_H - -#include "Arduino.h" -#include "Sensor.h" - -class FloatSensor : public Sensor { - public: - FloatSensor(uint8_t pin); - private: - float read() override; - bool init() override; - /** - * Digital input pin. - * */ - uint8_t pin; -}; - - -#endif \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/LED.cpp b/software/PeaPodOS-Arduino/src/LED.cpp index e380a266..77152682 100644 --- a/software/PeaPodOS-Arduino/src/LED.cpp +++ b/software/PeaPodOS-Arduino/src/LED.cpp @@ -1,7 +1,8 @@ #include "LED.h" -LED::LED(uint8_t pin, led_t color) : Actuator(parseLED(color), String("CREE" + parseLED(color)), "Lighting"){ +LED::LED(uint8_t pin, led_color_t color) : Actuator(parseLED(color), String("CREE " + parseLED(color)), "Lighting"){ this->pin = pin; + this->color = color; } String parseLED(led_t led){ diff --git a/software/PeaPodOS-Arduino/src/LED.h b/software/PeaPodOS-Arduino/src/LED.h index 9c093549..fec8ca71 100644 --- a/software/PeaPodOS-Arduino/src/LED.h +++ b/software/PeaPodOS-Arduino/src/LED.h @@ -3,17 +3,17 @@ #include "Actuator.h" -enum led_t { +typedef enum { LED_BLUE, LED_COOL, LED_WARM, LED_RED, LED_FAR -}; +} led_color_t; class LED : public Actuator { public: - LED(uint8_t pin); + LED(uint8_t pin, led_color_t color); private: void update() override; bool init() override; @@ -21,6 +21,7 @@ class LED : public Actuator { * PWM output pin. * */ uint8_t pin; + led_color_t color; }; #endif \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/NAU7802.cpp b/software/PeaPodOS-Arduino/src/NAU7802.cpp deleted file mode 100644 index 39209885..00000000 --- a/software/PeaPodOS-Arduino/src/NAU7802.cpp +++ /dev/null @@ -1,435 +0,0 @@ -/* - This is an Arduino library written for the NAU7802 24-bit wheatstone - bridge and load cell amplifier. - By Nathan Seidle @ SparkFun Electronics, March 3nd, 2019 - - The NAU7802 is an I2C device that converts analog signals to a 24-bit - digital signal. This makes it possible to create your own digital scale - either by hacking an off-the-shelf bathroom scale or by creating your - own scale using a load cell. - - The NAU7802 is a better version of the popular HX711 load cell amplifier. - It uses a true I2C interface so that it can share the bus with other - I2C devices while still taking very accurate 24-bit load cell measurements - up to 320Hz. - - https://github.com/sparkfun/SparkFun_Qwiic_Scale_NAU7802_Arduino_Library - - SparkFun labored with love to create this code. Feel like supporting open - source? Buy a board from SparkFun! - https://www.sparkfun.com/products/15242 -*/ - -#include "NAU7802.h" - -//Constructor -NAU7802::NAU7802(TwoWire *wire) -{ - _i2cPort = wire; -} - -//Sets up the NAU7802 for basic function -//If initialize is true (or not specified), default init and calibration is performed -//If initialize is false, then it's up to the caller to initalize and calibrate -//Returns true upon completion -bool NAU7802::begin(uint8_t addr, bool initialize) -{ - _deviceAddress = addr; - - // Serial.print("Connection attempt 1... "); - if(!isConnected()){ - // Serial.print("Failed.\nConnection attempt 2... "); - if(!isConnected()){ - // Serial.print("Failed. Check wiring."); - return false; - } - } - // Serial.println("Success."); - - bool result = true; //Accumulate a result as we do the setup - - if (initialize) - { - - // Serial.print("Resetting... "); - result &= reset(); //Reset all registers - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - - // Serial.print("Powering up... "); - result &= powerUp(); //Power on analog and digital sections of the scale - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - - // Serial.print("Setting LDO... "); - result &= setLDO(NAU7802_LDO_3V3); //Set LDO to 3.3V - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - - // Serial.print("Setting gain (x4)... "); - result &= setGain(NAU7802_GAIN_4); //Set gain to 4 - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - - // Serial.print("Setting sample rate (20Hz)... "); - result &= setSampleRate(NAU7802_SPS_20); //Set samples per second to 20 - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - - // Serial.print("Turning off CLK_CHP... "); - result &= setRegister(NAU7802_ADC, 0x30); //Turn off CLK_CHP. From 9.1 power on sequencing. - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - - // Serial.print("Enabling channel 2 decoupling cap... "); - result &= setBit(NAU7802_PGA_PWR_PGA_CAP_EN, NAU7802_PGA_PWR); //Enable 330pF decoupling cap on chan 2. From 9.14 application circuit note. - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - - // Serial.print("Recalibrating... "); - result &= calibrateAFE(); //Re-cal analog front end when we change gain, sample rate, or channel - // if(!result){Serial.println("Failed.");}else{Serial.print("\n");} - } - - return (result); -} - -//Returns true if device is present -//Tests for device ack to I2C address -bool NAU7802::isConnected() -{ - _i2cPort->beginTransmission(_deviceAddress); - if (_i2cPort->endTransmission() != 0) - return (false); //Sensor did not ACK - return (true); //All good -} - -//Returns true if Cycle Ready bit is set (conversion is complete) -bool NAU7802::available() -{ - return (getBit(NAU7802_PU_CTRL_CR, NAU7802_PU_CTRL)); -} - -//Calibrate analog front end of system. Returns true if CAL_ERR bit is 0 (no error) -//Takes approximately 344ms to calibrate; wait up to 1000ms. -//It is recommended that the AFE be re-calibrated any time the gain, SPS, or channel number is changed. -bool NAU7802::calibrateAFE() -{ - beginCalibrateAFE(); - return waitForCalibrateAFE(1000); -} - -//Begin asynchronous calibration of the analog front end. -// Poll for completion with calAFEStatus() or wait with waitForCalibrateAFE() -void NAU7802::beginCalibrateAFE() -{ - setBit(NAU7802_CTRL2_CALS, NAU7802_CTRL2); -} - -//Check calibration status. -NAU7802_Cal_Status NAU7802::calAFEStatus() -{ - if (getBit(NAU7802_CTRL2_CALS, NAU7802_CTRL2)) - { - return NAU7802_CAL_IN_PROGRESS; - } - - if (getBit(NAU7802_CTRL2_CAL_ERROR, NAU7802_CTRL2)) - { - return NAU7802_CAL_FAILURE; - } - - // Calibration passed - return NAU7802_CAL_SUCCESS; -} - -//Wait for asynchronous AFE calibration to complete with optional timeout. -//If timeout is not specified (or set to 0), then wait indefinitely. -//Returns true if calibration completes succsfully, otherwise returns false. -bool NAU7802::waitForCalibrateAFE(uint32_t timeout_ms) -{ - uint32_t begin = millis(); - NAU7802_Cal_Status cal_ready; - - while ((cal_ready = calAFEStatus()) == NAU7802_CAL_IN_PROGRESS) - { - if ((timeout_ms > 0) && ((millis() - begin) > timeout_ms)) - { - break; - } - delay(1); - } - - if (cal_ready == NAU7802_CAL_SUCCESS) - { - return (true); - } - return (false); -} - -//Set the readings per second -//10, 20, 40, 80, and 320 samples per second is available -bool NAU7802::setSampleRate(uint8_t rate) -{ - if (rate > 0b111) - rate = 0b111; //Error check - - uint8_t value = getRegister(NAU7802_CTRL2); - value &= 0b10001111; //Clear CRS bits - value |= rate << 4; //Mask in new CRS bits - - return (setRegister(NAU7802_CTRL2, value)); -} - -//Select between 1 and 2 -bool NAU7802::setChannel(uint8_t channelNumber) -{ - if (channelNumber == NAU7802_CHANNEL_1) - return (clearBit(NAU7802_CTRL2_CHS, NAU7802_CTRL2)); //Channel 1 (default) - else - return (setBit(NAU7802_CTRL2_CHS, NAU7802_CTRL2)); //Channel 2 -} - -//Power up digital and analog sections of scale -bool NAU7802::powerUp() -{ - setBit(NAU7802_PU_CTRL_PUD, NAU7802_PU_CTRL); - setBit(NAU7802_PU_CTRL_PUA, NAU7802_PU_CTRL); - - //Wait for Power Up bit to be set - takes approximately 200us - uint8_t counter = 0; - while (1) - { - if (getBit(NAU7802_PU_CTRL_PUR, NAU7802_PU_CTRL) == true) - break; //Good to go - delay(1); - if (counter++ > 100) - return (false); //Error - } - return (true); -} - -//Puts scale into low-power mode -bool NAU7802::powerDown() -{ - clearBit(NAU7802_PU_CTRL_PUD, NAU7802_PU_CTRL); - return (clearBit(NAU7802_PU_CTRL_PUA, NAU7802_PU_CTRL)); -} - -//Resets all registers to Power Of Defaults -bool NAU7802::reset() -{ - setBit(NAU7802_PU_CTRL_RR, NAU7802_PU_CTRL); //Set RR - delay(1); - return (clearBit(NAU7802_PU_CTRL_RR, NAU7802_PU_CTRL)); //Clear RR to leave reset state -} - -//Set the onboard Low-Drop-Out voltage regulator to a given value -//2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.2, 4.5V are available -bool NAU7802::setLDO(uint8_t ldoValue) -{ - if (ldoValue > 0b111) - ldoValue = 0b111; //Error check - - //Set the value of the LDO - uint8_t value = getRegister(NAU7802_CTRL1); - value &= 0b11000111; //Clear LDO bits - value |= ldoValue << 3; //Mask in new LDO bits - setRegister(NAU7802_CTRL1, value); - - return (setBit(NAU7802_PU_CTRL_AVDDS, NAU7802_PU_CTRL)); //Enable the internal LDO -} - -//Set the gain -//x1, 2, 4, 8, 16, 32, 64, 128 are avaialable -bool NAU7802::setGain(uint8_t gainValue) -{ - if (gainValue > 0b111) - gainValue = 0b111; //Error check - - uint8_t value = getRegister(NAU7802_CTRL1); - value &= 0b11111000; //Clear gain bits - value |= gainValue; //Mask in new bits - - return (setRegister(NAU7802_CTRL1, value)); -} - -//Get the revision code of this IC -uint8_t NAU7802::getRevisionCode() -{ - uint8_t revisionCode = getRegister(NAU7802_DEVICE_REV); - return (revisionCode & 0x0F); -} - -//Returns 24-bit reading -//Assumes CR Cycle Ready bit (ADC conversion complete) has been checked to be 1 -int32_t NAU7802::getReading() -{ - _i2cPort->beginTransmission(_deviceAddress); - _i2cPort->write(NAU7802_ADCO_B2); - if (_i2cPort->endTransmission() != 0) - return (false); //Sensor did not ACK - - _i2cPort->requestFrom((uint8_t)_deviceAddress, (uint8_t)3); - - if (_i2cPort->available()) - { - uint32_t valueRaw = (uint32_t)_i2cPort->read() << 16; //MSB - valueRaw |= (uint32_t)_i2cPort->read() << 8; //MidSB - valueRaw |= (uint32_t)_i2cPort->read(); //LSB - - // the raw value coming from the ADC is a 24-bit number, so the sign bit now - // resides on bit 23 (0 is LSB) of the uint32_t container. By shifting the - // value to the left, I move the sign bit to the MSB of the uint32_t container. - // By casting to a signed int32_t container I now have properly recovered - // the sign of the original value - int32_t valueShifted = (int32_t)(valueRaw << 8); - - // shift the number back right to recover its intended magnitude - int32_t value = (valueShifted >> 8); - - return (value); - } - - return (0); //Error -} - -//Return the average of a given number of readings -int32_t NAU7802::getAverage(uint16_t averageAmount) -{ - long total = 0; - uint16_t samplesAquired = 0; - - unsigned long startTime = millis(); - while (1) - { - if (available() == true) - { - total += getReading(); - if (++samplesAquired == averageAmount) - break; //All done - } - delay(1); - } - total /= averageAmount; - - return (total); -} - -//Call when scale is setup, level, at running temperature, with nothing on it -void NAU7802::calculateZeroOffset(uint16_t averageAmount) -{ - setZeroOffset(getAverage(averageAmount)); -} - -//Sets the internal variable. Useful for users who are loading values from NVM. -void NAU7802::setZeroOffset(int32_t newZeroOffset) -{ - _zeroOffset = newZeroOffset; - Serial.print("{\"type\":\"debug\",\"msg\":\"New zero: "); - Serial.print(String(_zeroOffset)); - Serial.println("\"}"); -} - -int32_t NAU7802::getZeroOffset() -{ - return (_zeroOffset); -} - -//Call after zeroing. Provide the float weight sitting on scale. Units do not matter. -void NAU7802::calculateCalibrationFactor(float weightOnScale, uint16_t averageAmount) -{ - int32_t onScale = getAverage(averageAmount); - float newCalFactor = (onScale - _zeroOffset) / (float)weightOnScale; - setCalibrationFactor(newCalFactor); -} - -//Pass a known calibration factor into library. Helpful if users is loading settings from NVM. -//If you don't know your cal factor, call setZeroOffset(), then calculateCalibrationFactor() with a known weight -void NAU7802::setCalibrationFactor(float newCalFactor) -{ - Serial.print("{\"type\":\"debug\",\"msg\":\"New calibration factor: "); - Serial.print(String(newCalFactor)); - Serial.println("\"}"); - _calibrationFactor = newCalFactor; -} - -float NAU7802::getCalibrationFactor() -{ - return (_calibrationFactor); -} - -//Returns the y of y = mx + b using the current weight on scale, the cal factor, and the offset. -float NAU7802::getWeight(bool allowNegativeWeights, uint16_t samplesToTake) -{ - int32_t onScale = getAverage(samplesToTake); - - //Prevent the current reading from being less than zero offset - //This happens when the scale is zero'd, unloaded, and the load cell reports a value slightly less than zero value - //causing the weight to be negative or jump to millions of pounds - if (allowNegativeWeights == false) - { - if (onScale < _zeroOffset) - onScale = _zeroOffset; //Force reading to zero - } - - float weight = (onScale - _zeroOffset) / _calibrationFactor; - return (weight); -} - -//Set Int pin to be high when data is ready (default) -bool NAU7802::setIntPolarityHigh() -{ - return (clearBit(NAU7802_CTRL1_CRP, NAU7802_CTRL1)); //0 = CRDY pin is high active (ready when 1) -} - -//Set Int pin to be low when data is ready -bool NAU7802::setIntPolarityLow() -{ - return (setBit(NAU7802_CTRL1_CRP, NAU7802_CTRL1)); //1 = CRDY pin is low active (ready when 0) -} - -//Mask & set a given bit within a register -bool NAU7802::setBit(uint8_t bitNumber, uint8_t registerAddress) -{ - uint8_t value = getRegister(registerAddress); - value |= (1 << bitNumber); //Set this bit - return (setRegister(registerAddress, value)); -} - -//Mask & clear a given bit within a register -bool NAU7802::clearBit(uint8_t bitNumber, uint8_t registerAddress) -{ - uint8_t value = getRegister(registerAddress); - value &= ~(1 << bitNumber); //Set this bit - return (setRegister(registerAddress, value)); -} - -//Return a given bit within a register -bool NAU7802::getBit(uint8_t bitNumber, uint8_t registerAddress) -{ - uint8_t value = getRegister(registerAddress); - value &= (1 << bitNumber); //Clear all but this bit - return (value); -} - -//Get contents of a register -uint8_t NAU7802::getRegister(uint8_t registerAddress) -{ - _i2cPort->beginTransmission(_deviceAddress); - _i2cPort->write(registerAddress); - if (_i2cPort->endTransmission() != 0) - return (-1); //Sensor did not ACK - - _i2cPort->requestFrom((uint8_t)_deviceAddress, (uint8_t)1); - - if (_i2cPort->available()) - return (_i2cPort->read()); - - return (-1); //Error -} - -//Send a given value to be written to given address -//Return true if successful -bool NAU7802::setRegister(uint8_t registerAddress, uint8_t value) -{ - _i2cPort->beginTransmission(_deviceAddress); - _i2cPort->write(registerAddress); - _i2cPort->write(value); - if (_i2cPort->endTransmission() != 0) - return (false); //Sensor did not ACK - return (true); -} diff --git a/software/PeaPodOS-Arduino/src/NAU7802.h b/software/PeaPodOS-Arduino/src/NAU7802.h deleted file mode 100644 index a4399f0e..00000000 --- a/software/PeaPodOS-Arduino/src/NAU7802.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - This is an Arduino library written for the NAU7802 24-bit wheatstone - bridge and load cell amplifier. - By Nathan Seidle @ SparkFun Electronics, March 3nd, 2019 - - The NAU7802 is an I2C device that converts analog signals to a 24-bit - digital signal. This makes it possible to create your own digital scale - either by hacking an off-the-shelf bathroom scale or by creating your - own scale using a load cell. - - The NAU7802 is a better version of the popular HX711 load cell amplifier. - It uses a true I2C interface so that it can share the bus with other - I2C devices while still taking very accurate 24-bit load cell measurements - up to 320Hz. - - https://github.com/sparkfun/SparkFun_NAU7802_Scale_Arduino_Library - - SparkFun labored with love to create this code. Feel like supporting open - source? Buy a board from SparkFun! - https://www.sparkfun.com/products/15242 -*/ - -#ifndef NAU7802_h -#define NAU7802_h - -#include "Arduino.h" -#include "Wire.h" - -#define ACK_TRIES 2 - -//Register Map -typedef enum -{ - NAU7802_PU_CTRL = 0x00, - NAU7802_CTRL1, - NAU7802_CTRL2, - NAU7802_OCAL1_B2, - NAU7802_OCAL1_B1, - NAU7802_OCAL1_B0, - NAU7802_GCAL1_B3, - NAU7802_GCAL1_B2, - NAU7802_GCAL1_B1, - NAU7802_GCAL1_B0, - NAU7802_OCAL2_B2, - NAU7802_OCAL2_B1, - NAU7802_OCAL2_B0, - NAU7802_GCAL2_B3, - NAU7802_GCAL2_B2, - NAU7802_GCAL2_B1, - NAU7802_GCAL2_B0, - NAU7802_I2C_CONTROL, - NAU7802_ADCO_B2, - NAU7802_ADCO_B1, - NAU7802_ADCO_B0, - NAU7802_ADC = 0x15, //Shared ADC and OTP 32:24 - NAU7802_OTP_B1, //OTP 23:16 or 7:0? - NAU7802_OTP_B0, //OTP 15:8 - NAU7802_PGA = 0x1B, - NAU7802_PGA_PWR = 0x1C, - NAU7802_DEVICE_REV = 0x1F, -} Scale_Registers; - -//Bits within the PU_CTRL register -typedef enum -{ - NAU7802_PU_CTRL_RR = 0, - NAU7802_PU_CTRL_PUD, - NAU7802_PU_CTRL_PUA, - NAU7802_PU_CTRL_PUR, - NAU7802_PU_CTRL_CS, - NAU7802_PU_CTRL_CR, - NAU7802_PU_CTRL_OSCS, - NAU7802_PU_CTRL_AVDDS, -} PU_CTRL_Bits; - -//Bits within the CTRL1 register -typedef enum -{ - NAU7802_CTRL1_GAIN = 2, - NAU7802_CTRL1_VLDO = 5, - NAU7802_CTRL1_DRDY_SEL = 6, - NAU7802_CTRL1_CRP = 7, -} CTRL1_Bits; - -//Bits within the CTRL2 register -typedef enum -{ - NAU7802_CTRL2_CALMOD = 0, - NAU7802_CTRL2_CALS = 2, - NAU7802_CTRL2_CAL_ERROR = 3, - NAU7802_CTRL2_CRS = 4, - NAU7802_CTRL2_CHS = 7, -} CTRL2_Bits; - -//Bits within the PGA register -typedef enum -{ - NAU7802_PGA_CHP_DIS = 0, - NAU7802_PGA_INV = 3, - NAU7802_PGA_BYPASS_EN, - NAU7802_PGA_OUT_EN, - NAU7802_PGA_LDOMODE, - NAU7802_PGA_RD_OTP_SEL, -} PGA_Bits; - -//Bits within the PGA PWR register -typedef enum -{ - NAU7802_PGA_PWR_PGA_CURR = 0, - NAU7802_PGA_PWR_ADC_CURR = 2, - NAU7802_PGA_PWR_MSTR_BIAS_CURR = 4, - NAU7802_PGA_PWR_PGA_CAP_EN = 7, -} PGA_PWR_Bits; - -//Allowed Low drop out regulator voltages -typedef enum -{ - NAU7802_LDO_2V4 = 0b111, - NAU7802_LDO_2V7 = 0b110, - NAU7802_LDO_3V0 = 0b101, - NAU7802_LDO_3V3 = 0b100, - NAU7802_LDO_3V6 = 0b011, - NAU7802_LDO_3V9 = 0b010, - NAU7802_LDO_4V2 = 0b001, - NAU7802_LDO_4V5 = 0b000, -} NAU7802_LDO_Values; - -//Allowed gains -typedef enum -{ - NAU7802_GAIN_128 = 0b111, - NAU7802_GAIN_64 = 0b110, - NAU7802_GAIN_32 = 0b101, - NAU7802_GAIN_16 = 0b100, - NAU7802_GAIN_8 = 0b011, - NAU7802_GAIN_4 = 0b010, - NAU7802_GAIN_2 = 0b001, - NAU7802_GAIN_1 = 0b000, -} NAU7802_Gain_Values; - -//Allowed samples per second -typedef enum -{ - NAU7802_SPS_320 = 0b111, - NAU7802_SPS_80 = 0b011, - NAU7802_SPS_40 = 0b010, - NAU7802_SPS_20 = 0b001, - NAU7802_SPS_10 = 0b000, -} NAU7802_SPS_Values; - -//Select between channel values -typedef enum -{ - NAU7802_CHANNEL_1 = 0, - NAU7802_CHANNEL_2 = 1, -} NAU7802_Channels; - -//Calibration state -typedef enum -{ - NAU7802_CAL_SUCCESS = 0, - NAU7802_CAL_IN_PROGRESS = 1, - NAU7802_CAL_FAILURE = 2, -} NAU7802_Cal_Status; - -extern TwoWire Wire; - -class NAU7802 -{ -public: - NAU7802(TwoWire *wire = &Wire); //Default constructor - bool begin(uint8_t addr = 0x2A, bool initialize = true); //Check communication and initialize sensor - bool isConnected(); //Returns true if device acks at the I2C address - - bool available(); //Returns true if Cycle Ready bit is set (conversion is complete) - int32_t getReading(); //Returns 24-bit reading. Assumes CR Cycle Ready bit (ADC conversion complete) has been checked by .available() - int32_t getAverage(uint16_t samplesToTake); //Return the average of a given number of readings - - void calculateZeroOffset(uint16_t averageAmount = 8); //Also called taring. Call this with nothing on the scale - void setZeroOffset(int32_t newZeroOffset); //Sets the internal variable. Useful for users who are loading values from NVM. - int32_t getZeroOffset(); //Ask library for this value. Useful for storing value into NVM. - - void calculateCalibrationFactor(float weightOnScale, uint16_t averageAmount = 8); //Call this with the value of the thing on the scale. Sets the calibration factor based on the weight on scale and zero offset. - void setCalibrationFactor(float calFactor); //Pass a known calibration factor into library. Helpful if users is loading settings from NVM. - float getCalibrationFactor(); //Ask library for this value. Useful for storing value into NVM. - - float getWeight(bool allowNegativeWeights = false, uint16_t samplesToTake = 8); //Once you've set zero offset and cal factor, you can ask the library to do the calculations for you. - - bool setGain(uint8_t gainValue); //Set the gain. x1, 2, 4, 8, 16, 32, 64, 128 are available - bool setLDO(uint8_t ldoValue); //Set the onboard Low-Drop-Out voltage regulator to a given value. 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.2, 4.5V are avaialable - bool setSampleRate(uint8_t rate); //Set the readings per second. 10, 20, 40, 80, and 320 samples per second is available - bool setChannel(uint8_t channelNumber); //Select between 1 and 2 - - bool calibrateAFE(); //Synchronous calibration of the analog front end of the NAU7802. Returns true if CAL_ERR bit is 0 (no error) - void beginCalibrateAFE(); //Begin asynchronous calibration of the analog front end of the NAU7802. Poll for completion with calAFEStatus() or wait with waitForCalibrateAFE(). - bool waitForCalibrateAFE(uint32_t timeout_ms = 0); //Wait for asynchronous AFE calibration to complete with optional timeout. - NAU7802_Cal_Status calAFEStatus(); //Check calibration status. - - bool reset(); //Resets all registers to Power Of Defaults - - bool powerUp(); //Power up digital and analog sections of scale, ~2mA - bool powerDown(); //Puts scale into low-power 200nA mode - - bool setIntPolarityHigh(); //Set Int pin to be high when data is ready (default) - bool setIntPolarityLow(); //Set Int pin to be low when data is ready - - uint8_t getRevisionCode(); //Get the revision code of this IC. Always 0x0F. - - bool setBit(uint8_t bitNumber, uint8_t registerAddress); //Mask & set a given bit within a register - bool clearBit(uint8_t bitNumber, uint8_t registerAddress); //Mask & clear a given bit within a register - bool getBit(uint8_t bitNumber, uint8_t registerAddress); //Return a given bit within a register - - uint8_t getRegister(uint8_t registerAddress); //Get contents of a register - bool setRegister(uint8_t registerAddress, uint8_t value); //Send a given value to be written to given address. Return true if successful - -private: - TwoWire *_i2cPort; //This stores the user's requested i2c port - uint8_t _deviceAddress; //Default unshifted 7-bit address of the NAU7802 - - //y = mx+b, default to NO CHANGE - int32_t _zeroOffset = 0; //This is b - float _calibrationFactor = 1.0; //This is m. User provides this number so that we can output y when requested -}; -#endif diff --git a/software/PeaPodOS-Arduino/src/Scale.cpp b/software/PeaPodOS-Arduino/src/Scale.cpp deleted file mode 100644 index 3ee49da6..00000000 --- a/software/PeaPodOS-Arduino/src/Scale.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "Scale.h" -#include "NAU7802.h" - -Scale::Scale() : Sensor("Scale", "NAU7802", "mass", 3){ - //Look up the calibration factor in EEPROM? -} - -bool Scale::init(){ - return (nau7802.begin()); -} - -//if readings < avg readings: total += reading (checks available), readings++, return NAN -//else calculate average reading, reset readings and total, return avg converted to weight - -float Scale::read(){ - - if(!nau7802.available()){ - return NAN; - // Serial.println("NAU7802 not available!"); - } - - //Non-intrusive average - if(i < AVG_READINGS){ - total += nau7802.getReading(); - i++; - return NAN; - } - - double result = (total/AVG_READINGS-nau7802.getZeroOffset())/(nau7802.getCalibrationFactor()); - restart(); - - return (float)result; -} - -void Scale::calibrate(float grams){ - if(grams == 0.0){ - nau7802.calculateZeroOffset(CALIBRATION_READINGS); - } else { - nau7802.calculateCalibrationFactor(grams, CALIBRATION_READINGS); - } - restart(); -} - -void Scale::restart(){ - i = 0; - total = 0; -} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/Scale.h b/software/PeaPodOS-Arduino/src/Scale.h deleted file mode 100644 index cffbf7be..00000000 --- a/software/PeaPodOS-Arduino/src/Scale.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef Scale_H -#define Scale_H - -#include "Arduino.h" -#include "NAU7802.h" -#include "Sensor.h" - -#define AVG_READINGS 320 -#define CALIBRATION_READINGS 80 - -class Scale : public Sensor { - public: - Scale(); - void calibrate(float grams); //Calibrates the scale using a known load (or 0 to tare) - void restart(); - private: - bool init() override; - float read() override; - NAU7802 nau7802 = NAU7802(); //Default constructed NAU7802 object - uint16_t i = 0; - int32_t total = 0; - // float readings[AVG_READINGS]; -}; - -#endif \ No newline at end of file diff --git a/software/README.md b/software/README.md index edef1ebf..1dc08cb8 100644 --- a/software/README.md +++ b/software/README.md @@ -11,9 +11,12 @@ Designed as both a hassle-free food production system and a research tool for pr *** ### Table of Contents - [Background](#background) -- [Development](#development) - [Production](#production) - [Setting Up Raspberry Pi](#setting-up-raspberry-pi) +- [Development](#development) + - [Arduino CLI - Compilation and Upload](#arduino-cli---compilation-and-upload) + - [Raspberry Pi Serial Testing](#raspberry-pi-serial-testing) + - [Build from Source](#build-from-source) *** # Background @@ -22,10 +25,6 @@ Designed as both a hassle-free food production system and a research tool for pr *** -# Development - - - # Production ### Setting Up Raspberry Pi @@ -36,7 +35,7 @@ The following are performed on a computer: 2. Download the Raspberry Pi Imager [(Download)](https://www.raspberrypi.com/software/). 3. Flash the SD card with a *Raspberry Pi OS Lite* image. -> Note: In Future, a custom PeaPod image will be released with steps 4-6 already complete. +> Note: In Future, a custom PeaPod image will be released with steps 5, 6, and 10 already complete. 4. Plug in a keyboard and display, insert the microSD card, and power the Raspberry Pi device. @@ -44,23 +43,252 @@ The following are performed on the Raspberry Pi: 5. Login with the default username (`pi`) and password (`raspberry`). 6. Execute `sudo raspi-config` and perform these steps to setup the Pi: - 1. *System Options > Wireless Lan* - Setup WiFi + 1. *System Options > Wireless Lan* - Setup WiFi and connect to the Internet 2. *System Options > Hostname* - Set a unique host name (`e.g. john-peapod`) 3. *System Options > Boot/Auto Login* - Select `Console Autologin` - 4. Disable UART console - 5. *Interface Options > Camera* - Enable Camera + 4. *Interface Options > SSH* - `Enable` SSH Server + 5. *Interface Options > Serial Port* - `Disable` serial login shell, but `Enable` the serial port hardware + 6. *Localisation Options > Locale* (i.e. `en_US.UTF-8`) + 7. *Localisation Options > Timezone* (i.e. `US` > `Eastern`) + 8. *Localisation Options > Keyboard* (i.e. `Generic 105-key` > `English (US)` > `Default` > `No compose key`) + 9. Optional: *Advanced Options > Expand Filesystem* + 10. Reboot to save: `sudo reboot` + +> You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@peapod.local`, or with VS Code) + +> Note: In Future, steps 7-9 will be automated at startup. +7. Update Packages: + 1. Update package listings, upgrade existing packages: `sudo apt-get update && sudo apt-get upgrade -y` + 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude` (could take a while) + 3. Install main software package: `sudo npm i -g @peapodtech/peapodos --save` + +9. Install and configure the Arduino CLI: + 1. Install with `curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh`. Successful output ends with something like: + ``` + arduino-cli Version: 0.21.0 Commit: 10107d24 Date: 2022-02-08T15:05:43Z installed successfully in /home/pi/bin + ``` + 2. Open `nano ~/.bashrc` and add this line to the bottom: `export PATH=$PATH:/home/pi/bin` (*Ctrl-O* to save, *Ctrl-X* to exit) + 3. Reboot to reset the path: `sudo reboot` + 4. Perform initial configuration: `arduino-cli config init` + 5. Update the list of cores: `arduino-cli core update-index` + 6. Install the AVR cores (incl. Arduino Nano/ATMega328P): `arduino-cli core install arduino:avr` + +10. Create a custom configuration file for the AVR flash utility *avrdude* to be able to program the Arduino Nano via ICSP over the Raspberry Pi's GPIO pins: + 3. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/avrdude_gpio.conf`, then modify your copy with `nano ~/avrdude_gpio.conf`. Copy the following to the end of the file: + + ``` + # Raspberry Pi GPIO configuration for avrdude. + programmer + id = "peapod"; + desc = "Use the Linux sysfs interface to bitbang GPIO lines"; + type = "linuxgpio"; + reset = 5; + sck = 6; + mosi = 12; + miso = 13; + ; + ``` + (*Ctrl-O* to save, *Ctrl-X* to exit) + 4. Verify the configuration and connection to the Arduino with `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`. A successful output should look something like: + ``` + avrdude: Version 6.3-20171130 + Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ + Copyright (c) 2007-2014 Joerg Wunsch + + System wide configuration file is "/home/pi/avrdude_gpio.conf" + User configuration file is "/root/.avrduderc" + User configuration file does not exist or is not a regular file, skipping + + Using Port : unknown + Using Programmer : peapod + AVR Part : ATmega328P + Chip Erase delay : 9000 us + PAGEL : PD7 + BS2 : PC2 + RESET disposition : dedicated + RETRY pulse : SCK + serial program mode : yes + parallel program mode : yes + Timeout : 200 + StabDelay : 100 + CmdexeDelay : 25 + SyncLoops : 32 + ByteDelay : 0 + PollIndex : 3 + PollValue : 0x53 + Memory Detail : + + Block Poll Page Polled + Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack + ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- + eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff + flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff + lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 + signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 + + Programmer Type : linuxgpio + Description : Use the Linux sysfs interface to bitbang GPIO lines + Pin assignment : /sys/class/gpio/gpio{n} + RESET = 8 + SCK = 11 + MOSI = 10 + MISO = 9 + + avrdude: AVR device initialized and ready to accept instructions + + Reading | ################################################## | 100% 0.00s + + avrdude: Device signature = 0x1e950f (probably m328p) + avrdude: safemode: lfuse reads as FF + avrdude: safemode: hfuse reads as DA + avrdude: safemode: efuse reads as FD + + avrdude: safemode: lfuse reads as FF + avrdude: safemode: hfuse reads as DA + avrdude: safemode: efuse reads as FD + avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF) + + avrdude done. Thank you. + ``` + +11. Edit the `sudoers` file to allow `avrdude` to be executed using `sudo` *without a password*: + 1. Open the `sudoers` file: `sudo visudo` + 2. Add the following line to the end (assuming your username is `pi`, the hostname is `peapod`, and the `avrdude` binary is located at `/usr/bin/avrdude`): + + `pi peapod = (root) NOPASSWD: /usr/bin/avrdude` + + (*Ctrl-O* to save, *Ctrl-X* to exit; *avrdude* can be located with `whereis avrdude`) + +12. Import `.env` file with Firebase and auth keys (TBA), as well as the fields `SERIALPORT="/dev/ttyS0"` (Raspberry Pi Zero 2 W GPIO mini-UART) and `PASSWORD="raspberry"` with "raspberry" replaced with your password (used for `sudo` execution of *avrdude*). + +13. Run the main program by executing `peapodos`. + + + +# Development + +### Arduino CLI - Compilation and Upload + +To manually compile and upload the test sketch `blink` to an Arduino Nano (ATMega328P aka `m328p`): +1. `arduino-cli compile -b arduino:avr:nano blink -e` + + (`-b`: board FQBN, `-e`: export binary to sketch folder) + +2. `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v -U flash:w:blink/build/arduino.avr.nano/blink.ino.hex:i` + + (`-p`: microprocessor ID, `-C`: custom configuration file path, `-c`: programmer ID, `-v`: verify sketch after upload, `-U`: upload options and path to binary) + + Successful output should look like this: + + ``` + avrdude: Version 6.3-20171130 + Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ + Copyright (c) 2007-2014 Joerg Wunsch + + System wide configuration file is "/home/pi/avrdude_gpio.conf" + User configuration file is "/root/.avrduderc" + User configuration file does not exist or is not a regular file, skipping + + Using Port : unknown + Using Programmer : peapod + AVR Part : ATmega328P + Chip Erase delay : 9000 us + PAGEL : PD7 + BS2 : PC2 + RESET disposition : dedicated + RETRY pulse : SCK + serial program mode : yes + parallel program mode : yes + Timeout : 200 + StabDelay : 100 + CmdexeDelay : 25 + SyncLoops : 32 + ByteDelay : 0 + PollIndex : 3 + PollValue : 0x53 + Memory Detail : + + Block Poll Page Polled + Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack + ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- + eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff + flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff + lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 + calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 + signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 + + Programmer Type : linuxgpio + Description : Use the Linux sysfs interface to bitbang GPIO lines + Pin assignment : /sys/class/gpio/gpio{n} + RESET = 8 + SCK = 11 + MOSI = 10 + MISO = 9 + + avrdude: AVR device initialized and ready to accept instructions + + Reading | ################################################## | 100% 0.00s + + avrdude: Device signature = 0x1e950f (probably m328p) + avrdude: safemode: lfuse reads as FF + avrdude: safemode: hfuse reads as DA + avrdude: safemode: efuse reads as FD + avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed + To disable this feature, specify the -D option. + avrdude: erasing chip + avrdude: reading input file "blink/build/arduino.avr.nano/blink.ino.hex" + avrdude: writing flash (922 bytes): + + Writing | ################################################## | 100% 0.31s + + avrdude: 922 bytes of flash written + avrdude: verifying flash memory against blink/build/arduino.avr.nano/blink.ino.hex: + avrdude: load data flash data from input file blink/build/arduino.avr.nano/blink.ino.hex: + avrdude: input file blink/build/arduino.avr.nano/blink.ino.hex contains 922 bytes + avrdude: reading on-chip flash data: + + Reading | ################################################## | 100% 0.28s + + avrdude: verifying ... + avrdude: 922 bytes of flash verified + + avrdude: safemode: lfuse reads as FF + avrdude: safemode: hfuse reads as DA + avrdude: safemode: efuse reads as FD + avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF) + + avrdude done. Thank you. + ``` + + +### Raspberry Pi Serial Testing + +To test the GPIO serial port (`/dev/ttyS0`), connect GPIO pins 8 and 10 (TX and RX) and execute the following: +1. Install the Python package manager *pip*: `sudo apt-get install pip` +2. Install the Serial package *pyserial*: `pip install pyserial` +3. Execute the test script: `python ~/PeaPod/software/tests/serialTest.py` -7. Set up localization: - 1. Locale - 2. Timezone - 3. Keyboard +Correct output should look like: -> Note: In Future, step 8 will be automated at startup. -8. Update Packages: - 1. `sudo apt-get update` - 2. `sudo apt-get install nodejs npm` - 3. `sudo npm i -g @peapodtech/peapodos --save` +``` +Serial port '/dev/ttyS0' ready for test: +Sent 20 bytes: 'Testing Raspberry Pi GPIO serial port ...' +Received 20 bytes: 'Testing Raspberry Pi GPIO serial port ...' +Port '/dev/ttyS0' is is setup correctly! +``` -9. Import `.env` file with Firebase and auth keys (TBA), as well as the field `SERIALPORT="/dev/ttyS0"`. This is the GPIO mini-UART on the Raspberry Pi Zero 2 W. +### Build from Source -11. Run the program by executing `peapodos`. \ No newline at end of file +1. Install TypeScript language support and compiler, as well as a Node build tool: `sudo npm install -g typescript` +2. Clone source +3. Navigate to `software/` folder +4. Build the `serialport` package from source: `sudo npm install serialport --unsafe-perm --build-from-source` +5. Install all other Node dependencies: `npm i` +6. Compile the source with `tsc` and execute with `node .` \ No newline at end of file diff --git a/software/index.ts b/software/index.ts index b3057b5f..a7384cc6 100644 --- a/software/index.ts +++ b/software/index.ts @@ -8,10 +8,10 @@ import PiCamera from 'pi-camera'; import PeaPodArduinoInterface, { IPeaPodArduino } from './lib/PeaPodArduino'; import PeaPodPubSub, { IoTConfig, IPeaPodPublisher, PeaPodDataBatch } from './lib/PeaPodPublisher'; import { ArduinoSimulator, PeaPodLogger } from './lib/PeaPodSimulator'; +import PeaPodCamera, { IPeaPodCamera } from './lib/PeaPodCamera'; import { checkInternet, sleep, loadDotenv, findSerialPath } from './lib/utils'; //Utilities import Spinner from './lib/ui'; //UI utils -import PeaPodCamera, { IPeaPodCamera } from './lib/PeaPodCamera'; // Global State @@ -74,9 +74,9 @@ function main(): Promise { Spinner.info(`Running in ${chalk.bold(simulated ? 'Simulated' : 'Live')} mode with ${chalk.bold(offline ? 'Local Filesystem' : 'Google Cloud')} publishing.`); - return new Promise(async (res, rej)=>{ + return new Promise(async (res, rej) => { - if(simulated){ + if (simulated) { arduino = new ArduinoSimulator({ air_temperature: { min: 10, @@ -93,7 +93,7 @@ function main(): Promise { camera = null; } else { let serialpath; - if(process.env.SERIALPORT){ + if (process.env.SERIALPORT) { Spinner.info('Using serial port: '+process.env.SERIALPORT); serialpath = process.env.SERIALPORT; } else { @@ -110,7 +110,7 @@ function main(): Promise { } else { // Check Internet connection Spinner.start(`Checking for ${chalk.blue('Internet')} connection...'`); - if(!(await checkInternet())){ + if (!(await checkInternet())) { Spinner.fail(`Could not connect to the ${chalk.blue('Internet')}! Running in ${chalk.bold('Offline')} mode.`); publisher = new PeaPodLogger(); } else { @@ -127,52 +127,54 @@ function main(): Promise { let batch: PeaPodDataBatch = {}; let batchInterval: NodeJS.Timer; - // Initialize publisher (ready for Arduino), fetch initial instructions - publisher.start(config=>{ - console.log("[CONFIG] - "+config); - // TODO: Respond to config (update instructions) - }, command=>{ - console.log("[COMMAND] - "+command); - // TODO: Respond to commands (immediate actions) - if(command.type == 'livestreamoffer'){ - - // Assumes webcam hardware has been initialized - - // 1. Create answer from command data - // 2. Call cloud function with answer - // 3. Enable video feed - - // camera?.stream().pipe(); - - /** - * { - * type: 'livestreamoffer', - * data: {...} - * } - */ - + // Initialize Arduino communications interface first + arduino.start((msg) => { + if(msg.type == 'data') { + // Initialize batch array + if (batch[msg.data.label] === undefined) batch[msg.data.label] = {batch: []}; + // Accumulate data into batches + batch[msg.data.label].batch.push({ + timestamp: Date.now(), + value: msg.data.value + }) + + // TODO: Plan, act + } else { + Spinner.info(`[${chalk.blueBright('ARDUINO')} | ${msg.type.toUpperCase()}] - ${JSON.stringify(msg.data)}`) } - }).then(({projectid, projectname, run})=>{ - // Get program + // TODO: publish other message types + }).then(() => { + // Initialize publisher, fetch initial instructions + publisher.start(config => { + console.log("[CONFIG] - "+config); + // TODO: Respond to config (update instructions) + }, command => { + console.log("[COMMAND] - "+command); + // TODO: Respond to commands (immediate actions) + if (command.type == 'livestreamoffer') { + + // Assumes webcam hardware has been initialized + + // 1. Create answer from command data + // 2. Call cloud function with answer + // 3. Enable video feed + + // camera?.stream().pipe(); + + /** + * { + * type: 'livestreamoffer', + * data: {...} + * } + */ - // Initialize Arduino communications interface - arduino.start((msg)=>{ - if(msg.type == 'data') { - // Initialize batch array - if(batch[msg.data.label] === undefined) batch[msg.data.label] = {batch: []}; - batch[msg.data.label].batch.push({ - timestamp: Date.now(), - value: msg.data.value - }) - - // TODO: Plan, act - } else { - Spinner.info(`[${chalk.blueBright('ARDUINO')} | ${msg.type.toUpperCase()}] - ${JSON.stringify(msg.data)}`) } - // TODO: publish other message types - }).then(()=>{ + }).then(({projectid, projectname, run}) => { + // Get program + + Spinner.info(`${chalk.green('PeaPod')} start - Project ${chalk.bold(projectname ?? projectid)}, Run ${chalk.bold(run)}`); - batchInterval = setInterval(()=>{ + batchInterval = setInterval(() => { // Publish entire batch try{ publisher.publish({ @@ -189,17 +191,17 @@ function main(): Promise { return; } - console.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${Object.values(batch).reduce((sum, entry)=>{return sum+entry.batch.length}, 0)} datapoints published.`); + console.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${Object.values(batch).reduce((sum, entry) => { return sum+entry.batch.length }, 0)} datapoints published.`); // Reset batch to empty batch = {}; }, batchPublishInterval*1000); - }).catch(e=>{rej(e)}); - }).catch(e=>{rej(e)}); + }).catch(e => { rej(e) }); + }).catch(e => { rej(e) }); }); } -main().catch((err: Error)=>{ +main().catch((err: Error) => { Spinner.fail(err.message); process.exit(1); }); \ No newline at end of file diff --git a/software/lib/PeaPodArduino.ts b/software/lib/PeaPodArduino.ts index 2b7bd1e5..48d55de9 100644 --- a/software/lib/PeaPodArduino.ts +++ b/software/lib/PeaPodArduino.ts @@ -1,11 +1,13 @@ import { PeaPodMessage } from './PeaPodPublisher'; -import SerialPort from 'serialport'; -import { ArduinoInstructionsError, RevisionError } from './errors'; +import { SerialPort, ReadlineParser } from 'serialport'; +import { ArduinoInstructionsError, SerialTimeoutError } from './errors'; import Spinner from './ui'; +import { updateArduino } from './utils'; import chalk from 'chalk'; const BAUDRATE = 115200; -const ARDUINO_REVISION = "0"; +const ARDUINO_REVISION = "1"; +const TIMEOUT_SECONDS = 5; /** * Abstract base type for any PeaPod message source. @@ -43,39 +45,57 @@ type ArduinoInstructions = { /** * Interface between this computer and the Arduino. */ -export default class PeaPodArduinoInterface implements IPeaPodArduino{ +export default class PeaPodArduinoInterface implements IPeaPodArduino { serial : SerialPort; - parser : SerialPort.parsers.Readline; - constructor(readonly serialpath: string, private initialInstructions: ArduinoInstructions = {}){ - // Open the serial port - this.serial = new SerialPort(serialpath, { + parser : ReadlineParser; + constructor(readonly serialpath: string, private initialInstructions: ArduinoInstructions = {}) { + // Create the serial port interface + this.serial = new SerialPort({ + path: serialpath, baudRate: BAUDRATE, autoOpen: false }); // Create the newline parser - this.parser = this.serial.pipe(new SerialPort.parsers.Readline({ + this.parser = this.serial.pipe(new ReadlineParser({ delimiter: '\n', includeDelimiter: false })); } async start(onMessage : (msg : ArduinoMessage) => void): Promise { - this.serial.open(err=>{if(err){throw err};}); + // Open the serial port + this.serial.open(err => { if(err){ throw err }; }); + + // Reusable timeout, enabled at serial open (fails if no messages recieved after starting) + let timeout: NodeJS.Timeout; + const resetTimeout = (timeoutSeconds: number = TIMEOUT_SECONDS) => { + clearTimeout(timeout); + timeout = setTimeout(() => { + this.stop(); + throw new SerialTimeoutError(timeoutSeconds) + }, timeoutSeconds*1000); + } + // Initial timeout + resetTimeout(); // Set up the listener - this.parser.on('data', msgtxt =>{ - try{ - // Parse the raw text as a JSON object. + this.parser.on('data', msgtxt => { + clearTimeout(timeout); + try { + // Attempt to parse the raw text as a valid JSON object const msg = JSON.parse(msgtxt) as ArduinoMessage; // Handle all message types except: 'info', 'data', 'debug', 'error' switch (msg.type) { case 'revision': onMessage(msg); - if((msg.data as typeof ARDUINO_REVISION) == ARDUINO_REVISION){ + if((msg.data as typeof ARDUINO_REVISION) == ARDUINO_REVISION) { this.write(this.initialInstructions); } else { - // TODO: Update Arduino if revision does not match - throw new RevisionError(ARDUINO_REVISION, msg.data as string); + Spinner.fail(`Arduino revision check failed! Expected ${ARDUINO_REVISION}, recieved ${msg.data}`); + // Attempt to update the Arduino, and then restart + this.update().finally(() => { + process.exit(); + }); } break; default: @@ -83,10 +103,19 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino{ break; } } catch (err) { + throw err; } }); } - write(msg : any){ + async update() { + Spinner.start('Compiling Arduino software and flashing to board...'); + await updateArduino(this.serialpath).catch(e => { + Spinner.fail(e); + }).then(() => { + Spinner.succeed('Updated Arduino software successfully!'); + }) + } + write(msg : any) { Spinner.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); if(!this.serial?.write(JSON.stringify(msg)+'\n')){ throw new ArduinoInstructionsError(JSON.stringify(msg)); diff --git a/software/lib/PeaPodSimulator.ts b/software/lib/PeaPodSimulator.ts index d0e4bf89..8a65da81 100644 --- a/software/lib/PeaPodSimulator.ts +++ b/software/lib/PeaPodSimulator.ts @@ -30,39 +30,39 @@ function generateData(label: TDataLabels, min : number, max : number) : ArduinoM /** * A simulated Arduino for generating random data. */ -export class ArduinoSimulator implements IPeaPodArduino{ - intervals : NodeJS.Timeout[] = [] - constructor(public parameters : SimulatorParameters){} +export class ArduinoSimulator implements IPeaPodArduino { + intervals : NodeJS.Timeout[] = []; + constructor(public parameters : SimulatorParameters) { } async stop(): Promise { - for(const interval of this.intervals){ + for (const interval of this.intervals) { clearInterval(interval); } } async start(onMessage: (msg: ArduinoMessage) => any): Promise { - for(const label in this.parameters){ - this.intervals.push(setInterval(()=>{ + for(const label in this.parameters) { + this.intervals.push(setInterval(() => { onMessage(generateData(label as TDataLabels, this.parameters[label as TDataLabels].min, this.parameters[label as TDataLabels].max)); }, this.parameters[label as TDataLabels].interval)); } } } -export class PeaPodLogger implements IPeaPodPublisher{ +export class PeaPodLogger implements IPeaPodPublisher { async start() { - let config = {projectid: 'testproject', run: 'testrun-'+uuid()}; + let config = { projectid: 'testproject', run: 'testrun-'+uuid() }; Spinner.info(`Logging data to ${chalk.bold('projects/'+config.projectid+'/runs/'+config.run+'/')}`); return config; } - stop(){}; + stop() { }; publish(msg: PeaPodMessage): void { - switch(msg.type){ + switch(msg.type) { case 'data': - for(const label of Object.keys(msg.data)){ - for(const datum of msg.data[label].batch){ + for (const label of Object.keys(msg.data)) { + for (const datum of msg.data[label].batch) { console.log(`[${chalk.magenta(msg.type.toUpperCase())}] - [${(new Date(datum.timestamp)).toLocaleTimeString()}] - ${label}: ${datum.value}`); } const dir = `./projects/${msg.metadata.project}/runs/${msg.metadata.run}/${label}/`; - if (!existsSync(dir)){ + if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } writeFileSync(`${dir}${label+'-'+uuid()+'.json'}`, JSON.stringify(msg.data[label].batch, null, 2)) @@ -72,6 +72,5 @@ export class PeaPodLogger implements IPeaPodPublisher{ console.log(`[${chalk.yellow(msg.type.toUpperCase())}] - ${JSON.stringify(msg.data)}`); break; } - } } \ No newline at end of file diff --git a/software/lib/errors.ts b/software/lib/errors.ts index 17c49495..9c8dba09 100644 --- a/software/lib/errors.ts +++ b/software/lib/errors.ts @@ -1,17 +1,14 @@ -/** -* Error on revision check. -*/ -export class RevisionError extends Error { - constructor(expected : string, received : string){ - super(`Revision check failed! Expected ${expected}, received ${received}`); - } -} - /** * Error on instruction send. */ export class ArduinoInstructionsError extends Error { - constructor(instructions: any){ + constructor(instructions: any) { super("Failed to send instructions to Arduino: '"+JSON.stringify(instructions)+"'"); } +} + +export class SerialTimeoutError extends Error { + constructor(timeoutSeconds?: number) { + super(`Arduino serial communication timed out${timeoutSeconds === undefined ? '' : ` after ${timeoutSeconds} seconds`}.`); + } } \ No newline at end of file diff --git a/software/lib/utils.ts b/software/lib/utils.ts index 2b0a44d1..97e90815 100644 --- a/software/lib/utils.ts +++ b/software/lib/utils.ts @@ -1,8 +1,9 @@ import * as dns from 'dns'; -import fs from 'fs'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; import dotenv from 'dotenv'; -import SerialPort from 'serialport'; +import { SerialPort } from 'serialport'; import axios from 'axios'; +import { spawn } from 'child_process'; /** * Checks the internet connectivity. @@ -42,7 +43,7 @@ export const sleep = (millis : number) => { */ export const loadDotenv = (path: string = '.env') => { // Check for file - if(fs.existsSync(path)){ + if(existsSync(path)){ const config = dotenv.config({path}); if (config.error) { throw config.error @@ -83,5 +84,73 @@ export const stringsToTuple = (...data: T): T => }; export const fetchServerCert = async (): Promise => { - return (await axios.get("https://pki.goog/roots.pem")).data as string; + return (await axios.get('https://pki.goog/roots.pem')).data as string; +} + +export const checkArduino = async (processor: string = 'm328p') => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute(`avrdude -p ${processor} -C ~/avrdude_gpio.conf -c peapod -v`, [1]).catch(err => { + writeFileSync('logs/arduinoComms.log', err); + throw new Error(`Failed to communicate with the Arduino. See logs/arduinoComms.log`); + }).then(log1 => { + writeFileSync('logs/arduinoComms.log', log1); + }); +} + +export const updateArduino = async (sketch: string = 'PeaPodOS-Arduino', fqbn: string = 'arduino:avr:nano', processor: string = 'm328p'): Promise => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute(`arduino-cli compile -b ${fqbn} ${sketch} -e`, [1]) + .catch(err => { + writeFileSync('logs/arduinoCompile.log', err); + throw new Error(`Failed to compile '${sketch}'. See logs/arduinoCompile.log`); + }).then(log1 => { + writeFileSync('logs/arduinoCompile.log', log1); + // execute('arduino-cli', ['upload', '-p', serialport, '-b', fqbn, script], { sudo: true, failureCodes: [1] }) + execute(`sudo avrdude -p ${processor} -C ${process.env.HOME}/avrdude_gpio.conf -c peapod -v -U flash:w:${sketch}/build/${fqbn.replace(/:/g, '.')}/${sketch}.ino.hex:i`, [1]) + .catch(err => { + writeFileSync('logs/arduinoUpload.log', err); + throw new Error(`Failed to flash the '${sketch}' binary to the Arduino. See logs/arduinoUpload.log`); + }).then(log2 => { + writeFileSync('logs/arduinoUpload.log', log2); + }); + }); +} + +/** + * General purpose command execution and logging. No `sudo` support. + */ +export const execute = (command: string, failureCodes: number[] = []): Promise => { + return new Promise((res, rej) => { + const args = command.split(' '); + const eprocess = spawn(args[0], args.slice(1)); + let log = "> "+command+"\n"; + eprocess.stdout?.on('data', out => { + log += out; + }); + eprocess.stderr?.on('data', out => { + log += out; + }); + eprocess.on('error', error => { + eprocess.kill(); + log += error.message; + rej(log); + }); + eprocess.on('close', code => { + if (code) { + log += "> Process exited with code "+code; + if (failureCodes.includes(code)) { + rej(log); + } + return; + } + // If no options, no codes, OR non-failure: + res(log); + }); + }); } \ No newline at end of file diff --git a/software/package-lock.json b/software/package-lock.json index aa6d80d0..b876d6f5 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -19,7 +19,7 @@ "ora": "^5.3.0", "pi-camera": "^1.6.0", "rxjs": "^6.6.3", - "serialport": "^9.0.6", + "serialport": "^10.3.0", "uuid": "^8.3.2", "wrtc": "^0.4.7", "yargs": "^16.2.0" @@ -601,72 +601,87 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, - "node_modules/@serialport/binding-abstract": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/@serialport/binding-abstract/-/binding-abstract-9.2.3.tgz", - "integrity": "sha512-cQs9tbIlG3P0IrOWyVirqlhWuJ7Ms2Zh9m2108z6Y5UW/iVj6wEOiW8EmK9QX9jmJXYllE7wgGgvVozP5oCj3w==", + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", "dependencies": { - "debug": "^4.3.2" + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" }, "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "10.6.3", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.6.3.tgz", + "integrity": "sha512-jmALQ62JvIlCIx8osWifVXX1GoftlTPiX/HuEYeCYlRmtwXrbBzoD8dXu48YfGoZ/dwaYYKTsJarHqFoPq809Q==", + "hasInstallScript": true, + "dependencies": { + "@serialport/bindings-interface": "1.2.1", + "@serialport/parser-readline": "^10.2.1", + "debug": "^4.3.2", + "node-addon-api": "^4.3.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=12.17.0 <13.0 || >=14.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, - "node_modules/@serialport/binding-mock": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-9.2.4.tgz", - "integrity": "sha512-dpEhACCs44oQhh6ajJfJdvQdK38Vq0N4W6iD/gdplglDCK7qXRQCMUjJIeKdS/HSEiWkC3bwumUhUufdsOyT4g==", - "dependencies": { - "@serialport/binding-abstract": "9.2.3", - "debug": "^4.3.2" - }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", + "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, - "node_modules/@serialport/bindings": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/@serialport/bindings/-/bindings-9.2.5.tgz", - "integrity": "sha512-fyabNg56gWbOMuYJc5c45z94sANC/WzTnGeML7Nr1IYVk0SJ1uksN4ETI8Nea9ZAtr4DhNiIMQ3/IOkyof6Tqg==", - "hasInstallScript": true, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", + "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", "dependencies": { - "@serialport/binding-abstract": "9.2.3", - "@serialport/parser-readline": "9.2.4", - "bindings": "^1.5.0", - "debug": "^4.3.2", - "nan": "^2.15.0", - "prebuild-install": "^6.1.4" + "@serialport/parser-delimiter": "10.3.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.1.tgz", + "integrity": "sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, "node_modules/@serialport/parser-byte-length": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-9.2.4.tgz", - "integrity": "sha512-sQD/iw4ZMU3xW9PLi0/GlvU6Y623jGeWecbMkO7izUo/6P7gtfv1c9ikd5h0kwL8AoAOpQA1lxdHIKox+umBUg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.3.0.tgz", + "integrity": "sha512-pJ/VoFemzKRRNDHLhFfPThwP40QrGaEnm9TtwL7o2GihEPwzBg3T0bN13ew5TpbbUYZdMpUtpm3CGfl6av9rUQ==", "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, "node_modules/@serialport/parser-cctalk": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-9.2.4.tgz", - "integrity": "sha512-T4TU5vQMwmo9AB3gQLFDWbfJXlW5jd9guEsB/nqKjFHTv0FXPdZ7DQ2TpSp8RnHWxU3GX6kYTaDO20BKzc8GPQ==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.3.0.tgz", + "integrity": "sha512-8ujmk8EvVbDPrNF4mM33bWvUYJOZ0wXbY3WCRazHRWvyCdL0VO0DQvW81ZqgoTpiDQZm5r8wQu9rmuemahF6vQ==", "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" @@ -676,6 +691,7 @@ "version": "9.2.4", "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-9.2.4.tgz", "integrity": "sha512-4nvTAoYAgkxFiXrkI+3CA49Yd43CODjeszh89EK+I9c8wOZ+etZduRCzINYPiy26g7zO+GRAb9FoPCsY+sYcbQ==", + "dev": true, "engines": { "node": ">=10.0.0" }, @@ -684,20 +700,29 @@ } }, "node_modules/@serialport/parser-inter-byte-timeout": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-9.2.4.tgz", - "integrity": "sha512-SOAdvr0oBQIOCXX198hiTlxs4JTKg9j5piapw5tNq52fwDOWdbYrFneT/wN04UTMKaDrJuEvXq6T4rv4j7nJ5A==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.3.0.tgz", + "integrity": "sha512-wKP0QK85NHgvT6BBB1qBfKBBU4pf8kespNXAZBUYmFT+P4n8r8IZE2mqigCD+AiZcfWNQoAizwOsT/Jx/qeVig==", "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, + "node_modules/@serialport/parser-packet-length": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.3.0.tgz", + "integrity": "sha512-bj0cWzt8YSQj/E5fRQVYdi4TsfTlZQrXlXrUwjyTsCONv8IPOHzsz+yY0fw5SEMiJtaLyqvPkCHLsttOd/zFsg==", + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/@serialport/parser-readline": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-9.2.4.tgz", "integrity": "sha512-Z1/qrZTQUVhNSJP1hd9YfDvq0o7d87rNwAjjRKbVpa7Qi51tG5BnKt43IV3NFMyBlVcRe0rnIb3tJu57E0SOwg==", + "dev": true, "dependencies": { "@serialport/parser-delimiter": "9.2.4" }, @@ -709,36 +734,59 @@ } }, "node_modules/@serialport/parser-ready": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-9.2.4.tgz", - "integrity": "sha512-Pyi94Itjl6qAURwIZr/gmZpMAyTmKXThm6vL5DoAWGQjcRHWB0gwv2TY2v7N+mQLJYUKU3cMnvnATXxHm7xjxw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.3.0.tgz", + "integrity": "sha512-1owywJ4p592dJyVrEJZPIh6pUZ3/y/LN6kGTDH2wxdewRUITo/sGvDy0er5i2+dJD3yuowiAz0dOHSdz8tevJA==", "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, "node_modules/@serialport/parser-regex": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-9.2.4.tgz", - "integrity": "sha512-sI/cVvPOYz+Dbv4ZdnW16qAwvXiFf/1pGASQdbveRTlgJDdz7sRNlCBifzfTN2xljwvCTZYqiudKvDdC1TepRQ==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.3.0.tgz", + "integrity": "sha512-tIogTs7CvTH+UUFnsvE7i33MSISyTPTGPWlglWYH2/5coipXY503jlaYS1YGe818wWNcSx6YAjMZRdhTWwM39w==", "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.3.0.tgz", + "integrity": "sha512-JI0ILF5sylWn8f0MuMzHFBix/iMUTa79/Z95KaPZYnVaEdA7h7hh/o21Jmon/26P3RJwL1SNJCjZ81zfan+LtQ==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.3.0.tgz", + "integrity": "sha512-PDF73ClEPsClD1FEJZHNuBevDKsJCkqy/XD5+S5eA6+tY5D4HLrVgSWsg+3qqB6+dlpwf2CzHe+uO8D3teuKHA==", + "engines": { + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, "node_modules/@serialport/stream": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-9.2.4.tgz", - "integrity": "sha512-bLye8Ub4vUFQGmkh8qEqehr7SE7EJs2yDs0h9jzuL5oKi+F34CFmWkEErO8GAOQ8YNn7p6b3GxUgs+0BrHHDZQ==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.3.0.tgz", + "integrity": "sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==", "dependencies": { + "@serialport/bindings-interface": "1.2.1", "debug": "^4.3.2" }, "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" @@ -856,47 +904,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "node_modules/are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/axios": { "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", @@ -929,14 +936,6 @@ } ] }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -1009,11 +1008,6 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -1062,14 +1056,6 @@ "node": ">=0.8" } }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1114,11 +1100,6 @@ "typedarray": "^0.0.6" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, "node_modules/core-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -1135,9 +1116,9 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dependencies": { "ms": "2.1.2" }, @@ -1150,25 +1131,6 @@ } } }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -1177,22 +1139,6 @@ "clone": "^1.0.2" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -1264,14 +1210,6 @@ "node": ">=0.8.0" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -1310,11 +1248,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, "node_modules/firebase": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.4.0.tgz", @@ -1367,74 +1300,11 @@ } } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1443,11 +1313,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" - }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -1475,11 +1340,6 @@ "node": ">=8" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, "node_modules/help-me": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", @@ -1548,11 +1408,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, "node_modules/inquirer": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", @@ -1775,17 +1630,6 @@ "node": ">=6" } }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1802,11 +1646,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "node_modules/mqtt": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", @@ -1856,23 +1695,10 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, - "node_modules/node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "dependencies": { - "semver": "^5.4.1" - } + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" }, "node_modules/node-fetch": { "version": "2.6.5", @@ -1885,31 +1711,14 @@ "node": "4.x || >=6.0.0" } }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "engines": { - "node": ">=0.10.0" + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } }, "node_modules/once": { @@ -1985,32 +1794,6 @@ "node": ">=6.10.0" } }, - "node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "dependencies": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2055,20 +1838,6 @@ "once": "^1.3.1" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -2203,33 +1972,56 @@ } }, "node_modules/serialport": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-9.2.5.tgz", - "integrity": "sha512-nsDsD2GN/43T2a8jQYr1HH76gmDZ575Ts8FOdcBRUY8ecaI16BPbXa612cPPkQjOfg28+KL5qtQL9c0vvTaidg==", - "dependencies": { - "@serialport/binding-mock": "9.2.4", - "@serialport/bindings": "9.2.5", - "@serialport/parser-byte-length": "9.2.4", - "@serialport/parser-cctalk": "9.2.4", - "@serialport/parser-delimiter": "9.2.4", - "@serialport/parser-inter-byte-timeout": "9.2.4", - "@serialport/parser-readline": "9.2.4", - "@serialport/parser-ready": "9.2.4", - "@serialport/parser-regex": "9.2.4", - "@serialport/stream": "9.2.4", - "debug": "^4.3.2" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.3.0.tgz", + "integrity": "sha512-MZMRlQgWOG7boLkJwm200Z3W39GcFmGo3tWxQrvyrZpH7W1p3t16M1VBglVxR6wXRF8qm01VxXBA+rNV7fPMVA==", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "10.6.3", + "@serialport/parser-byte-length": "10.3.0", + "@serialport/parser-cctalk": "10.3.0", + "@serialport/parser-delimiter": "10.3.0", + "@serialport/parser-inter-byte-timeout": "10.3.0", + "@serialport/parser-packet-length": "10.3.0", + "@serialport/parser-readline": "10.3.0", + "@serialport/parser-ready": "10.3.0", + "@serialport/parser-regex": "10.3.0", + "@serialport/parser-slip-encoder": "10.3.0", + "@serialport/parser-spacepacket": "10.3.0", + "@serialport/stream": "10.3.0", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/serialport/node_modules/@serialport/parser-delimiter": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", + "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "funding": { "url": "https://opencollective.com/serialport/donate" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "node_modules/serialport/node_modules/@serialport/parser-readline": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", + "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", + "dependencies": { + "@serialport/parser-delimiter": "10.3.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } }, "node_modules/set-immediate-shim": { "version": "1.0.1", @@ -2244,35 +2036,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", - "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -2318,14 +2081,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2337,32 +2092,6 @@ "node": ">=8" } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -2389,17 +2118,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -2489,14 +2207,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -3080,79 +2790,108 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, - "@serialport/binding-abstract": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/@serialport/binding-abstract/-/binding-abstract-9.2.3.tgz", - "integrity": "sha512-cQs9tbIlG3P0IrOWyVirqlhWuJ7Ms2Zh9m2108z6Y5UW/iVj6wEOiW8EmK9QX9jmJXYllE7wgGgvVozP5oCj3w==", - "requires": { - "debug": "^4.3.2" - } - }, "@serialport/binding-mock": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-9.2.4.tgz", - "integrity": "sha512-dpEhACCs44oQhh6ajJfJdvQdK38Vq0N4W6iD/gdplglDCK7qXRQCMUjJIeKdS/HSEiWkC3bwumUhUufdsOyT4g==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", "requires": { - "@serialport/binding-abstract": "9.2.3", - "debug": "^4.3.2" + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" } }, - "@serialport/bindings": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/@serialport/bindings/-/bindings-9.2.5.tgz", - "integrity": "sha512-fyabNg56gWbOMuYJc5c45z94sANC/WzTnGeML7Nr1IYVk0SJ1uksN4ETI8Nea9ZAtr4DhNiIMQ3/IOkyof6Tqg==", + "@serialport/bindings-cpp": { + "version": "10.6.3", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.6.3.tgz", + "integrity": "sha512-jmALQ62JvIlCIx8osWifVXX1GoftlTPiX/HuEYeCYlRmtwXrbBzoD8dXu48YfGoZ/dwaYYKTsJarHqFoPq809Q==", "requires": { - "@serialport/binding-abstract": "9.2.3", - "@serialport/parser-readline": "9.2.4", - "bindings": "^1.5.0", + "@serialport/bindings-interface": "1.2.1", + "@serialport/parser-readline": "^10.2.1", "debug": "^4.3.2", - "nan": "^2.15.0", - "prebuild-install": "^6.1.4" + "node-addon-api": "^4.3.0", + "node-gyp-build": "^4.3.0" + }, + "dependencies": { + "@serialport/parser-delimiter": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", + "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==" + }, + "@serialport/parser-readline": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", + "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", + "requires": { + "@serialport/parser-delimiter": "10.3.0" + } + } } }, + "@serialport/bindings-interface": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.1.tgz", + "integrity": "sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==" + }, "@serialport/parser-byte-length": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-9.2.4.tgz", - "integrity": "sha512-sQD/iw4ZMU3xW9PLi0/GlvU6Y623jGeWecbMkO7izUo/6P7gtfv1c9ikd5h0kwL8AoAOpQA1lxdHIKox+umBUg==" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.3.0.tgz", + "integrity": "sha512-pJ/VoFemzKRRNDHLhFfPThwP40QrGaEnm9TtwL7o2GihEPwzBg3T0bN13ew5TpbbUYZdMpUtpm3CGfl6av9rUQ==" }, "@serialport/parser-cctalk": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-9.2.4.tgz", - "integrity": "sha512-T4TU5vQMwmo9AB3gQLFDWbfJXlW5jd9guEsB/nqKjFHTv0FXPdZ7DQ2TpSp8RnHWxU3GX6kYTaDO20BKzc8GPQ==" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.3.0.tgz", + "integrity": "sha512-8ujmk8EvVbDPrNF4mM33bWvUYJOZ0wXbY3WCRazHRWvyCdL0VO0DQvW81ZqgoTpiDQZm5r8wQu9rmuemahF6vQ==" }, "@serialport/parser-delimiter": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-9.2.4.tgz", - "integrity": "sha512-4nvTAoYAgkxFiXrkI+3CA49Yd43CODjeszh89EK+I9c8wOZ+etZduRCzINYPiy26g7zO+GRAb9FoPCsY+sYcbQ==" + "integrity": "sha512-4nvTAoYAgkxFiXrkI+3CA49Yd43CODjeszh89EK+I9c8wOZ+etZduRCzINYPiy26g7zO+GRAb9FoPCsY+sYcbQ==", + "dev": true }, "@serialport/parser-inter-byte-timeout": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-9.2.4.tgz", - "integrity": "sha512-SOAdvr0oBQIOCXX198hiTlxs4JTKg9j5piapw5tNq52fwDOWdbYrFneT/wN04UTMKaDrJuEvXq6T4rv4j7nJ5A==" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.3.0.tgz", + "integrity": "sha512-wKP0QK85NHgvT6BBB1qBfKBBU4pf8kespNXAZBUYmFT+P4n8r8IZE2mqigCD+AiZcfWNQoAizwOsT/Jx/qeVig==" + }, + "@serialport/parser-packet-length": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.3.0.tgz", + "integrity": "sha512-bj0cWzt8YSQj/E5fRQVYdi4TsfTlZQrXlXrUwjyTsCONv8IPOHzsz+yY0fw5SEMiJtaLyqvPkCHLsttOd/zFsg==" }, "@serialport/parser-readline": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-9.2.4.tgz", "integrity": "sha512-Z1/qrZTQUVhNSJP1hd9YfDvq0o7d87rNwAjjRKbVpa7Qi51tG5BnKt43IV3NFMyBlVcRe0rnIb3tJu57E0SOwg==", + "dev": true, "requires": { "@serialport/parser-delimiter": "9.2.4" } }, "@serialport/parser-ready": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-9.2.4.tgz", - "integrity": "sha512-Pyi94Itjl6qAURwIZr/gmZpMAyTmKXThm6vL5DoAWGQjcRHWB0gwv2TY2v7N+mQLJYUKU3cMnvnATXxHm7xjxw==" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.3.0.tgz", + "integrity": "sha512-1owywJ4p592dJyVrEJZPIh6pUZ3/y/LN6kGTDH2wxdewRUITo/sGvDy0er5i2+dJD3yuowiAz0dOHSdz8tevJA==" }, "@serialport/parser-regex": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-9.2.4.tgz", - "integrity": "sha512-sI/cVvPOYz+Dbv4ZdnW16qAwvXiFf/1pGASQdbveRTlgJDdz7sRNlCBifzfTN2xljwvCTZYqiudKvDdC1TepRQ==" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.3.0.tgz", + "integrity": "sha512-tIogTs7CvTH+UUFnsvE7i33MSISyTPTGPWlglWYH2/5coipXY503jlaYS1YGe818wWNcSx6YAjMZRdhTWwM39w==" + }, + "@serialport/parser-slip-encoder": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.3.0.tgz", + "integrity": "sha512-JI0ILF5sylWn8f0MuMzHFBix/iMUTa79/Z95KaPZYnVaEdA7h7hh/o21Jmon/26P3RJwL1SNJCjZ81zfan+LtQ==" + }, + "@serialport/parser-spacepacket": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.3.0.tgz", + "integrity": "sha512-PDF73ClEPsClD1FEJZHNuBevDKsJCkqy/XD5+S5eA6+tY5D4HLrVgSWsg+3qqB6+dlpwf2CzHe+uO8D3teuKHA==" }, "@serialport/stream": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-9.2.4.tgz", - "integrity": "sha512-bLye8Ub4vUFQGmkh8qEqehr7SE7EJs2yDs0h9jzuL5oKi+F34CFmWkEErO8GAOQ8YNn7p6b3GxUgs+0BrHHDZQ==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.3.0.tgz", + "integrity": "sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==", "requires": { + "@serialport/bindings-interface": "1.2.1", "debug": "^4.3.2" } }, @@ -3253,49 +2992,6 @@ "color-convert": "^2.0.1" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "axios": { "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", @@ -3314,14 +3010,6 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -3374,11 +3062,6 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -3412,11 +3095,6 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3455,11 +3133,6 @@ "typedarray": "^0.0.6" } }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, "core-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -3471,26 +3144,13 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } }, - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "requires": { - "mimic-response": "^2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -3499,16 +3159,6 @@ "clone": "^1.0.2" } }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -3573,11 +3223,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" - }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3604,11 +3249,6 @@ "escape-string-regexp": "^1.0.5" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, "firebase": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.4.0.tgz", @@ -3647,74 +3287,16 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" - }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -3733,11 +3315,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, "help-me": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", @@ -3789,11 +3366,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, "inquirer": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", @@ -3989,11 +3561,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4007,11 +3574,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "mqtt": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", @@ -4053,23 +3615,10 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, - "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "requires": { - "semver": "^5.4.1" - } + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" }, "node-fetch": { "version": "2.6.5", @@ -4079,26 +3628,10 @@ "whatwg-url": "^5.0.0" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" }, "once": { "version": "1.4.0", @@ -4152,26 +3685,6 @@ "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.6.0.tgz", "integrity": "sha512-WrnJ4R1X/5qA5/vXeou/Ty97c7urrnla03j5FhctF9nZy1lXnfYkPOsL+qDZAZcvg40cbkTE217WlHNnMMftaw==" }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - } - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4211,17 +3724,6 @@ "once": "^1.3.1" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -4316,28 +3818,41 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "serialport": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-9.2.5.tgz", - "integrity": "sha512-nsDsD2GN/43T2a8jQYr1HH76gmDZ575Ts8FOdcBRUY8ecaI16BPbXa612cPPkQjOfg28+KL5qtQL9c0vvTaidg==", - "requires": { - "@serialport/binding-mock": "9.2.4", - "@serialport/bindings": "9.2.5", - "@serialport/parser-byte-length": "9.2.4", - "@serialport/parser-cctalk": "9.2.4", - "@serialport/parser-delimiter": "9.2.4", - "@serialport/parser-inter-byte-timeout": "9.2.4", - "@serialport/parser-readline": "9.2.4", - "@serialport/parser-ready": "9.2.4", - "@serialport/parser-regex": "9.2.4", - "@serialport/stream": "9.2.4", - "debug": "^4.3.2" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.3.0.tgz", + "integrity": "sha512-MZMRlQgWOG7boLkJwm200Z3W39GcFmGo3tWxQrvyrZpH7W1p3t16M1VBglVxR6wXRF8qm01VxXBA+rNV7fPMVA==", + "requires": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "10.6.3", + "@serialport/parser-byte-length": "10.3.0", + "@serialport/parser-cctalk": "10.3.0", + "@serialport/parser-delimiter": "10.3.0", + "@serialport/parser-inter-byte-timeout": "10.3.0", + "@serialport/parser-packet-length": "10.3.0", + "@serialport/parser-readline": "10.3.0", + "@serialport/parser-ready": "10.3.0", + "@serialport/parser-regex": "10.3.0", + "@serialport/parser-slip-encoder": "10.3.0", + "@serialport/parser-spacepacket": "10.3.0", + "@serialport/stream": "10.3.0", + "debug": "^4.3.3" + }, + "dependencies": { + "@serialport/parser-delimiter": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", + "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==" + }, + "@serialport/parser-readline": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", + "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", + "requires": { + "@serialport/parser-delimiter": "10.3.0" + } + } } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -4348,21 +3863,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", - "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -4402,11 +3902,6 @@ "ansi-regex": "^5.0.1" } }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4415,29 +3910,6 @@ "has-flag": "^4.0.0" } }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4461,14 +3933,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -4536,14 +4000,6 @@ "webidl-conversions": "^3.0.0" } }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/software/package.json b/software/package.json index af73d922..fb228f08 100644 --- a/software/package.json +++ b/software/package.json @@ -39,7 +39,7 @@ "ora": "^5.3.0", "pi-camera": "^1.6.0", "rxjs": "^6.6.3", - "serialport": "^9.0.6", + "serialport": "^10.3.0", "uuid": "^8.3.2", "wrtc": "^0.4.7", "yargs": "^16.2.0" diff --git a/software/tests/README.md b/software/tests/README.md new file mode 100644 index 00000000..ac8c7b17 --- /dev/null +++ b/software/tests/README.md @@ -0,0 +1,13 @@ +# Software Testing Suite + +### Serial Port Test - `serialTest.py` + +Test the funcionality of the Raspberry Pi Zero 2 W GPIO mini-UART serial port by tying the RX and TX pins together. + +### Arduino Compilation & Upload Test - `blink.ino` + +Test the functionality of the `arduino-cli` and `avrdude` utilities (and their custom configurations) by compilation the script and flashing the binary. + +### Communications Protocol Test - `revision.ino` + +Test communications between the Arduino and Raspberry Pi including timeout and revision. Essentially a skeleton of the main `PeaPodOS-Arduino.ino` script, with no sensors or actuators. \ No newline at end of file diff --git a/software/tests/blink/blink.ino b/software/tests/blink/blink.ino new file mode 100644 index 00000000..8ee9681c --- /dev/null +++ b/software/tests/blink/blink.ino @@ -0,0 +1,10 @@ +void setup() { + pinMode(13, OUTPUT); +} + +void loop() { + digitalWrite(13, HIGH); + delay(250); + digitalWrite(13, LOW); + delay(250); +} \ No newline at end of file diff --git a/software/tests/revision/revision.ino b/software/tests/revision/revision.ino new file mode 100644 index 00000000..3784d23e --- /dev/null +++ b/software/tests/revision/revision.ino @@ -0,0 +1,99 @@ +#define REVISION 0 + +void setup() { + digitalWrite(13, LOW); + if (!post()) { + // Failed POST + while(true) { + delay(250); + digitalWrite(13, HIGH); + delay(250); + digitalWrite(13, LOW); + } + } + + // Tell computer 'ready', wait to receive valid program. + Serial.print("{\"type\":\"revision\",\"data\":"); + Serial.print(REVISION); + Serial.print("}\n"); + + String ins; + do { + while(!Serial.available()); + ins = Serial.readString(); + ins.trim(); + } while(!handleInstructions(ins)); + digitalWrite(13, HIGH); +} + +void loop() { + if (Serial.available()) { + String in = Serial.readString(); + in.trim(); //Whitespace or newlines + handleInstructions(in); + } + + delay(100); +} + +/** + * Handles incoming instruction "var":value string. + * @param s - Instruction string + * @return was the instruction handled in a valid way? + **/ +bool handleInstruction(String in) { + uint8_t split = in.indexOf(":"); + if (split!=-1 && in.charAt(0) == '"' && in.charAt(split-1) == '"') { + String var = in.substring(1, split-1); //Assumes surrounding "" + float value = in.substring(split+1).toFloat(); + + Serial.print("{\"type\":\"error\",\"data\":\"Unknown instruction target '"); + Serial.print(var); + Serial.print("'\"}\n"); + return false; + } + Serial.print("{\"type\":\"error\",\"data\":\"Failed to handle instruction '"); + in.replace("\"", "\\\""); + Serial.print(in); + Serial.print("'\"}\n"); + return false; +} + +/** + * Handles incoming instruction dictionary {"var":val,...} + * Continues regardless of failure of a single instruction, alerts only + * @param s - Instructions dictionary string + * @return were all instructions handled in a valid way? + * */ +bool handleInstructions(String ins) { + if (ins.equals("{}")) { + return true; //Empty dictionary, by default handled + } + if (ins.charAt(0) != '{' || ins.charAt(ins.length()-1) != '}') { + Serial.print("{\"type\":\"error\",\"data\":\"Invalid instructions dictionary '"); + Serial.print(ins); + Serial.print("'\"}\n"); + return false; + } + ins = ins.substring(1, ins.length()-1); //Strips surrounding {} + + //Handles each individual instruction + + bool result = true; + //While there are multiple instructions + while (ins.indexOf(',') != -1) { + //Did the leftmost one succeed? + result &= handleInstruction(ins.substring(0, ins.indexOf(','))); + //Truncate leftmost instruction + ins = ins.substring(ins.indexOf(',')+1); + } + //Handle last/only instruction (there are no commas left) + result &= handleInstruction(ins); + return result; +} + +bool post() { + Serial.begin(115200); + while(!Serial); //Waits until serial opens + return true; +} diff --git a/software/tests/serialTest.py b/software/tests/serialTest.py new file mode 100644 index 00000000..fff3e217 --- /dev/null +++ b/software/tests/serialTest.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import serial +test_string = "Testing Raspberry Pi GPIO serial port ...".encode('utf-8') +port = "/dev/ttyS0" +try: + serialPort = serial.Serial(port, 9600, timeout = 2) + print ("Serial port '", port, "' ready for test:", sep='') + bytes_sent = serialPort.write(test_string) + print ("Sent ", bytes_sent, " bytes: '", test_string, "'", sep='') + loopback = serialPort.read(bytes_sent) + if loopback == test_string: + print ("Received ",len(loopback), " bytes: '", test_string, "'\nPort ", port," is setup correctly!\n", sep='') + else: + print ("Received incorrect data: '", loopback, "' on serial port '", port, "' loopback\n", sep='') + serialPort.close() +except IOError: + print ("Error on ", port,"\n") From ebaae30c29682448d02c94fe23cec797f56f65eb Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Tue, 1 Mar 2022 14:29:45 -0500 Subject: [PATCH 03/85] More Sophisticated Sensors, Actuators Also: - More code formatting - Folders for sensors, actuators - .env template - Updated control flow diagram - Minor fixes --- software/.env.template | 14 + .../PeaPodOS-Arduino/PeaPodOS-Arduino.ino | 340 +++++++++--------- software/PeaPodOS-Arduino/src/Actuator.cpp | 84 +++-- software/PeaPodOS-Arduino/src/Actuator.h | 94 +++-- software/PeaPodOS-Arduino/src/Base.h | 22 ++ software/PeaPodOS-Arduino/src/K30.cpp | 46 --- software/PeaPodOS-Arduino/src/K30.h | 22 -- software/PeaPodOS-Arduino/src/LED.cpp | 36 -- software/PeaPodOS-Arduino/src/LED.h | 27 -- software/PeaPodOS-Arduino/src/SHT31.cpp | 167 --------- software/PeaPodOS-Arduino/src/SHT31.h | 147 -------- software/PeaPodOS-Arduino/src/Sensor.cpp | 128 ++++--- software/PeaPodOS-Arduino/src/Sensor.h | 127 +++++-- .../PeaPodOS-Arduino/src/actuators/LED.cpp | 43 +++ software/PeaPodOS-Arduino/src/actuators/LED.h | 35 ++ software/PeaPodOS-Arduino/src/sensors/K30.cpp | 55 +++ software/PeaPodOS-Arduino/src/sensors/K30.h | 35 ++ .../PeaPodOS-Arduino/src/sensors/SHT31.cpp | 145 ++++++++ software/PeaPodOS-Arduino/src/sensors/SHT31.h | 77 ++++ software/README.md | 22 +- software/assets/control_flow.png | Bin 460821 -> 681711 bytes software/index.ts | 47 +-- software/lib/PeaPodArduino.ts | 9 +- software/tests/README.md | 2 +- software/tests/revision/revision.ino | 1 + 25 files changed, 954 insertions(+), 771 deletions(-) create mode 100644 software/.env.template create mode 100644 software/PeaPodOS-Arduino/src/Base.h delete mode 100644 software/PeaPodOS-Arduino/src/K30.cpp delete mode 100644 software/PeaPodOS-Arduino/src/K30.h delete mode 100644 software/PeaPodOS-Arduino/src/LED.cpp delete mode 100644 software/PeaPodOS-Arduino/src/LED.h delete mode 100644 software/PeaPodOS-Arduino/src/SHT31.cpp delete mode 100644 software/PeaPodOS-Arduino/src/SHT31.h create mode 100644 software/PeaPodOS-Arduino/src/actuators/LED.cpp create mode 100644 software/PeaPodOS-Arduino/src/actuators/LED.h create mode 100644 software/PeaPodOS-Arduino/src/sensors/K30.cpp create mode 100644 software/PeaPodOS-Arduino/src/sensors/K30.h create mode 100644 software/PeaPodOS-Arduino/src/sensors/SHT31.cpp create mode 100644 software/PeaPodOS-Arduino/src/sensors/SHT31.h diff --git a/software/.env.template b/software/.env.template new file mode 100644 index 00000000..73608751 --- /dev/null +++ b/software/.env.template @@ -0,0 +1,14 @@ +FIREBASE_APIKEY="" +FIREBASE_AUTHDOMAIN="" +FIREBASE_PROJECTID="" +FIREBASE_STORAGEBUCKET="" +FIREBASE_MESSAGINGSENDERID="" +FIREBASE_APPID="" +FIREBASE_MEASUREMENTID="" +GOOGLE_SCOPES="email profile" +GOOGLE_CLIENTID="" +GOOGLE_CLIENTSECRET="" +GITHUB_SCOPES="read:user user:email" +GITHUB_CLIENTID="" +GITHUB_CLIENTSECRET="" +SERIALPORT="/dev/ttyS0" \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino index 31e15f1d..f5d25f86 100644 --- a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino +++ b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino @@ -1,41 +1,35 @@ +// IMPORTS #include "./src/Sensor.h" -#include "./src/SHT31.h" - +#include "./src/sensors/SHT31.h" +// #include "./src/sensors/K30.h" // #include "./src/Actuator.h" -// #include "./src/LED.h" -// #include "Wire.h" -// #include "K30.h" -// #include "FloatSensor.h" -// #include "Scale.h" +// #include "./src/actuators/LED.h" -//MACRO DEFINITIONS +// CONSTANTS #define NUM_SENSORS 2 // #define NUM_ACTUATORS 0 -// #define FLOATSENSOR_PIN 5 #define REVISION 0 - -// Sensors -SHT31 sht31; -SHT31_temp temp = SHT31_temp(&sht31); -SHT31_hum hum = SHT31_hum(&sht31); -// K30 k30; -// Scale scale; -// FloatSensor fs = FloatSensor(FLOATSENSOR_PIN); - -Sensor* sensors [NUM_SENSORS] = { - &temp, - &hum, - // &k30, - // &fs, - // &scale +// #define LEDPIN_BLUE 3 +// #define LEDPIN_COOL 5 +// #define LEDPIN_WARM 6 +// #define LEDPIN_RED 9 +// #define LEDPIN_FAR 10 + +// SENSORS +SHT31 sht31 = SHT31(); +// K30 k30 = K30(); + +Sensor* sensors[NUM_SENSORS] = { + &sht31, + // &k30, }; -//Actuators -// LED led_blue(3); -// LED led_cool(5); -// LED led_warm(6); -// LED led_red(9); -// LED led_far(10); +// ACTUATORS +// LED led_blue(LEDPIN_BLUE, LEDCOLOR_BLUE); +// LED led_cool(LEDPIN_COOL, LEDCOLOR_COOL); +// LED led_warm(LEDPIN_WARM, LEDCOLOR_WARM); +// LED led_red(LEDPIN_RED, LEDCOLOR_RED); +// LED led_far(LEDPIN_FAR, LEDCOLOR_FAR); // Actuator* actuators [NUM_ACTUATORS] = { // &led_blue, @@ -45,97 +39,92 @@ Sensor* sensors [NUM_SENSORS] = { // &led_far, // } - -void setup() -{ - digitalWrite(13, LOW); - if(!post()){ - // Failed POST - while(true){ - delay(250); - digitalWrite(13, HIGH); - delay(250); - digitalWrite(13, LOW); - } +void setup(void) { + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + if (!post()) { + // Failed POST + while (true) { + delay(250); + digitalWrite(13, HIGH); + delay(250); + digitalWrite(13, LOW); } - - // Tell computer 'ready', wait to receive valid program. - Serial.print("{\"type\":\"revision\",\"data\":"); - Serial.print(REVISION); - Serial.print("}\n"); - - String ins; - do{ - while(!Serial.available()); - ins = Serial.readString(); - ins.trim(); - }while(!handleInstructions(ins)); - digitalWrite(13, HIGH); + } + + // Send software revision + Serial.print("{\"type\":\"revision\",\"data\":"); + Serial.print(REVISION); + Serial.print("}\n"); + + // Await valid initial instruction set + String ins; + do { + // Await instructions + while (!Serial.available()); + ins = Serial.readString(); + + // Trim whitespace, newline terminator + ins.trim(); + } while (!handleInstructions(ins)); + digitalWrite(13, HIGH); } -void loop() -{ - if(Serial.available()){ - String in = Serial.readString(); - in.trim(); //Whitespace or newlines - handleInstructions(in); - } - - for(int i = 0; i < NUM_SENSORS; i++){ - float read = sensors[i]->getRead(); - if(!isnan(read)){ - Serial.print("{\"type\":\"data\",\"data\":{\"label\":\""); - Serial.print(sensors[i]->evname); - Serial.print("\",\"value\":"); - Serial.print(read); - Serial.print("}}\n"); +void loop(void) { + // Check for instructions + if (Serial.available()) { + String in = Serial.readString(); + + // Trim whitespace, newline terminator + in.trim(); + handleInstructions(in); + } + + for (int i = 0; i < NUM_SENSORS; i++) { + SensorState* state = sensors[i]->update(); + if (state->error == ERR_NONE) { + if (state->debug == DS_SUCCESS) { + for (int j = 0; j < state->numdata; j++) { + Serial.print("{\"type\":\"data\",\"data\":{\"label\":\""); + Serial.print(state->data[j].label); + Serial.print("\",\"value\":"); + Serial.print(*(state->data[j].value)); + Serial.print("}}\n"); } + } // else do nothing + } else if (state->error == ERR_WARNING) { + Serial.print("{\"type\":\"debug\",\"data\":\"Failed to read from sensor "); + Serial.print(sensors[i]->getID()); + Serial.print(" (non-fatal)\"}\n"); + } else if (state->error == ERR_FATAL) { + Serial.print("{\"type\":\"error\",\"data\":\"Failed to read from sensor "); + Serial.print(sensors[i]->getID()); + Serial.print(" (FATAL, SENSOR DISABLED!)\"}\n"); } - delay(10); + } + delay(10); } -/** - * Handles incoming instruction "var":value string. - * @param s - Instruction string - * @return was the instruction handled in a valid way? - **/ -bool handleInstruction(String in){ - uint8_t split = in.indexOf(":"); - if(split!=-1 && in.charAt(0) == '"' && in.charAt(split-1) == '"'){ - String var = in.substring(1, split-1); //Assumes surrounding "" - float value = in.substring(split+1).toFloat(); - - //INSTRUCTION HANDLING IF BLOCKS - EACH RETURNS TRUE - // if(var.equals("led_blue")){ - // led_blue.target = value; - // return true; - // } - // if(var.equals("led_cool")){ - // led_cool.target = value; - // return true; - // } - // if(var.equals("led_warm")){ - // led_warm.target = value; - // return true; - // } - // if(var.equals("led_red")){ - // led_red.target = value; - // return true; - // } - // if(var.equals("led_far")){ - // led_far.target = value; - // return true; - // } - Serial.print("{\"type\":\"error\",\"data\":\"Unknown instruction target '"); - Serial.print(var); - Serial.print("'\"}\n"); - return false; +bool post(void) { + Serial.begin(115200); + while(!Serial); //Waits until serial opens + + bool success = true; + for (int i = 0; i < NUM_SENSORS; i++) { + SensorState* state = sensors[i]->begin(); + bool latest = (state->debug == ERR_NONE); + if (!latest) { + Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize sensor "); + Serial.print(sensors[i]->getID()); + Serial.print(". Check wiring.\"}\n"); + } else { + Serial.print("{\"type\":\"debug\",\"data\":\"Sensor "); + Serial.print(sensors[i]->getID()); + Serial.print(" initialized successfully.\"}\n"); } - Serial.print("{\"type\":\"error\",\"data\":\"Failed to handle instruction '"); - in.replace("\"", "\\\""); - Serial.print(in); - Serial.print("'\"}\n"); - return false; + success &= latest; + } + return success; } /** @@ -143,69 +132,70 @@ bool handleInstruction(String in){ * Continues regardless of failure of a single instruction, alerts only * @param s - Instructions dictionary string * @return were all instructions handled in a valid way? - * */ -bool handleInstructions(String ins){ - if(ins.equals("{}")){ - return true; //Empty dictionary, by default handled - } - if(ins.charAt(0) != '{' || ins.charAt(ins.length()-1) != '}'){ - Serial.print("{\"type\":\"error\",\"data\":\"Invalid instructions dictionary '"); - Serial.print(ins); - Serial.print("'\"}\n"); - return false; - } - ins = ins.substring(1, ins.length()-1); //Strips surrounding {} - - //Handles each individual instruction - - bool result = true; - //While there are multiple instructions - while(ins.indexOf(',') != -1){ - //Did the leftmost one succeed? - result &= handleInstruction(ins.substring(0, ins.indexOf(','))); - //Truncate leftmost instruction - ins = ins.substring(ins.indexOf(',')+1); - } - //Handle last/only instruction (there are no commas left) - result &= handleInstruction(ins); - return result; + */ +bool handleInstructions(String ins) { + if (ins.charAt(0) != '{' || ins.charAt(ins.length()-1) != '}' || ins.indexOf(':') == -1) { + Serial.print("{\"type\":\"error\",\"data\":\"Invalid instructions dictionary '"); + Serial.print(ins); + Serial.print("', bad formatting.\"}\n"); + return false; + } + ins = ins.substring(1, ins.length()-1); // Strips surrounding {} + + // Handle each individual instruction + bool result = true; + // While there are multiple instructions: + while (ins.indexOf(',') != -1) { + // Did the leftmost one succeed? + result &= handleInstruction(ins.substring(0, ins.indexOf(','))); + //T runcate leftmost instruction + ins = ins.substring(ins.indexOf(',')+1); + } + //Handle last/only instruction (there are no commas left) + result &= handleInstruction(ins); + return result; } -bool post(){ - Serial.begin(115200); - while(!Serial); //Waits until serial opens - - // Test sensor protocols - per-sensor tests - bool success = true; - for(int i = 0; i < NUM_SENSORS; i++){ - bool latest = sensors[i]->begin(); - success &= latest; - if(!latest){ - Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize sensor '"); - Serial.print(sensors[i]->name); - Serial.print("'. Check wiring.\"}\n"); - } else { - Serial.print("{\"type\":\"debug\",\"data\":\"Sensor '"); - Serial.print(sensors[i]->name); - Serial.print("' initialized successfully.\"}\n"); - - // {type: debug, data: "Sensor 'Temperature Sensor' initialized successfully."} - } - } - - // Test actuator protocols - per-actuator tests - // for(int i = 0; i < NUM_ACTUATORS; i++){ - // bool latest = actuators[i]->begin(); - // success &= latest; - // if(!latest){ - // Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize actuator '"); - // Serial.print(actuators[i]->name); - // Serial.print("'. Check wiring.\"}\n"); - // } else { - // Serial.print("{\"type\":\"debug\",\"data\":\"Actuator '"); - // Serial.print(actuators[i]->name); - // Serial.print("' initialized successfully.\"}\n"); - // } +/** + * Handles incoming instruction "var":value string. + * @param s - Instruction string + * @return was the instruction handled in a valid way? + */ +bool handleInstruction(String in) { + uint8_t split = in.indexOf(":"); + if (split!=-1 && in.charAt(0) == '"' && in.charAt(split-1) == '"') { + String var = in.substring(1, split-1); //Assumes surrounding "" + float value = in.substring(split+1).toFloat(); + + //INSTRUCTION HANDLING IF BLOCKS - EACH RETURNS TRUE + // if(var.equals("led_blue")){ + // led_blue.target = value; + // return true; + // } + // if(var.equals("led_cool")){ + // led_cool.target = value; + // return true; + // } + // if(var.equals("led_warm")){ + // led_warm.target = value; + // return true; // } - return success; + // if(var.equals("led_red")){ + // led_red.target = value; + // return true; + // } + // if(var.equals("led_far")){ + // led_far.target = value; + // return true; + // } + Serial.print("{\"type\":\"debug\",\"data\":\"Unknown instruction target '"); + Serial.print(var); + Serial.print("'\"}\n"); + return false; + } + Serial.print("{\"type\":\"debug\",\"data\":\"Failed to handle instruction '"); + in.replace("\"", "\\\""); + Serial.print(in); + Serial.print("'\"}\n"); + return false; } diff --git a/software/PeaPodOS-Arduino/src/Actuator.cpp b/software/PeaPodOS-Arduino/src/Actuator.cpp index b2859768..8337cbd6 100644 --- a/software/PeaPodOS-Arduino/src/Actuator.cpp +++ b/software/PeaPodOS-Arduino/src/Actuator.cpp @@ -1,28 +1,72 @@ -#include "Arduino.h" +#include "Base.h" #include "Actuator.h" -/** - * Constructor. - * @param name - Meaningful, readable name of the sensor. - * @param id - Identifying name of the associated hardware. - * @param evname - Name of the environment variable the sensor records. - **/ -Actuator::Actuator(String name, String id, String evname){ - this->id = id; - this->name = name; - this->evname = evname; - //just in case - this->evname.toLowerCase(); - this->evname.replace(' ','-'); +Actuator::Actuator(actuatorid_t actuatorid, float failtarget) { + this->actuatorid = actuatorid; + state.error = ERR_NONE; + state.debug = DS_DISABLED; + + // Default start target + target = this->failtarget = failtarget; +} + +ActuatorState* Actuator::begin(void) { + state.error = initialize(); + if (state.error > ERR_NONE) { + // Failed + state.debug = DS_DISABLED; + } else { + // Success + state.debug = DS_INITIALIZED; + // Perform first update + update(); + } + return &state; } -void Actuator::updateActuator(){ - if(this->ready){ - this->update(); +ActuatorState* Actuator::update(void) { + // Check state preconditions, and do not attempt set if the target hasn't changed + if (state.error < ERR_FATAL && state.debug >= DS_INITIALIZED) { + if (state.lasttarget != target) { + // Attempt to set actuator state from target + state.error = set(state.lasttarget); + + switch (state.error) { + case ERR_NONE: + // Success! + state.debug = DS_SUCCESS; + state.lasttarget = target; + break; + + case ERR_WARNING: + // Set didn't go as planned, non-fatal + // DO NOT UPDATE STATE VALUES + break; + + case ERR_FATAL: + // Set failed catastrophically + state.debug = DS_DISABLED; + + // Failsafe + set(failtarget); + break; + } + } else { + // No `target` updates since last `update()` call + state.debug = DS_WAITING; } + } + return &state; +} + +ActuatorState* Actuator::getState(void) { + return &state; +} + +actuatorid_t Actuator::getID(void) { + return actuatorid; } -bool Actuator::begin(){ - this->ready = init(); - return ready; +void Actuator::setTarget(float target) { + this->target = target; } \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/Actuator.h b/software/PeaPodOS-Arduino/src/Actuator.h index 8ba0aab1..ccaa9d6a 100644 --- a/software/PeaPodOS-Arduino/src/Actuator.h +++ b/software/PeaPodOS-Arduino/src/Actuator.h @@ -1,32 +1,80 @@ -#ifndef Actuator_H -#define Actuator_H +#ifndef ACTUATOR_H +#define ACTUATOR_H -#include "Arduino.h" +#include "Base.h" -class Actuator{ - protected: - // Wrapper functions - virtual void update() = 0; - virtual bool init() = 0; - //Has begin() been performed by THIS object? - bool ready = false; +// Actuator identifiers +typedef enum actuatorid_t { + ACTUATOR_LED, +} actuatorid_t; + +// All actuator state info +typedef struct ActuatorState { + // Error level + errorlevel_t error; + + // Other state information + debuglevel_t debug; + + // Last successfully set target - is set to the value of `target` on successful `set()` + float lasttarget; +} ActuatorState; + +class Actuator { public: /** - * Hardware ID. - */ - String id; + * @param actuatorid Identifier + * @param failtarget Target that the actuator should be set to in case of failure + */ + Actuator(actuatorid_t actuatorid, float failtarget); + /** - * Meaningful, readable name of the actuator. - */ - String name; + * Wrapper function for `set()`. Checks debug state (initialized). + * @returns Pointer to actuator state + */ + ActuatorState* update(void); + /** - * Preformatted (lowercase and underscored) name of the environment variable the actuator controls. - */ - String evname; + * Wrapper function for `initialize()`. Sets debug state to indicate initialization success or failure, and also performs a first `update()`. + * @return Pointer to actuator state + */ + ActuatorState* begin(void); + + // @return Pointer to actuator state + ActuatorState* getState(void); + + // @return Actuator identifier + actuatorid_t getID(void); + + // @param target Actuator target + void setTarget(float target); + + protected: + /** + * Initializes actuator. To be implemented by the child class. + * @return Error level for this initialization attempt + */ + virtual errorlevel_t initialize(void) = 0; + + /** + * Sets actuator state. To be implemented by the child class. + * @param target Target for this actuator + * @return Error level for this set attempt + */ + virtual errorlevel_t set(float target) = 0; + + private: + // Stores all the latest state data for this actuator. + ActuatorState state; + + // Actuator identifier + actuatorid_t actuatorid; + + // Failsafe target + float failtarget; + + // Target state value for this actuator float target; - Actuator(String name, String id, String evname); - void updateActuator(); - bool begin(); }; -#endif \ No newline at end of file +#endif diff --git a/software/PeaPodOS-Arduino/src/Base.h b/software/PeaPodOS-Arduino/src/Base.h new file mode 100644 index 00000000..350b0cf2 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/Base.h @@ -0,0 +1,22 @@ +#ifndef BASE_H +#define BASE_H + +#include "Arduino.h" + +// Degree of error occurred +typedef enum errorlevel_t { + ERR_NONE, + ERR_WARNING, + ERR_FATAL, // NOTE: Implies debug level 'DISABLED' +} errorlevel_t; + +// General state information - On error level > 0, indicates last successful state +typedef enum debuglevel_t { + // NOTE: The various debug states imply the value of state.data as follows: + DS_DISABLED, // state.data[*].value == NULL + DS_INITIALIZED, // state.data[*].value == NULL + DS_WAITING, // state.data[*].value != NULL, age > 0 + DS_SUCCESS, // state.data[*].value != NULL, age == 0 +} debuglevel_t; + +#endif \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/K30.cpp b/software/PeaPodOS-Arduino/src/K30.cpp deleted file mode 100644 index 9afa81eb..00000000 --- a/software/PeaPodOS-Arduino/src/K30.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "Arduino.h" -#include "K30.h" -#include "Wire.h" - - -K30::K30(TwoWire *wire, uint16_t i2c_address) : Sensor("CO2 Sensor", "K30", "ppm CO2", 2000) { - this->wire = wire; - _i2c_address = i2c_address; -} - -bool K30::init(){ - wire->begin(); - return true; -} - -float K30::read() -{ - byte recValue[4] = {0,0,0,0}; - - // Instruct to take a reading - wire->beginTransmission(_i2c_address); - wire->write(0x22); - wire->write(0x00); - wire->write(0x08); - wire->write(0x2A); - wire->endTransmission(); - delay(30); - - // Request reading - wire->requestFrom(_i2c_address,4); - delay(20); - - byte i=0; - while(wire->available()) - { - recValue[i] = wire->read(); - i++; - } - - byte checkSum = recValue[0] + recValue[1] + recValue[2]; - - if (checkSum == recValue[3]){ - return (recValue[1] << 8) + recValue[2]; - } - return NAN; -} diff --git a/software/PeaPodOS-Arduino/src/K30.h b/software/PeaPodOS-Arduino/src/K30.h deleted file mode 100644 index 67ff96d7..00000000 --- a/software/PeaPodOS-Arduino/src/K30.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef K30_h -#define K30_h - -#include "Arduino.h" -#include "Wire.h" -#include "Sensor.h" - -#define K30_DEFAULT_ADDR 0x68 - -extern TwoWire Wire; - -class K30 : public Sensor -{ - public: - K30(TwoWire *wire = &Wire, uint16_t i2c_address = K30_DEFAULT_ADDR); - private: - float read() override; - bool init() override; - int _i2c_address; - TwoWire *wire; -}; -#endif diff --git a/software/PeaPodOS-Arduino/src/LED.cpp b/software/PeaPodOS-Arduino/src/LED.cpp deleted file mode 100644 index 77152682..00000000 --- a/software/PeaPodOS-Arduino/src/LED.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "LED.h" - -LED::LED(uint8_t pin, led_color_t color) : Actuator(parseLED(color), String("CREE " + parseLED(color)), "Lighting"){ - this->pin = pin; - this->color = color; -} - -String parseLED(led_t led){ - switch(led){ - case LED_BLUE: - return String("Royal Blue LED"); - case LED_COOL: - return String("Cool White LED"); - case LED_WARM: - return String("Warm White LED"); - case LED_RED: - return String("Photo Red LED"); - case LED_FAR: - return String("Far Red LED"); - default: - return String("Unknown LED"); - } -} - -bool LED::init(){ - this->target = 0; - pinMode(this->pin, OUTPUT); - return true; -} - -void LED::update(){ - // Clamp to 0target = min(max(target, 0), 1); - Serial.println(this->target*255); - analogWrite(this->pin, this->target*255); -} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/LED.h b/software/PeaPodOS-Arduino/src/LED.h deleted file mode 100644 index fec8ca71..00000000 --- a/software/PeaPodOS-Arduino/src/LED.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef LED_H -#define LED_H - -#include "Actuator.h" - -typedef enum { - LED_BLUE, - LED_COOL, - LED_WARM, - LED_RED, - LED_FAR -} led_color_t; - -class LED : public Actuator { - public: - LED(uint8_t pin, led_color_t color); - private: - void update() override; - bool init() override; - /** - * PWM output pin. - * */ - uint8_t pin; - led_color_t color; -}; - -#endif \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/SHT31.cpp b/software/PeaPodOS-Arduino/src/SHT31.cpp deleted file mode 100644 index 88e97348..00000000 --- a/software/PeaPodOS-Arduino/src/SHT31.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "SHT31.h" - -/** - * One constructor, begin, and error checks all in one! solid. - * Assumes serial is ready. - * */ -SHT31::SHT31(TwoWire *wire){ - _wire = wire; - humidity = NAN; - temp = NAN; -} - -bool SHT31::begin(uint8_t i2caddr){ - _wire->begin(); - _i2caddr = i2caddr; - reset(); - begun = (readStatus() != 0xFFFF); - return begun; -} - -uint16_t SHT31::readStatus(void) { - writeCommand(SHT31_READSTATUS); - _wire->requestFrom(_i2caddr, (uint8_t)3); - uint16_t stat = _wire->read(); - stat <<= 8; - stat |= _wire->read(); - // Serial.println(stat, HEX); - return stat; -} - -void SHT31::reset(void) { - writeCommand(SHT31_SOFTRESET); - delay(10); -} - -void SHT31::heater(bool h) { - if (h) - writeCommand(SHT31_HEATEREN); - else - writeCommand(SHT31_HEATERDIS); - delay(1); -} - -/*! - * @brief Return sensor heater state - * @return heater state (TRUE = enabled, FALSE = disabled) - */ -bool SHT31::isHeaterEnabled() { - uint16_t regValue = readStatus(); - return (bool)bitRead(regValue, SHT31_REG_HEATER_BIT); -} - -float SHT31::readTemperature(void) { - if (!readTempHum()){ - return NAN; - } - return temp; -} - -float SHT31::readHumidity(void) { - if (!readTempHum()){ - return NAN; - } - return humidity; -} - -/** - * Performs a CRC8 calculation on the supplied values. - * - * @param data Pointer to the data to use when calculating the CRC8. - * @param len The number of bytes in 'data'. - * - * @return The computed CRC8 value. - */ -static uint8_t crc8(const uint8_t *data, int len) { - /* - * - * CRC-8 formula from page 14 of SHT spec pdf - * - * Test data 0xBE, 0xEF should yield 0x92 - * - * Initialization data 0xFF - * Polynomial 0x31 (x8 + x5 +x4 +1) - * Final XOR 0x00 - */ - - const uint8_t POLYNOMIAL(0x31); - uint8_t crc(0xFF); - - for (int j = len; j; --j) { - crc ^= *data++; - - for (int i = 8; i; --i) { - crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1); - } - } - return crc; -} - -bool SHT31::readTempHum(void) { - uint8_t readbuffer[6]; - - writeCommand(SHT31_MEAS_HIGHREP); - - delay(20); - _wire->requestFrom(_i2caddr, sizeof(readbuffer)); - if (_wire->available() != sizeof(readbuffer)) - return false; - for (size_t i = 0; i < sizeof(readbuffer); i++) { - readbuffer[i] = _wire->read(); - } - - if (readbuffer[2] != crc8(readbuffer, 2) || - readbuffer[5] != crc8(readbuffer + 3, 2)) - return false; - - int32_t stemp = (int32_t)(((uint32_t)readbuffer[0] << 8) | readbuffer[1]); - // simplified (65536 instead of 65535) integer version of: - // temp = (stemp * 175.0f) / 65535.0f - 45.0f; - stemp = ((4375 * stemp) >> 14) - 4500; - temp = (float)stemp / 100.0f; - - uint32_t shum = ((uint32_t)readbuffer[3] << 8) | readbuffer[4]; - // simplified (65536 instead of 65535) integer version of: - // humidity = (shum * 100.0f) / 65535.0f; - shum = (625 * shum) >> 12; - humidity = (float)shum / 100.0f; - - return true; -} - -void SHT31::writeCommand(uint16_t cmd) { - _wire->beginTransmission(_i2caddr); - _wire->write(cmd >> 8); - _wire->write(cmd & 0xFF); - _wire->endTransmission(); -} - -bool SHT31::hasBegun(){ - return begun; -} - -//Yatta yatta - -SHT31_temp::SHT31_temp(SHT31 *sensor) : Sensor("Temperature Sensor", "SHT31", "Air Temperature", 1000){ - this->sensor = sensor; -} - -float SHT31_temp::read(){ - return sensor->readTemperature(); -} - -bool SHT31_temp::init(){ - return sensor->hasBegun() || sensor->begin(); -} - -SHT31_hum::SHT31_hum(SHT31 *sensor) : Sensor("Humidity Sensor", "SHT31", "Air Humidity", 1000){ - this->sensor = sensor; -} - -float SHT31_hum::read(){ - return sensor->readHumidity(); -} - -bool SHT31_hum::init(){ - return sensor->hasBegun() || sensor->begin(); -} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/SHT31.h b/software/PeaPodOS-Arduino/src/SHT31.h deleted file mode 100644 index e25b138a..00000000 --- a/software/PeaPodOS-Arduino/src/SHT31.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef SHT31_H -#define SHT31_H - -#include "Arduino.h" -#include "Sensor.h" -#include "Wire.h" - -#define SHT31_DEFAULT_ADDR 0x44 /**< SHT31 Default Address */ -#define SHT31_MEAS_HIGHREP_STRETCH \ - 0x2C06 /**< Measurement High Repeatability with Clock Stretch Enabled */ -#define SHT31_MEAS_MEDREP_STRETCH \ - 0x2C0D /**< Measurement Medium Repeatability with Clock Stretch Enabled */ -#define SHT31_MEAS_LOWREP_STRETCH \ - 0x2C10 /**< Measurement Low Repeatability with Clock Stretch Enabled*/ -#define SHT31_MEAS_HIGHREP \ - 0x2400 /**< Measurement High Repeatability with Clock Stretch Disabled */ -#define SHT31_MEAS_MEDREP \ - 0x240B /**< Measurement Medium Repeatability with Clock Stretch Disabled */ -#define SHT31_MEAS_LOWREP \ - 0x2416 /**< Measurement Low Repeatability with Clock Stretch Disabled */ -#define SHT31_READSTATUS 0xF32D /**< Read Out of Status Register */ -#define SHT31_CLEARSTATUS 0x3041 /**< Clear Status */ -#define SHT31_SOFTRESET 0x30A2 /**< Soft Reset */ -#define SHT31_HEATEREN 0x306D /**< Heater Enable */ -#define SHT31_HEATERDIS 0x3066 /**< Heater Disable */ -#define SHT31_REG_HEATER_BIT 0x0d /**< Status Register Heater Bit */ - -extern TwoWire Wire; /**< Forward declarations of Wire for board/variant - combinations that don't have a default 'Wire' */ - -/** - * Driver for the Adafruit SHT31-D Temperature and Humidity breakout board. - */ -class SHT31 { -public: - /** - * Constructor. - */ - SHT31(TwoWire *theWire = &Wire); - - bool begin(uint8_t i2caddr = SHT31_DEFAULT_ADDR); - - /** - * Gets a single temperature reading from the sensor. - * - * @return A float value indicating the temperature. - */ - float readTemperature(void); - - /** - * Gets a single relative humidity reading from the sensor. - * - * @return A float value representing relative humidity. - */ - float readHumidity(void); - - /** - * Gets the current status register contents. - * - * @return The 16-bit status register. - */ - uint16_t readStatus(void); - - /** - * Performs a reset of the sensor to put it into a known state. - */ - void reset(void); - - /** - * Enables or disabled the heating element. - * - * @param h True to enable the heater, False to disable it. - */ - void heater(bool h); - - /** - * Gets the current status register heater bit. - * - * @return Boolean value, True = enabled, False = disabled. - */ - bool isHeaterEnabled(); - - TwoWire *_wire; /**< Wire object */ - - bool hasBegun(); - -protected: - bool begun = false; -private: - /** - * Placeholder to track humidity internally. - */ - float humidity; - - /** - * Placeholder to track temperature internally. - */ - float temp; - - /** - * Placeholder to track the I2C address. - */ - uint8_t _i2caddr; - - /** - * Internal function to perform a temp + humidity read. - * - * @return True if successful, otherwise false. - */ - bool readTempHum(void); - - /** - * Internal function to perform and I2C write. - * - * @param cmd The 16-bit command ID to send. - */ - void writeCommand(uint16_t cmd); - - /** - * Internal function to read data over the I2C bus. - * - * @return True if successful, otherwise False. - */ - bool readData(void); -}; - -//WRAPPER CLASSES - Sensor derivatives do define "_read()" using ONE SHT31 object. - -class SHT31_temp : public Sensor{ - private: - SHT31 *sensor; - float read() override; - bool init() override; - public: - SHT31_temp(SHT31 *sensor); -}; - -class SHT31_hum : public Sensor{ - private: - SHT31 *sensor; - float read() override; - bool init() override; - public: - SHT31_hum(SHT31 *sensor); -}; - -#endif diff --git a/software/PeaPodOS-Arduino/src/Sensor.cpp b/software/PeaPodOS-Arduino/src/Sensor.cpp index e6559504..12a6a020 100644 --- a/software/PeaPodOS-Arduino/src/Sensor.cpp +++ b/software/PeaPodOS-Arduino/src/Sensor.cpp @@ -1,51 +1,95 @@ -#include "Arduino.h" +#include "Base.h" #include "Sensor.h" -/** - * Constructor. - * @param _name - Meaningful, readable name of the sensor. - * @param _id - Identifying name of the associated hardware. - * @param _evname - Name of the environment variable the sensor records. - * @param _delta - The minimum time (in ms) between sensor reads. - * @param __read - Pointer to the hardware-level read function. - * @param _args - Pointer to static arguments to pass to hardware-level read function. - **/ -Sensor::Sensor(String name, String id, String evname, uint16_t delta){ - this->id = id; - this->name = name; - this->evname = evname; - //"Air Temperature" -> "air-temperature" - this->evname.toLowerCase(); - this->evname.replace(' ','-'); - this->delta = delta; - //Buffer - Resets the last read time to the current time - lastread = millis(); +Sensor::Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t delta) { + this->sensorid = sensorid; + this->delta = delta; + + // Initial error and debug states + state.error = ERR_NONE; + state.debug = DS_DISABLED; + + // Allocate data state + state.data = (t_datapoint*)malloc(sizeof(t_datapoint)*(setup->numdata)); + state.numdata = setup->numdata; + for (int i = 0; i < setup->numdata; i++) { + // Since setup->labels[i] is a const char*, we can just reassign our pointer + state.data[i].label = setup->labels[i]; + state.data[i].value = NULL; + } } -/** - * Wrapper for hardware-level read function. Checks that enough time has passed between reads, and checks for `ready` flag and types. - * @return Sensor read, or nan. Usually nan. Like 99% of the time nan. - **/ -float Sensor::getRead(){ - float result = NAN; - //Has enough time passed between reads? - if(ready && millis()-lastread > delta){ - result = read(); - //Reset the last read time to now - //BUFFER - Happens post-read - lastread = millis(); - cachedread = result; - } - return result; +SensorState* Sensor::begin(void) { + state.error = initialize(); + if (state.error > ERR_NONE) { + // Failed + state.debug = DS_DISABLED; + } else { + // Success + state.debug = DS_INITIALIZED; + // Refresh read delay + state.timestamp = millis(); + } + return &state; } -bool Sensor::begin(){ - ready = init(); - return ready; +SensorState* Sensor::update(void) { + // Check state preconditions + if (state.error < ERR_FATAL && state.debug >= DS_INITIALIZED) { + // Check timing + if (millis() - lastread > delta) { + + // Allocate new data buffer on heap + float* buffer = (float*)malloc(sizeof(float)*state.numdata); + + // Read and refresh read delay + state.error = read(&buffer, state.numdata); + lastread = millis(); + + switch (state.error) { + case ERR_NONE: + // Success! + // Indicate that new data is available + state.debug = DS_SUCCESS; + state.timestamp = lastread; + + // Make state data pointers point to the buffer + for (int i = 0; i < state.numdata; i++) { + // Free old data buffer + free(state.data[i].value); + // Point to new data buffer + state.data[i].value = &buffer[i]; + } + break; + + case ERR_WARNING: + // Read didn't go as planned, non-fatal + // DO NOT UPDATE ANY STATE VALUES + break; + + case ERR_FATAL: + // Read failed catastrophically + state.debug = DS_DISABLED; + + // Free data buffer and reset to NULL + for (int i = 0; i < state.numdata; i++) { + free(state.data[i].value); + state.data[i].value = NULL; + } + break; + } + } else { + // Attempted to update between valid read cycles + state.debug = DS_WAITING; + } + } + return &state; } -// Class = Type -// Object = Variable -// Constructor: function on class that creates the object (same name as class) -// `this` keyword +SensorState* Sensor::getState(void) { + return &state; +} +sensorid_t Sensor::getID(void) { + return sensorid; +} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/Sensor.h b/software/PeaPodOS-Arduino/src/Sensor.h index 5f5c0ed7..197a8fcf 100644 --- a/software/PeaPodOS-Arduino/src/Sensor.h +++ b/software/PeaPodOS-Arduino/src/Sensor.h @@ -1,38 +1,103 @@ -#ifndef Sensor_H -#define Sensor_H +#ifndef SENSOR_H +#define SENSOR_H -#include "Arduino.h" +#include "Base.h" -class Sensor{ - private: - //The minimum time (in ms) between sensor reads. - uint16_t delta; - //The last time the sensor was read, ms since program start. - unsigned long lastread; - protected: - virtual float read() = 0; - virtual bool init() = 0; - //Has begin() been performed by THIS object? - bool ready = false; - float cachedread; +// Sensor identifiers +typedef enum sensorid_t { + SENSOR_SHT31, + SENSOR_K30, +} sensorid_t; + +// Single datapoint +typedef struct t_datapoint { + // Value (pointer to heap memory) + float* value; + + // Dataset label (string literal) + const char* label; +} t_datapoint; + +// All sensor state info + latest datum - used as the "cache" in the sensor object. +typedef struct SensorState { + // Error level + errorlevel_t error; + + // Other state information + debuglevel_t debug; + + // Last data update (millis since program start) - NOT THE SAME AS `lastread`, an unsuccessful read attempt will NOT update `state.timestamp`. + uint32_t timestamp; + + // Array of datapoints + t_datapoint* data; + + // Length of `data` array + uint8_t numdata; +} SensorState; + +typedef struct t_sensordatasetup { + // Number of datasets recorded by this sensor + const uint8_t numdata; + + // Array of string literals corresponding to the labels for each dataset + const char** labels; +} t_sensordatasetup; + +class Sensor { public: /** - * Hardware ID. - */ - String id; - /** - * Meaningful, readable name of the sensor. - */ - String name; + * Sensor interface constructor. + * @param sensor Identifier + * @param setup Pointer to datasets setup + * @param delta Minimum delay (in milliseconds) between sensor read attempts + */ + Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t delta); + + /** Wrapper function for `initialize()`. Sets debug state to indicate initialization success or failure. + * @return Pointer to sensor state + */ + SensorState* begin(void); + + /** + * Wrapper function for `read()`. Checks debug state (initialized), ensures `delta` compliance, and sets `lastread` and `data` flags on read success. + * @return Pointer to sensor state + */ + SensorState* update(void); + + // @return Pointer to sensor state + SensorState* getState(void); + + // @return Sensor identifier + sensorid_t getID(void); + + protected: + /** + * Initializes sensor. To be implemented by the child class. + * @return Error level for this initialization attempt + */ + virtual errorlevel_t initialize(void) = 0; + /** - * Preformatted (lowercase and underscored) name of the environment variable the sensor records. - */ - String evname; - Sensor(String name, String id, String evname, uint16_t delta); - //Calls read() and manages delta - float getRead(); - //Sets ready in accordance with init() - bool begin(); + * Reads data from sensor. To be implemented by the child class. + * @param data Pointer to the data array to be read into + * @param numdata Length of the data array + * @return Error level for this read attempt + */ + virtual errorlevel_t read(float** data, uint8_t numdata) = 0; + + private: + // Stores all the latest state data for this sensor. + SensorState state; + + // Sensor identifier + sensorid_t sensorid; + + // Minimum delay (in milliseconds) between sensor read attempts + uint32_t delta; + + // Last read attempt (milliseconds since program start) - NOT THE SAME AS `state.timestamp`, an unsuccessful read attempt will still update `lastread`. + uint32_t lastread; }; -#endif \ No newline at end of file +#endif diff --git a/software/PeaPodOS-Arduino/src/actuators/LED.cpp b/software/PeaPodOS-Arduino/src/actuators/LED.cpp new file mode 100644 index 00000000..3253741a --- /dev/null +++ b/software/PeaPodOS-Arduino/src/actuators/LED.cpp @@ -0,0 +1,43 @@ +#include "Arduino.h" +#include "../Base.h" +#include "../Actuator.h" +#include "LED.h" + +LED::LED(uint8_t pin, led_color_t color) : Actuator(ACTUATOR_LED, 0) { + this->pin = pin; + this->color = color; +} + +errorlevel_t LED::initialize(void) { + pinMode(this->pin, OUTPUT); + return ERR_NONE; +} + +errorlevel_t LED::set(float target) { + // Clamp to 0 < x < 1 + target = min(max(target, 0), 1); + analogWrite(this->pin, target * 255); + return ERR_NONE; +} + +/** + * UNUSED. + * @param led Enum representation of the LED. + * @return String representation of the LED. + */ +// static String parseLED(led_color_t led) { +// switch (led) { +// case LEDCOLOR_BLUE: +// return String("Royal Blue LED"); +// case LEDCOLOR_COOL: +// return String("Cool White LED"); +// case LEDCOLOR_WARM: +// return String("Warm White LED"); +// case LEDCOLOR_RED: +// return String("Photo Red LED"); +// case LEDCOLOR_FAR: +// return String("Far Red LED"); +// default: +// return String("Unknown LED"); +// } +// } \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/actuators/LED.h b/software/PeaPodOS-Arduino/src/actuators/LED.h new file mode 100644 index 00000000..a002a9ac --- /dev/null +++ b/software/PeaPodOS-Arduino/src/actuators/LED.h @@ -0,0 +1,35 @@ +#ifndef LED_H +#define LED_H + +#include "Arduino.h" +#include "../Base.h" +#include "../Actuator.h" + +typedef enum { + LEDCOLOR_BLUE, + LEDCOLOR_COOL, + LEDCOLOR_WARM, + LEDCOLOR_RED, + LEDCOLOR_FAR +} led_color_t; + +class LED : public Actuator { + public: + /** + * Constructor. + * @param pin PWM pin for controlling this LED. + * @param color Color of this LED (enum). + */ + LED(uint8_t pin, led_color_t color); + private: + errorlevel_t initialize(void) override; + errorlevel_t set(float target) override; + + // PWM output pin. + uint8_t pin; + + // LED color. + led_color_t color; +}; + +#endif diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.cpp b/software/PeaPodOS-Arduino/src/sensors/K30.cpp new file mode 100644 index 00000000..32791906 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/sensors/K30.cpp @@ -0,0 +1,55 @@ +#include "Arduino.h" +#include "../Base.h" +#include "../Sensor.h" +#include "Wire.h" +#include "K30.h" + +// Data setup +static const char* labels[1] = { "co2_ppm" }; +static const t_sensordatasetup datasetup = { + 1, + labels +}; + +K30::K30(TwoWire* wire, uint8_t address) : Sensor(SENSOR_K30, &datasetup, K30_DELTA) { + this->wire = wire; + this->address = address; +} + +errorlevel_t K30::initialize(void) { + wire->begin(); + return ERR_NONE; +} + +errorlevel_t K30::read(float** data, uint8_t numdata) { + byte recValue[4] = {0,0,0,0}; + + // Instruct to take a reading + wire->beginTransmission(address); + wire->write(0x22); + wire->write(0x00); + wire->write(0x08); + wire->write(0x2A); + wire->endTransmission(); + delay(30); + + // Request reading + wire->requestFrom(address, (uint8_t)4); + delay(20); + + // Store reading + for(int i = 0; wire->available() && i < 4; i++) { + recValue[i] = wire->read(); + } + + // Checksum and return + byte checkSum = recValue[0] + recValue[1] + recValue[2]; + + if (checkSum != recValue[3]) { + return ERR_WARNING; + } + + (*data)[0] = (float)((recValue[1] << 8) + recValue[2]); + + return ERR_NONE; +} diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.h b/software/PeaPodOS-Arduino/src/sensors/K30.h new file mode 100644 index 00000000..93337dc6 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/sensors/K30.h @@ -0,0 +1,35 @@ +#ifndef K30_H +#define K30_H + +#include "../Base.h" +#include "../Sensor.h" +#include "Wire.h" + +#define K30_DEFAULT_ADDR 0x68 +#define K30_DELTA 2000 + +// Import the default `Wire` interface provided by Arduino. +extern TwoWire Wire; + +// Interface for the SenseAir K30 Extended Range CO2 sensor. +class K30 : public Sensor { + public: + /** + * Constructor. + * @param wire I2C interface. Defaults to the provided `Wire` interface (by reference). + * @param address Address of the K30 on the I2C bus. Defaults to 0x68 (hardware default). + */ + K30(TwoWire* wire = &Wire, uint8_t address = K30_DEFAULT_ADDR); + private: + errorlevel_t initialize(void) override; + + errorlevel_t read(float** data, uint8_t numdata) override; + + // Address of the K30 on the I2C bus. + uint8_t address; + + // I2C interface. + TwoWire* wire; +}; + +#endif diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp b/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp new file mode 100644 index 00000000..e5fa7a78 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp @@ -0,0 +1,145 @@ +#include "Arduino.h" +#include "../Base.h" +#include "../Sensor.h" +#include "Wire.h" +#include "SHT31.h" + +static uint8_t crc8(const uint8_t *data, int len); + +// Data setup +static const char* labels[2] = { "air_temperature", "air_humidity" }; +static const t_sensordatasetup datasetup = { + 2, + labels +}; + +// CONSTRUCTOR + +SHT31::SHT31(TwoWire *wire, uint8_t address) : Sensor(SENSOR_SHT31, &datasetup, SHT31_DELTA) { + this->wire = wire; + this->address = address; +} + +// PUBLIC METHODS + +errorlevel_t SHT31::initialize(void) { + wire->begin(address); + reset(); + if (readStatus() == 0xFFFF) { + return ERR_FATAL; + } else { + return ERR_NONE; + } +} + +errorlevel_t SHT31::read(float** data, uint8_t numdata) { + uint8_t buffer[6]; + + // Ready up + writeCommand(SHT31_MEAS_HIGHREP); + delay(20); + wire->requestFrom(address, sizeof(buffer)); + + // Invalid size + if (wire->available() != sizeof(buffer)) { + return ERR_WARNING; + } + + // Read into buffer + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = wire->read(); + } + + // Checksum + if (buffer[2] != crc8(buffer, 2) || buffer[5] != crc8(buffer + 3, 2)) { + return ERR_WARNING; + } + + // Temperature conversion + int32_t stemp = (int32_t)(((uint32_t)buffer[0] << 8) | buffer[1]); + stemp = ((4375 * stemp) >> 14) - 4500; + (*data)[0] = (float)stemp / 100.0f; + + // Humidity conversion + uint32_t shum = ((uint32_t)buffer[3] << 8) | buffer[4]; + shum = (625 * shum) >> 12; + (*data)[1] = (float)shum / 100.0f; + + return ERR_NONE; +} + +// UTILITY FUNCTIONS + +void SHT31::writeCommand(uint16_t cmd) { + wire->beginTransmission(address); + wire->write(cmd >> 8); + wire->write(cmd & 0xFF); + wire->endTransmission(); +} + +uint16_t SHT31::readStatus(void) { + writeCommand(SHT31_READSTATUS); + wire->requestFrom(address, (uint8_t)3); + uint16_t stat = wire->read(); + stat <<= 8; + stat |= wire->read(); + return stat; +} + +void SHT31::reset(void) { + writeCommand(SHT31_SOFTRESET); + delay(10); +} + +/** + * Performs a CRC8 calculation on the supplied values. + * + * @param data Pointer to the data to use when calculating the CRC8. + * @param len The number of bytes in 'data'. + * + * @return The computed CRC8 value. + */ +static uint8_t crc8(const uint8_t *data, int len) { + /* + * + * CRC-8 formula from page 14 of SHT spec pdf + * + * Test data 0xBE, 0xEF should yield 0x92 + * + * Initialization data 0xFF + * Polynomial 0x31 (x8 + x5 +x4 +1) + * Final XOR 0x00 + */ + + const uint8_t POLYNOMIAL(0x31); + uint8_t crc(0xFF); + + for (int j = len; j; --j) { + crc ^= *data++; + + for (int i = 8; i; --i) { + crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1); + } + } + return crc; +} + +// UNUSED + +// void SHT31::heater(bool h) { +// if (h) { +// writeCommand(SHT31_HEATEREN); +// } else { +// writeCommand(SHT31_HEATERDIS); +// } +// delay(1); +// } + +/** + * @brief Return sensor heater state + * @return heater state (TRUE = enabled, FALSE = disabled) + */ +// bool SHT31::isHeaterEnabled() { +// uint16_t regValue = readStatus(); +// return (bool)bitRead(regValue, SHT31_REG_HEATER_BIT); +// } diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.h b/software/PeaPodOS-Arduino/src/sensors/SHT31.h new file mode 100644 index 00000000..7b310785 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/sensors/SHT31.h @@ -0,0 +1,77 @@ +#ifndef SHT31_H +#define SHT31_H + +#include "Arduino.h" +#include "../Base.h" +#include "../Sensor.h" +#include "Wire.h" + +#define SHT31_DEFAULT_ADDR 0x44 // SHT31 Default Address +#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Measurement High Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_MEDREP_STRETCH 0x2C0D // Measurement Medium Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_LOWREP_STRETCH 0x2C10 // Measurement Low Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_HIGHREP 0x2400 // Measurement High Repeatability with Clock Stretch Disabled +#define SHT31_MEAS_MEDREP 0x240B // Measurement Medium Repeatability with Clock Stretch Disabled +#define SHT31_MEAS_LOWREP 0x2416 // Measurement Low Repeatability with Clock Stretch Disabled +#define SHT31_READSTATUS 0xF32D // Read Out of Status Register +#define SHT31_CLEARSTATUS 0x3041 // Clear Status +#define SHT31_SOFTRESET 0x30A2 // Soft Reset +#define SHT31_HEATEREN 0x306D // Heater Enable +#define SHT31_HEATERDIS 0x3066 // Heater Disable +#define SHT31_REG_HEATER_BIT 0x0d // Status Register Heater Bit +#define SHT31_DELTA 1000 + +// Import the default `Wire` interface provided by Arduino. +extern TwoWire Wire; + +// Interface for the Adafruit SHT31-D Temperature and Humidity breakout board. Does NOT inherit from `Sensor`. +class SHT31 : public Sensor { + public: + /** + * Constructor. + * @param wire I2C interface. Defaults to the provided `Wire` interface (by reference). + */ + SHT31(TwoWire* wire = &Wire, uint8_t address = SHT31_DEFAULT_ADDR); + + /** + * UNUSED + * Enables or disabled the heating element. + * @param h True to enable the heater, False to disable it. + */ + // void heater(bool h); + + /** + * UNUSED + * Gets the current status register heater bit. + * @return Boolean value, True = enabled, False = disabled. + */ + // bool isHeaterEnabled(void); + + private: + errorlevel_t initialize(void) override; + + errorlevel_t read(float** data, uint8_t numdata) override; + + /** + * Internal function to perform and I2C write. + * @param cmd The 16-bit command ID to send. + */ + void writeCommand(uint16_t cmd); + + /** + * Gets the current status register contents. + * @return The 16-bit status register. + */ + uint16_t readStatus(void); + + // Performs a reset of the sensor to put it into a known state. + void reset(void); + + // I2C address + uint8_t address; + + // Wire object + TwoWire* wire; +}; + +#endif diff --git a/software/README.md b/software/README.md index 1dc08cb8..209ddaa8 100644 --- a/software/README.md +++ b/software/README.md @@ -48,11 +48,12 @@ The following are performed on the Raspberry Pi: 3. *System Options > Boot/Auto Login* - Select `Console Autologin` 4. *Interface Options > SSH* - `Enable` SSH Server 5. *Interface Options > Serial Port* - `Disable` serial login shell, but `Enable` the serial port hardware - 6. *Localisation Options > Locale* (i.e. `en_US.UTF-8`) - 7. *Localisation Options > Timezone* (i.e. `US` > `Eastern`) - 8. *Localisation Options > Keyboard* (i.e. `Generic 105-key` > `English (US)` > `Default` > `No compose key`) - 9. Optional: *Advanced Options > Expand Filesystem* - 10. Reboot to save: `sudo reboot` + 6. *Performance Options > GPU Memory* - 256 (MB) + 7. *Localisation Options > Locale* (i.e. `en_US.UTF-8`) + 8. *Localisation Options > Timezone* (i.e. `US` > `Eastern`) + 9. *Localisation Options > Keyboard* (i.e. `Generic 105-key` > `English (US)` > `Default` > `No compose key`) + 10. Optional: *Advanced Options > Expand Filesystem* + 11. Reboot to save: `sudo reboot` > You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@peapod.local`, or with VS Code) @@ -163,12 +164,17 @@ The following are performed on the Raspberry Pi: (*Ctrl-O* to save, *Ctrl-X* to exit; *avrdude* can be located with `whereis avrdude`) -12. Import `.env` file with Firebase and auth keys (TBA), as well as the fields `SERIALPORT="/dev/ttyS0"` (Raspberry Pi Zero 2 W GPIO mini-UART) and `PASSWORD="raspberry"` with "raspberry" replaced with your password (used for `sudo` execution of *avrdude*). +12. Install the *UV4L* camera library: + 1. `curl https://www.linux-projects.org/listing/uv4l_repo/lpkey.asc | sudo apt-key add -` + 2. Add the package listings: `echo "deb https://www.linux-projects.org/listing/uv4l_repo/raspbian/stretch stretch main" | sudo tee /etc/apt/sources.list.d/uv4l.list` + 3. Update packages: `sudo apt-get update` + 4. Install the core library, the Raspberry Pi driver, extra scripts, adn WebRTC support: `sudo apt-get install uv4l uv4l-raspicam uv4l-raspicam-extras uv4l-webrtc-armv6` -13. Run the main program by executing `peapodos`. +13. Populate a `.env` file with Firebase and Google and/or GitHub auth keys (a template is provided as `.env.template`), as well as the field `SERIALPORT="/dev/ttyS0"` (Raspberry Pi Zero 2 W GPIO mini-UART). - +14. Run the main program by executing `peapodos`. + # Development ### Arduino CLI - Compilation and Upload diff --git a/software/assets/control_flow.png b/software/assets/control_flow.png index c15312e51efda387e832152c78c5e349e33a2cd3..2592d5fb54e62ac9607018f38e9786fe4ba95f02 100644 GIT binary patch literal 681711 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

}nx`Cnp6fgD~TNJd*=M zhB6o!xrmjZ!)`vnVGrURz#L} zt$Zdmk%VCQjAJV!|L1-)kP8ckahTG-*`PB>kmckX`M;s5E85cXCe~q=fHi!&QsnV^Q{u8!PV(*A5WnEXAqC~B>6fBHN@ScCYRMrk^>9pg!~Q^ z#k@J&dX<&#{*!?0Dgu-X(z1Y+Ry~Pq!^>}8IDW8Gq03tN7HIBkD!FOlD+hVVa&?5TUhGK zr}Fw@RO^Hy!33yFO22`|6uNO*J@q zx(gO}1*aDqs8b53{ytu$f5&opRRMGC_nJr(8$bF=M{LfPfJ!42R%TDl)YyhEg-WRHnAH}iVDlN&GXHkdCAfHPvR~5^$~9m=;w!D(A_5nF_R2rRBZiD^j}qD-{msWouh(AE|LuaZ$PSRTt*HcYlNAYfV9& zreTG^2v!31UZiJp@_8Bh?LmZYtMg|yZ}inPK7|l2(7IV^1C^$Iy1kAGLp$2Jtv{Va z&H0*CzL1Y15C^Q=9GTg=q@j|#_V8| z(RhvdoT8N6Xl=d4ht?|k@A7aKqJ$cxoCb`UBsrAXRygGr=>kZGG@dCrkt(UOa;Udp zmA5dmy7E#czfH;p+Ii6&m)pT}TF7V&s^zUby1g6~QflfnQlVz0?`*gu zb82=_ZP*g$M9`@4g*tCi_kL{_a#N8CokSOe&6nv4eyD@BoE;s;`Rbx&<>{ zYw4jf@SnZ#P2Tibw6e-I%{D0siKbeEMOp?RlnDq0^!WBhoGtEsP{!TUn4w~8erV7WvrOKqWHf!!o<-+&$d1j-aw_>J6 zmC#_Us;-Zcvqd_~D!O8`I7}@8I{ha_q1CXYIAQS4#|@zl_Pu3V1I=02Qdo_d<&Y;7 zE?9!Jh!#_;^%cJ`lq;4;7Vo4Cj=qSGOjmAWHF=t+Eu)h)?cJx8*}e#xtS-<8?_JYJ*pu-0Ip~a|=nKR5DmWN_`Re?% zVXr1=Mp9NUs@mn1(OV=HKMpC$bimA%qxO{MUi4zw!TS5v{UB<;k1p+IOEDf;m#O$OA)ryd4?`0bPQd4?ADOo6*9Cxy^T;=hsLOuQF@EJ(Wj&#aPkH_EwAdZ}R`rV5O{+zUTY4E#$~J18C)3C|k(dt& zl-YTHd#m%7TvE>`(uS-a?7pG1K-vQ9bK4bdhLbhb94Ux-UoxG&E<4-hRR$6XQ5yeO zP&ETsPNSX#+OuW9dPbuz`E#0%hvhLe{-hZfu}DkTobVjjs4~)q2s5G^OYr;*`Z3Xf zqv;`d!ql*b!5-F(QU=*M%;xAM3)ZDsB;;gG?C|yiDN-iF43~?0^qgaOuBtAwIZDot zj=%*J_34OKg)Ur)?0ok!0e&c#(e0AHE^1&BATALQy2gvH1=rqUT@&B%`q%g6R!sW2 zm9i;O+tK_2QRt(8s{I+Cv&aD>gr%O3 z)l5%oCrl7rrY5boKmUXO7OaH5V1#zyA^eGkr|@-M^Ep9uS?qR@-r%rpo- z)>%BaWZfb&v?arf`>jVxg-le3iieOGR7bRh_bZta=K2iVms+n@7bWT2UP$CrmXEd) z)-P+0>BG!+`|e9t^nI7R6EkVANc_~j@<0ARcarAb0aZ|UP{}D$ccL3w>3Kq+q3x|O zsNaWaH1;?-wD;W}0@4X;O?JPi%-LyQ^zznms9Fd6HL#rgo8w$6-6R zA@G-u4!QgaeJ&TqU%~q{=lI_-9)Aq@PM6ok=$Poj!lNS7lZ$zhEhCJV(dP-c@6jH( zpfxDj5P`U%qc(AWF<|BqYErjf)WqjwlPRRRV+w`9>!y??LrZO4th-CaOY@!Wh6JIDhRm-A7oko!WlQQ zqN=9cvx^V)%ywt~Cfhr`URtF3VwNnca`R)m6x_B3l~M$9hh#2e@ApSpSyO_+-oe2( zz`I+moQ^u6$P|lsf?VAVqtj5}}see_!`I zv0eu-iVhi69zY-Phk!E(Hqm_9uP|{T`qDvxc;~vOcqk0_e1~2yA;6Ka&5*2s@Q%Pn zAP4oaEN^nwJYo(V75vBub=1+TuOs4op%Az%+x;*chv z!0<+QxvH@7bufY;(lyWWK-QqJ-zZs=L}8k##C9pd2Qtc$5`L)S(qudoT$%q#;Lcs% zN53r^{bJoe#gQk3!E<^3(FFGBVv%yY%MvmD9{ktt(dPGUH_UN&1djfXTRF)*?VKs` zu)ps@R%h1{&ZS$HIbw#QxAut}&v1R>@QN=Uc|4nDsXBX)r}pvg-O}4MW?e5Rqu2^Ye!ODCea*2I`D^a^#)XIArR%KWIxv!E~SRS6+QGqEHmCbArBw`E9ukt8kqQ z4hFiPhL%+8jRT#b4V%`*IF3{Zc`)s3yFR)mNmFx<4`Yv)AHE#j zzEYckl;s^-Lw@U)(D$5hW~{lOFq z(^TBuyJB0_gAhNe(WR_4+J|gJKuGjW@yWZ!etUK`mJCY{d!YnT*nh#ME1F4L6-FqW z5{MK7os+sux}qt;s1obblRdVjp07l{^b4jc>Z{6UnZ8iccR@;C45IIdX{(tsb#l8r-iKyFRZg|lv5NT39nS_K5O*VD46(`<*_p_o##HTqyu)7rcV8uhq1ADAp{vv zMJ+q-2H{B%EfDy!iw|hmi`51YZ@PDp7Z}Xu@^I^ zLpTmQ4;%x*sX9-B|1#W2zo%dp;s0ocyGTj=q~imH@!Mv3$t;Sx+n9Qb-iw$R8EP~38ycWN|`sJH{T=oMcHz7wnM{5vp!@Y&hg9g9c6aIOUnM?;! z$dTu4%yO2!jHCcnL%mTO88kyt(6u5t$VSt|*a~38n(aGc2 z(g7ulLIwFbZhU-D^2tX|SQsC)$i9jMD2NpJPl52L6l&N(JC; zHFSG&qwHY&Bmxl{B^A#KW6lzodV<7A8N99`UoauBap2~GMtp-5Ht=rb4p|p;6=tIl z9az>t=;`SBKW^ZKaNQ7tP#vWwxM@ETA9^nBhrOX+7H|~+E0{J;XH4w*{49cO9IfOff8%97KnEqu@ zR!N^W7*)X8IET<9VnxYvC`vl8x?z7x@43l>L zn*TLIntS0&$SRRRQ^O2qw^}d}m$ESg>2?AFh^Kl9U@xNLsh0hQcZn8s~ky4A_)A2_ZjKLVZ;pp$E%vP@lo$TJ@b3dC~ zzrMNt85oLyNznyut!_)utc2`0HsO&c3DeSi@cjSS`VV-l`}chuzeFWvr6`+{r~pQs;55B0W6K! z#B{sb2`#6$lNZTJpWdPmvNbq;hX8&ejb{k+BE}`0`;cko5*{4Z)HCE_>fQDQ08B2B z#10BdBV!D*PFS@+of^i1O-0PO5>1YgN1mT<+m5TYZ$V2(g3B`Br^fF8ogc=ptjgae z{F2{TC-LWa`Ui_hgnAF)c|N9Y6a;jqG=wNr`}v=*vAsvhNU45!IIwJD2FE9CZg}7l zL>F6_tRkGBUntb+a{QdF>*`!cub%b_X{-Ff>Au^1Von16`Nd^|cyy?M@Ne0I= z4*++YZz)=|a2>7xa3!P5E{vbA#BAXuxcJAWGgo$>CaSimC`DIY+`>W&zukl1ze1fT z$EA1XW8&?S4w> zb=}jj3%~{{si$rGJwv3bB;=GA9!j@|6*2Mt?&nGe;n^jQ%Oq?*FSjrK6`X-r9N z;nku&v(|J^^F7r;6U}*RMfM-=F3)!p2L74`4o}!XdPOaS-4>&v0>Ta9?jMF;DD{f% zp}(`i!=MfyL$|0tg?A`S=I@r2wdiac5IdwZg}Sxh7AKMsEc5-fU=Iro!ie;X=&na4 zi3sd*zp;3Ctjr42ib*WQFF(CYnkOC#vYp-IiA<9-5-3FNoZE*Dou})pR=QGoJ*gTX z5hu|Esy(Rl;28|dMI2a7O`Gqu%hd3A6|D<#=l?O^s+SOPTynhu3r7;*C!ijWHqHYF z>hg=1C&X7Ko8!NKF1L8{*#74$YuC?Zo*)H?+j*9?wR>`_#|G8eo5`QQSp12j-2*92VYmbyjjTF9y0=ijiF)A@5>QO!o=SZl>(GA~ zsl##VCxX}R;&y1dF2pw7A2K8|zppLFr6hl=JO}bwP)97_TK7Nq_;ugB#6bI(KaHdH zM^a0ljLr`1D;Kc1E|)q%;nBt1T&qA{2e#T1Dc5qd#BRl_#lKx;cGluZN~_y$+7)L1QfY!7fC$`nGI}Hy+}%DJ zYXh}r78LI>Z|Z>WMOCdPh^A?Efu;$a;n}0NMt9C!%tMLhGuhiJ@sH;j)Lbgv?e!Fx zMu89QsGaC8dUB)(@tdxjGXdQrEhGB$s3)18p^v@w=WT9M>c&_}Qrs_M0-2g~@7|j6 z)!akM`wX->AenI5X0Fk6v0b<@LQYODoEV7@O8fq+DWdN7SJ9jPows|cxUpDCr{$#8 zZoJ(cMpbg@?Z8mg%81j6F9QSdfAx9JJkW*E!pgK`X(&Oto`_u%#~A5)s;GeIlUHh4 zUbeSe-hsu<%)%C**-XFPHz(a@6+(dL4yxWSes(G=kI|#e=-h9Wtg}Wc7mIb(7nxaD zzf5fHo1bnf>8f^eG$K6p!f-3+foCa*561h7CKh6LU0f--z=mawea~OsTYTxFc5vyF z@f0nGi?w7;LVr2xyKKm$$iL0bHjgz6^Ur6G+`irQdhXeW+Vm$n%#j_!evKxx6Rtta zSVx6Zx!raASe){ge%;*bT1&Iy_J?exbRvp2M{xAD^4F#%yCHYm6C5sI?kn8w2HP^h zhXsWaONYyZ*#u91Thj61GY@=e2kvO$e~oV15j64ND;Si=YVPj>;r!$4v@+?xKF5JQ zo%<}uE&FMwz|K8S>2I)|NZEC3S+|lYO^o^$rkaL_hi^DIV@(Hq7?thRav)9y!VP7?H>nJZ^aW#e)mZg$}b1TE30kJRx8=rUF$AP0-dq-VFk+Q;jE|XPD8+|yrmF&e)+4X)QDcGM`wYN8EzWcQ{ zj0pam3FzZgWq`g?wAYdngCIPA3$Y|k3Dilx3l|YAC?ro}t;zZA<(}qyNUd3h%07O6m@f#Yxg~Y1<=$v$ z%B6H6S9=de&pECJKh3UR_pq43UTuddo39{@5p!G02A40h-A?p+8l+<&;-6R3OMzv& zACO-9zE}dy6%R%*v?)Na=4*SpUf`HV`#nBicZ0?la|cmUmq-II*__ph>(#$hY6ozN z*g!tMzPSa0F*vBVWv}Nt* zQQw@201B^qwzWb~nVjWheQV!^NEZ1AQ)7LQjkf&%`|SS)s1_e9YbC=$DEZ}II-Rc}YiFc# zHbi0}yoG2<#1VwheNDLQ`<=saJ2Rs5Gda9{(eR^`pkJzR1KjtPMyv|ScM1CY|C_Me z(=jp{^z^OR$E=x*t&0ESrdsxP%&1=0>X>0+uBPW(qpMHm_}~1GjbiaKMfz@Y%Gv8=O%>RPe*#gSq zv);k1qEA6ab$WrboA|yT=3K9T%w8G|&I{M!W>#$*$P)XWH*@jkz{{3zys)18}J*e@&)oREw2#NGK1bJf~OeyypkJxkb{$A|PRC^~l&#zHA9SE2ie?T8{Z z6qP2AGJ`Rd&vf&$o9vK?%#AhXRN?ghwDYe8Nu(YT_uu&hEf?#bHPwjH(HK{vQ!cA^ zLsOO2iA`}w{_cQmbI_o)psoJDN>4WbQF^-M>4~`EA|&IL0Nx- zf>1sK4FTyT9X-8j(s)8bLOuUZDVB;~yFIEZe$f$E0=ow$MlH#eab^3Fx}D&vsxiol z&`}S4x#Kg-Uyj&-7ztNCadB<5TbLhxQ{~?1-p{zW>|14Bfq$?;Rc_%>)ZqSsK9wC9 z=*C)>jB_u|Pb8r~DXZ4_{B~J0SV$qLL~#9Il?alB z{BfczmTi_ZW#EKwtSK)qhd>#Iby-JciL=(vTPv=UB3b`orL~&FcuGCy;T2EvLXM~S zQ|=S4F!zn5#eYOiVGH8eML4iVq)aPDFB0Ejw2H=&yWPK&DWtU477buUTaC*9V`Ad^Oum{pRkJx_ZQ@iD{+3EbPT7zWb$%2ffuvaS?Ohzn{mkAtd=lAnI%>`C%61jFD8MvIj3o zOM7BO=y^|%HgUxNyj0GvImIRywP~B0#D0(yXqCfJ*#7=$UHt9cW{Q97Gaii3I~`$3 zvsoV%vD%C~5{`m6Pi+XWp%2=TSqQeH*SeYwLv2Du;{Q1SiPTC^>>C|^bcl?Do!tUr zQB9MH)1rx75^e%wb&W)Il?3Pa7+g7eAo#9YA#}~)$pwoJ;0g^qQ9xdM8BPCv!uHm7 z$G1>ZKfpFA?A~>Wn(zxwc~vLrShWwV7=+*{ap%?p!gzr>=WTI*@yj=Q7GMgXl!hUSRiPs`Y>mRz@UEV zcU&rDwdT0~Jq4o!NGk;b+I3x1RY#7)ex8dGQDuiRP5RRTOh#xH*xb0iv9#F+e#-<@ zhu*hTED`WAhgyx@* zw>Ma!Wf%i}+Q0Br{i6H3C;y&DQwP9mqh|28Cx9m1^sU$W`lq+Y{%YNQS7+mst+%nY zoqxCakP$e3)?zf)?8AtJ#awNpuxeN!!9$Fa#h(f-Csy z^I!Tz0Y-S)NEA*ISj=?sbR4M!apfk>PfsrO&bS+zD2!oe7E=GFpy94*SAQ7wp(Q6U z?Cvsn=~Z!df(Vwg&dMHhgrMP6Z_%ahJj?V8NuxC}5?@;4i#vzgyQdk1+Y0jYHL#D` z;chm)5b{F5e$^h*)aX>Ar*v;Ue`=o+Q|u)R)zdjGHYh=q@# zj(3}y?twe6Ee?{nA0Kgiuq(`$>+FxcQ1xDWdzh|$MB3G)G1ut=1&lFLZP_AF^qVnc zj_cH4mzElVD0z*SR-vjj;O^Zoe$__X{~L`-T{D&#-$^pRgKjs3z*C>LyPys7xlbG> zoh&I{?{WQ{H2RGT)5YO&rg9u7m13XP<$-9KedPDjL^?}0iPa!o|GplMa1v;fI7rej6paaxT=70(b9}9>bvUTD*_Ysg6G#!zvT$(eGXy>OHRN z8GRh?{I>0@3(2MF@_vhLce@kpRzF;f-YcGDIrnEkXxr2nodirK`iFxK|2T&gN%PP3 zX@Mv`RmKbyv`L+b>!^MIU4|R53^lWPW1M6Tz4_*4{Re|z&nZTbYz)~+8g=Mr2~#V6 z+L!TU-NyL~$eABJgGii=!A zt}fF=%}bC(uM8zio0~|^jekyz=aLW72yg}T_l%5yOugbKQ zlp2B}H_Q(`m{GZOmSfis+b=crS+$<;Va5@~+RWFVc zbOYI#$?3IilyLbu;J23Yb{s zF_ZV7Avc=kb9i`c|6xNyi|quLM19|GqYZ^>9ke~`^_qnUUufE%*1Gx7o6oN~?$Xe< za@@L^ZVEZk7 z|AFF7>|iesUVbe{Sn`%{917GQKl<^Q8l6(kI%^gx5-X?UoNb!4(Ra&7;wE#uE*$n{ zVq)44-8O=od@U|zSUcc_pqUn^naKu>K4 zX}@dXv3X(Vr$d^_WjDM}(8-dVGRYB%t*ED(>^Z&pYL&5p-NTk|VCj`Q`}hDC<=@Wl zmUZOh+nJfa9{ru$ZE>tfAg=j;u27B{?1OPJxEE=-b7;3_DaUCy4Q`yih&9kTOqQG! zPR5wn!?8<-ojTmVI^G!Sc7r{W}^YoVj-@iZQbgob51Tu@rD&SH&zUwz^h}2I0OSm^Cxb9T$mBenJfU!wX3vyNjL9Lo#e}RCvjudj zy1^7n=wXRVu*@`Gfzx?zmK6y;Pc+oO&o6~U!y0+#<7X`0udYg9JXbUGst>N47u+aW zXOW7vBFj<*pA~|YJ*SCK1AW#(!T;LrZEH!o@W2zx5-w8EhFMH1wt^crZwTREDja|@ z9w4)*&2Fx4gEE-7`h1Bq(GWnn{i^%{r3`s;G$CR@c^n{*DW*xjcITxx#7LxoFXas6 z4k9q=g^UVos%)36Z3v9%fmZJ|>m1gfv4A}!C-swzfI=rZpfC17^e`NI1TsZm#T}^pf=_&Y_ZBly0yES!cqwFi1^2<+a&KkTAb+I1cti z!H&ZGmV4bVg>b;T!5xQ+e>~Vd9_`p?{NP6aWJx9%-g&xDkUUcWsVyP@V#ztkRj(++!P!5}8;$Uq4kz96?YwQkNyq@cn zAU2;eJG6?x-IYnS9@T=ZRT*?G}9cIN_o7F2!k6b-_A;Tmg?vokCwR>Ll%xtBS9|h~VX~h{nHJaWHPJ1QT zu5H^ee>tDMZ*Ij?J>{gW?J=;gzteuSooYQJzxLScUQFC}WLzPyGdQNj`{+z2u#2!^ zb0HRlyuKI!i)oFGi3tYRAtlASa7CWIkIty;rm7!Ha#K#L6ciMEMU4Q^_Od+Rqo?`# zJJeGiZF(m9N_{`VU&>VVTCMc+FYevHPZqCIGl5AkUzzwaY>y1ncK)$>mu-xUGTiqw zGLgV)0@w~3H?{`Bch#~ZaL&qa&1z9`l1Mc_j!3na5BNZ7$i>BY&ur33%;_E$H_QM)v?#!k>fVX(S*#{H}5>8Fox@lGBvh5IA^<^h z(JR~wTFq2O?)#KB1*`^HJDGzv5L2C(0mjdtI(zoEZn~dLJc%@BC-y8cQMBM!npPeS zRLSs3e4ff@-T4>WpPi z3^ImaF0d5qa9puy8XPo5J=II&fUkOry=ZQ6K)t~=?lI)$>qx*3$w;t@pC3g_-a0`N z5xsFdcLsBLO4Xmed*KJk^qFpsj*PsXi;9V%L1(J~u5fPba6=_&Ba;?eB?))^$hTkL zYY#d9p{M1*R8}~~c=Mh})2&cdN=W};EeysJ!rMPaMyZK-M$&ZApAi{$ym3Cp%)-(s zX8&=E;*MYWc~9i&6zS?kpzyGhlUJX=JX$4`EbJoj#4Lue3>f zKNamMn)C%Ev>CbU%0ObLFZ3i{W$(R5k1B)f3n&k1xGb?*wpHi=xAyz z8U)tJd^QF$9+ZLj%mt3_EcoA%2lbn($ci@4AD?+cUaT8}@kp@(j){2}Y#y_oe0oAb zl-ZT$Nxd(`LBn$HPbi-`Lj~?_sv!B>L{+G@Ukg#p>s4!+Swvn&0?b&iF)f0;R}l)! ztE)4n+z!0bAl+q>^V$tNkyJ#dvv=D&lbowixWjHMhLPa;aOl8+>zSX}@1OEUWrqjT zqpeEs?1{(Zq+w)a0^Z94A<}22e;#OZQoPkX>K7 zQsz6@=PDp57@w9F9A-SOI&h^ZSnRBxj-^CE;&@AvtEtHcq>?Zq5W6isBOqonC zxXxOfKfhJTtb>Gy2%(;esw&*X*CCe3iG~B-9y@xJM1stP1he4_H7Y#nRQ1ipx{bC* z>Mzu=0;P~qhSKRIG?Pd$vNa5fn|)+nX%}qedql+?RF!zj8Offa#$>wd}X++3*FlsJ8%D64Qo@^n8UqVw7jJIji#0dbqwR)@;Y zZ`@tZ*ZzPL7v*5{m2Yv<8jQStOR)+~M&|2D zUq(i_g@u_ARyC+UvLl0oV$274uQ|q8P-w&4jQ__HLtsUm}qJR+LjTAbKZF7oEZr zru30HwR`%8lp2b`35JQ( zgPfe4E36jpBVa$+!Y-b#+cxP^omk)70h_}~R|1B92jj(0`^gJ5wk1Hchg(UC^+ zIYwpgwrB?np$J6{z;C62x4_Xv>hPI_@eXJwU-WIq!epQ6 zq?}RJ(+g>9Q%{=zCvI4n>h{AO-Fc|w=hZx3@e^h$J(53FdKNi)Q2u{+Pf#`lOFqL`&TX7Xt-sVwnq*c+H+g?JTC zq%%_vN=6gsM3_dvizM?x?VXv>5HMXOuUP4PbKeLRr- zo4<}8J$7vQS-;Sepn$j_`Gio0>|JpFQt%rd%V3XaieSjFRr(ST5YS|jlcAYfH_)vr zrK_sy2`Xfg!{`j$0Sm$xz&HxV#`_SvFFz|K?&T4ceAh*Oz}TkX)-E4jC@Yil_Ve4d zcP}NBSEFOpPC9LbLwo#~Q9wW-uCTD{w0>29j2`s+-heMNWKz!V&a&U;JIkvBus@%J zQqRAr$Tn^<66ZLA9+40)uXQ}WtH0+Oi=r-PxE*NDIZb$wiUdlD8(WEkOXtVO$CJTd zTq)Iid`>=jZ`?+~t#UKc??ydqOHV4jYiHt*Z#Hpkcwb*%2@S8Y%45>vPl+9zu~`-P zfS8@VW|jfWD=UCQd-e)LM{_ehxgy;P(5Wk42#)sk7cE1$ZD1WzvvJP zhW*YM4mq*p&y)q8JGVAH!ouvJ#J9k>w%_1eLU|mKQ`*_5Jnxo~3s6u{aB^~z@Rg~l zsrh!}TVK9>=`;+&bWcJPT_eY&b=P9B@8Igd?ZJ{6V-u5ic)qpn`yZVV2diG61>?Ya z!olL%e;FPo%P2~_1^u9CAwOe|v*~ZXQ$bv$ z5{%MstuMPRg#(=^Pp^DcxH(^5Dt1vwKw#UU(%(mIY;3;0=Z)1zQ_7F~S4c;lnmAvb zaq?WTFf*%Y*Sj?}WdOsoM~<6*8`XN!XC!vs^#-&7Pwo%29m`T8iT-EY;Pe5{yBxPfB6i}M>Y97Rc zk?xT6uNsoViGcYt0*j`J`2+5r%THH{=i?@_Xhf=W69a9=>FmZJNi5LW&rOd;TNR># z^&qvcI!!4Cl9;+!Ne&1*@^FpGdW3|~@+imL{WUjwtt;F3jX8`D&XyXL(&FAa_uGT3 z%^rg#i!V}AhE^;j(<%~9o;pQ2^Q&NgL`7HiM{Jp~scB+MP)Iwi-zx1|W{~yv`cb@{ zf1m24=Ly{eerW&T;OW-aF_7(zP*j?D6Eu|XFj6}0PBFz-EVz&cjWQ`!ReC@H`U1?Z ztQYWIa{UP~?OIy3?xhPi1vbz=)#82nXA?DbB?uh7fbedE*WG>7U5ax1dVQClD|6Y` zs%a2FxDY9oQvlQ%Ne*wngGe;ODx%y-;3~8jYwGI}2aJv$^PGNqGLY1v^AOxT9a+y> zO)8<5N+Mm&3?HrPZnrW&${h4Yh|Hnc>grW)v=D`RXzbo9%^yZif@Y}m3?C6uRmtw% z_WHZj=*f2o6)5yeZ*>>D=C!y|z{nt4eLaL!D8&4%vhqffS$|3I_#3m{!tF3>8YMTK z^Q!OIjye4HH_p*iRa2vgR;q66Eph9QXiHY-v&gA za^)n{=AYV)1gZ)PF0Oro>(QA`XIt^URINOxmEJ=8{gyY6Zw<2@oGjUPhXHqw_C)0C zl%ES{5-xRHEl%s){L!pHgsYZrMn-!fUKj!}rKny_T09?{^e(#uGvIPZ7lka`L*k>b zrD=IjIt}4#BE=Q0|J+&FXeOc4J$I+@J=p3yUMun>Oj=X$oGY{}n?t9By^CbE2Grri z5Fz|HLO$^8HnyY{wS=cjTuBN0nZ&Im6%CD!31iiBJPcbl$R?eyKA|WQY-(83(BRHl z4ZX3rxHu9@t%sk~D|IKH-`AF|*UvldfRN8W#u{h;du8U9VQG?|-(ZBQ@{g}a%p7Op zSJfGI?^?HQ^2O_j z?c&bAh-)>u{DcUhkt*^`>g1SgXE;V)1{uS6{H|r3c&qGH(ba8OY5*y0QH^Fw>+3?q zI`KJlGXT-ztu$w zZmteyyWMQ$4dM|cC8fM-u&T3@G*+?<>b>L>u8cC15a53@5qx~$^5DXX-&=hT&D0GZ zH2wEk#q9ip0*6Gckg>|)h=^72es+Ez3x`rR>-O!H-MnIAkN=v=O;JDz$I1zoL3FMf+T5B(5765JA1iTm7Ezdg{@${O z-4x?41@|Ki0=|~;^5vb>x!Kvb(BciL=D5|ArNA&KzK~j^Y$^v0LoZ+e*t6yfd4%-dXJ3k;kn4 z_JC)EotFPDPTGLDWwHRd!J{W9({zgPl$<%zdmc?uFTL!G`5b<}m5qywtIxt9Z|~l{ z9`XsE04~lX$|x#sk&%(HT9~~2@Zm$L^mf@7YFEKL#4b5PgaIody6K~d){5>zuiL*0 zwdqs~KN^h})qJOQXko6h`%u07gURs|Q(8Jh2gkOIlH$4tl}x)WUj;{l(#^?ZkVMWm-G}kbUei*iSgW5j7PYleZ|D8MUP>UeT>a3$^ z!rB5_q5G=|X{!&~nQj8T#0IRZ)Z`Dtg^6vl$4&$nS|-q3tS=(=sJ!|tDd`&e7sNBu z5^5qDx*dM>ywQKVPBBV^ULl0tM?K}nQ(YP#8Bg%*CI!^U#NUf%s$R)A`~@*0`@Opt zyjAwfxx1eLvbe5Tmlo&8vMZ(aPGltW!>}-DU1ZC89vi(k3FM(lt{Qa~gQTwNfJx{G zmsO1Q{Ine{zYkqO2VGX3rv-CSVPSqcMGn$<2%MTDXxI6{R1|c*~L9>=H0~SEm2;k!&%lR+J%m zgm2yB5d*9w{a2vBF~;nxhZ4a(-vo!9h17F6bE$M`m-I z$6tZXQ>x5X9N7%Oz38o-&!0Wx+_PsrVm&A~UQ_2%4vn+5v5AVQ)JYxsCUE&UZX~Wj z=n3`w{5+e1*n?BMvFD@tVu3rcq%_=3Rphy?$Rmr`U6F@Dj$1V;CZ}NT!J|j(11r~& zEE-RvR;dvGIwkYL`|BOq#tcAwN#DA5Z=;H?>buAWqz)CZDK))9u}yugzZL~n*@C3a z%npDO?@Fe|H#8H@YJb?XHK1j-C#Su6DvF5mHf%*0sU*_`_y95T7UoaFjfE9usO4-# zCI`D%SD6Li29P6eJMEiO;e#<&mF4VFz;0%+>7Qoc1$GVHrO+8#*LN$&OsCj6U?D)5=hE|(j0 zoWRr2RQ zdf2hv;%a6`YDzNz2(-ufK@4xp&UZ{t(H(8pott`Ls4iNe+Nr1XmL$nlQ}fa~)9O_+ zB5|W*VckX5><0G4*WzmWBu#<4dfY*)kPq$`a&XjmN2e>nluhP_EZ0+L|0K> z4c!ap@hX?v^<6Me3{pwF71xYq#Je9SDte6@>^d*(MsrPvT;2VbqtUUj`O|;AW&LDC z-nqV5_4f<=(4`knD%4n^k)sLSXv0W$a=WBVYA@~7J_#8PrumvHoebQPdUpNkS&Ll4 zMqZra&G(Huw#&p@m7ROKosvT5>)A`Sj(*c^{S57+!DH{8Q5Znfjf~-9Y)*RKcx~ox zBDb|9!UN<6v)*y^^z`B4&aVOEZXs=;X7+|j`?`MgBpurYvW95wq@d#Ln>gDrlt#I{x1@pfUM7)&0xz6KEZUpGEq@FN|I3RNP* z!7J0U+x3|^ZI^AWYQgaQra$=BkOZ$a5CK z2*p$9&fOR^CmlEkw6hXC(_)Is-7MD)4YM51CZ5N0CxBD|X2QUZe@uMy9Vj|5{h`FS z>nD|ZrgS1j^9Ay?BQK||HY^vhiwZr!xLJ+wQMa{4zrwUQ ziU^?Bn_Czf%*@ER=SIryGz&lO$iKfjh>J;iYFXoNKE0rIfH7;qibJ2R_W1LPiz%&U z5(LiXr1F{P0jYRmo!bwE<<0uWr|8ZbiY3iGzDrqAm0hV~x_G6hhEpR_%Zd9u>2R-^ z_khVk6U!dCv7ud@)H?$*;y8H~dhbN0B%8kR`T3sV$n^};d|sq}zGmR=&X!lf_sU2$ z@8pl?l+W%swu`smRV=6W?0}~%$>ZV~p9_=gq#jHftLYZ|*3MHK6}nX$7fnq|d=d~S z)3_9(m1lYFlRy2A<40@WZ}07D^L=2;q!Ep9589^9Hfp8wSiPx#c=Uj!y5aJF+7{-jx4H4d&mbN`j=QbPwqL$*`ZA20|#)zEO$Q)oH zTECr(M0*VoSSx#)IC$$U!UK8JVLjk!P-S+M46VGq*h+Sy8ezc&+5CY$%GKQ!~MIHim zhn+>f&3@s2;X9qeZiMwjT7H0W7Z;aj`-ZH}EvNzk;=O?}*90&VVJ=`xl$3yJKpbu6;Y16vqo}H#BxGlC>M6h%vEuOp=cI<^PlB2w1NMJNGlte{ur>a`n3BarIefAS;{o*U3)hndiVjSM1Lz|XHN z_!ofR=oFqyX*B`3QBhfWL{$W^id52z7Xgbhx7sSKi`}=tzPRUT>QEg@7@BOLT`liU1)^-%s1=QuOn#noK@&1u@4ALwU zX|j)!23qSyFQuR7TdKC}Ms=;*i2f zPv@%>XXnh5a~9rHae0!RS~fdQ$_O7Bl++T8<@M6Nr152exhp(($H#l0Pp~i`BCB&h zb7z-iC-1NuNd9(v5zt$qw;EsK`%0{~3i5jNE4*J`;VdL%i{|=R&_fW4byW);BkQ3m zYVA^YHrOdmZSA|@!L$@kJ*(2gpfhkKz!hGP5f#%LgVQg0e7n|=r;rW1y{5U)Fk(nk zgd*T(CVp?FBv~{ipIz*q*3Z$*y#9VG5YMY<0W~X@hL8_IwB4L-V6V(Grb5(La_ratGo2DPFO6{sE^N~#Dd`4fGn_PpY3p41yd%6fX%UFoZ01-wAp9?=88$}vz6(n<&ues{gBVSoITrpzE1c8$rwRv8$LQ7mi*8ZN+1w^a zN5n^U)yxVz$Ly$Eg#`!7$^8(@mF1?Ny`QzWK)IS%m$^Z%36I1G(Q9extyuO@A_Q7{ zkFhZ`GiM-KUqE|B$BoCQy{vX4R>Pyc?$v~L6DgihiNh9S+ZN>t(o6HGzN?&pfgBGV2!mKrPWght)kEWe!8)VhBiE`(B-Hv9c|uzj zra8A!-GBJ7qDU2NmcV4c&CQXMcvPl00og(Ihvb3WFNPGBqQ@VGv9Ici3c!g^pFe+o zw)zW2X^hb!U55&Ttau;+q{Be_!|M$`wYFwXMST{=80od*sR*H<8Hat_s3K}08P*bA zXuO@u2a#bV5J#&YW2*3{_!In_XD@17sIchMZn>1EO{O1m@Y0YtTCM+9AbIJt)%ff| z;msP(HgpZ$#ILB)+oJKt=}0$t&jfhs!fJ>4mx0D>Amj-f)k!>mpA-r2AB=XItYIeC z&h?syo#;+zzM(UYHTNqhSRc^IA&bx)xI*|*p}N$~sX3`Zh_FjDy+`}r%t?OCyxRT8 zvri)^K~kOT^n!q;9s=|1h;cTto;Jc?wUaVq4>ei~pN*h6YkSPj*A@on?F)A=D>>$^ zTTXtu4ucgZk*JUvPZgj_?;mlPhF0Hx?IAjb(Hrm^0#Zs-vo+P8( zwiMTFqWtGK3P0BXa;{jL{AU4i`m?XWLdf|h^8oH~6Dit};mgR1&Xanj*J=FBh| znp#scgTUL$;xhqJ%+wMqkiivd@>lm_n72O8Z))0N%8W_DUy?D_}NkPHwqzsc*KHkh( zxI7Yy;e9tsMeW3N!aim}5ze6?71pFX*L>okN8y_{tfX(BE-2fN??`L7hQ8jU;BkMI zz21M55Ca1q)X%OQ-l3wYNd-*JyCy2|DDP(ox3gDwZinxmQ4{?yU=t)y9wcARuJQ29 z9_3T!hj)-VDjU+)3YOZ1b^)oOwZljz%vf*%w`EYdG%+>pocVLaK{1^JcoDPn*;W6) zX}ToS}jhpxvfSH}(E}gLFp?Cu@rMMmW3dq#t#jmzlSE|N1_9_&DDKUVr`- zi7Gv}BW*~RzR&px?531bpc8F}nZzf>w>&AM>0 zBi(tcGyw#zy~TI@o{Rk>ckSA)WeNMQYLFgrD!N=j?nOL#rRY0Af!C4H3o+YxYt^=_ zlp!vk*X7-9X5>vB)}O3;O59lbZ_ZJ-yg&{0o&Mn;rt(JjPkq`$O||yzA?&-o+)2Qt zfN%ppAd%n};O&9FK9)}yS6xB&IP+$X2F7F?l}~MiYt&xF1B{NBqb35<#BM-(hXv8c ztAoHs70rs=mK}IKMHZjf)-CV=_(3#+HxfmZ(%_f?If)A!q*>~)je2-^K%VIRdubCB zJ{ndL61s@kH4sezRg2PoE-Nc5(KaeF?IuX?0jw!q^wz4l7R~ncoHaqF=E?!l0_>Tg zRpp2vv%=CpC+R6F9)2mRxvsu>D{r-7*|bxU!6nZNZjZawQSw^akYt`-(vkDyA#dZK zyQadH?^JB{woJd&^|JD-X=FOfFqkR8#f~5}N=R3JykK+F&dDP&TFK>1da)Jpe{Mbz z4Gz=GHGJ2R{wJ8pmG$vRw?&BXVu7JpuaK!0%b5lpsi~kR$+PFROGO4sC<~FiA_OIX ztutExZq;C8Bcrksx8;r*408+x@Wy;|$a1OZD|RL8Xvtn)TBu(>H)$gxp-l3rR|qDAT#>ay6T?XCng|JTWE63M}Ys7(ZIP z))}Jb|eopD$wLlHfy)p$E$SgGA9qqd9&Z`OI2c0^;(6|tFf%G z+o@*kWm!8LQ3YE!1}5VX<}DskE0VvfI7chY!No= z_^cLVD?Y;dc;i-h6Ld=YcA8beN;r?Unh7ob5efG;zpdJqX5gW_FQ>ns=S<0R@Ihs^V=o&+cwsL$Jq_cjARe^ypZPy;dwRTRX<5C5 zYPyM{1De^C@J5c8N05uk z;SUVz!acE7!*2X=k)Yu?aW^iG^8f@ZhmN2Rn2ZEO_iLWD-Q`FIx!19O-XTl-;}h#r#~EElTUOLZ{r&yR z(7hS}SS4h(=_1UX>gCG;n;3Y0Ea%%BYU{fHx*@)jJz94?y4Ya@H|_Zz28!k*M{2Q6NEPPLcu+ zh+_37Bt)Oxd8~pLe7!s*<_h+km%k6?MbpE0cAt*3qt)de)Z3lpeYHpIFRgSIIUZ^$ zv>WMwNx8K$q33*UUs$aKRU?sq4XXOeHPMoYUWiFy!C;xoBm!JgCE6BYIZ%hMdH>$} zxNMN^c_HQ?Gj;7-<@I;SJ%~ysuLt0eH!NDg85xI#l!ApnYwMN#8etKi|8&7WXe#o; zb6&)&=-wk*r4Z=UTz^fo2$K(RG`+NV&$Agy;C)aAp}?S)Z?n<$r)JZY>dStcXl}p# zoZEo9mb>xs+0Wm#O!xb$EQw$BAlxqU$jtswS@{K579b{9uXSBb{w{#%Gw$gK-}NgA z^xGM!;8mV+No)K`l3aM7Dfy@H;ikBjTaL5C9WL1vRfP}SIcNt52w(Jabhs(zLFLca(nLNNM_Drifr?=QIyD>MbzpKvfl&n|h%vkHp;)F;|xs3G}URTB- z*n<0%OZ0xze*f5-tDkLV2T%4bb4Mt)O(^+d8704Z<=t##x6jMM)Ku0!XC?aF#fy)$ z=mi#OB)PetmR}=uRY)EKG@+wVub9m%T}i#*rTEkpMWveSN25--F?+1s)P3V*T^3-! zylYxZQxl~tbZm4Me){x@Y`Eu*aSIQ{#*LC{GB+*1#Z^e4HBW&j^vJG4w7eXn_N_N4 z|J&>4ytBB#tPh1GWTvoH=nY2J;HjJL_(EcUrxCa`aIL2$C4D!vL<~*J(j3DDO&Kxw zau12;g9lrIogF?GaIMO^K$|A5qE8nj83}xE{&AY=W^lD z_p|b8yD}&KcBLFasM)u4=MGh0-J!tBkBJX=qUl*BrIq9n#3Jm{BaC(t9LON6(^#H^ zM1YN`^gE&B$MvGhr2n;LK$GQkFrnQn4qy#YYdZxp?_J(c~a+8nnxnw~MQ^UmQSOZPZuJ(cejPE(_Z-gaj9Tie+&NN`_PkZ3SeeS}zuNga?T?m% z9c>{8H~R*{GLXnb?$y5_Fo7()$~)u)XOud2fv6&{Ix%UPh{fcTf6e?^wajJR zv7>``U43%$US#P{flv}^8C}!i!>`dsVs37}L(Fb(x}ugA3$y#Jg=hie^M2KKdu)&^ zPZfFY-jSdXQia^8siT9kjQv_)UCGxwC3lt=)-9vEMZ4tY&i-3}BwnVZ5at))(?*+u zo^DwWC}t5oD(LT*NWbgEIT*v~pGFTr3e`*0VcOUvTwr0?=IPOc+>vX}0*j3zYzW3F z&_W``_lW3=0#K&3^Z-X+>)Gu!p(65lDMLd;U{qv?)(}DXoJsUTA067V(egx<()t0Z zOhkL=g9jN#&D_tPKbJ{wcl_|o0(m-VQpO}llR7}o3*a>Bo$nG`iW8o{%Z$dniHOUO zy{J$k|3I@;M`qYTtqlpylz>-{I*~83Wux^mswela(Kjqhp=-Ieq2X13pB!2gciNz& zg?fucwC!m9lZ^n(Qq=>|flu@rVvvao2-TCz1_I2pzeZz;j3_#C{zi^w=ecn7|DlJf z_nD2iw|B z6dWlWQ6VMT;!$Eo6nw9C7^s_qloW}`Iz-7QU#RA@T&lKiGs!_{R0YItN}vJV^b;A> zX>^nnPG~N~77qaC_h#v9O8C(mRA0Y-{f44EN@H_X71nhvA3t(FIx{=+#PW46$w4(g zCFK5w+n$T}RP^*3EBdC^cX&JIw-EcXbDljP2LdvgW?Un=Vee zOUdT*B&XeK+)k7~n!nod&(BhS%+^jda;iWs`qU`b>zTww6s zvU45OYqN85-d@c_p=%?kG8Fx5ZX}zbR|>@$GLo=mACcEV*`|R(%hq0mIhtq@lXlQ+ zX>0RE5u>NjJ}{>|R3!(w1vNEwzU?RtTF`tEN+ul{`H;gAic<@l?vWVA$yH)DioA^f z1jnhF^-KGw$;#T>nb2t?5aYgNjEeW?pR+?EH-8=`2A(mh!R^-Ry(-d*p9ZFuMMF}Gf>f0*dZEL*9YIRE0)zPUnRplIdE);#Di)#Zaa zP>9=TS1wFaOrJDQC>P*~akhH2e0F5Ab?>92ya%i+>NRR__a8i1hhntd?)2gX<{6y^ zG#W=lMCZNEBaC^4)Qz&hI1YpSotoMolF;-RCS&y9R&1aPa-|r0fHeyjv*#nSs#X(} zHN^y7+tA<3G`O%YiB)y)HpR~$dLLTTe25AZXs<#KgyS#yl&oW)@=qi_e?ExWGWdBj zgIJgx1r9fg2t{Aazo06W+-d?W50+Vfsw?MjM+jkEIS zQ^a`X$`xeYM6^*?cfA{ZaBXQi-NQmn)CG$>gby-j9n~F5T~5u}eyL&A<3bTtcyf|k z#H}~6wNX^DxAv?bdVbEc>CX{TS#gnLv(d})_E_`s`YzL^)PBlDvCUizRkL43e)Zon zG!Oq(wKb+ZK4<(A>(ZEI{$*F{^`@`y=T2SN!YtmA7&wp_6g%{B+$PM8%YS-Mgnq1` ziPxpr9k^=;5crqk)PUDWREI|k4C!W{QG{&rNj5g}$V_8racA=XkT{^o$+wffCd%g6 zcSmkf9_B#3a2is+{!7^|!)`JX!yaWjEs9hZT^azo(6ILk`SWlcsl$0t%P9D<6!TUJ zU@~{@sZr>fWJuB-e5-(fqf&Kbp>&r77yE_8f$+VX-bUy4TMhf}RG=RH|A_hya4Pt> z|060bBWVV=H$^^HvL)4+PJ! zj~@q?9i&E;XfjCssub&f4>h}ms;1_z>K`!RRSX>w3FBPxOs9B-SIY~2w}+OlZO_3Q zM@E*%bTIZFRK0xpVUvT-T&le|ovIE3Hvrur5NP$u z`0CRUS~_MPwU5iYHyygcL9ihd_iOOGGt(Xm4m}z(Fx+h4zn{Z{(bPtvSmaG^pro`> zERp{JpIuaMBLgCycSjpD=h!`F6k;Wes9s#3b2@{>TigBPhFhq#{DO2L9D92n-52Wl zS+*58pq|_0`m0w5Us6bG=aR<{DnQ~wpstF_e*HAXN%dw3A!Ye8E!ytszNJBxu`TRV z{qJ9W+9Db$l>B_&0mVEE1S`f=|8}3qkt~fHH*XdJG-K(zw|IX znliFxEzf3n5KtQ*#XNidaRVf!!bqLG^r)1q`4*@PNqy8=j{-&(V2JOcH z6&(crPvAzD-JjPV5*IfF(V%Qbf2H%+7118_S_m%%P8i}@ipt=XYj(=Hi9DE69J{DQ zLSS_(uBxgUlCT-Fp5$!SiN$+{25w2%CvW@{GX*&a+OqpKkWAr9MAjlr~B z>Qo?)_^XN*PB37tbbWdpoZj+Uz48HD?EV^JF-1zB{ z<=6Hi@*AQB3$YSWjNPPS30s(R$o(_#ao}1NPN}5ejo&s%z3|(J_9lBc--p*?elu#L z`0C3_bYH~+tp9=(W4YLTtCY}tEZemX^WPeB2c+x1Q9&x+?`fd$&V`+Jkl_etz5${H z9T>qETc>h&dUy*bG5Di;KE?V>J>+vXJlr`ywoOYETvOQh1^)tiD?yYWE=;aSyE5)X z|IShrD(P@Uq)t35NR`6D_}w`aetn$f3v`U3|HF6UbUIf*oK@BHUd~P{N_dkr>#r4~ zUwQTsEfHQ*hS{S_OvV%M{21lFcqN?g1i5`YbgO#}Cog|I<*H@oG1@<49WlaZE2lUi zq?KV>gzvH0++pYbj%Vz=F-{Lv?_4)Ap$0;(UX;I!bWOs9m`t>CasiCCN-t)|5vf zGy*R(v!{-5e}B`xf&#f#=Yf5&+C0z6*>doDMd86J>EZ(hx_QozT4L|{`Wm7whCdv- z8U^5E#C(bggo#zWmT&b6D!n7nM-{VxpRu8k1T+t zBBYT0vby?qIXQkTE{CI)MR%81qY6P$1k=E@rU#FXRl(ep7OAL5e$V}`(VB2*&NKl% zb*^u)&ruhClUfWjT#i?B{Pyj=`Lq3d@pOjcJzsKdhF2;vz1J2|QdDF?kV%B!jD-h- z8|%J(U?AY%e|HG}S;Nk5vRRply43~0k2C4c+qa^)SW!J$0epe>bQ9tyXut?k^ozV? z`9+h5fWwd?0%j#5hi2)5wk;@*Ixqw382C&HK7}a3wUnuWU}`W_{9;BQAMm9+Z7l4>#octm*wmo z>c8P}0S8OM#s|^73BhHbRjEh&Rn;rwanxOwYfe|%%F)gkE*Kt@;FzQNeZ9D;XJHfl z(A-LNIPccc6YLvW_+M6de2V8V!H#z9NH3jXNZo2q%DdOd?_pr~UUN9bx@T{JM7F>Z z`;L&k2?}0)>^wEnXYQ7`*+)T;QsFc8s)I-P%NJ8Ea4c^m_sf)|V!0e|k{peR^S<`_M;75g;uo9-P{1Xl_oEN7_v zj!j*H7i)f7ARVo~}o5pK0%b;?mfU7Fnt~$(+WU2k96AiWT@6RMliep|cf3Be02(q)g~F4+(Lv%7?XqRBJHw&!ml5mjar z54O|{%nCU+)FHbkh)43(o~?I|7-U9_Z;BN@?3<1sZ^lg&!uP?_P%EbAIi zDK+TG?r?OjJoMz;tG91WZZ^d~f3BVJC^4+*da8!Xl`Bhey8w(E`)m^yBQvSGYn}X# zwWDR9QlqtbgNN4oE6P+F8R4??Hn=d?-B`*SR^ER2*7sL@>33X$-!AIqijmZ(ov&p* zSb)%&vS8lw+d|^Oo&)u_^iB$dN=$yL8Pz_oHvQg%VPz+-8JT!jY5{04Eas?DmAM8M zr8r%PcgbzreloDHrVDW;%RF0tTh9OaeXinD-rLTSSIshz7;fj{&3ylUzlnM>pAvc} zy4V}Wv*UL;Hf{PCWvdjpsfg?Nn+5G6y=4H#48I;~%KZBv9P%C;O(&}iohWMdF`^8N4 zzRA8+j*_@jQc@x-b6trXEi^H@L~%k!#t}hxi($1~C~jRbiyThaLikiVYeS=K{#h2) z<@kH7oAh5OEs)598=oDmVXXuHN0$m;O$5ams%T+liEEP(?5F_2_h4jt3}hHuo8Xwa z#`ZPX;~)}M#H@dwijsEvXWI0m4y~i3gHYsq3!vf^oRM`J`?tof`RmvAL)T@l{XIeM zx|kd?kD8l%*>%tbetLO@7VcjXPs{d!tHAL}wI7m)W)lKNG*=Ycnal*Ks_{#&-z!+z znPl7a2G>yUU-z|e=i!}maUQ7M7d{|4`q#A?eV2IBCRR1m{$L=h6Lq&YH@zh5pw}2LB!Pi$0WJY#q}N<(kol3rc|6bKdCv>?Jz1T)QC; zE*3C&nKh~XewKYIpn2$*O`Z01iOlcZz66|JzAA%NX|6q{;N(20=68^m_uo{e2@1w3M>@X0d z&?p~(5F7T!A}9wjqnViFpcYP;1UQVMwTp1iGc#QwPW*TAp+d=BPZL&x2p<736%5aG z^A@5?1bm&OSI~M>8g{Q#_j^tWb>F}(6dEv$=t??rdHbe2r5z8}ML-Gy0c`ik<$Rm~0(KfM*3y>l8HuL@TD6!d@( zBzv6>N?|*yPO$u0_1(>}PCc8^Rq#Nl?k3{Z!=i|V2eqek(QTK|bwYgX?CcI}%MkFh zj*y$+KrddoQknrU`M~nN{0+yL?MIz(8DoP`>J2NK48G0>yQt%ukI%_@I@c3*@~)Q` zM#p0FM+D3wkf8bgv&g4U>lK;Q>lE-X>@`FTwZ^s6(fm_9i8;8`A2&8oBC@Yh2aRaA zxNsx7!XV@Q`-gS(_ICZ&$@<#bvSh;-mwwsm^1d}xI-cG&1*r|Wk#%S{4yBnmi%9u& zF`^P;oIiI^JUc`(!bjGe{q}nB?478V!-%Q;GQ>a zJ;(Mz#)t0fmw%5Tkq1UUys2QOa}|XMEv0l!z%gTuLJa^$5P>qG2z5X$4_XEygrh@Y zhyf`S`L{zh8H!j~S_Fm9U$~$Iu8^xA5D~%GbDa@I@LBN%z!lJs*iukxM2K7P4d4Bu zw=koussYVhccPj0UCW46oj)%&Y2pcNt;OBNP(LVx@)Ow}Nc zNs4{P-nzHXp9=_5dKw=gK!E~S3x6Q@eX@?EXcXn?ZK_(L4}t(RhT*zs@XfAtofr^q z_AQz6GN;WaUfu(187V1YBa7Zx+0FQ&uD6u(_zYEsF+IFo-yzAsw`{*U{j(cWuX%-WW|qS+AJ<)LFB9``Y(l}y=&MY${U0*vcb53KnAZT)E<@69 zejvZcpU~;pE(t4vntZfXUjE7jC}2t9Nm}2=#>S}6^XP6&v)v*+Kq!;Tjbh1|GE);l z!+xwvu}h~^ECb+lbl{d*E{jq(m#icKp5TV-+R>`W){F<+sQ~&D(N`35f6Tl8{ zz_x1BuP(-GaPusfb>Op=Uhd`~alQ1GsO~aP_)QY*nq9;lDl40%w$k}ZE^4&Q=Y{^b zMRfYlMhf$0;7bUkdIHAM)mH z>QskTkMjaknyOjaI!p5^QAw&eW?U_%E#<%$t{nc(kPTEB=etf$zJ22n2J;u6Lx+K_ zUW(JkQB=Ucm3A?TYgEya{gwbU$UMc)5)IGO)7K*!QwadZ1w|RRSz8N-p*x-g^AT9L z62TvTB^r+WNA~R56Y(H>?`|1M&a`-`x90>QQe5c)RhM!y6?_6tb?G}p#N1dfXMn7S zAhR0K2z&Ox4O0dq(4D2LHXPf^QD}7FIFcgTrBus}ApYNgv!6%8l7h}1pIp+Kp{8tr zuV8No2}CU3$r5;Kl0C%J{%2E?CK@%O|HcBHFlZ6UJ>MMa- zcH7gNNzMh{qCO%>M7lnvh5-3M^u!&U5Ne6tKTZImmzKzt-M4P-LhF~lD@byA?B?n^ zTe^Nz*6>L!(5~6qmEO5%!;%7V^t-ybUDVMD0;5LOGCD98%DIUBMaFr_&2drz@e)=T z_03m)bmdDae2qW4rXs~H5GVl@O&4}^S7^uaRtDwF15t%OxqC!tHYquIYM15E?GAgT znc3M7G@5S^tlK_y+E#h%r-K3gr`mjwG_xKq<=(PIy6l;d_?T9vd1`lXXV#lHU%Cz# z>u}*>9Bi~HMXmVlrcht-=bV5V_^^=!5hY>ewQ&Gj3hvwMTzh$by;>NuvVA?gZ`qmV zw}Gn^6fGD0YWWYJIdg_W0jE@qV=%YzF~qd(P-0TP#??r5@IkNv#+(NPVR%bG;gSpfV@py^45gv;A@$Azw7=siHuL&Qlu zQ84E~BB+-5C~rU`&pBPS0m!+Lkx`0+(uRN+#TDwoa6r6sV8=)Yq^0=$)eGue&%3ne z5fRqId{g^ID;4Xp778JUG;8g^l|J(JfpDd!O$#AZAdd^Ex{kBMsR=%}(Lami3)_T! zEQor}&c^I<(i~uoT4=O7=X(8SlTH>KGEw>s$C$`}TnKBq@ zFXAgFp)??NNlZnm%S1d|3p;1)breIu>0v2s_g4<#lUYg8cz@$!!hp4{_sxaS>quRS zKm^M@s^2wrw&G^rr{EnW%p#%0q);EchkO=VxNuRu>vriIc}xkJjPYS?3-;+GKUA4)hb)fm?!Jo)x$Old){` zKHc2SwCDx;4=B=cKA`NSA&dTltRKuMP+-{BUl36?G%#>{ZhYsM zCwc?;8wyR26dN6oEbRtQ?NpHcJ)5GTO{f+24K*~DDd*@mw`pE2^Vs8YqM)Ubgru2~ zo*tK68FS^H4f{_5iJ&yA)g>Y?vYU|(HU?~!{;7R2F>eYN*;lg zWui#W&vfNZf&h;=Xu@{=%TpgvC51N*5&9kP_f>Lo8E_)88(-&Gvd|Ur37M>6!Aq_)xqOmSeKal}s+VCQ7*Y%d7tkx0>w)H&7_D~fQsI@y8x zan4nvTJuhO-{F`(o8U9W5aU;*x8?<)+AP%H-~RozDrK@iS7zoPHppBi3K zs2{TqhCN)mgo#TRp(&*LcTJ9tI_*hKqiFn|n3!N&`jxWZfv&x7-8y`0+YcTL%M)TM zl#=yT*;N8F6R3t&i=x`D%OX-zk>Vl{&|sbGZ_a59cW~`R;rPG1anTy#=uL2(lQN|j z?nv}BBy>z?ZDQKCecOabV5V8gE5^>OOVKq4LC4XFV(rL(!TpyjSiEa z;pdx`OUpTx#E*hNWMtJ{5wc{XZ;#mv;=dIUI5DfZEakS}hd1!z#n2k}W+$79s4-CV z0tXLrp{MS2<5{%unZ!A{sJ+zl zV*HI!1&xdt9XqN?0=u54-msL@wg$Qc1I~cXo7q!FSwg%AM*%r}Ky$YQ z7kvH7FsR$6aBt#c_fgc8*nU}7T_;1{E~m#7ym+w`gI64+#Gd6g$H?s2waZUv%wdOw z#3t-q++su+N^W0>(G38HD0>a&N9?b@vP{02i`RzUEZOsX07kntY<$wJg|ZhD^0@bp zj%?cyzzO>!g~Jj8iO{I1Kon#-Zf*FlinO5g*uudT zU|b>jGi^xoF($$c9M_yL&pqk9oB_E80wwM$Cm23A_^WqLIQ^UcV9}vMf-}%Ig1W)^ zL9!MAv{Iw$Q6qWza|w+#e&Usa-}09m;fi1dgkU!qxg7X@Tg5&ueM(T(sLdl5jKjw2 zoRi*iUtU3H*0=Pe%wbNjtpm2dugy=_-1(vR-|>&$!6%me=x$?93GhE!&cV;0oAu|) zpyPPeqMRdEmhm-*AAHwF;I;!LJVg{YYxzC`GlHlXeHp4-yBrN z*7T{!P4WA{B*-V&1wdg(D{Jlz`$Vh&$Ux(V&=(v(WzZ@d-wLFH$~w%+l|IL|>i;rZ zvy2%zHvx7RMAUFouoQJTUSOeMSd51F?S8&6!SnK1=g7sn+uwf+)zr}yN3t0&u`p+T zH^g3NLsVH!bOVn8&VRSCbi}J+#{2#>MXqND0fLhV!kyPhq-gn|y;oG6sM*pD9)whUESY9t&IIC$txk2&6wjEcophuP{ zxVr8auw7?ft6ThOVP5JV8)=VCZ>f$e#BaZ4qIl7omda1lTTcvy$aMhoI~OzQ6-E{C zg_s~PazoJBgmSBH20#DN>MCepNDuk6kDj&&KIAquwQTcSzA`6v$Fidfvvma{3-fIY z1CNp@B)2`cCEAyb2z$J+)lq<8U>!5Gdmq3k*3Tw?4Q+q_PGY6S8@0r<+miQ&U-ulo z)qHVo*fO$p$c|-OZ?<19l*{Ca%qHE$Ixbwp${%37`_deQp1pZx zT&aN=XqnpAT}X5K8{8Bn<3i%b1~(_X6PO3$hlEe`U2*T(P%v- zW@z~$(Av8*hQzV{f zEWssZ!`9s_iCXTu+0^tj4NVNWn5bxJ1)vVL4?P1&wV~iwA4fo-pl~*E5gI-QONtFLyNr zJBa2*U$v=nP|_2sJ{5-|Kgq;a+w6t6Ypw&^*vPNAqjJPP6FXOXtUFxKnYsdrN=(=` z&fMVK@dIfQCY=PmNH>k0PungLmjF6eSvk1I9xiwoMB=C^U9@m8t5W_qp2J0ii*uL6v7T~n<>O-O&6+Gx2_6|4QC{(kiMr`9I$;!S1_I{F4+j0f`{I_c zXu&KA6e(Xyo(Ffaj4X|%j1PqCS(;)*}_z20aHpq z4-yqapFX{hAoG==qC|w&ifCmd!ETmlpUoW)VjJcXKXnsJ%l&9a?jk;~>s^(sp<$on zd5N-%CduF@wDr1%vV@w)Jr;%+mQr#N>CP8hjd5F}n8UGs$BvS2mLaT00>ua)T&He= zJTe@9j>cq_LQ$7+x)x%VnfANvQJu7j4OFd!$Y^}|(B8th3gf*DZ0$?z?Cg+N(?7Mm+1KnG-+I1PYY!@Yc*kIW zPWR&E(u=5!TH`^rS^jcduIqbn3&r!=d`O4S`%=>dD9sJ&REul0NRP5~o8HkcCQaMs z_-poC4{lx383;@1iLLpGdzc8aj41jql!~b>lrWu#_ z`RI$v2fs!}GYc8XLkzS~4dcW;h9!jNGZ<*8NL}~7xcTDw>4yaK)(6oDpyF?!7-;oh z*1V828kIvyKb@rZH+KPG2*Q?LDR7GR(is5rv3?(CelbW*>Ao0$ay0l&%`XxItUE`} z69h4FIolYR#aH)TGId2@2gUODwRfT&vr8!G%a!G~(}yk9OMeA-YQt|bvDyp*9LN;- zWb_;!Lv27hb<|QwWV-fRohhIC6T0!Jm>QHXA^?Jc5$VQeu{UE`Y9L)1y6+ zbfX%Gp)GLnE}^*3O{#}~aD$!@`yMYMEid?Wv((yo^8I=5C9; zA(VL}AsD+^q^Sd4xp=E1^OY-CvK&VRq{<)*!HnO2+trmm8GA*Z=lK{WNtWq*RM%Mz zS9XAyT?g0#f0t-g_xAKw?%a3~1)+{?IC+`m2!K`|LV2FYGNq${u?XuMxJhgYh5QG_ z-k~4S-p{aTVb>;|J-RCdv~3GL)qM04)$4X09$^V_hngPtAJ}F#ScbsFW%F=nYZI$= zJCFwivXaXKtO&XWTtvlO=WXgnF<`|PEg#xTZg99ApyM`So8ed*RCTy+L6hl-!Rn%r z3~ec4#<#Pg*)5OZc>%&rykx-8B&>h360RBIcP2<-^t+RxlSyNOD{33k$~@=tOa7*vkG3la z^y!u$(Hab*r`uFA^-#Rxu)(1%4WSDC{SV|AVbB(%%gNn zizILD+_7WAY0Elcz4e!FePGqQVM+U9NMJ zbvhdqxnz1#OOzmGG5~5@7wT>-V@lySCLuw==YPQhddAiqIT9yMqM%(7<<@F`CieKW zf}%zIVv@xwI3_63_Fruj5g{uh6P?{lLgR^A4xc2Mwm>O_2&(0E?}^G5_4w*%FJCSL z(+jK&>B&VrY#X6N zTc}(GhEdj8HrpJ4ac}~AqCCSGzZwP!gl3lw9`1pjDev38gjn}2Kv>?>4nUF$*W#U~ zA?P>xGO0~R06kk-A?*FHs1atmH6MrM6qJY3l8a6;QH{JIXKHDyDCleOw!rN z9@?TL)Dv^R9klNVIJ;{Yb3hsNx(P}WIJ;P?97WL2T=Y~hw2Guv2k`J5X@zKRD1%%P znWhb$KiA5bRPqRGrm>(?ASRJBFRU9xP$OY$lJL`8=}%dpbs*Or`w9eioB-TjzdQ{b zpf1-->&||h$x~Fz?*^&*;6c!hY)KJ0ibVjM>vH`CPnryiQzY~^B=h;iiP75ORx=0R z+p!|`T-sMP*yzk+G%TtSVDghUT`lL@d2;=huPUGo(S3@z%Iv6Yzs9WtpNy?ej+$f2 zOcsAueR+AnFFlUaF@Mnx=O5i~eXI`}Hp6$lT{9zEBC~ZP7RQ z>wIG-jJI-b*>WH5P?BoNQq9hUKnxX4#;T{o?+pe&ZasC9#hH4#g!!1lS?+u6n^(8R zF;Dv>{uMU0i&re?+PRIx{=gfdj;Hr}Hndt)UU?-0?|}4iTY#hFaKgE5;m~o%8~5j( zdvMe2evAZ&up95*?GO^`g7yoagvoO)wp5KkR0zsHwugO+NV$?wlRC}K19Q{qe*s`$ z2UiS&0pkuvGY}=w%5s&KHq^9&f+gI-$80X0J!NACX~v*s0UAasuUw(XtD(ww_8>37 zUOh?CUd-Al#)RR~U+heW^c_OA|H|P}c|vDXRdy8pEQv^4XrdlrE(BbS6dVnYhi8m^ zhp@G0`r<<#JbrwyqT(DyztfRT`THOn@=+F3*~{l`6h*?f1M#_8Fg1MfsDe6gAu4OW zlYHL2KXeBMhAUHeWQPQi7pzw;TZ>qaoWYb9on7=579C$mIYxBU-7)v?f-wBT@|6dX z?=YSaFAdjmo5~u@aL6?T*tH2AsszI2$j6EPTwv=~Pvae=mvN+j#fmLk&hP4pGFTsn zx=0fOPg5~m1KGUos6Om&@h`4Tgv83@H21bS*(rPLikjNg{aup?83SPf+tR?aY&6`Q zN27x!kF7bB#ctQEbPy{%FffqMO=4)D(SaxBM z;*L*8?c1Fm&Wy*cia0oG^{3;BP;SdQ;?;MZx7&Sx&6WL`TtoNgTthdlzf-3hc=zMm z@td;zYWMfJD}L|$YJS1BuR>`lI^c7$vb;437nGp%Bw^4vGS@0Pkaj57A8}>b?#OXM z%N%*O`emY9mR)dFE-Hyw*Rd5O(hY4Ssb65#{(wajtr(R&alZ0$dcq3%K8I;CD{R4V7VMdSMw2$)esb{*7bVnBxh z91xkexlbWcq~6xhh$xTfoc&pkc)@$nqLakawd#wmw_=1YI$E_uA0*POeCkwt5b?-z za&dJ{DB~J+mR?9s_Qx5A!uYWdbfujauRoK0JGI;`+9`Ca#Ij3CYyQpPU>9F^n_VtB z?I0#zjk)U;xJU0Ji9~na?S+%|E&wtVlglXk5pR)j0Jat7szx5D+ohRoF<>Xt*N=35 z08&r%t%whYE87!Fi_3dT5z6F`T?>SFDS@)AT3W0Uf3053LtKhrKwCx%4*G^)&J@R_ z@*nPm*{Qy34jBi|lz+Km`#sb_7biAegi5=!J1hvrUfh&%4BwMXh6hC~p1CRw7sGJ!&snWS?@YQVH zq{z^1stfzxj5&TmcQwWHCogO4T5^wFX)$Y}GIe3BO=Ilis<=$(y<0WT3-m3goV8hX z*(nxj2TPDZgCGJ6yF%4x;pmrGJV+mN&)B+D2mtS5m-9IN?$4hg#0 zC4Fz4`>6J1_&w`vr9}oGZw)+WptV-VK!YXdCu`RaGao)kb})Kb+MzhZREp{54=B{4#sIgz#%D?)fVSNiZyX<_9ZIg+6oc3&qw zQOaz{JUcm1RmrgHlnG<_lu}oJ@`e9R{oorIIV2d(3q|VB(M`!2pNtM*3pi)uv>K&6 z+fBXyO6ZT6+d2J^0qZ|(HM;+v;BY&So}qj-0)ZG`IKu&P3bx*Np*WskFzd4N-+56Z zpC_}L8TS~Y3$cMMeA&kxex#TpWiNWqH1r6RW8q!hbJJ}m+ z8>g*kDw*hEFJ_JiSPGI~UDiElG(GZnQcKJm$XsFU9xOu8%~P|kZ~q{_8=sFpLL+f{ zl01Z2&l`(LXquWd!=CQ$xU8&*7sB1aQm7lIiUOKq?`qjgKmKGSk2I6Fe1-ExwnR$q zzH$BfR zV-r68`^D%6tzbXDzUoolrPYh|)dUZuWbi4V9{pR|D=vSileaX~^3>Y^4nCJbF$;$v z#}Y@3!H(GUJ<@Ee-+W1=mEQbWE-zPKXBXCGJm8p2vokVk-z27Omsx+-#uchIWJj21 z$^oZAy{BW*l7k_g_e6g1b8vW~a|0c_6@2H-4LDtLd z_$Pe0fc5yvTnw)6$B!Q$5Nd;X0%pM4eAH%-um8AlNdCp8ROjr8I9N2$GlSh!er`}m z7si#+Fq~Yu#O_~Q2%b8Y<&qOPYo}80+a!0^Mk0`_u5R4>vo+m2GY9~DSBGS^07*1=&wBD%YBC^2r9K?W9G;LGreGih`rszWR zd|9i;VkghyHbL7X*e^5EyWna0YW%D!q`e=bq`14o6R#lytEQ}6dZSTOi%Y%B?CRBr z({~lPE)^)~*d%^{)h5Pe8S;I``y1W*58#jV}hE7E!0=3q~G?A-&i zn!4P=U-oIJJ8oLc4tL+89J8?5c%$fXoQf0Pj?nM3+&q<{8i~lk4g|jJl5x0`zT4SU zV(oK5s$E(}Mta=O=B3|Tv;3dVF7pmrE#F@uA$Y$9e-p7jM%{ITX%|L5X+9im3A>PG zglNHYlp#8gzx&4dy$b_e&8Frj&nqyC#^4pq{292UoowORs<9^f5D(($u3a<@4AVNW zkzMn%k3;s&UYxuI@WAffE5L4GXOM#Q@@EYPXw`aHbKU2h z1(zwGK7CE3094AUGiSUIGmYMv>v*PvWKS(3S+PYj_MxXmH~^Ryk)`c;D~P#-tM_UM zuUEG3dL=O{;F$a%HnmxfGr`n37v_Gy83dU(m;3YX0^-8$`E#m=3V}c+W!w z$O`S;c@1nOb`FVL-yp2fzAhFo4nL%XY2sWAh&kY z4S33lkdUHpx_|pCF7q!@yAH@~YkkrFj{g9XA zY;t&pAoI_+;fRH$74|ZXryNzaT1|~3mElyU;4d(_ZW`DU;n7nezX)g`feftgMAdr$Z^c-)YRKbqXS{PPC;;FodG_UPi%J zx{NelkjW*?{*TFgqpoAo>B287k{8D17Pw&lD8n_$v3YaD?#$cId$Djy;tNIk>1P6m z5apQCK@&UCaU?Wh;udsU8OnPek+oxbM5@Ibhh@Z^I zbHsEL_R~cTjlKynAxxH|q=02$8xVVPuAJ3^E+mH9km~wr-Ft`iMcr>e@Or zej?+REGbs8at?p<*tmIMbuWP-_KJ>;L-*=*ANDF(pmr^ch?3;=k1skXo)&wmqY_h8 zOkHhnG(!Hn_Lkr@p2MHz{;a}_UGrnyno>xWnl428c9Y5U>l&mlzZ-amhvIE0z$ad5 z$u>Seu)wSzVtnG^T(>LUBp!Pm6g^zGY(8{W9>BP&1Up<`rrCK9BDs90AA)Xe7(XWSa2G2QDad zu(NR^q@s?gRl-cZ=4NfA+7b~`u3J_q&h!lKs>US}- zZ@p}2{P02~RdVP;MY`Gn0geEJAI=jhr}}rZQpTv zwk&;W)A2=XOetosIb!Psy<0omzn8B#U&GajSlK83d0|Eu6p(6w)3!hI>Fh`aXI{{# zgO!NT;Lsi=k0b;0^2Q2d;oyI_6@9%fa(Bidd=eRn5<`2jpevsn0qK*+?ShQ5C$D}F zc5hJ=F&|UR7^GiCebN?}J=l8|nJx%T@V;;N+`XClA)9~c(Aqj5J1l0bI3 zKM&H)0KzzSKT=OI@M-bx_SvX=%+fN2W+&lg8jEb73OO_(bRL; zjYS5JK#di2M}$W(TEOWoeV^4?w={l@kmUw{MG{?Pp;a^doFA?e%z$EHi~1VD6*naW zfIuJO8eE4HT4z-BntU$1;lU8%@|KsX7j{cRKBOo3H3k9m?abvqqaB- z4E!k4vwKz!Mf5<-v=Ta!q>!iw(NgBRx_mZ%Q_Fxj0Ra3vE|hP$F?|&Z2(WtC7ZsD5 zZ|j&-q?D*O-OD@yStQs6_kQI9!hvGw(Csj+%b3$)f~npAdFaquT&DLsK44@U)Mm8R zr}a5pG*RC#b&g~6;SZV=$y+6?n0A$1bThKPs_s_=>eM-1i&fg3;x`3`GvS~k-!eu~ zAPpYTwJ6cQWYCni3}06I-lsy*SFWav%nc`hjSC5}-1GK}c5S!Sw%iNdKW@u37EClR zrO;1&uQsF|Gm-EuZkuagXENShCHW{MR+}7laVWjrap<8=?gS&X zIGU${Nnh=!(WZej8j+}@2d}PNwX*Zkqd?Zd)BvaS--5#qDpbS&IsCAi=0e(rH)pYa zXqqq)MuZ{r()*=%N=e!OoyC09!7qkLMt{xy#u3DiTe`etsh6PZbpvPx6Em zqMe50b0nX^&V3{>J;7~ZF;^F3p*-ibvb)?BhqrUEMjd$M@Mx-`Zc#L~l?T8BJG61< zZrnPw1xR$u<9)k!6{0MTo*Le3-j+Y~o(>B*YDX6Rjr%Q#yJPRZePE2u6Z(7j6kArj z_5gjQ{G&#Pr?e-A<;{eS_vQLYwJ0OgK>0_I@aN^Ow z&Ucr;;1(CdR6+A{sT?`2r&>9}-aXCsrSAD0;AY-Ob6GF3_q|R#@JUQ3R&nQM&c64U zt+h2q<-6tN{J#;%D|GlzSDE9>ihuo=$9}Ai_f+-l&PZ39RP^!5(W%OQmTB_2et&`; zQ?V~qrYz}WW%d;<_gGuxD*H}7%r72OH?JSI!ru}(Q6tl>RAZMR4KK9rTetzpg|n5_ zV5@wv5pyJD{NV%8Pvo^6^6EPX=>5Y%00BDoE|s4@TT{QN>Yy4TN%#|1P&`QUtx7RD zYp-5blcB_m={0~*+mVomknRtv7i4@0r+!ia$!BTSLOXUG+uB|pj6GidIU1Qld#j$* zXaBb<<@QDpY?{%yxX>bXcTb&rg}zE2gSk4px}I!~{c>Fa7;Cx8P;YPV_a3?mL<7ii z0{J89rZjjfRuGngBV5vTG;C({04Nd#kfVK5JA2Bq?tZDkog#h9qq5}Am%fi_C(9w& zrcnO2>78lkKnkMEA<>9WpRT-ftWYsK>RE>943x)FwY(U6wGX-et>0pLOuR7>N!Rcgkk^; zNYYe|ipoa-L3+=Mguk;{QE%}dH4MyttP!C@htdt+y#Q>`eUHeVAyy3dsVMEk!|V`pS%|+@51r==3l476>9~}q zqcN92>G5u|3qn8{E~)7MG86l5)7u=Nw;Xq~Uj29V zEEX**NdQi>Ihc@=zY2phohqGiUdY#D^}2vAuhnf@C;Yl2ke+kw`;9(wyre# zsL%1~BF4lIQMnX*9VU}qeXq3YbLs?1bf zwPvGL8?I4Fe;disL=9nksmM1}OM0cJ&dflvJMTXr{`L_!nKgA=>X`!^fydB(fvv$I)%Vx%XxBEty8WNzi{%bEND|L%I13d)Fbu{Tb8{QeUr4g*H`%F(YbrKknlY8JSb6&UBf(XD!&UedK}f2VA^K;*XfLW=vIr=3Yd{ zG5d)ivB_cqn=zfkn7AdX!}OE)4O$XWuYD{G;9O`k+QrT+XOOTBh-VnLo4 z>D1>atPcK}l0x;2Jol1~5&6$QA9R!c+9E$(f0sI>&7Bqb3lq@=zv{v#K!Huh5U*GG zB#?J9Qcqi3JE5^-rFkv|LlI-r6JEVyMPab5sVr{?e4m^8&OJTkzpw79d0bp}sp0QB zuE7Qt#F`;@%vP%s?O*F}Yw<%$X9?iKL^0%TzM(U8J1NmYOHa@Ll+rBYUH8*_mHu*) zw7yj@JtWQc{x`!3WaL1o@TmUQv2o+xNi-DtRF^6^QeQ;Bk{emGUf zy&=6YR*S!Em~2Y7`uBm(R<`>mCUX1NKUS5Nl+%gj0uW4%{^~Lbq&fP));Zf zqQh~Mg|!KvXl`#BoeY`k>+4gNq1nAil7e#fF1$wT)~|Qc7Jty5b~xR`{cl|OhAu97&Hx{JP>&fvBn zzTo5r#Q#yKlv$)!-%%96&vDGChn<{>LeO!SI)SzR04CNHCT~t{FqL-r3FX*$V0UA& z@ey^*M+ZcR29Kr%jvq`2h?9s0UK$ul2)4Fzu;S~+gggw~352c@1rJCk3}5ez50Ukf zk3076?k^QRo7st*NAAG`mn&9LSqZ_1!4^!i`+SHGCpWPz;w6DxgjJ=X;CS+c5C9bv z%3kj86&0qy}ZTo=zUjM#JFpK0w9Q-2NIwYu%;u+tSIXJq_YgZ@vhomnu1}e zR=Sv$ogjOv_vcRxF?zQ-5HlRf+X>ige)1(g8i>dn+%c9YT{FHK!=%Z~NaDsl^J0m9 zL7aWBNDC*12mXNKD)su8EfvvH8?mVo=%U|yCh>)7pob7x& zXG4U$c_{WmKM5}-EF#FViOd+9-b|rxim`EvVYr+FWTl@(t!$#%z6;IXYt!v4Z}QeB z>-6;Sug1(z*wBxAhYQF4@R~-VcM@c2vr7wf*hiEL=5cJ?YY|MyO&;jUwS=g*=g#K(qM}17xKQn4x!GtJ_jYAxX`;Fqk}n9 z_3vU6ZR@Y}yY)XAY}rNYV?_j0;XLoZ@85rat$p_FDyWfC5J5ojNC-}S5Z`+?z$N|; z6i8iQ0ZWMc1gRfHnC|#})2Y-W4;nkocF`Fd?jQEryrbE^{0ieY#p&l(CQ zU16jt#`vb+p$M&YLokYJB336x>4EyvTEQ}e6;}(V= zCFKStofBjM<0J0B{L{w^!W*R0FK*@7-+NnqVG8xcQ)zi}nGSJG!cVnY2(n)n>#h^!c+JU_=E z&Np1v{fTeW#$z#L5ELIDFJ|{XmwN(88&kTG#i158P38E;8(A~&ZTwsBgGQYvZVn(w z-hHVZtZv%gbF;${?OYT>I0zIlZmtin@bf|NT(-|q*U`z-x#3AA7YZVtNjmHCq@(tl zYh3s--b2PH0OgObSMkZzdm4n1a7glgXMa8{*ti?Xn0Jxx4U&SOt^W#@6no6MW`FkFh5lZlG1v3G8Z-*g!B=FnKtF-&!O(zL*qm%8Qu zhWKEb+;T*3?q3iIe0;VT)U$EMTO-A-c*?kgQFr%TV1|aM!im0_XP&;irz8n+;Gg^o z-3Kl(j*v;L&aK+t54Xw_(FM+w@O#^Z`|_pi`U9*$po}K_G;)hyR8eE2I5J)|aIDU# z64F`6&5S%zT;P5yrGh*=il6Li4!v5w@6c7P1`9HnrJ7K_(Y4T72|ziCQY<$_A^G#v zGWe*FDLp!+vj6YYf#R63^(&X6@3cgG(o{Z)V8BIHpKW9Ewjgn_iS6vzy2~#%^;LTM zBRY2@)M_NS3qw)6AUFb1R{ZSvlu1{t$NUq?Q`RJI9KJHR(hK?LN0x)Rdzz_(Uxocx znE~(GUG?c=W$2uS9T*yeXLr%1?seu{j9kaOhmOFm5^of2Dd)O_Enk{JZkjuN0f#aq zHjuk7wQO6Oy&YXe4UynsRQ7h%m)Kfn=1})S%a<yQ92ZT;a2xAB3^cr_TrSu?t0t$#WD6S}PVN|q(S zF37?1tA6P8nKRZd@3j7oTbr4qKkp5$udf#gqQV8dXZTk1_Tv%O7o1ZnkwM z7!^D!K@aBT<_;jgH?+L|#5Y{%OFY@SDr)MpX1@+e2qyQN$GVrQ^5BG8%|aqF6~6}6 zs%a+`+*z@m-$6?k(Nob2p~1NdN&nyg!_^aGg- z(V11Va&xJAEFXd~Fp&@&jt;$i5s_LLMP|}e;+{WqV^luoffn%C_fIVJgY5Yy*yL|g zRAfYHw?&bxTcwt_`bx|)PWZ6m>pYA9l9BY?aL9ya|v!u-qfLBHR9|DWf1Jznnny6)>PzUO%!=W%>K@AX-tp^`|5J$wY< zFMQYnD4WgKMDc2OK%an3ZG2J!+8rCa7R3VfU+i1a=SOkgFY3U8Z*ltvQv#&Y|4`)bhiw9jNR8)9zGvrzxre73Q^b-^)YnEVgL-1I~6Da%6N4CcXV|x zMjiH+FN&7|qy6P`UN6J8jg%Neo^j>VtIJiuRkPtwAmuuzY_ zbc#PXzu`9K8oBqcm_u)v%XGsT6jiLUL!WNtA9iVTZj^C!1#&}?dDxkCFFv#@OrNf+ z@662VK~32u2E`2!5+bIhvXhntOW%M!X}N^jBX5zjoQK8NX*E}Rr++$i%jc$d4Tj@z zTWOF*03uDKIK?{Cl_quM#Mag%(z z_>&Yvj2)rPj96ASo9A@6&?BT;RsHJSkIKy=qChlH<=E@f&8|Yz@bwl(9WJ$78|yi(2Ttx8Ccv}kUKC(mTll27{w_jNSLdf^ z1zS!R`)rU~TRTXo0ymEZORjMZ{iRz$2mvBj04hTa!m#1-A?#QL9YE+4Yl-s@QSDQN zwyHVv4YC?k{URU(F!@mTTN2+q(3fZ$z$>*Hw;wV4hoZxJ9S56OF(4#R2M`@#8RrIC z@3Reo(~O?y!`)VE$c!Eh7MhJm<_VZ3@jodt5Vk@7;*A~~d^1!uxtE$zw*b>poYz!7 zGS!53rs8c@+e5v$wal*!vRynO^y)>^EhS zSW`X{i75p8R5qN93{*nK&!yMA`b+Boq&jy;1t&tpL?%+g9TnugRkZULbB$?Y*3;BM zt{an01@*QX%NOw%+-w0-AtIH!wJyt)z}^%q#eZdp0m*Kan`Ic!{|<(~4c3+`0JBK8 z#6TIYj`tg2^M__3g=emTbc7JjcsDnhHwvfySnO7Wot_c$0q~6q?CgSmC_TY(!;mY8 zmILV4d^+D?$`~Qx^K$?7dfC2_ZwsN2kVz<$m}3HnueF7IXiWaFbl7$$JV5!DQ74Zk z7!HG_4`t>EI1VMLhM=vTOQBCvpQP(``eH2D0Ot+cNyjL$&~43C!yr1&o4%95;t#q@ zNk~`qZW(}j=UuZdH)r4RFMaVJ0w-P*>#wVzY zQ&!ru6XIfGrJI#FtKE@?_T|eL^~??5$4(-Gr@#*P0ck$!{5^g=sFV(FyrbTK{%vg{=5kPu!}exon{hXRjn*uhDd97Mwqj!T{NP}2 z{mG?xI9U#aFSK3*lcoo8 zvT?8A;b8$nI4s+C^mfB04y`rEN7T0MT&C_nPKGCe*J(J*p(T75MLz&A%=HtT2N5Yx z_-Xe@1|4{&06#Tu8MPkH2cQ~940$FnGTeCQBjtZ2&% zLq;m#^Hg{HkkkUGu3!-7@DYD>STI|JmkDm96Dd}_RA*Zk=-XBal&Sl1O~kbcG0%dS z!MZbVYxUejU_f+mfU=t-ApvhkD4ho z4Y;o2dWU3Wp*d{LoZjJ_HI!=27c@CmwKmXI&`v0{-o`=nIiy)+>>&2U6m90}B{d1<^aO+RdfVUW;hNf5EoIv7Sft)Gt^{mLplhKgddAwMQI5j$9VVSR4y zLK4A3`3lE0`or_SJDs3Y(Z*l*);iIH-lgp?Qa?+D3XlQ8n{m&Aq`tK6@?F~S#X7+ruK*##>rhiZZbK;^{%G6s7D;-@I; z_3?{z!#*h91m(A0zjkcPsxTt(7!CET-!NtVwlnF$zs?urQ`@t(Kuo@7K z#VhY7-2USruhaAFpP7|0+Aon=M>L@2wkokzCQvaQ`Npw!tshASiPA3g_|&3 zh5i=F&CMM~dc|srh)i}AgEn9PVC2&}YhQ& z6-&k9BsGO3iY+;v``hC`-A$_Edg}rXz!Tv=!c0ba;Z?qc*9+5leJ^q0KeTQ>iS2HY zriWspq@ z(WfDy=bStw+4;Qu0*}=zkIqo1({F3>Z3DW8;CTQC*`8XHb;1+=QXW6EQXU$&nVIn{ z|ALk0o@~kaS9L|ZsQ&=G?YvZWW23=Xub83p~>yH{*V1KQXsw|R+WBK;fX@Z5s9Fb+QRT$LTnW!F8EVN(|*kkpPIwSop zVq(^I^e8L&cuDCL@Lm^wmA?jZ70IScl6Quhs;hM~_{6QAM76kvT8@!5v45jY`n z#m-1eKD|GyW^Gi;5dWWoNkD)ijd39Gjdq~zgD z+hOxvKCLK27)Ezeqb+9PDl;n`c49)pKHdW|Rp|82M~PSd*wKb@{#s`YFZP_cB=?oI zWlX$o;b`Zo=a)FpX0+-bR02yPfm?vS>0D`8|8ChGJG2^7Su1JpSQmT!#yp8F$ODEq zfKZLmE>Vx`?ZRNlB@_@eBDe#BpII7yNP$HEToxy@$%{r4bbaC zO-8m}Oo+tiLyWNqpKfjKMs=PoPD?5k9C>H!T4d<*E3{1Y4azWw#C*4R{c5sz70At8 z#Zl$(`6t3JSb>+pZibet%J|82o4l!sN!>d>oNv)*-yg?n!a;h89Aa{E%};Yr>2NO& z?H3zk<(F+Rei9phPCF%HK5%6>U3SvnU0a_16tQa=nBvjedQt~o?fswK;nkpe#PZ0p zAYm>aEwEFhxwH;eA=C0k_A^u=$Y$%nh5=~4)zxR(DGdWz7sO<#7FX#=J})D|3Y6~% z@r;w9MlRz6+g@ZM|GH)tsI?7OHE*p4KX&ui77jQocyLx!pA4KFP)?{eg`T)|8Zr(B z3I+9kaJ>+74ntJZS}fk&_e*>qv06}`3=0?g8b_;#=FCo&&VCPdYO-avqd$I7CnPD! zjiXq?w$6ev2v-tup9VNbZViLhzN`PH0vzfYl_CkPm3P+t7E6Ck#ZILYRWL!%1Uka6IA`Ep_A%Qvs$;}6){?jR9m6p+oh+5a_y{LXoJ;gjfQ2V zrS;$IIX6hP**4owChy`I50@Tcp4ghMQlr0Ae)9KkB8>COJ;H(qh%+O)MM9km&dMe9)Tc3wm>X>E8>u`nUuZ!^yB@@C=I>4B_DUEu(wae%a2O7j;pvP zd2lyFLM`TmU3Z*UsSIb0vS=!uZi@Ht1l@5P74h}Bm)rjH+l6n2YD(S(sDa3OPQawC zF-MJMA4f?XDG!&Aqu8|TIbNZu zuCD&JVt2_)8R8rpGck={ND%Dqw0y*u83ieODdaz-Fej&r^FD7IzMg#@&5s1&VkNJqQa(_>E zbhx8)GvAsYRfhfir*&ehkS?)Ce1k!TbCQZfK4Jr`%dY!ev=uUc&!jlI_WW%O?93g4MzMHnukhVC9lj znOv0H@Qe3UKGn5@kCn>Cwn9+^bIzKbwx+VNi(dyc=U>+s-Cq$MQ~|rF5Ee1}wk9FS zbC5`AXrW_aVe!lX93mqkmlV*v;g=8!N%~irQs{>YM-5>anTx3C?1B{yj;c(}tQU+N zEALJ?_nbe=JMm+aFZYXk^p!Z6dW>;uqie!G#Y%k^kqEGzVb3wjUvbh7KQIS3bcnR6 z`MjK2f0C9;t`!r~Dt(jC#j?fHiCzanEMCXygg|7Nf=k9uP5yhy?1=Ult+v#21cC&2 zGb4qhLCbf~0uh*^BxBjV_ccr_8x9+}_V4K$oe=6(J9yBBnNN+k5ugmAF=^dF$S?Sm z*)Jj}nA4K{@w7aoda+7ntX^%TJRAjG%=<|fr>%MN`wK>7*bQ``XtHQk<|Ng=mi4i( zughx$tK5iBMZ3^wiMb{2&MKG_kqvJY$mI;;_)&rXZQdww&o$vwNr*GsN7sWAU6gr$b*z?@f zi4^NT)<{Gif=(=_qM|}TL|<2zCO6%!fvv;-;Y?Tg#1A&KCr^}4Pml!woNBn?QfUi{ z9JY~u?B%ZuK0F{oIn0;e1&QtV#6Duff9-&kPI)33x}Zgd8gBjql{YDhXl7CU=}1w* z)I9FS7m)dwz-1g)Cp6hnT~%cazoyA$*5Fwb2<0u~at@-H|A9J)3pHUw6q-3Lsoq}# zU2VbfN>N?;%Xg#uDKz7mP67L_k3eEtXjQ#@1K9Zx=;5=8DPJcTy)lQZwT|E zW`9)dul3V7ap*uNc*Ms=Mp|0g<`3Q@WBl0kwuO+{j_5FAV>IwAbanrs{- zY5A~A(9BS(C!+sD6dO8Wl5Iu}`1nDO$ZL)iUKIpu&F3pWU=t$f&8hOi?^en~(xru`Ce zb@{`JD0){M36nnQt#EasByr(lmRt1KY@?ISvmeTB$3iXERnVQRrKf*Y;NE%Xn|ws) z{JpJhp|$nkfld#YBRROZn1PLA;~>+!UMih;8bx4%R0i;&Hw~MHQ>{G<+$MW(7P#pO zGg|Yz_9dN*c35?12G34FF$=??KEbq2B1to_8K&B_ECA<7rr1mIK0{iKXHXD3`UHO@ z3Ps>x^!QU`WP@l@C>nil4das#(Je(GPbUxn10PWRnz!eJn0Jex#pdLU<|gmYRZMyc z!lrAzb7;CmYH)Yj5rlC&)^eo3w8Ty4{P+W2R>-AR%F4>Z=d+OfD3sn$@ym#9$17{9 zLyYaIS&#m1?jv_QOK|kY^qRQP$Um2$OF(}09t3$YQ1{<+TgW`2wLes~3;x!RgXlYS zDF9I1{iS5Ze-wo#v=xQ+8=2-7{LwBw?hw1!ny+vLIlIs_&z)Vxs#ojg#88V_xxI&m zY@)j{4T^!niUP%y1FeKDC|=}Z=c4)XzKa6L;6ifU0s201GS(U0A~hPcOV+;YR#7*4 zqy@+$1xn~g*ne5<$krPo9dJBDxg@x06C)}rWGg6O)P>NI z$KK=wz-Q-3#tJvV+bK9C4UaO2b9@uADOz_(4A89t$N-QH!X}4IU67vad)v?L9k#>N zr{){>ArdY`G7tWYbP^Q+KH?%q#(j(t4Bt^_u&wDkx@8qMML@L#_sID5RSnRH$sfEv z*tx(4eI)bwkT{~kcX<&$AKDvot1-LAcI3yAD=--1j{_4D4H7`_5r!RU1#rsGMX|GP zPCb>I0j+&DI8HNH{r)zp$gKDcQfIz*bZi>`(d>i@nls^9WaRhm?#)iBXz5dV_{NLp z)~6#@)^PeuO&gsNHHCH(9R&>%6GgZr(t2Tdy*Kg$O-jRLARn@O-29>K67CtKGQQ~W zA;DAej(|G!5+#o^(a>%5*zofV1aJ|93Rd!kHwT=YoM?$}va&qxinC%Wg2AF(14|vy zm!#aswzz_9pA}PMEoUb!C9nKWWL6;H549bh>ZB={RDfJ804T^$vTyN_b}d4Ip==+% zWA2}oH<4>I9HU76Gt<4)uH@nJU2o2|tQYK=>4A}FOn<``%RMHLmi9U;(0nBuaC3)H z>fI3i7m0jlJ*#qal@|F#4T)*;vwiCw?ZT1V^@=3+Pfbm&j?0RSRLoFUQ@d!N4!jB5 z6$D3E3q&`8(^=9 z{<=NGdHW(}PV|a6H|VHzf5EnNcJ|bi;R53Ce&*cYv)K_;U;&|7esyU@FaHHzJoH-i z*MMs6HRmYe5Ad8=$M~FjH~lkYnL^4p3`}co->|H;?`g(=^Ykhj6$>F+cjg#jN{xlhLPmTfs`NvpsB=y6Q0J?_nF7qu6qVX zYQIG2*mV+|OaLN`U_}>w^5mn2D2{!#!wGlstZz7Hf%gs;uJ?;M|L?#5E}&>uIoNef zQn#Xt#X#5lxzb;Mk^k_JMq>h5wN2FXkb;qJ1%~Sw$LiHT%8p3@0NxuC zSIEuHcDG5q-~$`_ZR9eoBzLB(l4tem#hC0>{xWUP^V1Hfe_kJtJ#j9}eR?@3=hq8s zIx$}XGv!;*6fLG`W@vlzWi8CZ_fA>pTc)g}QlhnAl5P;O<86pWgm7gs+37eo97ye> z8Y|QB=l6RW^t4+v7uL`GzBQKgt{+BU)R)(Tf`TfBfkai_eOYA=2H9U&XVg1B)!w7V zDR*>yoCfnEedE;v8Hal%R~np-jb*Lt?df@<_^BAg8r4HTNPp~vjXj`b5(zzfPiiRP z@j7ukiZ#ZD0O4hgkS*&o4|(2J^1=W8#=U#LnLSKMNT^7}^EdrH)I!c9L-#*tNv2$o z>_m*tdh*142gH0L$RpGP0LBYmS^3bR8(n(0UAnewx4ziP?8fix>Pp5G5?3gyOoFXV z^%~7`t~vX7m*wT1?o#`n7dwz6m2D7{+TGHeie;+R`#URgDn4(eyNdXDdk+Z;aO|^L zB{DiRbYZ8>EA5w0a_9j^&{01TmDsyCjz&{?7W!aae8n-=ZI51H+|zZP6De`e_4M8z z#?}wTWpEq!K<*3KgcoB@mqwB|Iyc-gKtZG0c+Zb11iVpYUCko8PtO%DWS1^$9i775dohC2Al{%2 zb_sHC@mefH!9N9f@jPjq1qIip78+0luGVeo?+-wVgA|LJj6K(}6!Uh`IF;eCu}>b; z)gH#+>YV|o4dX+8zpcV|oBrDMfm0IanynU=M#~FZ$($OUTwfCrhld=OZ`avmNLr<3Z~8(e#~U~gz27!bWyhz`We zaRLsEC$esJX1@^~*K6l88LixSvHAzZ?BbFo$HafUzmro-Zr#2n6z@eYh+y;r9LPgX z4q(Jys9`bJg9*X61x7PJ*Hl(jB~QM`g>q`_Pwl>4u)=tRF{StNs0mEIaqd$a$^yRN+C-76~gY3zAtP~#KwW9SuvBxqrxW?44!ec!AXeT2S z^Dmec3C`7+5`Dn-5nihAnYI~ zH~nkHhuIm|VRv|o1z%UM9ZroND|-|)sTE>3{PhE8TG55Kcsn>UjzAh;YmgQnPX>en zQ=mNIdu5=oC2f3hJLlur2*O(^3bTHPUE{6WKi{T$q;}Oa#bIZGrns@EV^4MAzU|vJ zGeoy-bEtcPEoC8vc@hp9^*ZQ^FwX`+zM;+1K%gzYr}sG+pt-}Xz$TXGb}IVa2ej#F z;^BJV-RIg?*W9@~R4&7Xmnt`(q7KT~KUC;aj|A%ZP}WTnZ8>XRrmrmfJ0mBio?3FC zGYK!_@+52y`da~VeEf6&(C`c(M7Gc)eveU*wv{m+dwH}K#661nbGQrdyI$dOw+##g z5>W(&)M`bH1Lr2QQPImIFw~3$^&eYqdc)D){;ULSdR@F z`Y9yNu6l}2!hIsFHw3$!=S0but;b(4cgQ5G6^Mp^@P?{CKsVaJkcs9t#nrw*wpGK( z4ByT(%xM2S!PDVo;)X;u2ZLJ@J!ai8@s+$FI4~_|ZOHB#b4|@O9z>o#f zF6|H*Ei)DgU{U#UFs`Aae70?%g+W~0DXl2ND&j}59x1|G!{dF8S>&J? zcke{x=70_FSuB5)wj3YZUqG10#Kgw4+_(q($A-tMkD7UMw3HGAjA;<1Ie@J63;RH==Q3 z*Pf+enZF(UW2Cn0>Z{ass%V+fT^R}x9RmsYe{antK#FR?qWPG&Nl^$A`Up6La6l2x$Y;zW!=!)F4oU*=AgDChw?zQgOgJMNk~Nt zQq5Msl8eh1s;#}&cj9U<1f)Wdwh&kzDePfcaBjb?vK_Jqfb6uh3rN)5 zma*ViH;ggy!LDKg*q%aw*v2FtKXjB}kGG`<8*aujArYO=|Rr@N#gJcm2YB zZ{(NOY!hu^%Vg_B~uD%dO!Z=q<&+O0_cTgw;_~-6nBTE9a72 z53(tqrsnE6-xwN1pgF*g^V0acXV)@sa>cBe)dh=f#t-zw-zbntR(6sa@N(5aMcUNnuqD(B2jaG4AMT>$mqmJxjy z2B%_b7z2rZMT?^{PqPdBAPg)xVo`IT(bvi^c;g#sUf{dG?{u~(J@-~%&z@rz!GN?e zP6Xd*nGH&GKH*IiLJK4mzo?xia8T6_zqvO<9VN)^d-uF? z*Cbg=ft{|All8Rrm{J|!^owC62T}BOM0kxvD3KzBKncJRjQ?|Qne6axg2Ie|*&`9y zHql*T!@W(HzEg|HdmF^^)7C>3kcU5${_FB0ydn1nTpCu4?gqypcq;^KB?1=67&<&) zL2o+NDV2@JfucZnu@#{=VASNV+wI!~pF>5lzTbY6xyMsco2<_t89g0fp6Hb0GldBfRnTqEHq%y`w?Bxu!(c-#Jhb{T1Eyz@I5w71r~eT zu#ZFaNA9{RayTGHSp2qD#AoyUL&UdMQK2*Nv+ibrOxoARAlJySc4Q1Kq{`h+KGn>I zEWy^+wg&10%D1Fg+qc#{S#nV*QstIVC*E7Eg&5|-_W_+n&u=AWB3qcCjGz>;DLj;M zeSgB823=5>ol+=QsI#d<37g$BoxTIQFZR2C>uN>etkRxsF-YK=E;PX zR8~2QPKHQBVykk@lL;$EZ3hlTl`z;+zagg0lfCe2*W1b?Fsw92hP(&s1SExuz}Pb^ z2Yd^1VGh=DT1f>q5OVxe6iU-`t~`R+Jwmz3?$Q7H+ou)o%8QT&OrndCsKAyBSz%Te3W zrXJ{h+Z^vx{|o^>;o(7KOyW~b1eq(|yp(Jd<&m)N8<4?DUIH^ga$*?fZ0XV2IelhU z>19trcze^P$|X|;0}?h4Ii4#1KVX0-vK28OJ>UQEOsrWDOqy9DmOlYGKT<3 z3!ajIF+6y(Z_IUWf)0sz6UnY(WnW!#%*j5~V2Zj}WuMFnIlhPvzRnm`My2FR(E{pPh?yLXYQXGCLRj-<8ssa}ZDx0(B3-_vl&G(1&9Bsq0^a%6Z6o(nG_q zPON`ZWquu4f>OZ`A4E#9XFD9E3rr5wK>1<+YMYSIftxAphf$(BsPI`DE#kO+MP#*q z?j!^)h1vx;l?T4rPl#OO5BfN3+vX-DQ(O9CS?egqnS%2^^Wu2yupH2707Ab3(+8qx z*&&+nIybT90ZL{r~&Z*Nlcvsk|8{NE2E6eSge(zoy4^@7mZ zw*_?{zA-ZaOVI4+?as@zNec!i*I*4E#PS4ZBjG- zA@r-kL( z^65Lotr{p#q3TVy7j#n)qzzoo!nWza6gpGgnfFw7NVjQnjeuZJ6UMW23>n5teOc#| zAQrwQ=ew=(c$wdPw#U(o?%_ceynE(#T*9^x_P+~8C!L3q>(y@4)+mb^zD;2 z+%sGFZ}N|=KfwM|smyc6W>7#NN31l=$+_83{Qc16nM@%49M?)jBpRPB7zf6od2V`b z^L?YM!`Ie=PzbI-yKM~VF_J~?@Gyrbm)Ew91Mo)IrwUi-c8ICqwb|D&C3>BafEO!Q zF2Wm_rWUoA=)7@iE0-08;eZA1>r%;DB|YG}m~ULUF&ukFf^}0yw;eY`HQ%i?PdwGc zqz6%lHLtUwXPa|20IZ9Ub8Gjal&u*8%jFM?D?Zo}=$wP>|q$Z^}8_X%|RQ zi)A?fW%HH!Qran+Z9k+oYzQO-eT!Z2{!rhQK7luGqa6}ey^@REv;2@r!6UkNxxJNDe1-HJ$$=m6PGXT|mi2SCGHqdt$@TBI zj%l2F9TQ{o@B?(Lpv3mWY)kZa+l@fRLorqFFiT#XO;h((mB+8I^f8OYX@@qia`X1C z!NDMy3kuT%z|<Fx3*^#H@C_? zlO&k%#Wv(WeA?y4!02dUB2%?!%2+G!Ha<*Bs6*F<53v4J`h6>Yh0lkd;p={PPk8Y7 zOlzKGyp%t>S_}hj7NOpOw4x*bv18t#lFYg+c2ZDjX>tO%#Ucw570dK&a!ShYe;Mwu?F_Ut**d{hQeyLg;O-U}afTrE97 z7sY$HQT6a)Wjqv;4+7|H|017@7cb7q6fpkB;rFBr;<~Bq@Y$*JpoM9o1&FIe|3M~1 z5PLcXRz;WDUL^6fh=ov^?VK4*TW;vTDz`!U7Yg0^eHX@w zlBes&$shz+x@hYHV9p@6Kr;45eG(I8V(7*oHU{RTRyA*?kpleWiHNIbAvih`f`~S| z+va>m$FGw#m8&|X#I^@I>cGIxm}RJ?(CxEeYeN8>?T;^OiD*)kf5(N=W82e>BT{3v zw%jK)V6GkS8CVrzVUN$vk2!RT_2<7|3}$M~{19G5#8A*hYz#_b%NbOvL@hD@4BOHE z1A;n@u80U`K1}^o3vN7m(cGNJ%Gb%^lC*;;BvxbqGN75uR!W?-fvYlk?gS2gf z{T509LBNZPpb(nlc|j|5oC_X-MgrmkG8YModh)hFP6L4|TE);8P~oAqCrm38Am|Fg z+unrlrmfMb`8u8{j6you2+8rycEBn^X*2vI0|g85*hB1wj{7PZ`C+lw-_0y56de)y zDX@0qGzIDGgB|`m07H{ZtP1mr55hQ8mI8>56pec;W3?4sHEOY%nr&)opV8~x`wB}xJ9$uZUk z;^ZuYN_TJd__tHm##OJ5;cu9PjyZVKXyWuIIE2ebq46R%TzR+P-jpVnqO&I^!9nJO z{kt6&#)gN6hTGp3h9Ou9UlloEZ1Rakd2MZN`>VSjRHk0)R0kc|@bmx}Gq81Ejir+H zwxL~zM5tqX+`A(QSu-vnk~XB=Tg9TVqTCIKSd~@LQg-%&TLMboJ3Fg4d)W~=0j|P* zB&7K}iI~xh^x8iSeglH~w}v+`^%47@NpO`8K!XLoK54JiM|(&b+8HFNZdr zaw^?5lyaore!Q(|=Eq>X%Qo)<84guY{kRv9u-l?Kz~5L%l0!yDE_G3s)_4{tFrtT& zxPImQmxosY&{1UiR4!?QVAb8Dr-m3`qO`(xlDr|iU= zGfRLRVyUr0UFtiU`Uel20n}yrwSiY_9=hBJ zo0u;Xd}H=~wYGDeYE*7C0OHiIRF<(?QMvItDr?(BRA;;ETF}HiEAQC7hb@MkReW{p zF@SG96Ak)}@ej(69$9*Q0V*qKviJ91uv!;#DtxzxnwM*eyFGo3h1k(`5XXs8Me*pd}1w{P-!xzf@|lO1B)W!Xs;Y#im8L%)6@ zsbb|I&@lLr@a3?D{J_6op`o+}Mu@gJwCdLe9H3QW%;YWl#z;2I;a)$S{_54gFYj*U z(7H&9AqdwfPZ%=^cB&?K;}Pk%h5t04+)$RXCj5|!vU1!=;jW;FtjLsxv&&(4s(9q# zG4yh!a%F|X#B%$IVKwG;eE~7XhtkwY5#93+s1C2;u0?KZvNRmU@Klkr0FjgO@#CwZ zsuFZkRj#})Gn}{LH%37rplJyOaS=O-rXjo@>_Q5Fqb1K|nkVZG9KjM1?D<(~$dZT) z=?yqKewTpsJ=cCudilnT^!$$;t!yc|)D578QRxXa1>e7)q!iEV9^U8vw_bT;h=!wJA^;w;N%o^+Wemd8(^k;z>605CqultD~pgNg%(HWS)fB9<~h70BH+^yI_|0e}eI$p2h8{;kq7ZJy%h zXbeG2LmP_7fIvBTHxTEz&FN9)P4A!&xhl;w8=clFSF)kunS(G%t6UN$vlWYye2w4C z(nYf*`3)n?Iz+Fg`@vWA*Fa;-XP^6Zy#Ijd-F_*LlHPMJk%&6A*6cLb5j_{kKC(cc$|qtt8-2W zI?41=ngsOMv`&*K=E28I~Xkr-3KT6;SC4;1;6O zW@cmiFgOa6%X{-WfLG1q+=M3m`_(0iwTjBiPe_;yCi5gS82zo;QBSQ!cE0a8s#{3f zEr+LAgQ9@nP!vh0FeN|0WhL3pW9rd=5{OHgsTpI^Nkk6W*uKUr10JmcZwv9-p-CX7 z6lEwzz^`>fgAwN_nB+`R^t_l(>tDPY6f{7*=KR*^`~R!P)!1dpI(N>+#G1_iW`wHyQTBkD5txm& z|JbIhOn}OcQ1HUFxoeF0T)z$zV?)Yg8Nl!N?rqE&0}lELZXDFg9!B3#O}Nm;p%CE= zk|eQX_YQTq)vx)_%LSyf3Sb3b`?{{*;MMC|rl+Mf4jTklTAZzpH;7}4)gC7`_nwR2 z-gR$&Fa5yx^9=p-|Lvcs?0w$#xpY6R3LDx23av;Sn}XWN zix)4N#;3c{5`(Rq!XNeqwg4Cc2HdYLyurZZ1JGOSmXzJTJ@JU}m;bEKI-Ok4^>qQbVtZUWFKttdUAPl+xQwPF|=YL4a0DOgXe zs_mRb@a(iS=7yw`@T|6cd&4=SN6@PlV;(Qz4T33*hhB_W|@SyY!G}QG9)I@z!D}#P#$w zE~XUJ*RLZXZ|BYl5f`W_Ruk&EjLvDW0tN-}d7Roajmb$OPKAG$a^)ekp4*lxfG9dS zpzW8wQFapq>JZJXBKv%0y#Dp0gV7L6z<&Lv=BvuVjvqg6R6`-K@~R2(_ZePhBnT}9 zs)7kBSEsJW4T;Z2uyeo=j`6F^5i@yRU`O^lOTa>5+=vAs=)7&)Rt(n@sM%Wl3=U%q z(V<44Hf7C>CJ07|1ZW&Q=y^OgvvXP;LRy-9@=-LwYeJKvIxtQN9Yzbu4}>YUUZ5Bf zCb67WFn&%Fdm&#%`~uo=}Jh0nzB0JB7{= zJG9{UE7%ht;-aWGFsEz3BpJK&?5c=iMruC8h;W$m0)io7DG`7J$j6T`Ihregzr0?= zaWz*vtl$_ugUt~BQw6|L{x7U8FU*tGzP{~i&^IHv142)sXp~*I=||^ElSsb&BLfQ_ z@3M_ypSnNZbP|HM0TA&2)P6J2(b=%zpw5fKX2Vy&|8pi_4y_l?0l#t3V!#EJ^!4@M zhF%5vjCWjcK^XuhunHto69;I$!zloXVhhT&|EWE_{WMxNm|icWjKU5IwR1to5RrMK zXBN7v>ir0}Bmg!JCTa+g*EhQTZ`|PdabL0Uh(qrTCPEX6JLl`ynZpQQ!Pi}fEO;6T zQJ6dRRTk==z@p2TyW$Ce#`hK-5wT=qOsiW5HW@(c6mbm=I!?0a^$=bR0XdYcRT6`H z_{PY`rP z@5K{Q1Xd23a2jx&D_1g@MKEXnMt1VA8ad#4<@-)@2LpBZZWU}nNnHN<+}U#WbM`02 zvb`ZkL{y?HT3|0D6Lb;u#g51W9DhP72t-i&5(35yMFg-j8Q6cJGTh*j!th+*?TtUtRO;-y1H z--=Ex4@Vx^6W~QmEVo3>5jlVdt~gI{gOgrSab>hhm+}Pl=4Xt=OCW966`y8frR5KS ztb6A?s~Rp&O+gwnKDF=NCm2XcmJ@*=C{MT(+CRJw#_RU;*Ds6mHmg(I59Vcgf{`Tz zG&>aarhFKi-2=Mv-#4{DXPXtl0qQ=avAMQaE^!8LR1- zAT9~OA~U6dMnItmj)jvqnB3H}|E~OJ<&6WuS~%VjrshXksSFPdWe+sc_eXaimJ^f^ zDe-YBDj04mEnPPr?wn~Twx9^h$6gy&JBXcZAAf*e9fB+vaD1RSsWM;n@q$?so~3wN z37ii~eHsXx!8b)TTrhOiU+~mhDzQ-h^y!FP_X_i5!uovvoPA@}R-jkH@CV8@bzF&S zfn3(m=?n%3hD!aOnH(hNIf>sDq^1FM3anork^2ppL;%91Fa9)R0UC(2% zr!oQs!4=^f5JL7cv5fWC%HU)-`?+Hb_|__hE=W9$0YBmLIi5B@fW0i;*+5IH#SX9h zLJUGCAz_3G?s7*aE1QSMtWdATAz%T>t)qzFnuGWC+qZA`k9Qbmy7+aYfpMR#Zzoa#3eD`Xt9=A?_T+`f=m-cUhxZ*c?n0duJaXC9RXqK$nMeB4SM^Mh^&pf; z3?G0&NsOniKl^AKvCAdDTqc2i>BuF6u))#hbANA3kF@(}tWg?^!pM*F^Z)7cUrfiE z%w>F`5gv#f_J0j-s6largNTF+p!MTXjD;Wr=$ftn15>zQC7iG?5dIDxr>0iw9ps>x zZ|Ped;BXH`5jY3nXj>x?V0QQJhEJK8%m(Va@$WWOeD31t2UVP;P$GFwSXF3y0*_iq zOd09O-x$t9w?Q&ofv2JNv{0*X|5+$ZW*-22xMcMz_WjW;oS>ix$s{{4_WG62s(aTp zz5rf7oSv9iW*n~Caz0eOx@Wq4E#sm^r%~KHz{PSatODDTX9u|jbAKxKk&0vU!~ z`Kd}0Gaz6j!XWvZ354`D95Qeq@BtiA0 zc=+&P|4vuvc1R<}QR%Qs+7J>UY3ZKXS}^!D#HD^bP4ZO1KY)s2Zh|R@_;;{aac!~3 z5y?DVq zWF5kNFWP18eIKB0d1PBwQr+Xa(qhX~I`s`JU*2`XFpP%A%eQao9q6w*MMMm|aBA=K zjsOt~OUpI{75vZD1cXRByy{v!4aF{S)4!)(Rku=EcY&CIMWT45&*l!%>%jT;O%C{D z#ebaZF;$OapNEcg-hg?B{=_N)E7UuJ-|d%HT9JU%lq?weyY@N{&LC&?FMwCMElFoj zqXq&bh~yqbohPo^H+o7HOg=)G3O_N+d8+B$w4!PRX|u!_zV6uxMP8e3hSU<^qC5g z0)FxpeH<{Zzl?xG(2+k)8k* z>Q3fHCB$O1K;cN1EcvQ@HV0}qmh38(_JY5my`#ezGlKBa2R3Zd5p+dB1)OTg+)gwW@RG zYIi9hyMF`VZ$EnE2e;b>35gSc*Vp|80l~jAP!OoL|U?4KF9P})BuF|UhaJ#~Q z_WT)P`mATI!$Iz)qT zwkfF!2zRGAyTV(M1Aj_K6N>Y6_UFyVw8c9iV)+jIt*6YYq>fre$v0*V%7|V1azRcx zz)jQMTNXl&AXbHH46_F`MVO)b{$0-|OD*>1TI}6`Ip6{_5{wOK8FYpr|~)9`8N45$kgMuj9A&`>+1k7&=?L zfK-rNx*1txA*@%Yg)!uB$9g zi+djUmEtMkcXR@#PM#Ra;|Xp9q5NM779%^mVc5VIRN^FW2l*ywHKJ$FMQ#5Ds)G<) zVa*Y%F98QfD2cyS7|W6+ElG2%&bCiRkMQQlr6IQlMJtz#%RAKT@fn*ZSggb zCV+n1Mej?##M(-|^5ha88X@@;+ZdCP3H49@7mkHPl@CF+C@RNccr1;$$r03P_9QrG znGzFge)7K;6OW$Y`Gh$?&z?VD1Tb<++#6L$26^CYAGv@C;T{=(z9j(Xwgx?p7GxlN zhsh&BTj#k|2?}^ZtdZ2<><;VauA5ZE&ztjNcp>(V;Fd9HW;4mi5PKk?1V2DhFq_T{Y^&KXP0t3+fWv(YrUfnzwVz*%^SIYjGopV zXN{N<9LC<7gqM0w9aId)HeLAZkxzH#RlZ54>o~N;%TI`tq`Rk$={#zUPnaez8-J(7 z%8S#oH26V97G^GsSqra#C|ynir8;+j4{tbq2^5mV(T^Pv_)7uYUwNrm-=qLXJ-Z)f zqlPH$HAG4orCr;n!+_QU&yKvY>C0y(HWET>UKvg#mMG$EplO(ctRvO?t}^k-BY+we zDFiNg%O>Ct(#IR{-nw~U`{6ikQ-waAuKI#MNkvF~;aVlJbc0V&_D9q3r_Wz2->lEL zybG@iiBOxF#BDMt!4J>77^PiE?dE87TEg#Epk8HpR7b}Pn{IjWA8hfz5gJf2(D;O5 zJznOnAAPm;yiL9qP6j~7@LBn3#N_>fy%%N8L)QJ$2l zvcl2ju(^H2yyl9tAJ_xy?)g93u=sna#?3qQdwhSvG|*G$o>ky5mz4?dmE1I65&YhI z5A4xK+|csNw|SS6#jp>r7>GQY4%g|v7~6;3J62g)9dX)>siV2C3xhV;99+)UKCmVi zeYBRg_BBk(G5jtd7%7@RQmxTeP} z3K~%1!M;L1tp6ONL>_I-5)?-3_FCAu9PMoA9$7B2G4WUtH=Yo;9P60-nE289R}2(N zPNH9fm_PrB9*(Ba(CGjL2yuv9qSCZ6$f`u(qq*~VZ@^HwJ4IP~-e`Q{8i9vJJiSwU z`6O)C3mtw)X=v~*lPN};8}LA4M$|{bk-Xb-NtkEQ7qTeoJOc-d5fPy!Z&fjSa2wcg zQd{J2pNn}Q|LTirP2iRrjUVboeY#c?U6!KJ?5g)dLymuAi3cB3kekn)H=H}J0NMgC zkdIkTHAn9H@}R^bGyBl^VxJjT1@eiBy>k?L)P78qZrHS(!{H+Cg1p-r_m^`8kCJ%P z`wKP2wJBK(wuH;}{Wz?M!$5}u*~^bjpjdXTU%D8cJ#kZB*nMN*=l%Ql0j&_bWbSZf z3?Ap6I#w6F>99VKOE6#|75dy4E5s(o1%bTkjTN5)xoIysbn36%t}ZTSeJdmvm@S|n zaf@!mLKs*i20}+ryT{KLS=nCb#FH-ytMusM4k7H}vFHjPA;C!34GxzuqSX@+R^t=3 zLx;Hg@73yQi=XeeSv4`*pKn!CX+jnyYJ}V*E>6xItGGoGGgF_iSV%c;^#x6F9yT`| z!g(PXg;7S+J-VVwWhfk2N=ZAX6+16gzz085V3Kq?*N0Wz*ek7~Q^4lnX?py{qu>9z zFtxj8vbI^%G8TiNi_Z~XEzu7ENN8{3Ro-~_FKcR%@_mKFk-y>soGS?I+Cak3Dv1UG zyGi$uRSEmA-hhawhsuj<>tj)`T;6wUpb?xbPoU1?h{9Ff4{rtD|GTQ^lm!PuZ|TMP zz&6sgUUV%y*9eB-a8O08`3Lem3vZm4A*&l-GvWUqi-zrF^#9MI+2v=qg z4G!#djd-I8juh*o{^Mi4CDkD)gm5|b2G9rYew1sxj-bqVj3zZ@%G_`kWA*r;qm$diZ@9~ZMexCb% zpXc>)e{L85>R-R_IM3re#_HGp{u?}~5unv;o!`n4IV~ocIVi^|jD&Aoziz#sGx-24 zy-?&7XenD;C&7@I{KVOE7X;Dx zk2E1-_H#X-`_+G6j-)`b6J#xQ_z3o;NA$VV-&Qf&Zdo-S>N>yXO#9Gq^0K`gGm5uf zK{1*M%HQ349w-{$ptOhbx%T(>?`L#4SJnGZ@3NKJAI6z_=+Pc1Gh`&;zvF`D@M-Fv zu3xW%gNXu@^;QE0)o7qV?t;&y@N=MGLT*7e{)Ej`n%=Z_B`J(vudyYBoQB~+McH~+ z7$_ky+&?9@C_tRqMTWNDfG3#th*WE6c0=);yS}PJM-f89M!PDA1)hlWict!SgGuo6 zqP!E?kRYJ^+tcz?JO6KGss_4h}%}rNVtkhlC`hS-~KZ~9V zKY@nRc*zW&+=kaS7~;%zc`4DR(%`DVG(cw&R1u*%cHsQw3p4nR@MdlUfe9#3@+%f z-l}qK49ZBmKvZo(b63#qjA<C(PC@)f4;-9Q;&Y-;AJt$~Kgi z@N68Md*!k56Z(fXUhkA=fG1nYf5S?KnL?CnZj|HbZrC~!74~Ih$hI_=LZbim4?bXV zCP4h(H^ED=KJm1gN-CC8zZC*KvNpS1_ifkw{~t-b4?Jt0W8fK;&UyLgE$1aYK6IUD zO7RR28P=zqPg4?a@+zNy$=L7M^Hg4~7Bu!;1Dx`=+*2I)eZhF9>$=>&)-BTxlx`GvXyYy38t5o~)PG)-Y2!5G zR$VI*XBcF!MQy2!s6BL1ZM=^>g`GWavbo@L7vAli*J66(YwVy7yYpM#ejG8VNZ0hk zW~RBhG^OwhtlnA>56+5j1Q)UJc_H6EsOOkL2yVsAuuwY?GQ|P|N-p3i47))R-Sv01 zyZ!r0s8@#%GI1=!@#@j6bL4JLx#zopjNMQC{MKJN>A1l~=BDW;t4#Kna7jh<2n?Td zYHL-Y;v|Q%?Hx?@J;rt9rs2?W#t&VQT}wRVI>4Cy0@^Vj5_^0+Bqy5qi~~*1f>Xd5 zm_K5&1&mZ-zBYBge)vkO3;gCCr@@E49co5C0WiGaI$aAr+~j{M|F?A^aSPKCC{N9V zuU4HO?X6|QjD!F-3w^vT;~zFBsvyFp$@ z*S|M{w?BFZ%!!DX2QjSpt2!NJ7*pFFoG-ouH=0ppbm}DcJ<;pMg@8J9@a+&TCyYp5 zrKGHrm>Fmp8kb8K0_*XfZ;~mHKr@;*7KsB_3wckUIqB zBoU)Wi1wLs2*nKqu%MKb8ohd8PkE3vG~qJEypZ4*u!JDag#K^S9i|h)+93NVzy(eo z3+@NtHldw#w6uGn9+Q*#-1`m>l+0maGg&62=TzO_2#?z|EJIMtdB?qG#l!K8YXbo3 z!!MVPWE^(yTO_8LMWK^mWiQ465mkeTJ*T-Y3U<8D1zAR@-_*NqTsioS(-6$z+&I0m zy*GZJz?tzC>{T4)i3>0m{={k_^0(tC3qDZrrzWP6|9k}k>@(`ZDfYgB&o_lY%ETFQo z@^)lkfQ87r?#`-2i11kAA)~&V)8C$8?qVE9EW40Fz~K(A83)$az|hc53_DPfF)YJa zu`bPKCF<}SDh*igSg>xPFKw6N=)rp0g0q|0)MBD36`|CMXL$q^PmDSU!I)ns-_x{@ zjGAGg;@x&KKzKD84PqpQkHQNWDZK9)Z5ZtN~{qF-Fza#{K~NBZOo~$e|SgZ(dbP9uYaCU%gXXl2iN`H>VFfSR0 zrl@nD)#%S8YR4!O*N;$zLpMT;Wt#58GTq?9e^yOPcK;8jQvFIl1OXlK5nA3~6~zhf zz|_Om78q$!&Y*%R#EL1N#O{cAnF!(&_?i?27YvKf2RV?Y11b*?3!J5`Vm!n^-l*pL z%UfXF+WHWem$d%IOea5I8|(>eMsMRn^aLwgVWG7}IuFg}s%C8o0^=|f}F_o&!I?+e=lFT0*3UrS2}|@C5g2mPDvu!VToHcru3l#TApO(xqioS z5&Q9aD^eTa5%7amG16DR9xH-ayniv=vLW!k%a)ouU5gFA>7 z19WVOhZ#pRUP3wB4FC3WPQb1r-XA87;)82Z?0EHFo3J7NO=Out@lOnGFhBBb?j;tl zMU`a(&x8>YgykY^&S}0Cd+vV*(kk?fgSe~y6Gyx8Sdrbf^G65?AP($6bp*?m8Osyo z(%%xC>&0Q0?HU&P1$9EvcdgMuJ8dGyw@e;4$z=FRh2_pt_|3XRB zPY~oF6b}zi>!bo9$r#}OsLZwN{6ijXSRG}fQ!hVhJr?8d^Cec~b%KUcpjpIjT({~7k0{OE14y*I;>e)A%7 zi{U@K9Oxz$U+5ZhC+{tS!&6mMa1Wn-c{;WJ`LoA4GJ6yAwHFG zY3v+k7dGF4a^zImPXtZSI(d-vg`+igxZ;R?evk?6>Dd zeIBeAut%2w+Jle4UtMtSARmBfW-_AzDI&YEQKRo8*>@@7M0_jZ_>Y!rI;+5moqx0e z<%>}%pDF$eO$v!-uu*^8c99wWSX}Y{8@MB`{fK-{xJHuu97A^c)AP@4T(dXL;cgZN zA?I}8KD#)G_*tFlDdeC-R18OzWceGIzx5@1A7DLW1uO`&h&;P#pCTSD&LHC*2M!=o z<`kRSv#l^JqM?Wb5phHPP+7Hi$F+IY#Ae%7xARl0rza;5EsteBEMFh&!MK)d>`{68 zx}^0Rm>3vX0Mz?EYpk%m7*VNYWT1K?P@Dr#5L+n+c~^G{APNQoz*L3yqi^<(V_Eck z>_cbq8h0J&a8~Q8Uig)whubC{gkBzXhJO51M(?BVJ3xuk9dAbuh$Nrr)*FJ&ot=Lt zD+}2loja-17cY9jN=65pA&sASespZKm@+9U;8XKBUh4g7HK~46wqtx(K6`P`8Z>Iz z?{vQZ!J|(14fTCE;}RS+Sf<&3)k!8kV(vlCe<4xOeG z2PnOu8id=^UqvDKNO5}e6@MtEdgpn1-^I+9{fvv43=ix5<9VOA{UuBMc_}Fu>t5`@ z@_&*JfnsFI@UapT6X-$cDcEg@EPmuN)i_AO%luaff6C@*rtd2*6dLTD9;Ux*QMnxE z=3!re2K^yckh;A-D8w%srmeYgiqGn_V>a#IA6qI+a*-hCms`3c(GpZYFEnJ_aInM^ z!Cke+WfT<;*REY(->tMf>oS&q;F8=~>>k*@df^VDMhjkUcvXDrrOl$*)jUzD+ys#V zX;2}+1#?>bD6w+0+EWgA^obILOM6S^RF~gh6JU^(?qf zWPn@hP+(F_4mO-|x~RXk!0czrDc>cqCRf}o!(r;H+RHumQx3?Ds zm%y^Gu5+&w)Z-+zoZ?MAIp1fM=~{X%7X^pee4^LHX+dSM zW0NX~@4#R~jUQmtmy>2Q>@UracTF74kie8HiU&aI43a2vJpiUW#1#&KB)Q@sh~8z_ zICG}-{oa1W$uFY_X$B(K?b0RSF7S2-fMM-C#oK6-KDq{KR%iuS+1R#=r!tFAP|*Ja zgd{dE;0PgGr8TaP7*cKBny2M>q5Erbv5Dm~g-{s`hZ=0Vd0+}LI4L&y>k;p`F3K`c zxp$5Q%Yp+5b*8TK7X=Jv8Pek~RGo_VO`|#Zx~~P#hczl*#ppx(g%1@9*}pKg;zC)A zT7-d-(R`_rosA7?Ls0k{4PB`xeZ%tD{`@)>9vkV0A0!6>UX%DZ7@^?mK-`4t32<(= zyzQ<{oD-Fmlysg*{H=a0008?3D8j0*O-!SJ4U3Gle0F50M25K;eY~`Vg#Zjc?%^xE z3J*VVDepEJL=TvDFEJgAAE33Qry5I|$1m2=(`_%tL@nDVm``#u+*ckB62bG{D1 z%vy%!VrUr+g*Ap-|1?OJL33t9V)1yq(ND|hYyhI^Y~ ziv-szEFHXK49@`x!}!R)q-Tss)k#6au)m<706x}i6qr)(Z0gq8?|g3ET7)N!!yoLV z+s03DU@3Hilt##h0If-e!(H3-2EA$?UHp+j2$#Tu-f@l6hxjjjFDY57Mjg@xVqcAb z8&I@!3kq;t&=L(Zj4;J2ekvkEECn({{T%bDLc%eYfv%!-2X zYCN4+>Z2O#gZgkOuMgvi$z#?eV!+1jF~=fz&Vd!br3 zn#?~|po{{L!D(z277!tJPV^fCsYI#6C^;^P-VxqPIcVS(9Jp{84E(9IcmI2wpmrOO zB(&^1Hj3>gP5;}&3OACd>ax2_H@K}He(CDl(&wi0r?_s#{rDtR6p*GRPDmPxcp2u9 z9;Q^vnnbj9j}itEsLr`MJt3#N;rvHW5xas7Pp6^f_O60Z9#*R@>8tMhpuXy0GTiqZ zEj@G@dx#?!h+sH{ZY0mR8y;K_PBT^R5s1=)49CG~-UJ`C@-wGTZ~8rnY>Pb0E_g;O z$IyZ^Cj*Km&YJ2m3r$bWN%C8SvIJU6zGO8C-V#Si5Z^`Fk8sor5_6Y%gax*JR@cW9w5Co2Kh=od9Ev~qq&wYE(= z8xmuJslCR)_Sqv#uMPZ==c5VhuY>`U3mO?P=(B39`kv5+D^k@N`{P#KH=ba(p-5OL zxLLW=K%8S{aLdaj=7{h0BZ{gDP~kwoTp1AFE23VIpD&HzY79YRb3P&-9KKM4!XUFz zggxOgy8VNCnX>IdhME_?nE5rlh4^N>jkDdWKY;4R9^uqRg$^^|@Xrd}O9&h6-8DWS7#e zC(jPu*hV#hwG=87bO>n?i|iAPYTW^P(okU7LxbiC(AOS{Nh5&m?4AFxU?*@o&G176 zl4l#TUISMD0V${D1=S$U2BO1=(}Jm9l|~c69Ip#WD99izADZ$1FdASYR?VpENNN;M z#=EPiC8;hG`bJRs#OEe-$T$(p?Guz?_egdD*9M-G%P2G-dBoBjmLS9qlWAIgsT`sW z899W{*>C(|`^MsEU;(j##lLS5w@*?V%Y>95$MME$xqct8X$Yy)WB4LwV$a}WVe)%> z65w&%NiFV+g2Wlp@vkoviAYwThB-KTul8y9RLZ5=Xczu@J6Ghn9Ki5_k?b4duL`3?Pbw z&^Gy3`d+iaogt&i*Fis|SOVU!hgNrGw$Ze%eBo{a`w#^NWJVAMK^*%De6HLPTw2ry z;i6+eRZ6=Y_!cL|t3=4KKz#4%@B%C{RK7|6jp9N)y_{YjC>a0W`sw~O$#(!e$<_@G zcBt7L^!7a`SWt>m%KIB7K~hvnbHE2L&zi4uowSNpzsEoqbpd=p`UsSTq}-w?*lOmV zJtgLb3!ednE9khEFRWn0pdj^YY?E6o^F~omDELB+%h8t;9KrwhFc2op{PMJMHNOQw%-J)o2enznjlbjEKO8|_s_X{xv|x~$-GVSd(tcQjluu$A z_yZgSJ0X&&(+Y$@i5)1TkvRttD}%%ve7HPVq`D!DWc}3l9pMMRO7uhk*)OOW_H1SVt*d>Mgj7yd!!LjSlJl4e97?`UDB{eZ6sZuu!3hE4bMq^`$23;rCdV#7ZH8(;JGA+s5B`+IDSlxNlGO-!HS!>AA;z?DXmT9W#v7LJP|| zXL-II;7M*b;QP?tEnHoD-Z;t79I$<)pz}syG998)^L2uPf+UO{LvA_>+$?X+VE{aX zNGl;mxI}}DF(?ZQ%O>M+J$7efLkE1+^^O{oVsz>xd`@tlqjoxEvOfOa-nGs#=-^C z1u|VWzWWW8rRCx#|4i!?&9V1HqFvZPTY}GanR>BKz;@&s`tp-?J5FD^bO%G=g*c7{ z;5KwY+z4^_ogrQ&>DFE^tzCy}+2qQX|Bua*y6><5j{o%Z<>mhmzY8o%! z`FYFY1&7KeV`b$o{Y!{lrTqb5+t;oP9Pt&b-|wQJ#C(U9UD@f8GHy%{OD<^0S-KTq z$!cqxQrHR6A8s57dK-H7^`smkw{cMP)xO})#8GptstqJEMHB6Y61_eMefi)RfO6F`i_Hr`D)_?XUk)ERLTo&_7}CL9 z1HvT(@0`Eet$eF=$?AkkiI4Ch!~|#ePqR;VK;Id7z3&X&|1djGsO46++l9sX?Zu1` z<~z-jeUAL1Agh6<4%aQ?(Z!}X#}5$!Eh*~3CZ`dyWP2HS)Iy}PByGXe3AuXwfg7qSN*=aix~YPtxd>q9HVe+1@9M-qPhdWfzW_BLr?^C z2EEqp?lSVTz0?5{51`V~QMosBy<}=L_7b?J%WO|YA{-bzRWR~hAcxdD|9cc$74aXl zv(bCy0)9nANK7J)fboA`D3yXq_!DArAzD56t}I&GOfb50SC0WFQ$nES)JP|KD5P{C zW@m+#PLr|Fcz0Ch8k)`oQTm1_=&M*L*|5~jJE=WxX(2x4lCvv0dR&vnT6+%5$$dgf zE+b>&Z?lmrb158a7;@vD6EQOj>$R-eq*`cXxN~<$xb^ ziGe;ffgw`|r^c>(T=xugouRvoBQ3cb)toc z9HKW%<^kv`&fK~pXjHSvuZTV1oN<8<+G6xZ z5nT&w*!`!04<3}ZsK65eEl%sKC)YJTY^yyL2>Lwk6fA^pl8DwO3U^KFH0MU& zvFCA|DzPUc5TGf0bPkHn6&A<4>*EdBs3z>fp8KO`)91xc-=^EjQPw-{=xKkM-Tdqk zulb9+KfIc6-x+SxrjxtJex*l-s6(T{S6e>Ti~U|HgIS zpSs6RA*U}yai`Ux@|>TXH^vHDW(R)Rg$~qQ;FwO#SwP;-ra5)I^4M!V&@`PAP);UR z3w39T`MaDd0qX%xazV8?z|P_s zdJ~{6qMn9Nw^?7^AvB4&$A!(m7~ftstk&^ET)aOZUKA?R>W6U`Jmrh`{;2r^Pj zRY41hodzvIIQJSdry65Tn;)(Y+_=YiE->`|(3In|!rk|ycP!o5H{L#e-$5+k@QrD` zqTQU_W}Q*d+Fzo6@y5&BHMx9WX&?Hs8B|tNTUuo+E{6-WT_RI85>mea^xa@MDi079 zg$qTo@6{xd>xEpcx1U=GB9iogIjm6vZ2~(|9?(|mrrMCY0Rs*2D8_XiP}wAzcdkG? zN?|?$2HQtN5mbt2t`f-$6|Gmv@NGVbsxB|1aj(zE&H6R>~(ejZTzce0^c1^ z;m3a|RJ@L$P~EE!?2r8qSbUx2Y+k_s%}>~j5(uVaq*mOX>p`|m zRMi1$&LA(y=$gvK1Bop>ufwVxwi>lUnF!}`7!fpecYC9yCE6??V^%M&qL09VBq=B7 z{tSuFBqgnP-OYq3HP9x2gu_0;0}{CcdMc)GtZ$7%y`M+w`ZyZ>EwjAz;tdWDGTJqc zHV*Sqrb{PkjO+6sIQgA6|9#}#rOd_9Zrt**XW9Su&MKGA+h6^J28$L`l67&Vyj6O; zVzbU&$qsU9U|?R$m2&Eo&ADZVISbZ*E#%WU(%i|JHyvHdd~n?h@hhSp4`Y4JvS;>F znm=xBx<*%CLLZl2eMV>c%QVg6HFLJ%g|muvvys-rp* zKxhh^C+sE$v{3#M$zZx&V#JpMv@Do`3!1#FOKpF7{=x+?P=)QIVD*rNGP~b^g9y}6 z#bOQi7fy+aiW2u3h~+2>4T~8@z|(6!5uJ9|(v;k_Nx01iV|&?h`$WAmzcc5BpK%=M z>gD|-)GGhyJgyy=haapD1~bez>bV^Cw{|*pbH$l%BJcTs;C=Ob|534q%l`L?Wm4*} z;2|_dd+y4)LK}FH2W%uF3&das-W`i6p~ZpgYk;7^X{X~lj2ZmTG9-tLV(RpB1ob=C z4=K8^w3~rbTr-smQPhHBm*k&0%?)#g_NvBc6U}(@fqSzr{z=^u*UCl^P>v`XWXq4=Vlas7}`0~N2nirZT=KaIZk zE14f&wQp>-kh$x@Cl{l;+U{vagj@P|28Kp&o!#A0wlYX@>~Ns?#-ldrb36I&F1~+O zy^;Cs^TqGJ+@ssfQg$`nmmx1G)g=`djPmf;0)^qI>3T+wCNraYj+ss;<}A~sTrJ^F zhn&vZ8xp-We(ljcH(NOG&q&OhCkNU;QMc}n5A`2Arc?X;#f){Z)OD`c?hSVRVuQ#V zIJoWy85ONPj%$N>>SAWS6#P0gtT=NM-o3@W>tS53mWQ)*5CS`qUO$pU0(b>oc0MrH z+B7GXR!5J>5R0dvPtmV{pNx8N=0$Sq*uLgvy$MyQ=$cz|x7DpDOD>A~ow>EsWp*%k zhrqep>P6qDA=oHVxDoToeWmZN+K2|Y=&`@mpS#=+=dN<&?vV?-RLXazrYFX4ePzD$ zj(KLHK)?3ZZ%+~|RZZl5LdF^@pKg>F(^IV-&h&d~Y+yC9-b^B~qG$Cc^$U%W-TY46 z6XVf3iBFuE11u^sSvle(RwrnX!jrq*$sPBl;R^wTn7md9{~s>Qq~!X*0;>&xLF-Pe*C__KO^zPC?vgh%_05AvU|Ip@tie^5()>H^+1ciZS}NoK&Nr^DZu zSc#a(*Iu;k+oe98dFZH(gT(by{wr&%)o_@~K&DBVN9mMm$7F$gF@8^U=;#MuLNJp~ zU&6dm7J`Yw`~4WZArQdj!-ejslK@hKLHS`)3nr5wb#-2}Ix3h!5<|D&MSdL>AJ}kD z`ak76*$tBU_p<6@C$L3`%<=vEr5JY|#*l~z_0G7?rl1F@{B2x0HoXnE6cKsiA|Xf} zWtx1CI1a@qn|4?75I-FK_!=#{H%8Zz0+x>tqZ7`v6s%LQ8!M!1y8a?x94s4&uygO_ zLYlu|#d#qUGgQx8Se63R!7F=k&7&<*QT=Xvouw zwVf9bQnrn#*B`~&UDcsiY@x8BtL*irOLivRIx3An}k!79N+~F+|h;V zk!QJGbf&dKndN%Qbf$ZsGOFF>r*)0>p%GtPU$BwB3jvEgzmF#X*V3E={a$d(b@1z|)6Oz7n z1FzIqZ{d-yHW!}G(dO`o^}ja@?>o9jrmMss%W{Uh`&_Df5S<5;>AEqg{A1K8O$VLb zh1(AE|q|yOWe>Z8Z$&@<53O*`gyl4c057u(#}8;ZE-i+qotz zTy*w?h6hf2-ba|*(79$eoui`&Ap-G0ToXJ-uoSsj$Xkr&O9&CAKTnO->NSSIwXqi0 z6DSlnCPW=)yEO=(`MUCNXch-bk8on%TBGN1w}Sr<@y|?L0A&)0Qii?`gosyOVeyV7 z?((zagY&edy@}t#w7#x!`&?6tRbo6XosyOCKHawPV??imzp1K7LBiPMdE=C!1o3o< zlW9tJVpTmOGyU$hUyafQ)kaNZFH|dWb+O~^kzs1iUbKdbV@|y?354m~5#EWBVX2mx zcC&$>S9k18omTGt4>ZYcedU6YrtI=#G|>G%xfrzkbZZk^C=FE>?8=cZul;tB3=YF2 z`;es!G}1>Zt#%|TDS7K2#pOs*YrYw$$?HwFrNV&3@*xxR+MD({P9i4^BEp$(oZP_H z6c>JM<<0w8FYHXdaRx_pcSalb-<-TkNy<)~4lVikr!af=bvAE`Cg+5Fl-o!httHRG%mO{qC3Y6W%Xcd{_F!pV5 zoHk;;u?!{!odsc9CzPO>@ zz%#~ta*%5QRNPb$_nx3t{+a!TBl#(_3^yImstJaIz>Ulax3 zN4{UA%J2-+-IHjLYx=pQ;iKM?8{a*Hq9S71!mWOM6}Mc&e~Q`7?*rKH>s>}n6k24Z z-ZAX!mS^0n^rE@DcjwI?KK`lK)HG(PIPTT?>$N9lof^wJ(&DYUR3d^u8aXG=nR&9W zY%+ZweP6u9m;JP9*S;%af-4q>^3qY|+*9$#Pxe-0D7u9f##}GaR+?KLi+GJ<0z+hY zH(>BAjb(C}?IPC~t44?Ic6C6`?JhkMIG1XdINHxgMgX(*C)NSL^uogN9wd`7xZ-27 zejgT81P=ttv(#-=pbh~;l_yiS;s7UQn}<^|%xpih;OcZhaq5DxSjXs;(sHTVzG7Uj zG|Dphid2f@vkBPkWFrs&j2CC39l+~}r#np9@EdA1!$88eR4+zs;RG)|s&JBTfH)`8 zkK)Hp*jj)LiM<70PfT7w9d3c%f(l`Yt1&PkY)5scS&P)(awi1`AVFZE z>BqN{gJ~rC-^A{|-FPQxy}W7#j=w$m%!kH-UMN*m-+eQkdsdVOXF}iXiYwmng{m;c zXs~WwzGcf6O&Cspht>kWIv$u#24=e1+3iT7sG%V~+GzJ@qg&m#MO^KAbNY?bjTNc; zoN~k41DA7^0Qx=)smS3aJd1)CbnFS9ku^S8ZG}-gL2|75S z%67H$r+HsblE{^TriLuR?Y;Ta=56&}D#Ta_qJdTSimt@Q%In7vZ=^>1;ghb@ua!RZ zN>!~XfF%Z*3u~+4e5E1`k6p8;*0F8eXq>1}D;ZU8 zGeP+H%liU>qpGpBf;vN!;N!{k&4w%hrJoA<1?8_q}BQ(3>fvhVWlcJmAw7_Cp>K9nLDQz0zYSX7#J!?O0m zy^baIUzUB`lpN8cbCtu#SNLe+5lk4b4GJ5&dXQ{K@Z|l|RmVxm4%)~Ht;0~&ka_&x zy(}2m-o&{LwIh}*#t)#^i;nbEmsRUZoKtB%RP_|__~3qunw_dok4?+gyssv7S_8`+ z*R-yLJZR_I6X5D4sA6{D*_@J+GQFk*T!_oAWoU+a^6QIJJrGF$d80<^SZ%InnQpB2X4Fs`r=%0S8v z*CI6)6<8W>N^u|rf%prsvnM}K=p<7yoF?uVfb**-9>~aT-nZwW+QW_4d3kxC&HUQW zUb3CC`)K7o*yuRTgEkz*4s(snCTv-psE^y&)IQy|Sb~(U0x?~|S~WbH1eJhfDQ<_?2i$|(Fm&h=FKjT~*SDjhr&XsG;o1viC_nI}b@ zN_Q;V+Q@e(CHClipwD-cw4pcbd=}=sLKYXzGZVJDrEupjq9|$iF?!A3O=@PPcxBg<A_f_adoWdeibsTT+Sfo$dkhMMe@0b3)< z*@IXQR=3tj@ta8Wxf1R0y;_`&Qwekp?Vt7a^^vYo+|bW~M0eY;4zegu{J4qo3D%W; zhjiD!Fxn%zoCy3rQpN_b*)=g$fncSt@E`|-m#mvF!@rP9vaPU{Dy*0OolqZ~^XZc; zgG$#q2QWOS%4H(!M0~K5wUZ7-(O}Dr#}(r+hpjW4%LF3kEM0+cytcgeubWE$JSNmRwm~m?B)OGL`N0D)Zy|ZKoKYF804Fx}R20 zHKQQ%8|N8|oSLm8ZEX2<@f$PM)m{x&A(Q&uEkT&Ki++nZRd=k z>a+`YsO~6-*qNKZ?peyTMP=m?LOq5R%gq`3yqu!*V-CeDEe$r4R z4UOH$%L@ilvLvVO^UXYxehN=zl2AY>6kvMFYHDu6{<-{0T2D1V*y1L}p1^0pA460! z5A-XG=;=Q}9tq*f1Vl36Z<9+#8@aHrQ@iWWDJgN`2&DGHJA&0VG75vPg}iD|;jm=5 zVQ&GQ4Qz8j1&5Z%U(KomS0RbMBH*czX^>$JZs%LTD>U&5i8ULpH2B6qXi6;gh?ELO z5FmIX0pSMw#|St#q;f!xH2t|Ge1P}Dg8x1dbpKFI31CgG9zE1qW;m@sIkCHAI z7kA!1lv@S37oqAi%YbE%x@S zjit=PS3L@!4iENidAk3(F-^3kfbnf#4R%*7Fz(6V`p*+%+tXH#A5d8JxFt$8!fMt= zq;F^Gp~+w;2c~5W)wjukrTKkr+H_`cNlxOjr3z|mT-q+Q3#7YR$oo86rSD^Hyu9VN zm`mtOhTOL!!6jENO2l5{^6(5wLb=!AWB0yv)_&aS$QNN*iKSg0{ix6Tkt&L*-|fgR z&ZD2MA+IxQ-OZnq6Oy|q zh+L*$R<)s^gJy-i))ibvI1cL;2Tx`HuIw1uqn2fwXl8^k@8xixrp=mq!DSKDUlz)Y zcM7cQg@rv%+GsHR{1G+1)J=LOjcMJ1u+lceoUf1|S=Ju#E1Cc^u@w9PB@riZan=+k z12x2#7pw^&PR<=?VDGFm}**1wuM*5L|vR?Mo zYtJCp#T$}-a0}zTnPg?}JzP)EJ-2`JT56!{cez}9#k`D%OrVi;*0a4Qlt(lG@r__V)HFNeN@eY#rSZ z>xWUpcpOQy0sDlOddjEK*Q2IC{v*M@u_Ai66bl{Tf{y)lo>vVd|w+#sCad~W-G&&hO7MpA#u?Mp1JT-sV zdu*^|GyW<}q6%kVaSI~nRoD7QuyF&FPjaDg@KQsOg;@5__G;0hmxP7@Q9`?gB>-&K z8#^en2%&%^3^@3qVq}7_Ax>f^hx11Y%w0q%5EjsDoSs)=CnKk=N8gX`AB6>U^_xsx zM7CV&k-=iZ$$95bVWfqrwYK;>x&+vbU`0U!v%S*fpx@!9B%J6i{zs)c(7O`RFbX7+ zjet#nfOsB}?-*HfQDPt>jzDe;A_g%-g5C{H5(&yl^(ld9g+th)>lSEvgsB6{9~q6I zAt#pT5Hhirr(JJbDn8w_9~!aSk+tKm-@iXTHe>U1p_H=%40O1=yE-<-7Lo>9rABAiImM8QIZ*S`rdwU7}u@7R=bv`S(-wsnhD6bRGHpr6!MPgggw z;=eVrzq{jVh0<)h^5Mg7LV*O6>;0R{3{;2fPr0#DsA0cc}9&bVx3<9a=%&{N~OGS#wC z;O9vr4`68sZASRrV))?ZgHMKxAWcv!#>?YB;$j`!e5iWeOqG5-_|sYpDKy}%NzBje z_u-WAK~sv+l5~U;p-jQuiCDugf=;}8A2H;NzE=9$y=GJGS9@;bg=O!i%7duv}y1E2QKkjI(O1v2GSk>0wK24QL%lHqnEA|O7bRoGTGlYV^o4FBcNEN1+%phaS&>)4o}d(e*`re_yR z6l*LId9yh>tJtoyM)vvoeCcSAKy~pbhG3a-oqh&aKTun6)4LTM%LeM<#>j1o>gnA+kYb}z2 zlcT;N{BA~iR|X4su6a86(v~=n-qh8r#ubIJ{LjT-QQxUsH!qI4)5}ky!4?a{ox}^a zr_0e7ee6ptf?^;SfU8WOjhIm$(6U@pjQdqXShGO-A%{n7H6t`Nz$|E@U!KKe4#&JU zz>g$Q`_W&OzLHsjVon>>bhM=4@N?bb3m{+sR80ge!oG#y$QqX}D2#7;sLnry^538; zp#CCAd)ub%g3+uko}=k5_Us*w1B$_QEe#%7*AA>Jt(b)c%+I(lwhsyoqSCk7sdbjT za2;wBVaaE~^+wHmS-gp?7k6>hH?Tt*wP{bfP!| zpN0t%=b!1@D%){mz7(^&tvQWvccH#|ZF%=!Z9vLZ98E`0K3Vc*mH5(ccN}Q~gpqiv zJIlu zuZx~nRd0N&;?=eLi%4hBim4bnss-L1RRMF?8`B7o(~7hy7JJ&^^a6rg_4s~B-^5#ziO2J(tTlfiPI9N1DEF>3&{+Pa{YKU zp>s=V=;`TeicHq)o3B!jV>y?G;mRzJC-0ukwdub>CTJW8$Hc z6p2rtq#uK{WNHS`7Ls*ZIy&O#h^F0>CC>bnFZiFTnX4KeqcgeP)CDON^33*L4EuTT z+n!Ez2$aFq&Z+F<03uR#-}F_&WxS&yT?92#uH(VVfP{C3fd=E#T(6fv!K%RsF{-fD zuPzjcFYWq>SIBDgFHl#nDiFtwiv&O^RG$k)Ai14C^8VrwHkSlQfxnWal)H}aaU1H6 zx{6p4LQx)jWs&5x0$&(bzQxTqhC+PRKM}bhfCbvt1fa~VrpY9hPhcYjjIU-|8GW(r zxN&D7Ppv`)PVCj~Ii+d1sI2EE+xt_BaM#0qg*XexET9rc{*6k+*8hQL*)*OWmt$J% z_Br~qQm1WMo$Wp*T`IbNtS(*E>K5ZhADXW;%7GahLq|df3MVyWcO-?kT>nsU#y&Es zZMWWAhsKMpiCruL>eCKs>=Kr;Lfb_7SvlX#trxE!njh#tw!5hzd)B4=7dGDi2KgFa z9M{K$9ce%m1(B1mnBi6nmWpc!z_$ejeMM_u<(arc!wt*{1w+Qr0{$vj>Gy>oAL=im zS(}ltiTIRti64wV6{XXOrln*lTF|X-=WVJ|O)NQCyzymF9r&n&6Bh^TEh4&ss0VB*A_7tgkxz_;s+HAQ{o`e?@aYNQ|9NJ~ zjT}#BXJ$4&AHT$T}uMCl9@#!A{Jhh zrz8XL*JlY?{pzq7UB@pKj`6(eL`R_0pmkUWoAfDeb8#~(zE&XL{0wdLW`L@{f#1w3p zGd%d_l}_-vUC>O@xk=OT@kP;VM4?(7tTBjcCaf8lf}^f40p1F10?pRJi78~~Z0cf! z@G;5p$aE)`C*hNYC?Z6gI7wmyF)Uq5Oj3F(_aniS5y_JHUh;ALnzemhZPsIl1N2Rm zR&D!>-W&JP4~2=@PR!F1Y+QDP#m?f+3P*M3ZI6$ZtDtTLOKDpw$5!fu-(C*7DMr)d zC)1G%yn6R+q6{ICq+Ee^gZ_qw>XlJ64A&cox~Yad;yiLle0cj+km zz|8wZf!#X) zDM*mPW6nLy^CzY_VrVX`s{1fy3%`qJkY3eK63-mqX)x_xhOh;AmaW7km)f0ja@$Ih zSW0-WmE&uU6jWI{BIXA#f!+zjfUs-_!@D{|z$-j;yZZ$-do`aUsfL&3;Y+&FVhzxy z5L8o&E)hnMSz1BKF~fBwH$IOg0U~S0JR1&0CqDRFIjV299jRdEY)mrC(Q=LvDAzDS z@yTv!(lj29N(q%`oXYZq`q%5Z7T+;)lDKvPvSRk9H$#3Hq^&Z4s@@oq6|-t@oA+h&`VVj0}f~C*&(A!zcj9}7seL&r>U^m5qw3)kzUsezATmS!v!RZlU#B$kD@}6jn79*J3^*Adkr< zq}RHw|5fRw2mhGeCwYic6Sm66>K~XFpI>B>ImC3yNZ9VUG<~gyaa3bkLzAuGn<%!; zzh1q66_#?*jg){c{meft)H7_l-{x4rCC<}AXsHz0ksV9T893JMk}hf7Fd=u#Z+fJQ zE_0*f4)F_jOvaq8x0}l9;6q zTH{927UJd(zBsL$>g3b4@A(O<C~qO&JotoN~V2PWjb3 zHXR)mi!=T9)2+T*9BewTRTr7!5;fPqFKwbT)7xhM1^e5Dp$Ztk<=@iEea!Cv?dMgt zpr=-6al@XGWgPAXzVlxA9OR=Wei!fFt7G$#+_u`ar327UFgaB^*xSR4 zr`>cCRgTx`_dKXfiE^-|vK!FuW3@$GiycsFdBLXY_#Iz$wsr(81JFPyH_1AC93cW% zDX0s<6ylxn1FIOV4I<+-L2@GGLCmTTe@>aRUBjoo$8PzPgiJIeXSk`D`GkO&?i;IA zj3Wt2@|qBXp`8NJC}2|}An|>v1#FPCp=DBz(RRxjj)N)h#prq*Ag#)QDiQgh#&d~W ziEub!DAPBZmzyhOZxW6#hKNo~mmA)^2pLA8iVrM`y_nhvapd|h29(sjygwiC{(5&R zM6nslY2zPumNFAIPdZ9T*g7u7QZG^=jPoo~lh~lYJ~!h1`|CfSpI=%pezZPSRYKJX zlUg)~sRlWx@9!R4+#dL1kKK9mE__mSfGD${v-F8x=7+NBH|d zr!jfbia9ZtbOPKxM2VmhXb_7Eaa?c!sfh#o16}*(#_0K!L+5J8oN{{)~fa%zNBz~Z*j5azY z9I2kV>!#Ooo8Iw30%A~28eH>>qh^t5;gT^@tjMaapf)@bWU{i9~JIZ-< z{`t1wqg_1}n$*+}33;=3Gb)inuz>Pesk|>h`ZtM2C4; zS^3!n?M2jh@S=GpYO@d4f}3s@z{cueAdlXQRXg6}p~CnCC%8AffuLAkgC%Ls{{G{6 zB-tjA#*{w;vGN$lue5-YRnUOb0D<-P!#HAsLiC{m;IDiZCA>xWAHLUF}U3)HrVFPBs6CO zQ{7SAuw&NJ(n1_I^ym+YwpYX=mQt<&85Wy)il+95ROt79zi<{TZxU&VN%u~sjiNic z^{X1{vF96${M?c`ov4a_NSo8xWT{EZisg?34=tmj&(*WgbgV6W#IIG#Ns8>1Yi4QA zK3vHWnX1`OL(U6wM~}{2Ha|Ni7nMbwa2)5Mvf_UfCMoGJzbN{<&j-acTcPEdiPzqr z*mxr&leG5UrbZtBl3jG~vU=B@?rr&mxyp z~zg z0Tf42xwM`3@7tF<0@14`9s-Xrr{Z>OQpd^2;rHiuArY)Ek}YPGjc60X_CO(r^2e^N4N3h-$UK)-79p2Gr)K zA#$3FN{3*B2sKH}JVJ)>a%C$AR95(AKMwaCH8P4w^4y724)l8BGJwxWX!KF)_22fN zl8WLI%h+=HnHd=kEh7}8>kdx*tn-OhVCy{dCPW~;1Ep0w^un3G@ z>CFonl7FqbU$eJKRJ()W&c;Y%r5)9Z4(p@xGnQ%u_L=q%s9HS{zqgzJoWLra8uxFA zs@{65Dqm5wt1d||T`c@ay6NQpQ(~^$<-|+hNZ!EThyT2`e9bKX+N!KY#Fsi*=zqRbg56Cv$5FVzLe`hV%#4fjzH(EP$$B=P`%dO&# zO$en1wSsyXWu?Y7lN0W@Clj;o#H^R;_Yc4l`^7JVef2sIh{o0d5 zj`9i$PaR?c;H?2hRAGuHrse)(c1m}kw?p@EKjO%T3aGca4jxqMIXgS~W>yd@8FKrM z=&n5g6Yx9GY3>kEH-5kr=&eH4ZiAhv9$2s$B3EAyo4vozy=>MHwa?Rm_V~o3>9W%!rbr+hCdiRo3 zb0iEGMLto_PuAJq^HxItjj=a^(T!Sc{QUjzfCZU#?EqYZmcmF0PZJ@GF{zp|iqMhX z48-C2Ou{L{I^ZQTfPfW!t|B-CZak1&K*&v z0rT@*7Hjw%#+$%)sPKTmkdoNT$WcM`5waBA{)YN5TO?*jRw9NCBt|z#eZIb}$h-!-P$qIV5h6U?K2;ppbB@ut@6Wid_w^oS;!#t3?(uTC-200CD-rhKSL38<7A*b2Qm^^dw z)asaC@I0=Tx)(Z#-G2<{S$N`bY_B77K`rce?KJeX(mcK(r3i(rpZ9vOyI$mnBru)W zE2y0X?}fpr13)v0uwY;~#(D-;E4}O5kvtRy^|6kgo_tUd6-8$^FA4YxwK|qojKHr6 zZTRw79Cs;cG((dpxJO_f3qu3eu67-kD36R)Nuw-$6%{@w5cE4vl_XMcBayq_G$V-p zRsc4_fNbgcw~ygonT;Jm);#f12N^R9PUbg}P>%m)9oU`n5wZV9JS&xqQ-T*dnQ7)w5{j+Qh52sPSFTPso|kr#9z(@_rlP zR0-a1cIQW*+Igx?NH3I{m{TSYvFskc3n=by(Q-e*h^|+P`AS5ZT8p?b>T${N+PX<64E{n_Uu=DJSeNVGqvTVt7p$h= zP`^BZvb?3hFa4FR;c>36moYLYAlZ9xtPjBjNV%u>sNbHur{+t8i2aCbM=D99!r?;9 zkFrM|CtHh~9ev??X;oVytsbWU%(R#hUn1-XuM$!0TT+o3RL58HZm8C>nds|iY+P-} zC5c?x6(Xf=*s9x+e2a}IX=Cr@?$tT!;>>e$Roe4-9k& zH6+=*olMOsz_;HR(`oLbBC#1<1DhYUJs6Xhd9Y?`#BXX-%0&Bz)j^9_a$ZJyquSRNlXauJBeG1F`jO&V z_%SVO)5Ip8PcqJ@M^6ub>+|SsN$?W8Oq=)0K5OuEXQHy*&l_Et_YUpAI0Hpf0K-+q z_EzUha~Cai3_Ye;I_mEGIU6IAR!k}Do(!c|D^sdSnj%Or>4haMKd-9!3pVYxO3J`3}V>davL>9QXp)&*QB~npjK&$JBWG$ zPpUi`a|l9ipzK%=$_L{^YRT@zeU+)vK9B6%=HS5Z+R>955bjy?Mp z#o9MfhMDABVCp1pLBXLyYg>iw#nYjs=z!Qf9)VwiB%bWCy%UCIki2TE9)Yx$jD5^H zDmajXdgq;1RLUN+$?=h6T|F|uuvNr6MlsPzx+o_Hs~;UdB4Sm0JL_Ud&1QBSdkoOc z`tz2*Udu#H+>G?ol-nI{S5>)A%+;FN0;9dfjAQJ#?foXn2#_GFA}#hwS0#qM8S5Lo zCo~x`#A&-_?=sFMx^;sHGwRwdwc6Sg^IS*!YG>2nmYy$@ktT=j505iC*q>0n(N&u) ztv)DcF|~nuQ%SuE=eVWVL?1A)@tcyNt}6Y+J0)W(SiY-a1T-<`+K6tHW69%vF)$t8 z4rBowz5R(Q$razBfm)vT7^bW454Y-8OD0=%*S`-q)l!i1U|$ug*cdH!4?dYJWO+Ju zzQ4f15pI!EpAtQBO*8zA{HtQXu;mae%FvuVkefw^crn$)X*%PwcQv#{e} zcfhUQZ~Q;%a)YX*06VT)xI|15h%0gr90540r`f2?^~ZaIn&*UswRk6{M$UW8K>xv} zTC!vbhMZAwr6FmMxWk&v0|TNWJKKY^wUb^gep8ZkZh@57qel$GTX(~w3l2sI7G}k{ z(mS~(RRkRy0e+f}Glpl!U%hrUM&>|xZIMQss*2{T<33r&Dk@JC6W60JwI8dlS7;SS z95|jV>>wQzD}LDVFGY_J$WCDWdL~jU&miQK7QEw|ZVGR*sVTJfez9(~E*MD|Rqy(_ zZ?j%=i(X)S=f@3x%T`~)99(gztJXWt9pTK4us!%Q)seBxa1>F{S8t_vTY=OcSFRzC zap*MvoR&hr6B!vvyHt+B{4_za+MWQ1>eaOixpysuca1zGRcOwPi7Ql@a1Ms|7K`S{ zjdjE}9bY_QX2kF_~YtZPV&5gjuCsDZq@TAUfnaS6cHzH;+0RkpJ+V)t<==u z_rPU44Hsp%+~svglvy_BB@ggB@EN>if97GMX8HIQO3V7kSxdl6^gQ)Zy-lP#6!uBc za=1qEF*Rn|!=97sL5^+o$Gob#n92vEdpEk2s1(N+{{(v=i>u0Epe$?2Pr(&v^xmRs zLHCo?qV&e#OFXY^u*jqC^%%1s2s(K?jyJlN-6hB{WXRPZLuo<&yt#9CO29FU8yf@W znJ}r~p{IA0hI)1EiX33PfK*2_iG}A<+z!{C3%=h{3o<+O5j8^>{s35q)vJ#f2WT;X z;b0IkXj^Z1nxH4{8Z_gSHm3f1{;S*O8dT3qHnq@n>LUI zU&agR3etD8D^n`g!t&uJLDCKkfwokCM^ls-1g5bx3hD4G^z~sar zc&&B)AEBjgj{a)e@C6g3cwwYkJ%!zJmy#`-P_}->w%n1sr*(LR&0BA-ZNc%GwY9v! zFi}U((@OO`gcTcGMo26v{vrjqdWY<8B(XRo#iI=CKRJ4OJPfj6RRm$53xN-@J^P zI2GM?@sWO2>D9Bm5$!YfcJ7uPZEbe}fb`}LPq9*k%UhLbrwtO+{c;}-w<~)00OQ=b zg2|}e#+F~5D$2~1uTdpW5Q>U9R8~&T-ECDMbSWgZA`3&Fgc8R~B6>p923{PZs2JqL zFONu?{%}5DEnp0@g%$COjOB#jvCELLYSMF#A+|gQMhoG0x)@fCI088qhVei1G+eWY zVr2RtLJ^nm6|x}p1)0Bj!QB#cP%Nj)WUM24cg?m6=5G7&h#VK6^wchTXK7}mm7%iw zBN;O0)ALX%I6q`p#>%MASYo~_(jGq0XABeatJ+qH?rustWZ{!8TWxT#I*n#7e>>LT zk)Nb1>&P7p{6{0&ZC8+FL3F=xJ-oVup%rec-&$HO0F!f@R39B4b}pZ{$ytAFRl_Bf z>cwV9gcm>1NFZijsE4TG@Z#8#8T}MjOV)W1CaF*~`_F~s6H#QNaXj@HF zvy+}BZr2egj&CeBG(67TC0l#%@#Ad3j0;=u?8F%1O(;$3B36>Y8%JwfK9ZwwKUOt7 zp4DLoRcohWSgutcaGE~#%T@TDa%FOD4TJxCW5&}OhciEw+{&*rzq0yg>LLmUB^ddo zoMCRQ;-K9;qUjiosS~F`h}SpKZ$jWeKAb=UZGg-=Eu_jbm3v2!hA~}*4yjMC$b+ey z13%k)QC?7gg2A%-=JdIg<+XmJ1lprHMIb4OVlYi*6la>84Q_jOr9-w~v#Ixs zTDrMP^df1^RQmGT6cr=hj?zk2TdRS0fbSBo4cui>uE%1p|54#|n$6Ch(!6Nnf??S~ zb(k6O;*Z_1W*&<;1WJ^k@>YZN@Vtp${+L{((kF@}L>PT|E@_GzStsFvAJTtU#CIkp z!G^@Ft7au=@c}DfLgWPb2Ix<_E?dX1ZY4Ad;35aEUr%afu@XP!b&_~;=faJJjAzg! ztKLiKG@gh8JMoP~HxT(OBp>y9rH}7_?uEu5O$NF5QJE0fIN59ynhy^E0K^yEK}>nW zosWQQ5tPk_i7Sbx1(NiDcY_+HxF~sW;NgGaO=l%A*4f>S>{J%Oawc2aOG=bq`Le35 z@%6rqNi+jB_4NKdf`<=%)i>*DbuLz^cfCyY^k403Y%I-gw4&Shi`t3nF)`g+dFNBi zHJ(v@&nIPT{N*VBvsw9S)5$BVG5&Z|Dv)aEBVjTJ4~8g#yfmC%81EFYpLi_%0bzS_ zp#}7nYHsF2?sxC*gPmMhW?(q3G*prS_EVFK>ee+H!hXgX^a_wl1wk$#%*R_TI>a~1 zj`f!GSJ=r__J-KYzHn80(M#Zab@i#6FVJP&9l8%_bR}qGO;31sTNVoQ)I7g%4Uq!T za$n@bSJp%=)L? zmEk5OXxwh!8O$E9h*lce@|+f-l|;L>W~%T4c%(h8mfgJ8KkGIvTL7*n@`dIkCKwhf zxK~WN2SSIU^v0*$_wkj`^dqOoGFxEy$`PkG%ej&~Sykl9a=nJNapJ8HZNUS1C|!u; zi)z?cIgjTnF9fn{)zz!Y-0tMKvnWo!w!sK_SYV*OFxZsq-bz^}#)pi?5&b;w6P8DB z-*WoP5e$yQ(fa)oi`Z=JspL7lZ<8$pUKyP@YN;*g>-y|288L57*>&Nq)$WKDkqsF` zWe#~wBd*aGi&s7W>i7O#H@nvGeI<;XdBNW@nR zN4R+ToILl-X@Lz%WoXJ(-Hz}2_LINIc5JW5oLx*DTJ*PIt1#6d90ij=Iafeot&5f9S}w$wj+L#ikb|oWQyiivjdNU zOkee{zaE5HXvlsaS)X+h66bC|4z~X@F;T+gIJ!QP=vaCsTh8Th@SpRXgEMyaR41V^ zWMN^6k}kvKL$HAZND&Y@lw3(42jCi6fWqgx=Z{CeEg&i^eb*BIDKnJy<=PER)hE7^ z@(V!ufdiior;HqsnOdlvep*^x5v>U3+OpMaS>+frFwRP>Fbnsv+rhSS(-xDIHb)k| zBkesuClZCs7EuA7K_;!S$A)ts8<79gwtw+ z)4?%}^YZw&7YR`teyu;UfNh!ibXg$oy=%Xa1KfsH&D3c z6;`OWC5zCqPEE~O_A_olRGMsM+O7hSw?XbEvE%%cJG)!9yTHLZGX{Nt=*Nf#s0nB>FRc7qTh5m}mW?#9k190LCAjI6pi$ zoXD9CoItxqQa_;K)AqZ7xMa-0n9B96eP5KOWJOPmiWpGCHjY{^pU!w}2tqfnp=FO#M)PkJ{a$vU5n!Wf< z?A!jYM^M)?Xmv8#Lcs=AM0urdk$eaA2Qk8h$R+wS_K4Lt#BGgZq)X@?2x_QoG6(hX{`#FXpfByOBvpBwLHqTQHApsGdxP~D@eDH(b-~92 z=6ns5+;D{JO0!-q4?f~j_Ub*99gWP1Z9DERoPckMw z=h0Ho`=kgyOsI=wowtQX79yC4AYXKbzjCCE>)ezR*0>Lb1~hLeRhm*S^&>&8E-o(T z5W5ExJ0*w?L5$|u87rfS7GDvKi#w0xO+tsieOo|EswyJ$;-yO=5*EO|a}hO0W=zED z6YDMJ8PrKSEwSrz^B=%9k;s?OWVDvNHb|w{9m7oh^TaUY!%5|Pd2&8CgWH%4>l0nv zX;MLYS|H}!yAs8?lQQf0y7ENo;P!l8=^LyY>~FsA@atjcW|)dn!dyMRy6oQfj~hSc zDITgjG(0ThZnwrUpgyE=n?~YXK$&J;lWL1q2BgKT*cN6yeIB%NO2tFtmU1ia&J{9q zMl-WZmlv!}y>m6#E8N`DoHI{7U3*Wt?}AE8_DJC;gDrBO+8)N&n`-Uxp1&ty1AZDZ z=LWAvzdXABF}QMv58oAPkVLQWeE0Pw*zSlTpj)g!kzjC&*&g68X|OT=KpPbi-23oJntZnON7#pxV#`IJM|rJYsg6%RESJv}`!6}h?o&O;Dp6_gSt=LtFt z2tEZF%erfhfpu7MaWOfE;Mk%HC&)uJrzusz+M>Y8K6M0@K`ZqR zjUIki6mJrwgq>p(ZhmL^?VtvWvsOnx>OHxG>oWAekaH_pRXS&zEN%GFHoULAO4=Of zN)@Mt-mYm?eP%lIa^JL?7X#-E%NwF|YgUDMsji0_9tV4q5d{f#ldVTc5_G(vkcjXB zn_0bWTVbWH%(WMV*Wm5;@`PVqpt&uEgb-#l^+o~|$`}m7)E|En!z9mAf!?8^Tue(k z_fSMAhj_ta$f6rIiiPV1%Xz1j6rOXHo`{!l2f zt4K>@2=7}ZKGiLE{;PkN<@w>}{5qLH64$$0Qc|TXiJAJx3nEvnK8yq;>#GkL)^b0e zLOTn$iEpEzo|;5=2#$^p+_7dJN-<1HewqAkWEQk}7kcC@=6rvaR-yPoa22|ZQPy;= z)(nB@qZg_lOKFJ5r{|kZ{4m$A9b5OAGgf~bb9}znYE7rcnnIQm@dtP^Oh{;B<9)`r z>hQ5+`$74mv|xFtlf@<4D_g>C#595*%UM23mX-t$+0`-K7W4bW~84UY`YsXv=B)lp@q0& z*sea#j}2;x@3&!{KmUPwr7+m9z5GTnEk5fMwwjo zYYbcmB80f7wlfFpwat2ieNz!F&7ZQ}{&rf{Duup*L4}gxJwjHxZ-o|bH8`=miX~A*3)(?C~HDwO6M@>_oVxGTNny0vvRtzl4r#A^JpuMl&)VJM3k&WUWcDWo-c;64Ds z4kDEIn6+al(OMO^g{Uc&mDk=ZLX=G=aZ|rWCXdLv?kQ6PBuKFgg9-WCC5kB8I%5%qWT^DbScj@({X#Y2%p<6Hp)ISrBQH0So|H{-`(lsfy zG9jHpz*wW<*`xm)e!H+ofBW{CMZR;DAT}C8TLd6hS9zx=c;yKoPeeC#Z?X{s1sbfV zh(mq5md$-ERVw}U91T~3o1?4D?%k`x3ch?f#;Gtb%z`0mQJNG+&gCygAe6EK`C_PK zqz>NTD!GG(xe1c(zd+oEpsu#}vi{^tKF^n7p-E#~J1?BRY8Y1L=xc;w-MK{L)ttiW zt;VVo{&7L3qPSr$#Z{%$xdn!g$XVMN7O(D%H-t*icw5WD{&h@Y^}l4wY%ke%mDat^ z^gfb)6G~mGtZ+L8t^0$?FNZqht}*>w!Q%9Uq8zA03VW3=_i=Z&Eu^-TcPfsGZ(yL> zI3^m3 zzHCqMm%h%hrHnV@L5*h5$6rqk*0W4(g!3dx0feM&J~a$N8PO{2o|LdJ3;8(--`-Di z3b7ilE@ikxSx)wOKG_)}J^8ESK4)a!U|{&@7e>B|won6&9DYW^gfSJP99n#>*%+B8 z2Q3l-`w0{2DT>dPu4<{S?0-a~Ua^KIuih-sy+?-cl&F6qKPRd4sg{^Fw3 zB2&$=f^&V|N*pkAVRDG`PQxRs?ukpwa@gNJ$qTOo<9>ztIj@%|HKuQu9MTR>d;ZVeTq5=0U5wQ?gAsmhnp$G~Ig=K%? z0C9+c!k+^48;)ETi0A_`jGPV=20}vIEU5br9xy-=c8^a+)hs#!JUu`dc$LPDHwRL; zf);?quboaI&8!h$gmS>-pfcJMmpO66c=JZN!(L0vU+DgX07h1_Jq9FEH|`IEW^rO@ zj}NPj#5f-|r^qO6%2T@a(ZRgBO#5#mlp`;8{TThyUXeU>3Qul*n28AALQHD0U7 zA=ZAH_&d33tYr6aceiJ*N$IElPM*#V6#>gCIliGixnmfWt1A2eCO1Ra+p> z3@-{3K#bZp6C*gn*THIC)ZK#R7fuWs zR!U$NVoXW)Pbe*+EpdYX4~S4VzI^=n4ps5bj;|q$^6>AV#AG$Yu|noo<55_x7H#y$ z$Z`MqhA|Q_h#DaW!s9|1rl=pRAfHM0`n%x&WsfCK!~!27BIiAIXv;q2hxa9}E}wX@R=Ve%FZ+h&8-9RM2{FQ7<3jN->HMPA@ZYCVr09ZfIuIwzH z>K{zsRySvNcV059RD1l4>DABU8Wi=yV1y5yMS9Dqe&!d<3QPwZuXq6-0X8E?02Bu? z7TdOxO^7ursSG~JZH9kWp`^fj2B|q;xIK+~yN>gvB_sv_Cz^E1+NYxdb)W{m2YU%o zG-#1CU<%Gn)2K=qZC?qKr-z5v5pVk$5>vuPif`%CHB#HK=$BIsK*JH+N|b`rqq0tG z6p|rF+SdC#dNdF6aGZrT=BY4YfTbGNvLLmHoPNYvKKk1C!x({*U4fI&Ag+%tj@a*o z>>v}|Fz~`t4cYpS?VJiqd|*jLTbEq7;wV#Cbv$SB`lGoHvW(AZf-+j=MjE-(&P@+2 z435AF8e9e+8XrMKI3_%9p7vs(7$%adkDbPn_KR4gSz*<8W`I^6`RlBNdcwC+x9VdN ze=M!E%*a!|&MI5?cJYwiL2yn%%-X+Lb<}U~r5JNO!rmHUo0pZPpyxHP2V;r5{yjjj zeQ55;^Fr*$-60nN;v%V}N=mN7Ie?0wTY3Xt} z0OWubnU!6D*G$?=)$kuJ6LcK8gvkt&(KLtyNT9~>{W;sw@j=z=5LKcSiF@tGdS~Bx z@rVo)I0FVb5IR8{5ndmSLEst zrz)mf0f+KR^meI<{3`hLY2TSMXKwZRNSeOfzq;oK^sAocv;)-ygC63tQLly<`sRlhAEU8ESDyc znX!X9hWA#Ip9%s}jQuxtJVj@>>bX?Uz+hIz$w`V5FRxGAwcGC3zFT>9ZY2{V2fIXu z-m+1x-+I+rM_OW+(~GZmm%O`{lC#n&s@BB9LJ>$wSo>8tCqmGnM6(@25pn4+Sb8If zk48xW&ICU@U%30mYksyglV(Ra8j9h&(oPI8ZTtE2x@LSWrrW?1xiDuSK9nf77$~@_ z_=H6`5<;V&?cmA|AOG1_qV%6i zAyEF|JDrSVgR7zj@68sr?#QTIcWO(DU7ys9 z_|;cz2~SVA?~g3__`U!=m;lV9B%YWud2V3Jj68c*bgQBTr)zyR!+^8hxuM1|ri2}) zpIC?m6$)qApXu-hAA6Lr7$pR1>ZbMruDf0NX?4CZ0DWNF5!TArSsuCtR-St;e$7ey zB;m4`K}M(6zx)NS0xF8=Zv!q{+a!)1YX;39ct4J0qzSM%#O3{UCN!JmiJNuT)mE2ris1SZQKuHT@-*f49GO&2e9{yJ^cRFESu=QD+ zh>D1@*y-l%RLa;_1c2b!3|D~-9r4MG)_)cWNN%*eMTbF`dLm)B?aV7)M-<1KM1uFi zA}&wl-rm9JSZ&_!EScI?y8NwC=bm3MDcXnM>+^IB>W|?V^LWlcz1_Y+o%52N$Z96M zrW@&oU-rLyo%cjickaGhqXO4W7QTG@kZA_`E>vLK`=PK-;?FTwvkf8{<<(11?|hdp zThfV087?AJ#F@`A4C}`naLodgH*;`ug1KooF}^W0JRH73zF}iKv=(WvU%&o68jQ+y zQrWP*$S%085t$2{XCKg)Sr4zsd;Z?`0V~nn*Vc;6!KxQ*O30si<|S>j7dQ@r z%3=PyC$#dqz9gEbTuzXC8Bb{Q)@juSTn_HIxBaHZ^_Z|%Lb6HD`XzX@2+1;*UBz1~`kYS7H$!lL@B8`LmkS)RwMylal$4|wFen~f@0rt>Ih>4uX4N90!ElPz1Mv&b4yqrr|{tFs>yesT-ikF)6lq zhVLjFD-7iS^zzzJrf2;&_`89hSG>;y=svTIVZv!Q+{oNJjK+#y4Qf5S5PU3mgPTv4 z^}I1Fm#!Xw`RPhzT zp0Uh8&GquqmlyW5~JIn6c4| z#ip%O0O_yhPR9mFF{nKCTA73`!MWsfS8)G9;8V-5S4|ZMnkObD3-k_bT${#6tdJ-N zzPrN4rg7cjB^86jw>Z|9d;aO~ThAFzRW(Sh{pt6`LZS{%D_`ZjJ%EmdY^!ll%*}0R zuKlRu^kh;IdO_mg1@YR4n>RpSWYp?h$O9t;)-_)Co3z+yRgqNROa20ia)NRd1u+w4`;7VB910hcZuM7a43Nz_Y#WJ@UTbwiWUJPM z=G1^DVL%`ElO=wvz{v#hC2`-+Hr;TlVnlEs!O!$}$%mE(qHb8R^U{5N8VM1_rxgYK zglvJ;2tP#)1s9}*G2VW=By`Rz;481RvCHl}SsO(`?)F79?3uu^|NV@;?aFGT+`kPP zZvFBJ6?E{MHo4ULxj46`6C(tay@@z0=^z9a2%burlQ+zwmcO#;`6l|{YkH#NFwa(UQ_(F+o{DFu|XftlI`#wI4 zQ2OJ-6T}KYalaocF>T}rcV#ka;OX#3yg3QGaS7OEz+e3Kte=`?S5nJ#5|NoytC`f> zfig9C^)qT%cZvaEITDr^zY8fsFj|nkZ%TvPNs0jCjDDpVkz23ze8!2VGbgxNeNu9= zAkw!%5;=J6*z#uke-Au$HblmwsA796X?;@tln^&RFRvS(b>EQ_f`ZAB&uhOwRdrf6 z&0oaPqr18VoD-tN^6v}W@2Yd-8%QxlwULuCUSFvdx!jfg?=Ryb93@EFaWp(Td(s#Q(}JNM3lJx>xE?p6yJO@ zR?fL_BlR*j4`zM^cn@^1bvprx0Y7|U4d5lQ@w!}$2$)=iKcC)R?dIb0 z4lQ+0#RZR#Iq7(MR6~#YBJ5w1J}+fH+G%70Y*_p0D0vC+Nw?&tg79@~h(MIW4ZZA6aL2J8KZG7C5UFW5k`p{=Lq21`2c6Z6-|$KuAO0snOuz zN~Rw~myghS2OQ^B<~!23FY`kG4$7m@oH=tAFIjT4we=DQOBuLQFeP(vWNd=Yo6KpD zqNx*5hi(KiuVr|z7{IWP4t0CH^0oRDkq;)D%|PkdCrC)fgPMk@qM{bt4`=dsy*_;n zC4MVatCXONf#~?p6ZZR13eg0vV@*+1oN8y!ljNkN)na1H5gp(JN)2XwRo{*WVS!tI ze{?oVJ9_~(h$|jjpfx5scdpb`77S=%j$)nw2=KVsWSU*!nX0zy_{=d&^TU=7KuzTcCF+Nqp95ALpU@qK z2yNS9E)*dG${v~=*rvpK<6f2&Qj1@RjYbg$(o2SA z{I>lZm{SyiT~@UGI_Mp2^6x^=Mmi#68%G;;&d6Zuhqn>Zl%b1*bQk0qXvvlCAt>4D zOz3``+Op$4^4RWF2AGRoo96mFeL9~agjBQZm|7EcC?RaZom{yujXN27H^Q)H$cXDy5B*1k=SB5#EkI{M*3>#}du~ z%Nm}?KD2y!DCJf*8PRZH09*a6of7Q}EEBL8GZ^V;RROC= zAY`RBc>v<93m(w%A|tPOJ%>Xb1n_vg&}q10WGf&kxf1LG!vCZtTFzMBeaD>2U(tha z$c*iUz-PcN-BOW3$E&e(@D;&#B>n*vPG#2!FZwgJljCIjNfZi5GKb8UBKfB%=MxNT z=TJ3oub)=GB%hDts1PnYfZvb%5V`^m#f}(0)dhFv91cEpdy-#SyfuD32Blz@8bwL( z5c4zOx)glsqu*)LWpMvZ4J=AO!FT((hv?CRooD}rY(4#NWJ^26^j|}78z`F{+4;LV zeql>J%SH+%z7PVj!9aP?D$Ijthuwm?z$=U`isri!7I3xVhX5g9lg+LG_^0(n>q8N{vHE02((758 z9~F2Z9#zo6gDfz)>3%IAr(FNa&u+8($_Yomvo0ZVpjxIhXjm_F{u3!N|7b&*!Om5kQpfO9@KOrS)jHc>P87c`9^XSsL>^8H{3N@G zB(3--0=O73K!#0}c$I-Aj>@f7ikm`e3e)bb5Cxz3_}ss0Yih_jgO^Mg6Sw`b?OQi) zotKF!D9eiHj(rUtJSC1e$=_~@-W>n5mespg{TCkHw5H{EYxy5DG!Bl{@MkLe>2~MJR!-KJ$9)!H zyJb|Jti(5uF*%_FeEs^h<1tp-jl;q3fss5wQ{i)*+2^h&9Laq~81Y*v!H3Xd^P)P0Sa)Hk2kd3kx>>Wc6NL&~D+8GU0+fbqVQKfDqt3JPszwuww=Jt*lQXy5XwYxg& zp=97bdsAcZ$UP9AffP%-Dh8b#`y zd?LwsKW4g2h^_!PSR|oFM@Lf*Bw06H<5h`UqQ8GeV?VC-ITWc@n}ehJV7m?*I(Do> zeE;o+B)~w_2Bp!N$ka`+A7!LSAwyuaI^tA^12+exHwFrve4?R%ml1Z`D)k5=1{@fe zN3O}@O=H9uaVOG;OD5d2WWB*ZfK}lvn6Im)lD_Qra-|12pjB$!PLU+b=fh!2MOi?lLAmQSIAdp%7)Rg!_)A&^*%wgK8$X1IQ0|GD(sVK7Wg~lH6g8BQQ{K^R&?hc|C_lJl5old(UE#2rT(MWK#0 z*DT1^oczT_sOl&-@HubH)CYjv@50duV1Q^h@R7B1Iw0FcPft2%SOly;;d^hE76+^$ z2P*M}F;jdKm71XoK)Q z3u(MPckgUy=`bSWfJ|?E|Bg_9QFcXN{IZJt=IvVsm*K(g=ve2aiVSt|gy-U~k*W)L zPkb#QoU?gZk9_xffUr^ul-WB@PENJEQM?k4EqO7pasS~3x2WEgL zXGajgBmo)0H0PiNt;5|?fl?WvMBP2Z2lLY4>De)d98(6zwY3G2!My2a;n%5!`#i(( z(p_4Zv=;5z0Bhdahth37F=RhAWG|h<0{X}sqPW+##1hUIM(^UxCpzPA4@q&b{`i~u z#iITNl%7}l5B2oxylO*s^JBAxxc%Vd;jm##T|&v_huPKfm7WDR3+e!Te}ClE!U zT`sacw@MS-YeM@*E;V=s@$K7Ca{_E{Xl}m1B^!TEh>Q!`Q@sJ+JeRg$gAP{#{W($? z9S(Th=(LmmE6DB`EI~PGK`<2Ji5SzkYSeb^s|~t-ZFTuL3DrSJ2NSRNy zSwBAY`Zflmy!lmtrwG0UEzw;Qg1sWH7#e;uu9lhmq(C5W(0z6S_x#diz5V+aduw~& z_vC^&RU{8UZ+0M*%EC)!{Bs8U$_tu;N%IN>OXcNkdUP0w)H~e%;O(Foga$VJV`Ixp zp=OvJJ$bUisd$xYPC-F;gc$U^J68-2^zsT!?F+FT7f{S(43yHi1PuumV+D87&Bhc zd;|jl`m{MtQdh+|(wEOD6$73A_bcu=Qmmb1>O13E_h4T&7uZkkU@V+I0`WiWPcTO! zZN#!=YjbFr`r<4@&udP<-tRz5wOXzkeumS54OYJB8K&rf@)b%J{#~>c$rDc%gyz6! zDPe`)wv|za)vC`^%ZZAs zx{DE*_Tx?C`pt%A5(Z;{)4&sWI4=NQGD$ul$Pa+ld|3rEKd`Twa1o$!-A3xjVH`Bz z^=_w*l^p2(Pqv**$!rk>qwh*G<7>FzwHU*#=d>oq(|D)c^f0GNZBEjo$WHX1Mz#at zV@=->&!%Eq^g1?07}HmRwSE6O$b!Q?Ju0ut8ZgR64-HrG6T8x2$Uv?%^zADh?dFni z4A3P}`oS$gx0o<^@JDxv2wce=2AMRZ^O`B0VfKk{ot=r}kP^hcy*CvwJoj*(i1u{K zT(oCw-dm#?u||2snCVP2@I^$46g1?c>Isf#Vx~;47Mu?&?9qNwW%(IsTBp&c*ClgM z4iIb&2X)6w&}T0|--Z4=Uw9hT4fst-l-d3d7TEj!!G8~<BCE?q5?1s#umh+zzJbZK(T9TR`gxU!#oc> zFQlfxMpKYAvHIBzSbiDL2zSGk)sytuQq5!8Ke!fl+45$K|86n=Smw7S2LU%RCNnoP zA|D=J zgl-dGN=gb)A-*OPSV9m*yI%K!6lE}(a5&w~#Sewb%FyiT<>Hr(@J0Di-$kB->ioZr z7y3UC2-?PGH-Hlo8aT~#2>#9`x?^;nSzE=&O5M8H|JmVvVcR~vB8F7b?IyB}yV$*Y z+nzTXo7?Ubj(=h8Y5&(gLfpx!{JywI9@7j~+#}h*)b7J82}Q7Ehai3+Hw=J!E9g(E z)?+%cd!oPi$#qk85}4ZpB!E*2bt2>7&dZV_yU6fF352tgHOP`!$g)i4wn z#e5Miad$0P$ZJma_#Mfm_zay+TWM&t-Gr4%%9Ve?m*`S<^YB*x7rmx#-}EHDl>r7h z`z$RJhwnw}>aC_4e%24@Dz?&pR9*^W#)O$s1z!Aki1O=ohOiyrG z)xc7nGVc3v449*yS7pR&<;_GLO%c)@WOjQ4lq7IPUs2p;RM$Zov*&mJuAi;IuhR7x zQJfd(aIYW!vaC^*#y5;4}){^A%Q8iF7vj?FIRH)-VdmDC~9M%->xKQXz zEB52cly_WcLYG9N2?d}usM@{AOW^~2m1x{ZJgj2#to#XO#MpZNZMn%|gb5A-1L9w{ z;7)AbOqQYGm6=EsJd!{*iCb~N}0hl<1|uB)e8t`rmhZZQ}0ok_~V!q8=RnxHFXBCE4kbuQ(2T2D*DJg4&egQd9ZJ-Ttx{r(sL zeuAEsd<4cT%sgYr{k_}>$inuMEdaZ`H2#nOUi6N&&Q(bn8GFLcMZfQgbFSjT)Bhv% z%<9dXKUD^kI&StOPO{@~qLQ-5|9Qv^y`B7@H_CKoA^#k~D~z}T2xGQ>YI10!h)6Cb zuKwliKqhPTkQ6F`ytBqxoc$VEII|yBvthW-&o9lGZE}bS{(hLm-U#UZzvhK23p>m; zW*U-B?UM^B00yL8V}n)-buuett6mXsy^>scQC z|3Ax*?VAB7FOn|Hn^TC(o>(lC%C%ooJ z94L`*$7lUA62(5W86V%PP(uIx&-Fy8e=Uo`pgBX)GJMr7W7MX*(s@MvM zA%2vOYYU6DyQ6kpAg6@skA<*$rrHZ;vk?KMU;RDvY~@20bK%>E2gv!+-`T&MkIx-I z3zCc^QgpB*&yZUIWMUpEvx8>0*9%@l%eH?ZCjK$fD}QgX1%DHKomDfi^GQ`ja79pN z&I8aUd8>`!xMR?hiA6m93Q%~ors4J=5D6+g;F`pE99THQyJldvnG-?KD(jyYvH;Z? zOyh|Pi9j?U%mE&plTw49Ck40@{#ne&*VSWg3Uk!xB!IH*tA(O+p(iMfxWV_lC&U%o&du~@z=3W z5&8!5HvjH`9_|-kyGUI6U^r3jl#kpgA-PrEzi-E#G9x54ajhW=reL+P6GZx>7 zgj?LAUUe2@0E&q5$7tyqKa&KGO_$Gs%tj?4C*6*xs~wyzpqxX*660)^PcLy?BcMRQ zpmY_ELgGmhhM0LizOw3st?G$U``hc_B+R11SmqxKGae*^0w`q^7jIWA#3KVZjcBcC zbL8nZVB8O2QKSLhATamBr*<|U86Cx+Apc;P0nUemn3>P>=R!YDPmk`2?FjZMP9O#% zlAo@`ZBDf&Y#QU^;aNyw^^=@x;=@;T36pw@`hsfd?zfD9Op~`_K79cMftcnh%3X@?Pctc) zC149Qp0FSFtjL+r+5Qy#)z#8$0ONrtu(fEW*(6C!euZ5k5?pv>ZO+|8fY2HY(csyj zkWodqGOc8K&(61-g+e4+2+N*aY$rTTcr7q!E!v?b#7UOonl?WkB280lCHWOQ65OVNEwQ%Zu`0ybEAifeNa?qMyKIx3)8@n+BVvmDgMGP-TGwXr{&r&1E z&@sxf6pl1V-s8f_6Z3QEaGT)Pd451$Vd=IT3?Fi^BV|e+mwxuRqC3xJGg~~mN8v;G zj)^bt6T6XL-_OX^O+PU+ixazhZ)f&GkTealU7!9{6k=>UyM2>p{-eK;bPe^OTEXiF&TH^unN zF_$q}5-i*jz34MCafLT-j8YrAw{l08F>;Vs;NWK@woQ@)UZAs+vaus4CHI&a^{k7-{} z(`E_Nq(x;ZTgcLw=p-scsY9i#p|X>u)l5ifArfULB)bzT6P0Y)vL{)OL-zIXzuu0S z@AG`W&-45DdY(o&+voFs-|zdr?(4d4bnLh(=yb>OjKYeSjE{2Q=qWfDD8b<={Y6ZX zo6kjTHZdx{CXE?)( zZx@2*#+J0*)QTpv-EozWRSP)$cb;nq$Z4QYE`$-{IRVn#I&06#BSFD*8@xRV1w=36 zzT;q236}r+mYsuxgR8D|piN+m_BKlK0GN1qf}Z=#V`iF6#z3LfQ{4))bzilO-S9wE zIe-3n=QdF|wQ|uFq9Y?oC5_?n4;T3I$MI9ps@rjM6jL}X=31CLO1i+Ce1Ugd{`5q6 zxm0ZYFb-%kE{QOL*)*qh6;gb@`4XcLPlNK9>^AAJQw&UVG9kcf(8gnuAB&_cT%DWP zGJSJS#Zn?oV~_D7qu+D*Wv$Pyn9We$Q{jY($G;0X2Z~t;fum;sux5H#t|uSZ=g!P1 zEAROE@cfl4RjF?u+>%%MbL$N+>Vn2>fx$ap%tDj3jqOUmpHiI+mmJ*wL$X4h8Xfv! zzgC)}RnRBCMbec)dwTYNF5s~;fBJN5_|F#)|HOaVW9C%S_J_+>tj^lKr#It7MzxDk zbj`3~=J!jAHH>bR#e3&bC=``Nm{}PPOM%XHd1E(L?n(FZV}djwZCbLvb==rb(d4=& zlL4tj+2Y9Rkp|mRk)1n_KvQ|k+dJMi6eZ6AEv;(6(2SF%O(GyXsUuy+&{};HlPJ~0 zyD>}#MCUuYtKvd1(L&f9Cj!?n2&`p`g>AmP*n3EE_&%B}kE=18##&;Cjxj^V!GxQ9 z`jO(8Rg$=b#EX&@sX4h&S@NH5$!=Fdy9X$6KsR2*g_%~f{%Xz03l}$BPu~@jsNBm6!5+FN+_gw~>`HAL zz6XB}uInqX`6(2;UjXY9st6G$(=spYMa1*B_^l9SvVC_GzkomjF0~n2z1TSwh?df4 z{#p#|gzB%_(S}|drU`U|4TAJMRG66W^8uz&ec=cf06tmk$C!Ga2wj2(PSJ@LZjTt$ zNM6H;XK|R{W{$K%9;2o5UD8TTaVE=yor-w{F(|VeJ!V*ZGUo$TYr60fkgo&H9p)v-Nw4qGrljtIBg2z&b7!0#!w!K9@tnQa+g}+bu=;Lj4^9Pd zsHsROLXsMT=0^hHM?55K?}m6Ti*KHSE?q{dpTrhW%5J8%Gq?wM2?1($Q<0TGp=6%J z;x7Xyt5}TeG%OB@>tP(TU*Rv$dL-jfU@yhR#Jum(HfT_u89#(ZGXSh2{8JZeWDSH- zVEUwJ%swbg_1F1i1yWSDOQ9>o{F1EdUkP|51;qx1a@Y?DOcyNI85cNCUB=cqT)Jm) z`Druq0Ez+6hPf{`;8r?2IR(thB*T zTeEhpiG%so5A%ZlYvzf~>3!NkRS>v=fdVKORf(&BjnnYpx>HzpHt9 z%^hm-z}K1=b6jkbaz=twDK6NMd`g!yk%-hrK3x#9$5|WIs^r@;4~S&Rrj2Iooqesc zDRyY1T=0ghBn?_SwIpR+F*vN_;wc=)WrvijbsXz_=<7A^Alll45*W6ii1=-gV&W?t z8xiq4kkVLyAfLo$uehf}>ARkvW}n}6h< z=hXi!e-OlyV8u-mN04Z(78JDJqwF@3TDyrOM*VjoaVP<#=*uZ8{Kg~0izs34c2~($ zP`=85^(?pGm+o&}U9qS0Z$6@l;k#vGJ8;2b3zcDS-M<^dUek+x~VIkLgG9H%d ze_kdS>prG1DpMjwgF;E&YhvU

(S+<|9w)Sx>k{h$>BXit_nnoiNp{trPplp13al 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�Rdsv;WLEBLrKT?qW=XJJc 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

    Oh+r8`}6eyBPrueGMNeEg8!O{Q~A#J-We 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 z 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¬GnI-m<-16nh{vS55fHo{@BN}Wl)l0 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 { } }; - const RTCServers = { - iceServers: [ - { - urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'], - }, - ], - iceCandidatePoolSize: 10, - }; + // const RTCServers = { + // iceServers: [ + // { + // urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'], + // }, + // ], + // iceCandidatePoolSize: 10, + // }; // Seconds between data batch publications const batchPublishInterval = 5; @@ -70,9 +70,10 @@ function main(): Promise { const simulated = process.argv.includes('simulate'); const offline = process.argv.includes('offline'); - let arduino : IPeaPodArduino, publisher : IPeaPodPublisher, camera: IPeaPodCamera | null; + let arduino : IPeaPodArduino, publisher : IPeaPodPublisher; + // let camera: IPeaPodCamera | null; - Spinner.info(`Running in ${chalk.bold(simulated ? 'Simulated' : 'Live')} mode with ${chalk.bold(offline ? 'Local Filesystem' : 'Google Cloud')} publishing.`); + Spinner.info(`Running in ${ chalk.bold(simulated ? 'Simulated' : 'Live') } mode with ${ chalk.bold(offline ? 'Local Filesystem' : 'Google Cloud') } publishing.`); return new Promise(async (res, rej) => { @@ -90,7 +91,7 @@ function main(): Promise { } }); - camera = null; + // camera = null; } else { let serialpath; if (process.env.SERIALPORT) { @@ -103,18 +104,18 @@ function main(): Promise { } arduino = new PeaPodArduinoInterface(serialpath); - camera = new PeaPodCamera; + // camera = new PeaPodCamera; } if (offline) { publisher = new PeaPodLogger(); } else { // Check Internet connection - Spinner.start(`Checking for ${chalk.blue('Internet')} connection...'`); + Spinner.start(`Checking for ${ chalk.blue('Internet') } connection...'`); if (!(await checkInternet())) { - Spinner.fail(`Could not connect to the ${chalk.blue('Internet')}! Running in ${chalk.bold('Offline')} mode.`); + Spinner.fail(`Could not connect to the ${ chalk.blue('Internet') }! Running in ${ chalk.bold('Offline') } mode.`); publisher = new PeaPodLogger(); } else { - Spinner.succeed(`Connected to the ${chalk.blue('Internet')}!`); + Spinner.succeed(`Connected to the ${ chalk.blue('Internet') }!`); // Connect to Firebase initializeApp(firebaseConfig); @@ -124,7 +125,7 @@ function main(): Promise { await sleep(1500); } - let batch: PeaPodDataBatch = {}; + let batch: PeaPodDataBatch = { }; let batchInterval: NodeJS.Timer; // Initialize Arduino communications interface first @@ -140,7 +141,7 @@ function main(): Promise { // TODO: Plan, act } else { - Spinner.info(`[${chalk.blueBright('ARDUINO')} | ${msg.type.toUpperCase()}] - ${JSON.stringify(msg.data)}`) + Spinner.info(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) } // TODO: publish other message types }).then(() => { @@ -173,10 +174,10 @@ function main(): Promise { // Get program - Spinner.info(`${chalk.green('PeaPod')} start - Project ${chalk.bold(projectname ?? projectid)}, Run ${chalk.bold(run)}`); + Spinner.info(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); batchInterval = setInterval(() => { // Publish entire batch - try{ + try { publisher.publish({ type: 'data', metadata: { @@ -191,10 +192,10 @@ function main(): Promise { return; } - console.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${Object.values(batch).reduce((sum, entry) => { return sum+entry.batch.length }, 0)} datapoints published.`); + console.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.batch.length }, 0) } datapoints published.`); // Reset batch to empty - batch = {}; + batch = { }; }, batchPublishInterval*1000); }).catch(e => { rej(e) }); }).catch(e => { rej(e) }); diff --git a/software/lib/PeaPodArduino.ts b/software/lib/PeaPodArduino.ts index 48d55de9..135a1f12 100644 --- a/software/lib/PeaPodArduino.ts +++ b/software/lib/PeaPodArduino.ts @@ -2,7 +2,7 @@ import { PeaPodMessage } from './PeaPodPublisher'; import { SerialPort, ReadlineParser } from 'serialport'; import { ArduinoInstructionsError, SerialTimeoutError } from './errors'; import Spinner from './ui'; -import { updateArduino } from './utils'; +import { sleep, updateArduino } from './utils'; import chalk from 'chalk'; const BAUDRATE = 115200; @@ -93,6 +93,8 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { } else { Spinner.fail(`Arduino revision check failed! Expected ${ARDUINO_REVISION}, recieved ${msg.data}`); // Attempt to update the Arduino, and then restart + this.stop(); + clearTimeout(timeout); this.update().finally(() => { process.exit(); }); @@ -109,11 +111,12 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { } async update() { Spinner.start('Compiling Arduino software and flashing to board...'); - await updateArduino(this.serialpath).catch(e => { + await updateArduino().catch(e => { Spinner.fail(e); }).then(() => { - Spinner.succeed('Updated Arduino software successfully!'); + Spinner.succeed('Updated Arduino software successfully! Rebooting in 3 seconds...'); }) + await sleep(3000); } write(msg : any) { Spinner.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); diff --git a/software/tests/README.md b/software/tests/README.md index ac8c7b17..99f7a38a 100644 --- a/software/tests/README.md +++ b/software/tests/README.md @@ -10,4 +10,4 @@ Test the functionality of the `arduino-cli` and `avrdude` utilities (and their c ### Communications Protocol Test - `revision.ino` -Test communications between the Arduino and Raspberry Pi including timeout and revision. Essentially a skeleton of the main `PeaPodOS-Arduino.ino` script, with no sensors or actuators. \ No newline at end of file +Test communications between the Arduino and Raspberry Pi including timeout and revision checking. Essentially a skeleton of the main `PeaPodOS-Arduino.ino` script, with no sensors or actuators. \ No newline at end of file diff --git a/software/tests/revision/revision.ino b/software/tests/revision/revision.ino index 3784d23e..8ffba18a 100644 --- a/software/tests/revision/revision.ino +++ b/software/tests/revision/revision.ino @@ -1,6 +1,7 @@ #define REVISION 0 void setup() { + pinMode(13, OUTPUT); digitalWrite(13, LOW); if (!post()) { // Failed POST From 07c7b6a81c8a0843ac98879bc5af69196fd21a45 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Wed, 2 Mar 2022 21:30:05 -0500 Subject: [PATCH 04/85] Arduino Communications Fixes Fixed startup order of operations (Arduino Serial begin vs Post, initial instructions, etc.) No more dynamic allocation Added logging function to Spinner (pauses and restarts spinner) Fixed batch publishing in local logging mode --- .message | 4 + software/.npmignore | 9 +- .../PeaPodOS-Arduino/PeaPodOS-Arduino.ino | 122 +++-- software/PeaPodOS-Arduino/src/Base.h | 11 +- software/PeaPodOS-Arduino/src/Sensor.cpp | 23 +- software/PeaPodOS-Arduino/src/Sensor.h | 8 +- software/PeaPodOS-Arduino/src/sensors/K30.cpp | 4 +- software/PeaPodOS-Arduino/src/sensors/K30.h | 2 +- .../PeaPodOS-Arduino/src/sensors/SHT31.cpp | 6 +- software/PeaPodOS-Arduino/src/sensors/SHT31.h | 2 +- software/README.md | 6 +- software/index.ts | 30 +- software/lib/PeaPodArduino.ts | 135 +++-- software/lib/PeaPodSimulator.ts | 1 + software/lib/ui.ts | 19 +- software/lib/utils.ts | 38 +- software/package-lock.json | 501 ++++++++++++++++++ software/package.json | 1 + software/tests/serialMonitor.py | 13 + 19 files changed, 764 insertions(+), 171 deletions(-) create mode 100644 .message create mode 100644 software/tests/serialMonitor.py diff --git a/.message b/.message new file mode 100644 index 00000000..06ea587e --- /dev/null +++ b/.message @@ -0,0 +1,4 @@ + +Fixed Arduino Serial vs Post ordering +Added logging function to Spinner (pauses and restarts spinner) +Fixed batch publishing in local logging mode diff --git a/software/.npmignore b/software/.npmignore index 8aab899c..7869a83f 100644 --- a/software/.npmignore +++ b/software/.npmignore @@ -12,6 +12,13 @@ tsconfig.json package-lock.json .env +.env.template *.pem deviceInfo.json -projects/ \ No newline at end of file +projects/ + +# Arduino Binaries +*.elf +*.hex +*.eep +*.bin \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino index f5d25f86..c148adc4 100644 --- a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino +++ b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino @@ -6,14 +6,19 @@ // #include "./src/actuators/LED.h" // CONSTANTS -#define NUM_SENSORS 2 +#define NUM_SENSORS 1 // #define NUM_ACTUATORS 0 -#define REVISION 0 -// #define LEDPIN_BLUE 3 -// #define LEDPIN_COOL 5 -// #define LEDPIN_WARM 6 -// #define LEDPIN_RED 9 -// #define LEDPIN_FAR 10 +#define REVISION 0 +#define BAUDRATE 115200 +#define DEBUG true + +// PINS +#define PIN_STATUS 13 +// #define PIN_LEDBLUE 3 +// #define PIN_LEDCOOL 5 +// #define PIN_LEDWARM 6 +// #define PIN_LEDRED 9 +// #define PIN_LEDFAR 10 // SENSORS SHT31 sht31 = SHT31(); @@ -40,87 +45,104 @@ Sensor* sensors[NUM_SENSORS] = { // } void setup(void) { - pinMode(13, OUTPUT); - digitalWrite(13, LOW); + // Status LED + pinMode(PIN_STATUS, OUTPUT); + digitalWrite(PIN_STATUS, LOW); + + // Await serial start + Serial.begin(BAUDRATE); + while (!Serial) { + delay(1); + } + + // Send software revision first + Serial.print("{\"type\":\"revision\",\"data\":"); + Serial.print(REVISION); + Serial.print("}\n"); + + // Serial communications established, initialize sensors and actuators if (!post()) { // Failed POST while (true) { delay(250); - digitalWrite(13, HIGH); + digitalWrite(PIN_STATUS, HIGH); delay(250); - digitalWrite(13, LOW); + digitalWrite(PIN_STATUS, LOW); } } - // Send software revision - Serial.print("{\"type\":\"revision\",\"data\":"); - Serial.print(REVISION); - Serial.print("}\n"); - // Await valid initial instruction set - String ins; - do { - // Await instructions - while (!Serial.available()); - ins = Serial.readString(); - - // Trim whitespace, newline terminator - ins.trim(); - } while (!handleInstructions(ins)); - digitalWrite(13, HIGH); + // String ins; + // do { + // // Await instructions + // while (!Serial.available()); + // ins = Serial.readString(); + + // // Trim whitespace, newline terminator + // ins.trim(); + // } while (!handleInstructions(ins)); + digitalWrite(PIN_STATUS, HIGH); } void loop(void) { // Check for instructions - if (Serial.available()) { - String in = Serial.readString(); + // if (Serial.available()) { + // String in = Serial.readString(); - // Trim whitespace, newline terminator - in.trim(); - handleInstructions(in); - } + // // Trim whitespace, newline terminator + // in.trim(); + // handleInstructions(in); + // } for (int i = 0; i < NUM_SENSORS; i++) { SensorState* state = sensors[i]->update(); + + #if DEBUG + Serial.print(F("{\"type\":\"debug\",\"data\":\"Sensor ")); + Serial.print(sensors[i]->getID()); + Serial.print(F(" state: DS = ")); + Serial.print(state->debug); + Serial.print(F(", ERR = ")); + Serial.print(state->error); + Serial.print(F("\"}\n")); + #endif + if (state->error == ERR_NONE) { if (state->debug == DS_SUCCESS) { for (int j = 0; j < state->numdata; j++) { - Serial.print("{\"type\":\"data\",\"data\":{\"label\":\""); + Serial.print(F("{\"type\":\"data\",\"data\":{\"label\":\"")); Serial.print(state->data[j].label); - Serial.print("\",\"value\":"); - Serial.print(*(state->data[j].value)); - Serial.print("}}\n"); + Serial.print(F("\",\"value\":")); + Serial.print(state->data[j].value); + Serial.print(F("}}\n")); } } // else do nothing } else if (state->error == ERR_WARNING) { - Serial.print("{\"type\":\"debug\",\"data\":\"Failed to read from sensor "); + Serial.print(F("{\"type\":\"debug\",\"data\":\"Failed to read from sensor ")); Serial.print(sensors[i]->getID()); - Serial.print(" (non-fatal)\"}\n"); + Serial.print(F(" (non-fatal)\"}\n")); } else if (state->error == ERR_FATAL) { - Serial.print("{\"type\":\"error\",\"data\":\"Failed to read from sensor "); + Serial.print(F("{\"type\":\"error\",\"data\":\"Failed to read from sensor ")); Serial.print(sensors[i]->getID()); - Serial.print(" (FATAL, SENSOR DISABLED!)\"}\n"); + Serial.print(F(" (FATAL, SENSOR DISABLED!)\"}\n")); } } - delay(10); + delay(500); } bool post(void) { - Serial.begin(115200); - while(!Serial); //Waits until serial opens - bool success = true; for (int i = 0; i < NUM_SENSORS; i++) { SensorState* state = sensors[i]->begin(); - bool latest = (state->debug == ERR_NONE); - if (!latest) { - Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize sensor "); - Serial.print(sensors[i]->getID()); - Serial.print(". Check wiring.\"}\n"); - } else { + bool latest = (state->debug == DS_INITIALIZED && state->error == ERR_NONE); + if (latest) { Serial.print("{\"type\":\"debug\",\"data\":\"Sensor "); Serial.print(sensors[i]->getID()); Serial.print(" initialized successfully.\"}\n"); + } else { + Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize sensor "); + Serial.print(sensors[i]->getID()); + Serial.print(". Check wiring.\"}\n"); } success &= latest; } diff --git a/software/PeaPodOS-Arduino/src/Base.h b/software/PeaPodOS-Arduino/src/Base.h index 350b0cf2..2df16495 100644 --- a/software/PeaPodOS-Arduino/src/Base.h +++ b/software/PeaPodOS-Arduino/src/Base.h @@ -7,16 +7,15 @@ typedef enum errorlevel_t { ERR_NONE, ERR_WARNING, - ERR_FATAL, // NOTE: Implies debug level 'DISABLED' + ERR_FATAL } errorlevel_t; // General state information - On error level > 0, indicates last successful state typedef enum debuglevel_t { - // NOTE: The various debug states imply the value of state.data as follows: - DS_DISABLED, // state.data[*].value == NULL - DS_INITIALIZED, // state.data[*].value == NULL - DS_WAITING, // state.data[*].value != NULL, age > 0 - DS_SUCCESS, // state.data[*].value != NULL, age == 0 + DS_DISABLED, + DS_INITIALIZED, + DS_WAITING, + DS_SUCCESS } debuglevel_t; #endif \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/Sensor.cpp b/software/PeaPodOS-Arduino/src/Sensor.cpp index 12a6a020..74b93ef8 100644 --- a/software/PeaPodOS-Arduino/src/Sensor.cpp +++ b/software/PeaPodOS-Arduino/src/Sensor.cpp @@ -15,7 +15,6 @@ Sensor::Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t del for (int i = 0; i < setup->numdata; i++) { // Since setup->labels[i] is a const char*, we can just reassign our pointer state.data[i].label = setup->labels[i]; - state.data[i].value = NULL; } } @@ -34,16 +33,15 @@ SensorState* Sensor::begin(void) { } SensorState* Sensor::update(void) { + // Allocate new data buffer on stack + float buffer[state.numdata]; + // Check state preconditions if (state.error < ERR_FATAL && state.debug >= DS_INITIALIZED) { // Check timing if (millis() - lastread > delta) { - - // Allocate new data buffer on heap - float* buffer = (float*)malloc(sizeof(float)*state.numdata); - // Read and refresh read delay - state.error = read(&buffer, state.numdata); + state.error = read(buffer, state.numdata); lastread = millis(); switch (state.error) { @@ -53,12 +51,9 @@ SensorState* Sensor::update(void) { state.debug = DS_SUCCESS; state.timestamp = lastread; - // Make state data pointers point to the buffer + // Copy from buffer to state data for (int i = 0; i < state.numdata; i++) { - // Free old data buffer - free(state.data[i].value); - // Point to new data buffer - state.data[i].value = &buffer[i]; + state.data[i].value = buffer[i]; } break; @@ -70,12 +65,6 @@ SensorState* Sensor::update(void) { case ERR_FATAL: // Read failed catastrophically state.debug = DS_DISABLED; - - // Free data buffer and reset to NULL - for (int i = 0; i < state.numdata; i++) { - free(state.data[i].value); - state.data[i].value = NULL; - } break; } } else { diff --git a/software/PeaPodOS-Arduino/src/Sensor.h b/software/PeaPodOS-Arduino/src/Sensor.h index 197a8fcf..45496533 100644 --- a/software/PeaPodOS-Arduino/src/Sensor.h +++ b/software/PeaPodOS-Arduino/src/Sensor.h @@ -11,8 +11,8 @@ typedef enum sensorid_t { // Single datapoint typedef struct t_datapoint { - // Value (pointer to heap memory) - float* value; + // Value + float value; // Dataset label (string literal) const char* label; @@ -80,11 +80,11 @@ class Sensor { /** * Reads data from sensor. To be implemented by the child class. - * @param data Pointer to the data array to be read into + * @param data The data array to be read into * @param numdata Length of the data array * @return Error level for this read attempt */ - virtual errorlevel_t read(float** data, uint8_t numdata) = 0; + virtual errorlevel_t read(float* data, uint8_t numdata) = 0; private: // Stores all the latest state data for this sensor. diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.cpp b/software/PeaPodOS-Arduino/src/sensors/K30.cpp index 32791906..b24ef9e8 100644 --- a/software/PeaPodOS-Arduino/src/sensors/K30.cpp +++ b/software/PeaPodOS-Arduino/src/sensors/K30.cpp @@ -21,7 +21,7 @@ errorlevel_t K30::initialize(void) { return ERR_NONE; } -errorlevel_t K30::read(float** data, uint8_t numdata) { +errorlevel_t K30::read(float* data, uint8_t numdata) { byte recValue[4] = {0,0,0,0}; // Instruct to take a reading @@ -49,7 +49,7 @@ errorlevel_t K30::read(float** data, uint8_t numdata) { return ERR_WARNING; } - (*data)[0] = (float)((recValue[1] << 8) + recValue[2]); + data[0] = (float)((recValue[1] << 8) + recValue[2]); return ERR_NONE; } diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.h b/software/PeaPodOS-Arduino/src/sensors/K30.h index 93337dc6..836e7a7f 100644 --- a/software/PeaPodOS-Arduino/src/sensors/K30.h +++ b/software/PeaPodOS-Arduino/src/sensors/K30.h @@ -23,7 +23,7 @@ class K30 : public Sensor { private: errorlevel_t initialize(void) override; - errorlevel_t read(float** data, uint8_t numdata) override; + errorlevel_t read(float* data, uint8_t numdata) override; // Address of the K30 on the I2C bus. uint8_t address; diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp b/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp index e5fa7a78..3399a5cb 100644 --- a/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp +++ b/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp @@ -32,7 +32,7 @@ errorlevel_t SHT31::initialize(void) { } } -errorlevel_t SHT31::read(float** data, uint8_t numdata) { +errorlevel_t SHT31::read(float* data, uint8_t numdata) { uint8_t buffer[6]; // Ready up @@ -58,12 +58,12 @@ errorlevel_t SHT31::read(float** data, uint8_t numdata) { // Temperature conversion int32_t stemp = (int32_t)(((uint32_t)buffer[0] << 8) | buffer[1]); stemp = ((4375 * stemp) >> 14) - 4500; - (*data)[0] = (float)stemp / 100.0f; + data[0] = (float)stemp / 100.0f; // Humidity conversion uint32_t shum = ((uint32_t)buffer[3] << 8) | buffer[4]; shum = (625 * shum) >> 12; - (*data)[1] = (float)shum / 100.0f; + data[1] = (float)shum / 100.0f; return ERR_NONE; } diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.h b/software/PeaPodOS-Arduino/src/sensors/SHT31.h index 7b310785..e93bc042 100644 --- a/software/PeaPodOS-Arduino/src/sensors/SHT31.h +++ b/software/PeaPodOS-Arduino/src/sensors/SHT31.h @@ -50,7 +50,7 @@ class SHT31 : public Sensor { private: errorlevel_t initialize(void) override; - errorlevel_t read(float** data, uint8_t numdata) override; + errorlevel_t read(float* data, uint8_t numdata) override; /** * Internal function to perform and I2C write. diff --git a/software/README.md b/software/README.md index 209ddaa8..993cab68 100644 --- a/software/README.md +++ b/software/README.md @@ -179,12 +179,12 @@ The following are performed on the Raspberry Pi: ### Arduino CLI - Compilation and Upload -To manually compile and upload the test sketch `blink` to an Arduino Nano (ATMega328P aka `m328p`): -1. `arduino-cli compile -b arduino:avr:nano blink -e` +To manually compile and upload the test sketch `./tests/blink.ino` to an Arduino Nano (ATMega328P aka `m328p`): +1. `arduino-cli compile -b arduino:avr:nano tests/blink -e` (`-b`: board FQBN, `-e`: export binary to sketch folder) -2. `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v -U flash:w:blink/build/arduino.avr.nano/blink.ino.hex:i` +2. `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v -U flash:w:tests/blink/build/arduino.avr.nano/blink.ino.hex:i` (`-p`: microprocessor ID, `-C`: custom configuration file path, `-c`: programmer ID, `-v`: verify sketch after upload, `-U`: upload options and path to binary) diff --git a/software/index.ts b/software/index.ts index b2d66909..51c8a8a2 100644 --- a/software/index.ts +++ b/software/index.ts @@ -5,7 +5,7 @@ import { initializeApp, getApps } from 'firebase/app'; import { getAuth } from 'firebase/auth'; // import PiCamera from 'pi-camera'; -import PeaPodArduinoInterface, { IPeaPodArduino } from './lib/PeaPodArduino'; +import PeaPodArduinoInterface, { IPeaPodArduino, ArduinoInstructions } from './lib/PeaPodArduino'; import PeaPodPubSub, { IoTConfig, IPeaPodPublisher, PeaPodDataBatch } from './lib/PeaPodPublisher'; import { ArduinoSimulator, PeaPodLogger } from './lib/PeaPodSimulator'; // import PeaPodCamera, { IPeaPodCamera } from './lib/PeaPodCamera'; @@ -73,7 +73,7 @@ function main(): Promise { let arduino : IPeaPodArduino, publisher : IPeaPodPublisher; // let camera: IPeaPodCamera | null; - Spinner.info(`Running in ${ chalk.bold(simulated ? 'Simulated' : 'Live') } mode with ${ chalk.bold(offline ? 'Local Filesystem' : 'Google Cloud') } publishing.`); + Spinner.log(`Running in ${ chalk.bold(simulated ? 'Simulated' : 'Live') } mode with ${ chalk.bold(offline ? 'Local Filesystem' : 'Google Cloud') } publishing.`); return new Promise(async (res, rej) => { @@ -95,7 +95,7 @@ function main(): Promise { } else { let serialpath; if (process.env.SERIALPORT) { - Spinner.info('Using serial port: '+process.env.SERIALPORT); + Spinner.log('Using serial port: '+process.env.SERIALPORT); serialpath = process.env.SERIALPORT; } else { Spinner.start('Finding Arduino serial port...'); @@ -137,18 +137,20 @@ function main(): Promise { batch[msg.data.label].batch.push({ timestamp: Date.now(), value: msg.data.value - }) + }); // TODO: Plan, act } else { - Spinner.info(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) + console.log(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) } // TODO: publish other message types }).then(() => { - // Initialize publisher, fetch initial instructions + // Initialize publisher publisher.start(config => { + // Hot-swap programs console.log("[CONFIG] - "+config); - // TODO: Respond to config (update instructions) + + // TODO: Handle this }, command => { console.log("[COMMAND] - "+command); // TODO: Respond to commands (immediate actions) @@ -171,17 +173,23 @@ function main(): Promise { } }).then(({projectid, projectname, run}) => { - // Get program + // TODO: Get program + let initialinstructions = {} + + // START + Spinner.log(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); + + arduino.write(initialinstructions); + // Reset + batch = { }; - - Spinner.info(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); batchInterval = setInterval(() => { // Publish entire batch try { publisher.publish({ type: 'data', metadata: { - owner: (getAuth().currentUser?.uid ?? 'user'), + owner: (getApps().length ? (getAuth().currentUser?.uid ?? 'user') : 'user'), project: projectid, run }, diff --git a/software/lib/PeaPodArduino.ts b/software/lib/PeaPodArduino.ts index 135a1f12..5bac17ff 100644 --- a/software/lib/PeaPodArduino.ts +++ b/software/lib/PeaPodArduino.ts @@ -1,54 +1,64 @@ -import { PeaPodMessage } from './PeaPodPublisher'; -import { SerialPort, ReadlineParser } from 'serialport'; -import { ArduinoInstructionsError, SerialTimeoutError } from './errors'; +// import { PeaPodMessage } from './PeaPodPublisher'; +import chalk from 'chalk'; +import { ReadlineParser, SerialPort } from 'serialport'; +import { ArduinoInstructionsError } from './errors'; import Spinner from './ui'; import { sleep, updateArduino } from './utils'; -import chalk from 'chalk'; const BAUDRATE = 115200; -const ARDUINO_REVISION = "1"; -const TIMEOUT_SECONDS = 5; +const ARDUINO_REVISION = 0; +// const TIMEOUT_SECONDS = 5; /** -* Abstract base type for any PeaPod message source. -*/ + * Abstract base type for any PeaPod message source. + */ export type IPeaPodArduino = { /** - * Establish communications with the Arduino. - * @param onMessage Pipe recieved messages. - */ + * Establish communications with the Arduino. + * @param onMessage Pipe recieved messages. + */ start(onMessage : (msg : ArduinoMessage) => void): Promise + /** - * Halt communications. - */ + * Write new instructions to the Arduino. + * @param instructions + */ + write(instructions: ArduinoInstructions): void; + + /** + * Halt communications with the Arduino. + */ stop(): void; }; /** -* Expanded message type to include all types of messages from the Arduino -*/ + * Expanded message type to include all types of messages from the Arduino + */ export type ArduinoMessage = { - type: 'revision' | 'info' | 'debug' | 'error', - data: any + type: 'info' | 'debug' | 'error', + data: string } | { type: 'data', data: { label: string, value: number } +} | { + type: 'revision', + data: number } -type ArduinoInstructions = { +export type ArduinoInstructions = { [key: string]: number; } /** -* Interface between this computer and the Arduino. -*/ + * Interface between this computer and the Arduino. + */ export default class PeaPodArduinoInterface implements IPeaPodArduino { serial : SerialPort; parser : ReadlineParser; - constructor(readonly serialpath: string, private initialInstructions: ArduinoInstructions = {}) { + constructor(readonly serialpath: string) { // Create the serial port interface this.serial = new SerialPort({ path: serialpath, @@ -62,59 +72,78 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { includeDelimiter: false })); } - async start(onMessage : (msg : ArduinoMessage) => void): Promise { - // Open the serial port - this.serial.open(err => { if(err){ throw err }; }); + start(onMessage : (msg : ArduinoMessage) => void): Promise { + return new Promise(async (res, rej) => { + // Open the serial port + await new Promise((reso, reje) => { + Spinner.start('Establishing serial communications with the Arduino...'); + this.serial.open(err => { + if (err) { + reje(err); + } else { + Spinner.succeed('Arduino serial communications established!'); + reso(); + } + }); + }); - // Reusable timeout, enabled at serial open (fails if no messages recieved after starting) - let timeout: NodeJS.Timeout; - const resetTimeout = (timeoutSeconds: number = TIMEOUT_SECONDS) => { - clearTimeout(timeout); - timeout = setTimeout(() => { - this.stop(); - throw new SerialTimeoutError(timeoutSeconds) - }, timeoutSeconds*1000); - } - // Initial timeout - resetTimeout(); - - // Set up the listener - this.parser.on('data', msgtxt => { - clearTimeout(timeout); - try { - // Attempt to parse the raw text as a valid JSON object - const msg = JSON.parse(msgtxt) as ArduinoMessage; + Spinner.start('Awaiting Arduino software revision number...'); + + // Reusable timeout, enabled at serial open (fails if no messages recieved after starting) + // let timeout: NodeJS.Timeout; + // const resetTimeout = (timeoutSeconds: number = TIMEOUT_SECONDS) => { + // clearTimeout(timeout); + // timeout = setTimeout(() => { + // this.stop(); + // throw new SerialTimeoutError(timeoutSeconds) + // }, timeoutSeconds*1000); + // } + // Initial timeout + // resetTimeout(); + + // Set up the listener + this.parser.on('data', msgtxt => { + // Attempt to parse the raw text as a valid JSON object' + var msg : ArduinoMessage; + try { + msg = JSON.parse(msgtxt) as ArduinoMessage; + } catch (err) { + rej(err); + return; + } // Handle all message types except: 'info', 'data', 'debug', 'error' switch (msg.type) { case 'revision': onMessage(msg); - if((msg.data as typeof ARDUINO_REVISION) == ARDUINO_REVISION) { - this.write(this.initialInstructions); + if(msg.data == ARDUINO_REVISION) { + Spinner.succeed('Arduino software up to date!'); + res(); } else { - Spinner.fail(`Arduino revision check failed! Expected ${ARDUINO_REVISION}, recieved ${msg.data}`); + Spinner.fail(`Arduino software out of date! Expected ${ARDUINO_REVISION}.`); // Attempt to update the Arduino, and then restart this.stop(); - clearTimeout(timeout); + // clearTimeout(timeout); this.update().finally(() => { process.exit(); }); + // Do not reset timeout + return; } break; default: onMessage(msg); break; } - } catch (err) { - throw err; - } + // resetTimeout(); + }); }); } async update() { - Spinner.start('Compiling Arduino software and flashing to board...'); - await updateArduino().catch(e => { - Spinner.fail(e); - }).then(() => { + Spinner.start('Compiling latest Arduino software and flashing to board...'); + await updateArduino().then(() => { Spinner.succeed('Updated Arduino software successfully! Rebooting in 3 seconds...'); + }).catch(e => { + Spinner.fail(e); }) await sleep(3000); } diff --git a/software/lib/PeaPodSimulator.ts b/software/lib/PeaPodSimulator.ts index 8a65da81..8dd53487 100644 --- a/software/lib/PeaPodSimulator.ts +++ b/software/lib/PeaPodSimulator.ts @@ -45,6 +45,7 @@ export class ArduinoSimulator implements IPeaPodArduino { }, this.parameters[label as TDataLabels].interval)); } } + write() {} } export class PeaPodLogger implements IPeaPodPublisher { diff --git a/software/lib/ui.ts b/software/lib/ui.ts index 4f57abe4..fd26d793 100644 --- a/software/lib/ui.ts +++ b/software/lib/ui.ts @@ -75,7 +75,7 @@ namespace Spinner { } /** - * Set the loading spinner to info (blue `i`) + * Complete the loading spinner with info (blue `i`) * @param text Text to display. */ export const info = (text: string = "") => { @@ -87,6 +87,23 @@ namespace Spinner { Spinner = ora({text}).info(); } } + + /** + * If spinning: stop and clear the current spinner, log some text, then restart the spinner + * Else: Just log + * + * @param text Text to log + */ + export const log = (text: string) => { + let oldtext = Spinner.text; + if(Spinner.isSpinning) { + Spinner.stop(); + console.log(text); + Spinner.start(oldtext); + } else { + ora({text}).info(); + } + } } export default Spinner; \ No newline at end of file diff --git a/software/lib/utils.ts b/software/lib/utils.ts index 97e90815..66f56bde 100644 --- a/software/lib/utils.ts +++ b/software/lib/utils.ts @@ -100,24 +100,26 @@ export const checkArduino = async (processor: string = 'm328p') => { }); } -export const updateArduino = async (sketch: string = 'PeaPodOS-Arduino', fqbn: string = 'arduino:avr:nano', processor: string = 'm328p'): Promise => { - // Create log folder - if (!existsSync('logs/')) { - mkdirSync('logs/', { recursive: true }); - } - execute(`arduino-cli compile -b ${fqbn} ${sketch} -e`, [1]) - .catch(err => { - writeFileSync('logs/arduinoCompile.log', err); - throw new Error(`Failed to compile '${sketch}'. See logs/arduinoCompile.log`); - }).then(log1 => { - writeFileSync('logs/arduinoCompile.log', log1); - // execute('arduino-cli', ['upload', '-p', serialport, '-b', fqbn, script], { sudo: true, failureCodes: [1] }) - execute(`sudo avrdude -p ${processor} -C ${process.env.HOME}/avrdude_gpio.conf -c peapod -v -U flash:w:${sketch}/build/${fqbn.replace(/:/g, '.')}/${sketch}.ino.hex:i`, [1]) - .catch(err => { - writeFileSync('logs/arduinoUpload.log', err); - throw new Error(`Failed to flash the '${sketch}' binary to the Arduino. See logs/arduinoUpload.log`); - }).then(log2 => { - writeFileSync('logs/arduinoUpload.log', log2); +export const updateArduino = (sketch: string = 'PeaPodOS-Arduino', fqbn: string = 'arduino:avr:nano', processor: string = 'm328p'): Promise => { + return new Promise((res, rej) => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute(`arduino-cli compile -b ${fqbn} ${sketch} -e`, [1]) + .then(log1 => { + if (log1) writeFileSync('logs/arduinoCompile.log', log1); + // execute('arduino-cli', ['upload', '-p', serialport, '-b', fqbn, script], { sudo: true, failureCodes: [1] }) + execute(`sudo avrdude -p ${processor} -C ${process.env.HOME}/avrdude_gpio.conf -c peapod -v -U flash:w:${sketch}/build/${fqbn.replace(/:/g, '.')}/${sketch}.ino.hex:i`, [1]) + .then(log2 => { + writeFileSync('logs/arduinoUpload.log', log2); + }).catch(err => { + writeFileSync('logs/arduinoUpload.log', err); + rej(new Error(`Failed to flash the '${sketch}' binary to the Arduino. See logs/arduinoUpload.log`)); + }); + }).catch(err => { + writeFileSync('logs/arduinoCompile.log', err); + rej(new Error(`Failed to compile '${sketch}'. See logs/arduinoCompile.log`)); }); }); } diff --git a/software/package-lock.json b/software/package-lock.json index b876d6f5..9ea4995c 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.8", "license": "MIT", "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.8", "@peapodtech/firebasedeviceflow": "^0.2.0", "@types/yargs": "^16.0.0", "chalk": "^4.1.0", @@ -533,6 +534,39 @@ "node": ">=6" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@peapodtech/firebasedeviceflow": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@peapodtech/firebasedeviceflow/-/firebasedeviceflow-0.2.0.tgz", @@ -868,6 +902,22 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -904,6 +954,23 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/axios": { "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", @@ -1008,6 +1075,14 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -1072,6 +1147,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/commist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", @@ -1100,6 +1183,11 @@ "typedarray": "^0.0.6" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, "node_modules/core-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -1139,6 +1227,22 @@ "clone": "^1.0.2" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -1300,11 +1404,41 @@ } } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1340,6 +1474,11 @@ "node": ">=8" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "node_modules/help-me": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", @@ -1354,6 +1493,18 @@ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1622,6 +1773,39 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -1646,6 +1830,40 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mqtt": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", @@ -1721,6 +1939,39 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2023,6 +2274,11 @@ "url": "https://opencollective.com/serialport/donate" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "node_modules/set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -2092,6 +2348,22 @@ "node": ">=8" } }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -2207,6 +2479,14 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -2282,6 +2562,11 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -2722,6 +3007,32 @@ "yargs": "^16.1.1" } }, + "@mapbox/node-pre-gyp": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "@peapodtech/firebasedeviceflow": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@peapodtech/firebasedeviceflow/-/firebasedeviceflow-0.2.0.tgz", @@ -2971,6 +3282,19 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -2992,6 +3316,20 @@ "color-convert": "^2.0.1" } }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, "axios": { "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", @@ -3062,6 +3400,11 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -3108,6 +3451,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, "commist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", @@ -3133,6 +3481,11 @@ "typedarray": "^0.0.6" } }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, "core-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -3159,6 +3512,16 @@ "clone": "^1.0.2" } }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -3287,11 +3650,35 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3315,6 +3702,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "help-me": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", @@ -3329,6 +3721,15 @@ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3556,6 +3957,29 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3574,6 +3998,28 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, "mqtt": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", @@ -3633,6 +4079,30 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3853,6 +4323,11 @@ } } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -3910,6 +4385,19 @@ "has-flag": "^4.0.0" } }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4000,6 +4488,14 @@ "webidl-conversions": "^3.0.0" } }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -4040,6 +4536,11 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/software/package.json b/software/package.json index fb228f08..38bbc2c1 100644 --- a/software/package.json +++ b/software/package.json @@ -29,6 +29,7 @@ }, "homepage": "https://github.com/PeaPodTechnologies/PeaPod#readme", "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.8", "@peapodtech/firebasedeviceflow": "^0.2.0", "@types/yargs": "^16.0.0", "chalk": "^4.1.0", diff --git a/software/tests/serialMonitor.py b/software/tests/serialMonitor.py new file mode 100644 index 00000000..9ba45f1a --- /dev/null +++ b/software/tests/serialMonitor.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import serial +port = "/dev/ttyS0" +try: + serialPort = serial.Serial(port, 115200) + print ("Serial port '", port, "' open:", sep='') + while True: + if serialPort.in_waiting > 0: + print(serialPort.read(serialPort.in_waiting).decode('unicode_escape'), end='') +except IOError: + print ("Error on ", port,"\n") + serialPort.close() \ No newline at end of file From 00344f04b91bf6552685637ce7866fd458e111f2 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 3 Mar 2022 23:38:14 -0500 Subject: [PATCH 05/85] Minor Changes Arduino accepts empty instruction set Fixed spinner log ui util Better C++ styling (header order, file naming, #include protection naming) --- software/.npmignore | 13 +++++++++++-- software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino | 11 +++++++---- software/PeaPodOS-Arduino/src/Actuator.cpp | 7 +++++-- software/PeaPodOS-Arduino/src/Actuator.h | 6 +++--- software/PeaPodOS-Arduino/src/Base.h | 6 ++---- software/PeaPodOS-Arduino/src/Sensor.cpp | 13 +++++++++---- software/PeaPodOS-Arduino/src/Sensor.h | 8 +++++--- software/PeaPodOS-Arduino/src/actuators/LED.h | 9 +++++---- .../src/actuators/{LED.cpp => led.cc} | 8 +++++--- software/PeaPodOS-Arduino/src/sensors/K30.cpp | 10 ++++++---- software/PeaPodOS-Arduino/src/sensors/K30.h | 10 ++++++---- software/PeaPodOS-Arduino/src/sensors/SHT31.cpp | 8 +++++--- software/PeaPodOS-Arduino/src/sensors/SHT31.h | 9 +++++---- software/index.ts | 8 ++++---- software/lib/ui.ts | 2 +- subsystems/lighting/software/tests/LightingTest.ino | 6 +++--- .../lighting/software/tests/Test_SingleWave.ino | 2 +- 17 files changed, 83 insertions(+), 53 deletions(-) rename software/PeaPodOS-Arduino/src/actuators/{LED.cpp => led.cc} (93%) diff --git a/software/.npmignore b/software/.npmignore index 7869a83f..adf88c85 100644 --- a/software/.npmignore +++ b/software/.npmignore @@ -1,22 +1,31 @@ -node_modules/ -tsconfig.json +# Basically just include .js files ("binary" package) +# Build files **/build/ *.ts !*.d.ts +tsconfig.json +# Aux files .git/ **/.gitignore .gitattributes .npmignore package-lock.json +# Unpublishables .env .env.template *.pem deviceInfo.json projects/ +# Test Scripts +*.test.js +*.test.js.map +*.test.d.ts +*.test.ts + # Arduino Binaries *.elf *.hex diff --git a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino index c148adc4..5c376950 100644 --- a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino +++ b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino @@ -3,7 +3,7 @@ #include "./src/sensors/SHT31.h" // #include "./src/sensors/K30.h" // #include "./src/Actuator.h" -// #include "./src/actuators/LED.h" +// #include "./src/actuators/led.h" // CONSTANTS #define NUM_SENSORS 1 @@ -94,7 +94,7 @@ void loop(void) { // handleInstructions(in); // } - for (int i = 0; i < NUM_SENSORS; i++) { + for (int i = 0; i < NUM_SENSORS; ++i) { SensorState* state = sensors[i]->update(); #if DEBUG @@ -109,7 +109,7 @@ void loop(void) { if (state->error == ERR_NONE) { if (state->debug == DS_SUCCESS) { - for (int j = 0; j < state->numdata; j++) { + for (int j = 0; j < state->numdata; ++j) { Serial.print(F("{\"type\":\"data\",\"data\":{\"label\":\"")); Serial.print(state->data[j].label); Serial.print(F("\",\"value\":")); @@ -132,7 +132,7 @@ void loop(void) { bool post(void) { bool success = true; - for (int i = 0; i < NUM_SENSORS; i++) { + for (int i = 0; i < NUM_SENSORS; ++i) { SensorState* state = sensors[i]->begin(); bool latest = (state->debug == DS_INITIALIZED && state->error == ERR_NONE); if (latest) { @@ -156,6 +156,9 @@ bool post(void) { * @return were all instructions handled in a valid way? */ bool handleInstructions(String ins) { + if(ins.equals("{}")){ + return true; + } if (ins.charAt(0) != '{' || ins.charAt(ins.length()-1) != '}' || ins.indexOf(':') == -1) { Serial.print("{\"type\":\"error\",\"data\":\"Invalid instructions dictionary '"); Serial.print(ins); diff --git a/software/PeaPodOS-Arduino/src/Actuator.cpp b/software/PeaPodOS-Arduino/src/Actuator.cpp index 8337cbd6..82ab4da7 100644 --- a/software/PeaPodOS-Arduino/src/Actuator.cpp +++ b/software/PeaPodOS-Arduino/src/Actuator.cpp @@ -1,5 +1,8 @@ -#include "Base.h" -#include "Actuator.h" +#include "actuator.h" + +#include "Arduino.h" + +#include "base.h" Actuator::Actuator(actuatorid_t actuatorid, float failtarget) { this->actuatorid = actuatorid; diff --git a/software/PeaPodOS-Arduino/src/Actuator.h b/software/PeaPodOS-Arduino/src/Actuator.h index ccaa9d6a..b17b0ff6 100644 --- a/software/PeaPodOS-Arduino/src/Actuator.h +++ b/software/PeaPodOS-Arduino/src/Actuator.h @@ -1,7 +1,7 @@ -#ifndef ACTUATOR_H -#define ACTUATOR_H +#ifndef PEAPOD_ACTUATOR_H_ +#define PEAPOD_ACTUATOR_H_ -#include "Base.h" +#include "base.h" // Actuator identifiers typedef enum actuatorid_t { diff --git a/software/PeaPodOS-Arduino/src/Base.h b/software/PeaPodOS-Arduino/src/Base.h index 2df16495..ed9d1b44 100644 --- a/software/PeaPodOS-Arduino/src/Base.h +++ b/software/PeaPodOS-Arduino/src/Base.h @@ -1,7 +1,5 @@ -#ifndef BASE_H -#define BASE_H - -#include "Arduino.h" +#ifndef PEAPOD_BASE_H_ +#define PEAPOD_BASE_H_ // Degree of error occurred typedef enum errorlevel_t { diff --git a/software/PeaPodOS-Arduino/src/Sensor.cpp b/software/PeaPodOS-Arduino/src/Sensor.cpp index 74b93ef8..51ca5f21 100644 --- a/software/PeaPodOS-Arduino/src/Sensor.cpp +++ b/software/PeaPodOS-Arduino/src/Sensor.cpp @@ -1,5 +1,10 @@ -#include "Base.h" -#include "Sensor.h" +#include "sensor.h" + +#include + +#include "Arduino.h" + +#include "base.h" Sensor::Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t delta) { this->sensorid = sensorid; @@ -12,7 +17,7 @@ Sensor::Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t del // Allocate data state state.data = (t_datapoint*)malloc(sizeof(t_datapoint)*(setup->numdata)); state.numdata = setup->numdata; - for (int i = 0; i < setup->numdata; i++) { + for (int i = 0; i < setup->numdata; ++i) { // Since setup->labels[i] is a const char*, we can just reassign our pointer state.data[i].label = setup->labels[i]; } @@ -52,7 +57,7 @@ SensorState* Sensor::update(void) { state.timestamp = lastread; // Copy from buffer to state data - for (int i = 0; i < state.numdata; i++) { + for (int i = 0; i < state.numdata; ++i) { state.data[i].value = buffer[i]; } break; diff --git a/software/PeaPodOS-Arduino/src/Sensor.h b/software/PeaPodOS-Arduino/src/Sensor.h index 45496533..4e99346e 100644 --- a/software/PeaPodOS-Arduino/src/Sensor.h +++ b/software/PeaPodOS-Arduino/src/Sensor.h @@ -1,7 +1,9 @@ -#ifndef SENSOR_H -#define SENSOR_H +#ifndef PEAPOD_SENSOR_H_ +#define PEAPOD_SENSOR_H_ -#include "Base.h" +#include "Arduino.h" + +#include "base.h" // Sensor identifiers typedef enum sensorid_t { diff --git a/software/PeaPodOS-Arduino/src/actuators/LED.h b/software/PeaPodOS-Arduino/src/actuators/LED.h index a002a9ac..9c52e8ec 100644 --- a/software/PeaPodOS-Arduino/src/actuators/LED.h +++ b/software/PeaPodOS-Arduino/src/actuators/LED.h @@ -1,9 +1,10 @@ -#ifndef LED_H -#define LED_H +#ifndef PEAPOD_ACTUATORS_LED_H_ +#define PEAPOD_ACTUATORS_LED_H_ #include "Arduino.h" -#include "../Base.h" -#include "../Actuator.h" + +#include "../base.h" +#include "../actuator.h" typedef enum { LEDCOLOR_BLUE, diff --git a/software/PeaPodOS-Arduino/src/actuators/LED.cpp b/software/PeaPodOS-Arduino/src/actuators/led.cc similarity index 93% rename from software/PeaPodOS-Arduino/src/actuators/LED.cpp rename to software/PeaPodOS-Arduino/src/actuators/led.cc index 3253741a..eeedfe0b 100644 --- a/software/PeaPodOS-Arduino/src/actuators/LED.cpp +++ b/software/PeaPodOS-Arduino/src/actuators/led.cc @@ -1,7 +1,9 @@ +#include "led.h" + #include "Arduino.h" -#include "../Base.h" -#include "../Actuator.h" -#include "LED.h" + +#include "../base.h" +#include "../actuator.h" LED::LED(uint8_t pin, led_color_t color) : Actuator(ACTUATOR_LED, 0) { this->pin = pin; diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.cpp b/software/PeaPodOS-Arduino/src/sensors/K30.cpp index b24ef9e8..1a00637b 100644 --- a/software/PeaPodOS-Arduino/src/sensors/K30.cpp +++ b/software/PeaPodOS-Arduino/src/sensors/K30.cpp @@ -1,8 +1,10 @@ +#include "k30.h" + #include "Arduino.h" -#include "../Base.h" -#include "../Sensor.h" #include "Wire.h" -#include "K30.h" + +#include "../base.h" +#include "../sensor.h" // Data setup static const char* labels[1] = { "co2_ppm" }; @@ -38,7 +40,7 @@ errorlevel_t K30::read(float* data, uint8_t numdata) { delay(20); // Store reading - for(int i = 0; wire->available() && i < 4; i++) { + for(int i = 0; wire->available() && i < 4; ++i) { recValue[i] = wire->read(); } diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.h b/software/PeaPodOS-Arduino/src/sensors/K30.h index 836e7a7f..b285477d 100644 --- a/software/PeaPodOS-Arduino/src/sensors/K30.h +++ b/software/PeaPodOS-Arduino/src/sensors/K30.h @@ -1,10 +1,12 @@ -#ifndef K30_H -#define K30_H +#ifndef PEAPOD_SENSORS_K30_H_ +#define PEAPOD_SENSORS_K30_H_ -#include "../Base.h" -#include "../Sensor.h" +#include "Arduino.h" #include "Wire.h" +#include "../base.h" +#include "../sensor.h" + #define K30_DEFAULT_ADDR 0x68 #define K30_DELTA 2000 diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp b/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp index 3399a5cb..d546348d 100644 --- a/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp +++ b/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp @@ -1,8 +1,10 @@ +#include "sht31.h" + #include "Arduino.h" -#include "../Base.h" -#include "../Sensor.h" #include "Wire.h" -#include "SHT31.h" + +#include "../base.h" +#include "../sensor.h" static uint8_t crc8(const uint8_t *data, int len); diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.h b/software/PeaPodOS-Arduino/src/sensors/SHT31.h index e93bc042..cfb68070 100644 --- a/software/PeaPodOS-Arduino/src/sensors/SHT31.h +++ b/software/PeaPodOS-Arduino/src/sensors/SHT31.h @@ -1,11 +1,12 @@ -#ifndef SHT31_H -#define SHT31_H +#ifndef PEAPOD_SENSORS_SHT31_H_ +#define PEAPOD_SENSORS_SHT31_H_ #include "Arduino.h" -#include "../Base.h" -#include "../Sensor.h" #include "Wire.h" +#include "../base.h" +#include "../sensor.h" + #define SHT31_DEFAULT_ADDR 0x44 // SHT31 Default Address #define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Measurement High Repeatability with Clock Stretch Enabled #define SHT31_MEAS_MEDREP_STRETCH 0x2C0D // Measurement Medium Repeatability with Clock Stretch Enabled diff --git a/software/index.ts b/software/index.ts index 51c8a8a2..985f9759 100644 --- a/software/index.ts +++ b/software/index.ts @@ -141,18 +141,18 @@ function main(): Promise { // TODO: Plan, act } else { - console.log(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) + Spinner.log(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) } // TODO: publish other message types }).then(() => { // Initialize publisher publisher.start(config => { // Hot-swap programs - console.log("[CONFIG] - "+config); + Spinner.log("[CONFIG] - "+config); // TODO: Handle this }, command => { - console.log("[COMMAND] - "+command); + Spinner.log("[COMMAND] - "+command); // TODO: Respond to commands (immediate actions) if (command.type == 'livestreamoffer') { @@ -200,7 +200,7 @@ function main(): Promise { return; } - console.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.batch.length }, 0) } datapoints published.`); + Spinner.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.batch.length }, 0) } datapoints published.`); // Reset batch to empty batch = { }; diff --git a/software/lib/ui.ts b/software/lib/ui.ts index fd26d793..f910724f 100644 --- a/software/lib/ui.ts +++ b/software/lib/ui.ts @@ -101,7 +101,7 @@ namespace Spinner { console.log(text); Spinner.start(oldtext); } else { - ora({text}).info(); + console.log(text); } } } diff --git a/subsystems/lighting/software/tests/LightingTest.ino b/subsystems/lighting/software/tests/LightingTest.ino index 9948be87..c9a296c3 100644 --- a/subsystems/lighting/software/tests/LightingTest.ino +++ b/subsystems/lighting/software/tests/LightingTest.ino @@ -1,4 +1,4 @@ -#include "LED.h" +#include "led.h" #include "Actuator.h" #define NUM_ACTUATORS 5 @@ -27,7 +27,7 @@ void setup(){ while(!Serial); bool success = true; - for(int i = 0; i < NUM_ACTUATORS; i++){ + for(int i = 0; i < NUM_ACTUATORS; ++i){ bool latest = actuators[i]->begin(); success &= latest; if(!latest){ @@ -73,7 +73,7 @@ bool handleInstruction(String in){ float value = in.substring(split+1).toFloat(); //INSTRUCTION HANDLING IF BLOCKS - EACH RETURNS TRUE - for(int i = 0; i < NUM_ACTUATORS; i++){ + for(int i = 0; i < NUM_ACTUATORS; ++i){ if(var.equals(actuators[i]->evname)){ Serial.print("{\"type\":\"debug\",\"msg\":\"Actuator '"); Serial.print(actuators[i]->name); diff --git a/subsystems/lighting/software/tests/Test_SingleWave.ino b/subsystems/lighting/software/tests/Test_SingleWave.ino index b58407e9..c8401c36 100644 --- a/subsystems/lighting/software/tests/Test_SingleWave.ino +++ b/subsystems/lighting/software/tests/Test_SingleWave.ino @@ -1,6 +1,6 @@ #include "Arduino.h" #include "Actuator.h" -#include "LED.h" +#include "led.h" #define LED_PIN 3 #define STEP 0.1 From 55b56914c22caf11ad3702cdd0e0d6c9c716741b Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 3 Mar 2022 23:41:36 -0500 Subject: [PATCH 06/85] File Cases --- software/PeaPodOS-Arduino/src/{Actuator.cpp => actuator.cc} | 0 software/PeaPodOS-Arduino/src/{Actuator.h => actuator.h} | 0 software/PeaPodOS-Arduino/src/actuators/{LED.h => led.h} | 0 software/PeaPodOS-Arduino/src/{Base.h => base.h} | 0 software/PeaPodOS-Arduino/src/{Sensor.cpp => sensor.cc} | 0 software/PeaPodOS-Arduino/src/{Sensor.h => sensor.h} | 0 software/PeaPodOS-Arduino/src/sensors/{K30.cpp => k30.cc} | 0 software/PeaPodOS-Arduino/src/sensors/{K30.h => k30.h} | 0 software/PeaPodOS-Arduino/src/sensors/{SHT31.cpp => sht31.cc} | 0 software/PeaPodOS-Arduino/src/sensors/{SHT31.h => sht31.h} | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename software/PeaPodOS-Arduino/src/{Actuator.cpp => actuator.cc} (100%) rename software/PeaPodOS-Arduino/src/{Actuator.h => actuator.h} (100%) rename software/PeaPodOS-Arduino/src/actuators/{LED.h => led.h} (100%) rename software/PeaPodOS-Arduino/src/{Base.h => base.h} (100%) rename software/PeaPodOS-Arduino/src/{Sensor.cpp => sensor.cc} (100%) rename software/PeaPodOS-Arduino/src/{Sensor.h => sensor.h} (100%) rename software/PeaPodOS-Arduino/src/sensors/{K30.cpp => k30.cc} (100%) rename software/PeaPodOS-Arduino/src/sensors/{K30.h => k30.h} (100%) rename software/PeaPodOS-Arduino/src/sensors/{SHT31.cpp => sht31.cc} (100%) rename software/PeaPodOS-Arduino/src/sensors/{SHT31.h => sht31.h} (100%) diff --git a/software/PeaPodOS-Arduino/src/Actuator.cpp b/software/PeaPodOS-Arduino/src/actuator.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/Actuator.cpp rename to software/PeaPodOS-Arduino/src/actuator.cc diff --git a/software/PeaPodOS-Arduino/src/Actuator.h b/software/PeaPodOS-Arduino/src/actuator.h similarity index 100% rename from software/PeaPodOS-Arduino/src/Actuator.h rename to software/PeaPodOS-Arduino/src/actuator.h diff --git a/software/PeaPodOS-Arduino/src/actuators/LED.h b/software/PeaPodOS-Arduino/src/actuators/led.h similarity index 100% rename from software/PeaPodOS-Arduino/src/actuators/LED.h rename to software/PeaPodOS-Arduino/src/actuators/led.h diff --git a/software/PeaPodOS-Arduino/src/Base.h b/software/PeaPodOS-Arduino/src/base.h similarity index 100% rename from software/PeaPodOS-Arduino/src/Base.h rename to software/PeaPodOS-Arduino/src/base.h diff --git a/software/PeaPodOS-Arduino/src/Sensor.cpp b/software/PeaPodOS-Arduino/src/sensor.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/Sensor.cpp rename to software/PeaPodOS-Arduino/src/sensor.cc diff --git a/software/PeaPodOS-Arduino/src/Sensor.h b/software/PeaPodOS-Arduino/src/sensor.h similarity index 100% rename from software/PeaPodOS-Arduino/src/Sensor.h rename to software/PeaPodOS-Arduino/src/sensor.h diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.cpp b/software/PeaPodOS-Arduino/src/sensors/k30.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/K30.cpp rename to software/PeaPodOS-Arduino/src/sensors/k30.cc diff --git a/software/PeaPodOS-Arduino/src/sensors/K30.h b/software/PeaPodOS-Arduino/src/sensors/k30.h similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/K30.h rename to software/PeaPodOS-Arduino/src/sensors/k30.h diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.cpp b/software/PeaPodOS-Arduino/src/sensors/sht31.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/SHT31.cpp rename to software/PeaPodOS-Arduino/src/sensors/sht31.cc diff --git a/software/PeaPodOS-Arduino/src/sensors/SHT31.h b/software/PeaPodOS-Arduino/src/sensors/sht31.h similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/SHT31.h rename to software/PeaPodOS-Arduino/src/sensors/sht31.h From 8688ea458a82c164a9c2eda377380e0844df0ae5 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 3 Mar 2022 23:44:48 -0500 Subject: [PATCH 07/85] Package v0.0.9 --- software/.npmignore | 8 +++++++- software/package.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/software/.npmignore b/software/.npmignore index adf88c85..808c14a9 100644 --- a/software/.npmignore +++ b/software/.npmignore @@ -2,6 +2,8 @@ # Build files **/build/ + +# TypeScript files (keep type files) *.ts !*.d.ts tsconfig.json @@ -21,6 +23,7 @@ deviceInfo.json projects/ # Test Scripts +tests/ *.test.js *.test.js.map *.test.d.ts @@ -30,4 +33,7 @@ projects/ *.elf *.hex *.eep -*.bin \ No newline at end of file +*.bin + +# Extras +assets/ \ No newline at end of file diff --git a/software/package.json b/software/package.json index 38bbc2c1..ed0ee186 100644 --- a/software/package.json +++ b/software/package.json @@ -1,6 +1,6 @@ { "name": "@peapodtech/peapodos", - "version": "0.0.8", + "version": "0.0.9", "description": "A cloud-enabled IoT automated plant growth environment. Designed as a research tool for precise and distributed mapping of the plant-environment relationship.", "main": "index.js", "directories": { From ef8b0a290de3f98ac7859100439c6469430a6b35 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 3 Mar 2022 23:59:25 -0500 Subject: [PATCH 08/85] Delete .message --- .message | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .message diff --git a/.message b/.message deleted file mode 100644 index 06ea587e..00000000 --- a/.message +++ /dev/null @@ -1,4 +0,0 @@ - -Fixed Arduino Serial vs Post ordering -Added logging function to Spinner (pauses and restarts spinner) -Fixed batch publishing in local logging mode From 9203810df1bee5afa34142545f9a611e2f45d29f Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 4 Mar 2022 22:11:40 -0500 Subject: [PATCH 09/85] Minor Fixes --- README.md | 2 +- software/.npmignore | 5 +---- software/README.md | 28 +++++++++++++++------------- software/package.json | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 35e2c07a..f77c247d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Designed as both a hassle-free food production system and a research tool for pr Submitted to the [NASA/CSA Deep Space Food Challenge](https://impact.canada.ca/en/challenges/deep-space-food-challenge). _Update_: PeaPod has won Phase 1 and moved on to prototyping for Phase 2! - + *** diff --git a/software/.npmignore b/software/.npmignore index 808c14a9..c495e863 100644 --- a/software/.npmignore +++ b/software/.npmignore @@ -33,7 +33,4 @@ tests/ *.elf *.hex *.eep -*.bin - -# Extras -assets/ \ No newline at end of file +*.bin \ No newline at end of file diff --git a/software/README.md b/software/README.md index 993cab68..0178139c 100644 --- a/software/README.md +++ b/software/README.md @@ -1,9 +1,11 @@ -# PeaPodOS +# PeaPod OS [![issuesopen](https://img.shields.io/github/issues/PeaPodTechnologies/peapod)](https://github.com/PeaPodTech/PeaPod/issues) [![issuesclosed](https://img.shields.io/github/issues-closed/PeaPodTechnologies/peapod)](https://github.com/PeaPodTech/PeaPod/issues?q=is%3Aissue+is%3Aclosed) [![opensource](https://img.shields.io/badge/open-source-red)](https://github.com/PeaPodTechnologies/PeaPod/issues?q=is%3Aopen+is%3Aissue+label%3A%22Status%3A+Open%22) ![coffee](https://img.shields.io/badge/powered%20by-coffee-brown) [![24/7](https://img.shields.io/badge/Eat,%20Sleep,-PeaPod-darkgreen)](https://www.youtube.com/watch?v=2zWv9JC5G3w) [![FLDSMDFR](https://img.shields.io/badge/The-FLDSMDFR-orange)](https://www.youtube.com/watch?v=k8xFbWLUDoQ) +Main software for PeaPod. + A cloud-connected isolated and automated plant growth environment, able to generate any environment from a combination of independent environment parameters. Designed as both a hassle-free food production system and a research tool for precise and distributed mapping of the plant-environment relationship. @@ -21,7 +23,7 @@ Designed as both a hassle-free food production system and a research tool for pr # Background - + *** @@ -35,11 +37,11 @@ The following are performed on a computer: 2. Download the Raspberry Pi Imager [(Download)](https://www.raspberrypi.com/software/). 3. Flash the SD card with a *Raspberry Pi OS Lite* image. -> Note: In Future, a custom PeaPod image will be released with steps 5, 6, and 10 already complete. +> Note: In Future, a custom PeaPod image will be released with steps 5, 6, and 12 already complete. 4. Plug in a keyboard and display, insert the microSD card, and power the Raspberry Pi device. -The following are performed on the Raspberry Pi: +The following are performed on the Raspberry Pi, with a keyboard and monitor: 5. Login with the default username (`pi`) and password (`raspberry`). 6. Execute `sudo raspi-config` and perform these steps to setup the Pi: @@ -57,13 +59,13 @@ The following are performed on the Raspberry Pi: > You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@peapod.local`, or with VS Code) -> Note: In Future, steps 7-9 will be automated at startup. +> Note: In Future, steps 7-11 will be automated at startup. 7. Update Packages: 1. Update package listings, upgrade existing packages: `sudo apt-get update && sudo apt-get upgrade -y` 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude` (could take a while) 3. Install main software package: `sudo npm i -g @peapodtech/peapodos --save` -9. Install and configure the Arduino CLI: +8. Install and configure the Arduino CLI: 1. Install with `curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh`. Successful output ends with something like: ``` arduino-cli Version: 0.21.0 Commit: 10107d24 Date: 2022-02-08T15:05:43Z installed successfully in /home/pi/bin @@ -74,8 +76,8 @@ The following are performed on the Raspberry Pi: 5. Update the list of cores: `arduino-cli core update-index` 6. Install the AVR cores (incl. Arduino Nano/ATMega328P): `arduino-cli core install arduino:avr` -10. Create a custom configuration file for the AVR flash utility *avrdude* to be able to program the Arduino Nano via ICSP over the Raspberry Pi's GPIO pins: - 3. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/avrdude_gpio.conf`, then modify your copy with `nano ~/avrdude_gpio.conf`. Copy the following to the end of the file: +9. Create a custom configuration file for the AVR flash utility *avrdude* to be able to program the Arduino Nano via ICSP over the Raspberry Pi's GPIO pins: + 1. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/avrdude_gpio.conf`, then modify your copy with `nano ~/avrdude_gpio.conf`. Copy the following to the end of the file: ``` # Raspberry Pi GPIO configuration for avrdude. @@ -90,7 +92,7 @@ The following are performed on the Raspberry Pi: ; ``` (*Ctrl-O* to save, *Ctrl-X* to exit) - 4. Verify the configuration and connection to the Arduino with `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`. A successful output should look something like: + 2. Verify the configuration and connection to the Arduino with `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`. A successful output should look something like: ``` avrdude: Version 6.3-20171130 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ @@ -156,7 +158,7 @@ The following are performed on the Raspberry Pi: avrdude done. Thank you. ``` -11. Edit the `sudoers` file to allow `avrdude` to be executed using `sudo` *without a password*: +10. Edit the `sudoers` file to allow `avrdude` to be executed using `sudo` *without a password*: 1. Open the `sudoers` file: `sudo visudo` 2. Add the following line to the end (assuming your username is `pi`, the hostname is `peapod`, and the `avrdude` binary is located at `/usr/bin/avrdude`): @@ -164,15 +166,15 @@ The following are performed on the Raspberry Pi: (*Ctrl-O* to save, *Ctrl-X* to exit; *avrdude* can be located with `whereis avrdude`) -12. Install the *UV4L* camera library: +11. Install the *UV4L* camera library: 1. `curl https://www.linux-projects.org/listing/uv4l_repo/lpkey.asc | sudo apt-key add -` 2. Add the package listings: `echo "deb https://www.linux-projects.org/listing/uv4l_repo/raspbian/stretch stretch main" | sudo tee /etc/apt/sources.list.d/uv4l.list` 3. Update packages: `sudo apt-get update` 4. Install the core library, the Raspberry Pi driver, extra scripts, adn WebRTC support: `sudo apt-get install uv4l uv4l-raspicam uv4l-raspicam-extras uv4l-webrtc-armv6` -13. Populate a `.env` file with Firebase and Google and/or GitHub auth keys (a template is provided as `.env.template`), as well as the field `SERIALPORT="/dev/ttyS0"` (Raspberry Pi Zero 2 W GPIO mini-UART). +12. Populate a `.env` file with Firebase and Google and/or GitHub auth keys (a template is provided as `.env.template`), as well as the field `SERIALPORT="/dev/ttyS0"` (Raspberry Pi Zero 2 W GPIO mini-UART). -14. Run the main program by executing `peapodos`. +13. Run the main program by executing `peapodos`. # Development diff --git a/software/package.json b/software/package.json index ed0ee186..74336cb2 100644 --- a/software/package.json +++ b/software/package.json @@ -1,7 +1,7 @@ { "name": "@peapodtech/peapodos", "version": "0.0.9", - "description": "A cloud-enabled IoT automated plant growth environment. Designed as a research tool for precise and distributed mapping of the plant-environment relationship.", + "description": "Main software for PeaPod.", "main": "index.js", "directories": { "lib": "lib" From b7f580ab8e8c29d5fbc6701a18b9790a3a601aa4 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Wed, 16 Mar 2022 15:10:48 -0400 Subject: [PATCH 10/85] Migrated to PlatformIO * Migrated to PlatformIO for compilation * Separated JSON messaging, instruction parsing into utility files * Moved globals to header + PIO Testing Suite * Updated README --- software/.npmignore | 9 +- software/PeaPodOS-Arduino/.gitignore | 4 +- .../PeaPodOS-Arduino/PeaPodOS-Arduino.ino | 226 - software/PeaPodOS-Arduino/README.md | 26 - software/PeaPodOS-Arduino/avrdude_gpio.conf | 14392 ++++++++++++++++ software/PeaPodOS-Arduino/platformio.ini | 12 + .../src/{ => actuators}/actuator.cc | 6 +- .../src/{ => actuators}/actuator.h | 7 +- .../PeaPodOS-Arduino/src/actuators/led.cc | 8 +- software/PeaPodOS-Arduino/src/actuators/led.h | 6 +- software/PeaPodOS-Arduino/src/main.cc | 115 + software/PeaPodOS-Arduino/src/main.h | 55 + software/PeaPodOS-Arduino/src/sensors/k30.cc | 16 +- software/PeaPodOS-Arduino/src/sensors/k30.h | 8 +- .../src/{ => sensors}/sensor.cc | 10 +- .../src/{ => sensors}/sensor.h | 21 +- .../PeaPodOS-Arduino/src/sensors/sht31.cc | 18 +- software/PeaPodOS-Arduino/src/sensors/sht31.h | 8 +- .../PeaPodOS-Arduino/src/{ => utils}/base.h | 4 +- .../src/utils/instructions.cc | 86 + .../PeaPodOS-Arduino/src/utils/instructions.h | 37 + .../src/utils/jsonmessenger.cc | 56 + .../src/utils/jsonmessenger.h | 48 + software/PeaPodOS-Arduino/test/README.md | 9 + .../test/test_blink/test_blink.cc | 40 + .../test_instructions/test_instructions.cc | 82 + .../test/test_messenger/test_messenger.cc | 35 + software/README.md | 54 +- software/lib/utils.ts | 29 +- software/tests/README.md | 12 +- software/tests/blink/blink.ino | 10 - software/tests/revision/revision.ino | 100 - 32 files changed, 15088 insertions(+), 461 deletions(-) delete mode 100644 software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino delete mode 100644 software/PeaPodOS-Arduino/README.md create mode 100644 software/PeaPodOS-Arduino/avrdude_gpio.conf create mode 100644 software/PeaPodOS-Arduino/platformio.ini rename software/PeaPodOS-Arduino/src/{ => actuators}/actuator.cc (95%) rename software/PeaPodOS-Arduino/src/{ => actuators}/actuator.h (94%) create mode 100644 software/PeaPodOS-Arduino/src/main.cc create mode 100644 software/PeaPodOS-Arduino/src/main.h rename software/PeaPodOS-Arduino/src/{ => sensors}/sensor.cc (89%) rename software/PeaPodOS-Arduino/src/{ => sensors}/sensor.h (89%) rename software/PeaPodOS-Arduino/src/{ => utils}/base.h (84%) create mode 100644 software/PeaPodOS-Arduino/src/utils/instructions.cc create mode 100644 software/PeaPodOS-Arduino/src/utils/instructions.h create mode 100644 software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc create mode 100644 software/PeaPodOS-Arduino/src/utils/jsonmessenger.h create mode 100644 software/PeaPodOS-Arduino/test/README.md create mode 100644 software/PeaPodOS-Arduino/test/test_blink/test_blink.cc create mode 100644 software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc create mode 100644 software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc delete mode 100644 software/tests/blink/blink.ino delete mode 100644 software/tests/revision/revision.ino diff --git a/software/.npmignore b/software/.npmignore index c495e863..99ab6e20 100644 --- a/software/.npmignore +++ b/software/.npmignore @@ -1,8 +1,3 @@ -# Basically just include .js files ("binary" package) - -# Build files -**/build/ - # TypeScript files (keep type files) *.ts !*.d.ts @@ -33,4 +28,6 @@ tests/ *.elf *.hex *.eep -*.bin \ No newline at end of file +*.bin +**/.vscode/ +**/.pio/ \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/.gitignore b/software/PeaPodOS-Arduino/.gitignore index e9ab1843..3b8da3a4 100644 --- a/software/PeaPodOS-Arduino/.gitignore +++ b/software/PeaPodOS-Arduino/.gitignore @@ -1,2 +1,2 @@ -# Build Files -build/ \ No newline at end of file +.pio +.vscode \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino b/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino deleted file mode 100644 index 5c376950..00000000 --- a/software/PeaPodOS-Arduino/PeaPodOS-Arduino.ino +++ /dev/null @@ -1,226 +0,0 @@ -// IMPORTS -#include "./src/Sensor.h" -#include "./src/sensors/SHT31.h" -// #include "./src/sensors/K30.h" -// #include "./src/Actuator.h" -// #include "./src/actuators/led.h" - -// CONSTANTS -#define NUM_SENSORS 1 -// #define NUM_ACTUATORS 0 -#define REVISION 0 -#define BAUDRATE 115200 -#define DEBUG true - -// PINS -#define PIN_STATUS 13 -// #define PIN_LEDBLUE 3 -// #define PIN_LEDCOOL 5 -// #define PIN_LEDWARM 6 -// #define PIN_LEDRED 9 -// #define PIN_LEDFAR 10 - -// SENSORS -SHT31 sht31 = SHT31(); -// K30 k30 = K30(); - -Sensor* sensors[NUM_SENSORS] = { - &sht31, - // &k30, -}; - -// ACTUATORS -// LED led_blue(LEDPIN_BLUE, LEDCOLOR_BLUE); -// LED led_cool(LEDPIN_COOL, LEDCOLOR_COOL); -// LED led_warm(LEDPIN_WARM, LEDCOLOR_WARM); -// LED led_red(LEDPIN_RED, LEDCOLOR_RED); -// LED led_far(LEDPIN_FAR, LEDCOLOR_FAR); - -// Actuator* actuators [NUM_ACTUATORS] = { -// &led_blue, -// &led_cool, -// &led_warm, -// &led_red, -// &led_far, -// } - -void setup(void) { - // Status LED - pinMode(PIN_STATUS, OUTPUT); - digitalWrite(PIN_STATUS, LOW); - - // Await serial start - Serial.begin(BAUDRATE); - while (!Serial) { - delay(1); - } - - // Send software revision first - Serial.print("{\"type\":\"revision\",\"data\":"); - Serial.print(REVISION); - Serial.print("}\n"); - - // Serial communications established, initialize sensors and actuators - if (!post()) { - // Failed POST - while (true) { - delay(250); - digitalWrite(PIN_STATUS, HIGH); - delay(250); - digitalWrite(PIN_STATUS, LOW); - } - } - - // Await valid initial instruction set - // String ins; - // do { - // // Await instructions - // while (!Serial.available()); - // ins = Serial.readString(); - - // // Trim whitespace, newline terminator - // ins.trim(); - // } while (!handleInstructions(ins)); - digitalWrite(PIN_STATUS, HIGH); -} - -void loop(void) { - // Check for instructions - // if (Serial.available()) { - // String in = Serial.readString(); - - // // Trim whitespace, newline terminator - // in.trim(); - // handleInstructions(in); - // } - - for (int i = 0; i < NUM_SENSORS; ++i) { - SensorState* state = sensors[i]->update(); - - #if DEBUG - Serial.print(F("{\"type\":\"debug\",\"data\":\"Sensor ")); - Serial.print(sensors[i]->getID()); - Serial.print(F(" state: DS = ")); - Serial.print(state->debug); - Serial.print(F(", ERR = ")); - Serial.print(state->error); - Serial.print(F("\"}\n")); - #endif - - if (state->error == ERR_NONE) { - if (state->debug == DS_SUCCESS) { - for (int j = 0; j < state->numdata; ++j) { - Serial.print(F("{\"type\":\"data\",\"data\":{\"label\":\"")); - Serial.print(state->data[j].label); - Serial.print(F("\",\"value\":")); - Serial.print(state->data[j].value); - Serial.print(F("}}\n")); - } - } // else do nothing - } else if (state->error == ERR_WARNING) { - Serial.print(F("{\"type\":\"debug\",\"data\":\"Failed to read from sensor ")); - Serial.print(sensors[i]->getID()); - Serial.print(F(" (non-fatal)\"}\n")); - } else if (state->error == ERR_FATAL) { - Serial.print(F("{\"type\":\"error\",\"data\":\"Failed to read from sensor ")); - Serial.print(sensors[i]->getID()); - Serial.print(F(" (FATAL, SENSOR DISABLED!)\"}\n")); - } - } - delay(500); -} - -bool post(void) { - bool success = true; - for (int i = 0; i < NUM_SENSORS; ++i) { - SensorState* state = sensors[i]->begin(); - bool latest = (state->debug == DS_INITIALIZED && state->error == ERR_NONE); - if (latest) { - Serial.print("{\"type\":\"debug\",\"data\":\"Sensor "); - Serial.print(sensors[i]->getID()); - Serial.print(" initialized successfully.\"}\n"); - } else { - Serial.print("{\"type\":\"error\",\"data\":\"Failed to initialize sensor "); - Serial.print(sensors[i]->getID()); - Serial.print(". Check wiring.\"}\n"); - } - success &= latest; - } - return success; -} - -/** - * Handles incoming instruction dictionary {"var":val,...} - * Continues regardless of failure of a single instruction, alerts only - * @param s - Instructions dictionary string - * @return were all instructions handled in a valid way? - */ -bool handleInstructions(String ins) { - if(ins.equals("{}")){ - return true; - } - if (ins.charAt(0) != '{' || ins.charAt(ins.length()-1) != '}' || ins.indexOf(':') == -1) { - Serial.print("{\"type\":\"error\",\"data\":\"Invalid instructions dictionary '"); - Serial.print(ins); - Serial.print("', bad formatting.\"}\n"); - return false; - } - ins = ins.substring(1, ins.length()-1); // Strips surrounding {} - - // Handle each individual instruction - bool result = true; - // While there are multiple instructions: - while (ins.indexOf(',') != -1) { - // Did the leftmost one succeed? - result &= handleInstruction(ins.substring(0, ins.indexOf(','))); - //T runcate leftmost instruction - ins = ins.substring(ins.indexOf(',')+1); - } - //Handle last/only instruction (there are no commas left) - result &= handleInstruction(ins); - return result; -} - -/** - * Handles incoming instruction "var":value string. - * @param s - Instruction string - * @return was the instruction handled in a valid way? - */ -bool handleInstruction(String in) { - uint8_t split = in.indexOf(":"); - if (split!=-1 && in.charAt(0) == '"' && in.charAt(split-1) == '"') { - String var = in.substring(1, split-1); //Assumes surrounding "" - float value = in.substring(split+1).toFloat(); - - //INSTRUCTION HANDLING IF BLOCKS - EACH RETURNS TRUE - // if(var.equals("led_blue")){ - // led_blue.target = value; - // return true; - // } - // if(var.equals("led_cool")){ - // led_cool.target = value; - // return true; - // } - // if(var.equals("led_warm")){ - // led_warm.target = value; - // return true; - // } - // if(var.equals("led_red")){ - // led_red.target = value; - // return true; - // } - // if(var.equals("led_far")){ - // led_far.target = value; - // return true; - // } - Serial.print("{\"type\":\"debug\",\"data\":\"Unknown instruction target '"); - Serial.print(var); - Serial.print("'\"}\n"); - return false; - } - Serial.print("{\"type\":\"debug\",\"data\":\"Failed to handle instruction '"); - in.replace("\"", "\\\""); - Serial.print(in); - Serial.print("'\"}\n"); - return false; -} diff --git a/software/PeaPodOS-Arduino/README.md b/software/PeaPodOS-Arduino/README.md deleted file mode 100644 index b0af71b2..00000000 --- a/software/PeaPodOS-Arduino/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# PeaPodOS Arduino - -Includes all libraries and code for PeaPod's Arduino Micro. - -## References - -Todo. - -# Development - -## Requirements - -- Arduino IDE - -## Uploading - -Open the [main sketch](./PeaPod-Arduino.ino) in the Arduino IDE. Connect the PeaPod Arduino Micro to your computer. Select Arduino Micro as the board. Select the appropriate serial port. - -Verify, then upload the sketch. - -## Interfacing - -Open the Arduino IDE Serial Monitor (Tools > Serial Monitor). To start the PeaPod's data gathering, send it a valid "Instruction Set" JSON-formatted string where each key-value pair is an Instruction. - -The currently supported Instructions are: -- None. GTFO. \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/avrdude_gpio.conf b/software/PeaPodOS-Arduino/avrdude_gpio.conf new file mode 100644 index 00000000..d9475693 --- /dev/null +++ b/software/PeaPodOS-Arduino/avrdude_gpio.conf @@ -0,0 +1,14392 @@ +# $Id: avrdude.conf.in 1236 2013-09-16 19:40:15Z joerg_wunsch $ -*- text -*- +# +# AVRDUDE Configuration File +# +# This file contains configuration data used by AVRDUDE which describes +# the programming hardware pinouts and also provides part definitions. +# AVRDUDE's "-C" command line option specifies the location of the +# configuration file. The "-c" option names the programmer configuration +# which must match one of the entry's "id" parameter. The "-p" option +# identifies which part AVRDUDE is going to be programming and must match +# one of the parts' "id" parameter. +# +# Possible entry formats are: +# +# programmer +# parent # optional parent +# id = [, [, ] ...] ; # are quoted strings +# desc = ; # quoted string +# type = ; # programmer type, quoted string +# # supported programmer types can be listed by "-c ?type" +# connection_type = parallel | serial | usb +# baudrate = ; # baudrate for avr910-programmer +# vcc = [, ... ] ; # pin number(s) +# buff = [, ... ] ; # pin number(s) +# reset = ; # pin number +# sck = ; # pin number +# mosi = ; # pin number +# miso = ; # pin number +# errled = ; # pin number +# rdyled = ; # pin number +# pgmled = ; # pin number +# vfyled = ; # pin number +# usbvid = ; # USB VID (Vendor ID) +# usbpid = ; # USB PID (Product ID) +# usbdev = ; # USB interface or other device info +# usbvendor = ; # USB Vendor Name +# usbproduct = ; # USB Product Name +# usbsn = ; # USB Serial Number +# +# To invert a bit, use = ~ , the spaces are important. +# For a pin list all pins must be inverted. +# A single pin can be specified as usual = ~ , for lists +# specify it as follows = ~ ( [, ... ] ) . +# ; +# +# part +# id = ; # quoted string +# desc = ; # quoted string +# has_jtag = ; # part has JTAG i/f +# has_debugwire = ; # part has debugWire i/f +# has_pdi = ; # part has PDI i/f +# has_tpi = ; # part has TPI i/f +# devicecode = ; # deprecated, use stk500_devcode +# stk500_devcode = ; # numeric +# avr910_devcode = ; # numeric +# signature = ; # signature bytes +# chip_erase_delay = ; # micro-seconds +# reset = dedicated | io; +# retry_pulse = reset | sck; +# pgm_enable = ; +# chip_erase = ; +# chip_erase_delay = ; # chip erase delay (us) +# # STK500 parameters (parallel programming IO lines) +# pagel = ; # pin name in hex, i.e., 0xD7 +# bs2 = ; # pin name in hex, i.e., 0xA0 +# serial = ; # can use serial downloading +# parallel = ; # can use par. programming +# # STK500v2 parameters, to be taken from Atmel's XML files +# timeout = ; +# stabdelay = ; +# cmdexedelay = ; +# synchloops = ; +# bytedelay = ; +# pollvalue = ; +# pollindex = ; +# predelay = ; +# postdelay = ; +# pollmethod = ; +# mode = ; +# delay = ; +# blocksize = ; +# readsize = ; +# hvspcmdexedelay = ; +# # STK500v2 HV programming parameters, from XML +# pp_controlstack = , , ...; # PP only +# hvsp_controlstack = , , ...; # HVSP only +# hventerstabdelay = ; +# progmodedelay = ; # PP only +# latchcycles = ; +# togglevtg = ; +# poweroffdelay = ; +# resetdelayms = ; +# resetdelayus = ; +# hvleavestabdelay = ; +# resetdelay = ; +# synchcycles = ; # HVSP only +# chiperasepulsewidth = ; # PP only +# chiperasepolltimeout = ; +# chiperasetime = ; # HVSP only +# programfusepulsewidth = ; # PP only +# programfusepolltimeout = ; +# programlockpulsewidth = ; # PP only +# programlockpolltimeout = ; +# # JTAG ICE mkII parameters, also from XML files +# allowfullpagebitstream = ; +# enablepageprogramming = ; +# idr = ; # IO addr of IDR (OCD) reg. +# rampz = ; # IO addr of RAMPZ reg. +# spmcr = ; # mem addr of SPMC[S]R reg. +# eecr = ; # mem addr of EECR reg. +# # (only when != 0x3c) +# is_at90s1200 = ; # AT90S1200 part +# is_avr32 = ; # AVR32 part +# +# memory +# paged = ; # yes / no +# size = ; # bytes +# page_size = ; # bytes +# num_pages = ; # numeric +# min_write_delay = ; # micro-seconds +# max_write_delay = ; # micro-seconds +# readback_p1 = ; # byte value +# readback_p2 = ; # byte value +# pwroff_after_write = ; # yes / no +# read = ; +# write = ; +# read_lo = ; +# read_hi = ; +# write_lo = ; +# write_hi = ; +# loadpage_lo = ; +# loadpage_hi = ; +# writepage = ; +# ; +# ; +# +# If any of the above parameters are not specified, the default value +# of 0 is used for numerics or the empty string ("") for string +# values. If a required parameter is left empty, AVRDUDE will +# complain. +# +# Parts can also inherit parameters from previously defined parts +# using the following syntax. In this case specified integer and +# string values override parameter values from the parent part. New +# memory definitions are added to the definitions inherited from the +# parent. +# +# part parent # quoted string +# id = ; # quoted string +# +# ; +# +# NOTES: +# * 'devicecode' is the device code used by the STK500 (see codes +# listed below) +# * Not all memory types will implement all instructions. +# * AVR Fuse bits and Lock bits are implemented as a type of memory. +# * Example memory types are: +# "flash", "eeprom", "fuse", "lfuse" (low fuse), "hfuse" (high +# fuse), "signature", "calibration", "lock" +# * The memory type specified on the avrdude command line must match +# one of the memory types defined for the specified chip. +# * The pwroff_after_write flag causes avrdude to attempt to +# power the device off and back on after an unsuccessful write to +# the affected memory area if VCC programmer pins are defined. If +# VCC pins are not defined for the programmer, a message +# indicating that the device needs a power-cycle is printed out. +# This flag was added to work around a problem with the +# at90s4433/2333's; see the at90s4433 errata at: +# +# http://www.atmel.com/dyn/resources/prod_documents/doc1280.pdf +# +# INSTRUCTION FORMATS +# +# Instruction formats are specified as a comma seperated list of +# string values containing information (bit specifiers) about each +# of the 32 bits of the instruction. Bit specifiers may be one of +# the following formats: +# +# '1' = the bit is always set on input as well as output +# +# '0' = the bit is always clear on input as well as output +# +# 'x' = the bit is ignored on input and output +# +# 'a' = the bit is an address bit, the bit-number matches this bit +# specifier's position within the current instruction byte +# +# 'aN' = the bit is the Nth address bit, bit-number = N, i.e., a12 +# is address bit 12 on input, a0 is address bit 0. +# +# 'i' = the bit is an input data bit +# +# 'o' = the bit is an output data bit +# +# Each instruction must be composed of 32 bit specifiers. The +# instruction specification closely follows the instruction data +# provided in Atmel's data sheets for their parts. +# +# See below for some examples. +# +# +# The following are STK500 part device codes to use for the +# "devicecode" field of the part. These came from Atmel's software +# section avr061.zip which accompanies the application note +# AVR061 available from: +# +# http://www.atmel.com/dyn/resources/prod_documents/doc2525.pdf +# + +#define ATTINY10 0x10 /* the _old_ one that never existed! */ +#define ATTINY11 0x11 +#define ATTINY12 0x12 +#define ATTINY15 0x13 +#define ATTINY13 0x14 + +#define ATTINY22 0x20 +#define ATTINY26 0x21 +#define ATTINY28 0x22 +#define ATTINY2313 0x23 + +#define AT90S1200 0x33 + +#define AT90S2313 0x40 +#define AT90S2323 0x41 +#define AT90S2333 0x42 +#define AT90S2343 0x43 + +#define AT90S4414 0x50 +#define AT90S4433 0x51 +#define AT90S4434 0x52 +#define ATMEGA48 0x59 + +#define AT90S8515 0x60 +#define AT90S8535 0x61 +#define AT90C8534 0x62 +#define ATMEGA8515 0x63 +#define ATMEGA8535 0x64 + +#define ATMEGA8 0x70 +#define ATMEGA88 0x73 +#define ATMEGA168 0x86 + +#define ATMEGA161 0x80 +#define ATMEGA163 0x81 +#define ATMEGA16 0x82 +#define ATMEGA162 0x83 +#define ATMEGA169 0x84 + +#define ATMEGA323 0x90 +#define ATMEGA32 0x91 + +#define ATMEGA64 0xA0 + +#define ATMEGA103 0xB1 +#define ATMEGA128 0xB2 +#define AT90CAN128 0xB3 +#define AT90CAN64 0xB3 +#define AT90CAN32 0xB3 + +#define AT86RF401 0xD0 + +#define AT89START 0xE0 +#define AT89S51 0xE0 +#define AT89S52 0xE1 + +# The following table lists the devices in the original AVR910 +# appnote: +# |Device |Signature | Code | +# +-------+----------+------+ +# |tiny12 | 1E 90 05 | 0x55 | +# |tiny15 | 1E 90 06 | 0x56 | +# | | | | +# | S1200 | 1E 90 01 | 0x13 | +# | | | | +# | S2313 | 1E 91 01 | 0x20 | +# | S2323 | 1E 91 02 | 0x48 | +# | S2333 | 1E 91 05 | 0x34 | +# | S2343 | 1E 91 03 | 0x4C | +# | | | | +# | S4414 | 1E 92 01 | 0x28 | +# | S4433 | 1E 92 03 | 0x30 | +# | S4434 | 1E 92 02 | 0x6C | +# | | | | +# | S8515 | 1E 93 01 | 0x38 | +# | S8535 | 1E 93 03 | 0x68 | +# | | | | +# |mega32 | 1E 95 01 | 0x72 | +# |mega83 | 1E 93 05 | 0x65 | +# |mega103| 1E 97 01 | 0x41 | +# |mega161| 1E 94 01 | 0x60 | +# |mega163| 1E 94 02 | 0x64 | + +# Appnote AVR109 also has a table of AVR910 device codes, which +# lists: +# dev avr910 signature +# ATmega8 0x77 0x1E 0x93 0x07 +# ATmega8515 0x3B 0x1E 0x93 0x06 +# ATmega8535 0x6A 0x1E 0x93 0x08 +# ATmega16 0x75 0x1E 0x94 0x03 +# ATmega162 0x63 0x1E 0x94 0x04 +# ATmega163 0x66 0x1E 0x94 0x02 +# ATmega169 0x79 0x1E 0x94 0x05 +# ATmega32 0x7F 0x1E 0x95 0x02 +# ATmega323 0x73 0x1E 0x95 0x01 +# ATmega64 0x46 0x1E 0x96 0x02 +# ATmega128 0x44 0x1E 0x97 0x02 +# +# These codes refer to "BOOT" device codes which are apparently +# different than standard device codes, for whatever reasons +# (often one above the standard code). + +# There are several extended versions of AVR910 implementations around +# in the Internet. These add the following codes (only devices that +# actually exist are listed): + +# ATmega8515 0x3A +# ATmega128 0x43 +# ATmega64 0x45 +# ATtiny26 0x5E +# ATmega8535 0x69 +# ATmega32 0x72 +# ATmega16 0x74 +# ATmega8 0x76 +# ATmega169 0x78 + +# +# Overall avrdude defaults; suitable for ~/.avrduderc +# +default_parallel = "/dev/parport0"; +default_serial = "/dev/ttyS0"; +# default_bitclock = 2.5; + +# Turn off safemode by default +#default_safemode = no; + + +# +# PROGRAMMER DEFINITIONS +# + +# http://wiring.org.co/ +# Basically STK500v2 protocol, with some glue to trigger the +# bootloader. +programmer + id = "wiring"; + desc = "Wiring"; + type = "wiring"; + connection_type = serial; +; + +programmer + id = "arduino"; + desc = "Arduino"; + type = "arduino"; + connection_type = serial; +; +# this will interface with the chips on these programmers: +# +# http://real.kiev.ua/old/avreal/en/adapters +# http://www.amontec.com/jtagkey.shtml, jtagkey-tiny.shtml +# http://www.olimex.com/dev/arm-usb-ocd.html, arm-usb-tiny.html +# http://www.ethernut.de/en/hardware/turtelizer/index.html +# http://elk.informatik.fh-augsburg.de/hhweb/doc/openocd/usbjtag/usbjtag.html +# http://dangerousprototypes.com/docs/FT2232_breakout_board +# http://www.ftdichip.com/Products/Modules/DLPModules.htm,DLP-2232*,DLP-USB1232H +# http://flashrom.org/FT2232SPI_Programmer +# +# The drivers will look for a specific device and use the first one found. +# If you have mulitple devices, then look for unique information (like SN) +# And fill that in here. +# +# Note that the pin numbers for the main ISP signals (reset, sck, +# mosi, miso) are fixed and cannot be changed, since they must match +# the way the Multi-Protocol Synchronous Serial Engine (MPSSE) of +# these FTDI ICs has been designed. + +programmer + id = "avrftdi"; + desc = "FT2232D based generic programmer"; + type = "avrftdi"; + connection_type = usb; + usbvid = 0x0403; + usbpid = 0x6010; + usbvendor = ""; + usbproduct = ""; + usbdev = "A"; + usbsn = ""; +#ISP-signals - lower ADBUS-Nibble (default) + reset = 3; + sck = 0; + mosi = 1; + miso = 2; +#LED SIGNALs - higher ADBUS-Nibble +# errled = 4; +# rdyled = 5; +# pgmled = 6; +# vfyled = 7; +#Buffer Signal - ACBUS - Nibble +# buff = 8; +; +# This is an implementation of the above with a buffer IC (74AC244) and +# 4 LEDs directly attached, all active low. +programmer + id = "2232HIO"; + desc = "FT2232H based generic programmer"; + type = "avrftdi"; + connection_type = usb; + usbvid = 0x0403; +# Note: This PID is reserved for generic H devices and +# should be programmed into the EEPROM +# usbpid = 0x8A48; + usbpid = 0x6010; + usbdev = "A"; + usbvendor = ""; + usbproduct = ""; + usbsn = ""; +#ISP-signals + reset = 3; + sck = 0; + mosi = 1; + miso = 2; + buff = ~4; +#LED SIGNALs + errled = ~ 11; + rdyled = ~ 14; + pgmled = ~ 13; + vfyled = ~ 12; +; + +#The FT4232H can be treated as FT2232H, but it has a different USB +#device ID of 0x6011. +programmer parent "avrftdi" + id = "4232h"; + desc = "FT4232H based generic programmer"; + usbpid = 0x6011; +; + +programmer + id = "jtagkey"; + desc = "Amontec JTAGKey, JTAGKey-Tiny and JTAGKey2"; + type = "avrftdi"; + connection_type = usb; + usbvid = 0x0403; +# Note: This PID is used in all JTAGKey variants + usbpid = 0xCFF8; + usbdev = "A"; + usbvendor = ""; + usbproduct = ""; + usbsn = ""; +#ISP-signals => 20 - Pin connector on JTAGKey + reset = 3; # TMS 7 violet + sck = 0; # TCK 9 white + mosi = 1; # TDI 5 green + miso = 2; # TDO 13 orange + buff = ~4; +# VTG VREF 1 brown with red tip +# GND GND 20 black +# The colors are on the 20 pin breakout cable +# from Amontec +; + +# On the adapter you can read "O-Link". On the PCB is printed "OpenJTAG v3.1" +# You can find it as "OpenJTAG ARM JTAG USB" in the internet. +# (But there are also several projects called Open JTAG, eg. +# http://www.openjtag.org, which are completely different.) +# http://www.100ask.net/shop/english.html (website seems to be outdated) +# http://item.taobao.com/item.htm?id=1559277013 +# http://www.micro4you.com/store/openjtag-arm-jtag-usb.html (schematics!) +# some other sources which call it O-Link +# http://www.andahammer.com/olink/ +# http://www.developmentboard.net/31-o-link-debugger.html +# http://armwerks.com/catalog/o-link-debugger-copy/ +# or just have a look at ebay ... +# It is basically the same entry as jtagkey with different usb ids. +programmer parent "jtagkey" + id = "o-link"; + desc = "O-Link, OpenJTAG from www.100ask.net"; + usbvid = 0x1457; + usbpid = 0x5118; + usbvendor = "www.100ask.net"; + usbproduct = "USB<=>JTAG&RS232"; +; + +# http://wiki.openmoko.org/wiki/Debug_Board_v3 +programmer + id = "openmoko"; + desc = "Openmoko debug board (v3)"; + type = "avrftdi"; + usbvid = 0x1457; + usbpid = 0x5118; + usbdev = "A"; + usbvendor = ""; + usbproduct = ""; + usbsn = ""; + reset = 3; # TMS 7 + sck = 0; # TCK 9 + mosi = 1; # TDI 5 + miso = 2; # TDO 13 +; + +# Only Rev. A boards. +# Schematic and user manual: http://www.cs.put.poznan.pl/wswitala/download/pdf/811EVBK.pdf +programmer + id = "lm3s811"; + desc = "Luminary Micro LM3S811 Eval Board (Rev. A)"; + type = "avrftdi"; + connection_type = usb; + usbvid = 0x0403; + usbpid = 0xbcd9; + usbvendor = "LMI"; + usbproduct = "LM3S811 Evaluation Board"; + usbdev = "A"; + usbsn = ""; +#ISP-signals - lower ACBUS-Nibble (default) + reset = 3; + sck = 0; + mosi = 1; + miso = 2; +# Enable correct buffers + buff = 7; +; + +programmer + id = "avrisp"; + desc = "Atmel AVR ISP"; + type = "stk500"; + connection_type = serial; +; + +programmer + id = "avrispv2"; + desc = "Atmel AVR ISP V2"; + type = "stk500v2"; + connection_type = serial; +; + +programmer + id = "avrispmkII"; + desc = "Atmel AVR ISP mkII"; + type = "stk500v2"; + connection_type = usb; +; + +programmer parent "avrispmkII" + id = "avrisp2"; +; + +programmer + id = "buspirate"; + desc = "The Bus Pirate"; + type = "buspirate"; + connection_type = serial; +; + +programmer + id = "buspirate_bb"; + desc = "The Bus Pirate (bitbang interface, supports TPI)"; + type = "buspirate_bb"; + connection_type = serial; + # pins are bits in bitbang byte (numbers are 87654321) + # 1|POWER|PULLUP|AUX|MOSI|CLK|MISO|CS + reset = 1; + sck = 3; + mosi = 4; + miso = 2; + #vcc = 7; This is internally set independent of this setting. +; + +# This is supposed to be the "default" STK500 entry. +# Attempts to select the correct firmware version +# by probing for it. Better use one of the entries +# below instead. +programmer + id = "stk500"; + desc = "Atmel STK500"; + type = "stk500generic"; + connection_type = serial; +; + +programmer + id = "stk500v1"; + desc = "Atmel STK500 Version 1.x firmware"; + type = "stk500"; + connection_type = serial; +; + +programmer + id = "mib510"; + desc = "Crossbow MIB510 programming board"; + type = "stk500"; + connection_type = serial; +; + +programmer + id = "stk500v2"; + desc = "Atmel STK500 Version 2.x firmware"; + type = "stk500v2"; + connection_type = serial; +; + +programmer + id = "stk500pp"; + desc = "Atmel STK500 V2 in parallel programming mode"; + type = "stk500pp"; + connection_type = serial; +; + +programmer + id = "stk500hvsp"; + desc = "Atmel STK500 V2 in high-voltage serial programming mode"; + type = "stk500hvsp"; + connection_type = serial; +; + +programmer + id = "stk600"; + desc = "Atmel STK600"; + type = "stk600"; + connection_type = usb; +; + +programmer + id = "stk600pp"; + desc = "Atmel STK600 in parallel programming mode"; + type = "stk600pp"; + connection_type = usb; +; + +programmer + id = "stk600hvsp"; + desc = "Atmel STK600 in high-voltage serial programming mode"; + type = "stk600hvsp"; + connection_type = usb; +; + +programmer + id = "avr910"; + desc = "Atmel Low Cost Serial Programmer"; + type = "avr910"; + connection_type = serial; +; + +programmer + id = "ft245r"; + desc = "FT245R Synchronous BitBang"; + type = "ftdi_syncbb"; + connection_type = usb; + miso = 1; # D1 + sck = 0; # D0 + mosi = 2; # D2 + reset = 4; # D4 +; + +programmer + id = "ft232r"; + desc = "FT232R Synchronous BitBang"; + type = "ftdi_syncbb"; + connection_type = usb; + miso = 1; # RxD + sck = 0; # RTS + mosi = 2; # TxD + reset = 4; # DTR +; + +# see http://www.bitwizard.nl/wiki/index.php/FTDI_ATmega +programmer + id = "bwmega"; + desc = "BitWizard ftdi_atmega builtin programmer"; + type = "ftdi_syncbb"; + connection_type = usb; + miso = 5; # DSR + sck = 6; # DCD + mosi = 3; # CTS + reset = 7; # RI +; + +# see http://www.geocities.jp/arduino_diecimila/bootloader/index_en.html +# Note: pins are numbered from 1! +programmer + id = "arduino-ft232r"; + desc = "Arduino: FT232R connected to ISP"; + type = "ftdi_syncbb"; + connection_type = usb; + miso = 3; # CTS X3(1) + sck = 5; # DSR X3(2) + mosi = 6; # DCD X3(3) + reset = 7; # RI X3(4) +; + +# website mentioned above uses this id +programmer parent "arduino-ft232r" + id = "diecimila"; + desc = "alias for arduino-ft232r"; +; + +programmer + id = "usbasp"; + desc = "USBasp, http://www.fischl.de/usbasp/"; + type = "usbasp"; + connection_type = usb; + usbvid = 0x16C0; # VOTI + usbpid = 0x05DC; # Obdev's free shared PID + usbvendor = "www.fischl.de"; + usbproduct = "USBasp"; + + # following variants are autodetected for id "usbasp" + + # original usbasp from fischl.de + # see above "usbasp" + + # old usbasp from fischl.de + #usbvid = 0x03EB; # ATMEL + #usbpid = 0xC7B4; # (unoffical) USBasp + #usbvendor = "www.fischl.de"; + #usbproduct = "USBasp"; + + # NIBObee (only if -P nibobee is given on command line) + # see below "nibobee" +; + +programmer + id = "nibobee"; + desc = "NIBObee"; + type = "usbasp"; + connection_type = usb; + usbvid = 0x16C0; # VOTI + usbpid = 0x092F; # NIBObee PID + usbvendor = "www.nicai-systems.com"; + usbproduct = "NIBObee"; +; + +programmer + id = "usbasp-clone"; + desc = "Any usbasp clone with correct VID/PID"; + type = "usbasp"; + connection_type = usb; + usbvid = 0x16C0; # VOTI + usbpid = 0x05DC; # Obdev's free shared PID + #usbvendor = ""; + #usbproduct = ""; +; + +programmer + id = "usbtiny"; + desc = "USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/"; + type = "usbtiny"; + connection_type = usb; + usbvid = 0x1781; + usbpid = 0x0c9f; +; + +programmer + id = "arduinoisp"; + desc = " "; + type = "usbtiny"; + connection_type = usb; + usbvid = 0x2341; + usbpid = 0x0049; +; + +programmer + id = "butterfly"; + desc = "Atmel Butterfly Development Board"; + type = "butterfly"; + connection_type = serial; +; + +programmer + id = "avr109"; + desc = "Atmel AppNote AVR109 Boot Loader"; + type = "butterfly"; + connection_type = serial; +; + +programmer + id = "avr911"; + desc = "Atmel AppNote AVR911 AVROSP"; + type = "butterfly"; + connection_type = serial; +; + +# suggested in http://forum.mikrokopter.de/topic-post48317.html +programmer + id = "mkbutterfly"; + desc = "Mikrokopter.de Butterfly"; + type = "butterfly_mk"; + connection_type = serial; +; + +programmer parent "mkbutterfly" + id = "butterfly_mk"; +; + +programmer + id = "jtagmkI"; + desc = "Atmel JTAG ICE (mkI)"; + baudrate = 115200; # default is 115200 + type = "jtagmki"; + connection_type = serial; +; + +# easier to type +programmer parent "jtagmkI" + id = "jtag1"; +; + +# easier to type +programmer parent "jtag1" + id = "jtag1slow"; + baudrate = 19200; +; + +# The JTAG ICE mkII has both, serial and USB connectivity. As it is +# mostly used through USB these days (AVR Studio 5 only supporting it +# that way), we make connection_type = usb the default. Users are +# still free to use a serial port with the -P option. + +programmer + id = "jtagmkII"; + desc = "Atmel JTAG ICE mkII"; + baudrate = 19200; # default is 19200 + type = "jtagmkii"; + connection_type = usb; +; + +# easier to type +programmer parent "jtagmkII" + id = "jtag2slow"; +; + +# JTAG ICE mkII @ 115200 Bd +programmer parent "jtag2slow" + id = "jtag2fast"; + baudrate = 115200; +; + +# make the fast one the default, people will love that +programmer parent "jtag2fast" + id = "jtag2"; +; + +# JTAG ICE mkII in ISP mode +programmer + id = "jtag2isp"; + desc = "Atmel JTAG ICE mkII in ISP mode"; + baudrate = 115200; + type = "jtagmkii_isp"; + connection_type = usb; +; + +# JTAG ICE mkII in debugWire mode +programmer + id = "jtag2dw"; + desc = "Atmel JTAG ICE mkII in debugWire mode"; + baudrate = 115200; + type = "jtagmkii_dw"; + connection_type = usb; +; + +# JTAG ICE mkII in AVR32 mode +programmer + id = "jtagmkII_avr32"; + desc = "Atmel JTAG ICE mkII im AVR32 mode"; + baudrate = 115200; + type = "jtagmkii_avr32"; + connection_type = usb; +; + +# JTAG ICE mkII in AVR32 mode +programmer + id = "jtag2avr32"; + desc = "Atmel JTAG ICE mkII im AVR32 mode"; + baudrate = 115200; + type = "jtagmkii_avr32"; + connection_type = usb; +; + +# JTAG ICE mkII in PDI mode +programmer + id = "jtag2pdi"; + desc = "Atmel JTAG ICE mkII PDI mode"; + baudrate = 115200; + type = "jtagmkii_pdi"; + connection_type = usb; +; + +# AVR Dragon in JTAG mode +programmer + id = "dragon_jtag"; + desc = "Atmel AVR Dragon in JTAG mode"; + baudrate = 115200; + type = "dragon_jtag"; + connection_type = usb; +; + +# AVR Dragon in ISP mode +programmer + id = "dragon_isp"; + desc = "Atmel AVR Dragon in ISP mode"; + baudrate = 115200; + type = "dragon_isp"; + connection_type = usb; +; + +# AVR Dragon in PP mode +programmer + id = "dragon_pp"; + desc = "Atmel AVR Dragon in PP mode"; + baudrate = 115200; + type = "dragon_pp"; + connection_type = usb; +; + +# AVR Dragon in HVSP mode +programmer + id = "dragon_hvsp"; + desc = "Atmel AVR Dragon in HVSP mode"; + baudrate = 115200; + type = "dragon_hvsp"; + connection_type = usb; +; + +# AVR Dragon in debugWire mode +programmer + id = "dragon_dw"; + desc = "Atmel AVR Dragon in debugWire mode"; + baudrate = 115200; + type = "dragon_dw"; + connection_type = usb; +; + +# AVR Dragon in PDI mode +programmer + id = "dragon_pdi"; + desc = "Atmel AVR Dragon in PDI mode"; + baudrate = 115200; + type = "dragon_pdi"; + connection_type = usb; +; + +programmer + id = "jtag3"; + desc = "Atmel AVR JTAGICE3 in JTAG mode"; + type = "jtagice3"; + connection_type = usb; +; + +programmer + id = "jtag3pdi"; + desc = "Atmel AVR JTAGICE3 in PDI mode"; + type = "jtagice3_pdi"; + connection_type = usb; +; + +programmer + id = "jtag3dw"; + desc = "Atmel AVR JTAGICE3 in debugWIRE mode"; + type = "jtagice3_dw"; + connection_type = usb; +; + +programmer + id = "jtag3isp"; + desc = "Atmel AVR JTAGICE3 in ISP mode"; + type = "jtagice3_isp"; + connection_type = usb; +; + + +programmer + id = "pavr"; + desc = "Jason Kyle's pAVR Serial Programmer"; + type = "avr910"; + connection_type = serial; +; + +programmer + id = "pickit2"; + desc = "MicroChip's PICkit2 Programmer"; + type = "pickit2"; + connection_type = usb; +; + +# Parallel port programmers. + +programmer + id = "bsd"; + desc = "Brian Dean's Programmer, http://www.bsdhome.com/avrdude/"; + type = "par"; + connection_type = parallel; + vcc = 2, 3, 4, 5; + reset = 7; + sck = 8; + mosi = 9; + miso = 10; +; + +programmer + id = "stk200"; + desc = "STK200"; + type = "par"; + connection_type = parallel; + buff = 4, 5; + sck = 6; + mosi = 7; + reset = 9; + miso = 10; +; + +# The programming dongle used by the popular Ponyprog +# utility. It is almost similar to the STK200 one, +# except that there is a LED indicating that the +# programming is currently in progress. + +programmer parent "stk200" + id = "pony-stk200"; + desc = "Pony Prog STK200"; + pgmled = 8; +; + +programmer + id = "dt006"; + desc = "Dontronics DT006"; + type = "par"; + connection_type = parallel; + reset = 4; + sck = 5; + mosi = 2; + miso = 11; +; + +programmer parent "dt006" + id = "bascom"; + desc = "Bascom SAMPLE programming cable"; +; + +programmer + id = "alf"; + desc = "Nightshade ALF-PgmAVR, http://nightshade.homeip.net/"; + type = "par"; + connection_type = parallel; + vcc = 2, 3, 4, 5; + buff = 6; + reset = 7; + sck = 8; + mosi = 9; + miso = 10; + errled = 1; + rdyled = 14; + pgmled = 16; + vfyled = 17; +; + +programmer + id = "sp12"; + desc = "Steve Bolt's Programmer"; + type = "par"; + connection_type = parallel; + vcc = 4,5,6,7,8; + reset = 3; + sck = 2; + mosi = 9; + miso = 11; +; + +programmer + id = "picoweb"; + desc = "Picoweb Programming Cable, http://www.picoweb.net/"; + type = "par"; + connection_type = parallel; + reset = 2; + sck = 3; + mosi = 4; + miso = 13; +; + +programmer + id = "abcmini"; + desc = "ABCmini Board, aka Dick Smith HOTCHIP"; + type = "par"; + connection_type = parallel; + reset = 4; + sck = 3; + mosi = 2; + miso = 10; +; + +programmer + id = "futurlec"; + desc = "Futurlec.com programming cable."; + type = "par"; + connection_type = parallel; + reset = 3; + sck = 2; + mosi = 1; + miso = 10; +; + + +# From the contributor of the "xil" jtag cable: +# The "vcc" definition isn't really vcc (the cable gets its power from +# the programming circuit) but is necessary to switch one of the +# buffer lines (trying to add it to the "buff" lines doesn't work in +# avrdude versions before 5.5j). +# With this, TMS connects to RESET, TDI to MOSI, TDO to MISO and TCK +# to SCK (plus vcc/gnd of course) +programmer + id = "xil"; + desc = "Xilinx JTAG cable"; + type = "par"; + connection_type = parallel; + mosi = 2; + sck = 3; + reset = 4; + buff = 5; + miso = 13; + vcc = 6; +; + + +programmer + id = "dapa"; + desc = "Direct AVR Parallel Access cable"; + type = "par"; + connection_type = parallel; + vcc = 3; + reset = 16; + sck = 1; + mosi = 2; + miso = 11; +; + +programmer + id = "atisp"; + desc = "AT-ISP V1.1 programming cable for AVR-SDK1 from micro-research.co.th"; + type = "par"; + connection_type = parallel; + reset = ~6; + sck = ~8; + mosi = ~7; + miso = ~10; +; + +programmer + id = "ere-isp-avr"; + desc = "ERE ISP-AVR "; + type = "par"; + connection_type = parallel; + reset = ~4; + sck = 3; + mosi = 2; + miso = 10; +; + +programmer + id = "blaster"; + desc = "Altera ByteBlaster"; + type = "par"; + connection_type = parallel; + sck = 2; + miso = 11; + reset = 3; + mosi = 8; + buff = 14; +; + +# It is almost same as pony-stk200, except vcc on pin 5 to auto +# disconnect port (download on http://electropol.free.fr/spip/spip.php?article27) +programmer parent "pony-stk200" + id = "frank-stk200"; + desc = "Frank STK200"; + buff = ; # delete buff pin assignment + vcc = 5; +; + +# The AT98ISP Cable is a simple parallel dongle for AT89 family. +# http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2877 +programmer + id = "89isp"; + desc = "Atmel at89isp cable"; + type = "par"; + connection_type = parallel; + reset = 17; + sck = 1; + mosi = 2; + miso = 10; +; + + +#This programmer bitbangs GPIO lines using the Linux sysfs GPIO interface +# +#To enable it set the configuration below to match the GPIO lines connected to the +#relevant ISP header pins and uncomment the entry definition. In case you don't +#have the required permissions to edit this system wide config file put the +#entry in a separate .conf file and use it with -C+.conf +#on the command line. +# +#To check if your avrdude build has support for the linuxgpio programmer compiled in, +#use -c?type on the command line and look for linuxgpio in the list. If it's not available +#you need pass the --enable-linuxgpio=yes option to configure and recompile avrdude. +# +#programmer +# id = "linuxgpio"; +# desc = "Use the Linux sysfs interface to bitbang GPIO lines"; +# type = "linuxgpio"; +# reset = ?; +# sck = ?; +# mosi = ?; +# miso = ?; +#; + +#This programmer bitbangs GPIO lines using the Linux sysfs GPIO interface and direct +#GPIO memory registers read/write. +# +#To enable it set the configuration below to match the GPIO lines connected to the +#relevant ISP header pins and uncomment the entry definition. In case you don't +#have the required permissions to edit this system wide config file put the +#entry in a separate .conf file and use it with -C+.conf +#on the command line. +# +#To check if your avrdude build has support for the arduinotre programmer compiled in, +#use -c?type on the command line and look for arduinotre in the list. If it's not available +#you need pass the --enable-arduinotre=yes option to configure and recompile avrdude. +# +#programmer +# id = "arduinotre"; +# desc = "Arduino TRE bitbanging using GPIO registers"; +# type = "arduinotre"; +# reset = ~65; +# sck = 49; +# mosi = 48; +# miso = 61; +#; + +# some ultra cheap programmers use bitbanging on the +# serialport. +# +# PC - DB9 - Pins for RS232: +# +# GND 5 -- |O +# | O| <- 9 RI +# DTR 4 <- |O | +# | O| <- 8 CTS +# TXD 3 <- |O | +# | O| -> 7 RTS +# RXD 2 -> |O | +# | O| <- 6 DSR +# DCD 1 -> |O +# +# Using RXD is currently not supported. +# Using RI is not supported under Win32 but is supported under Posix. + +# serial ponyprog design (dasa2 in uisp) +# reset=!txd sck=rts mosi=dtr miso=cts + +programmer + id = "ponyser"; + desc = "design ponyprog serial, reset=!txd sck=rts mosi=dtr miso=cts"; + type = "serbb"; + connection_type = serial; + reset = ~3; + sck = 7; + mosi = 4; + miso = 8; +; + +# Same as above, different name +# reset=!txd sck=rts mosi=dtr miso=cts + +programmer parent "ponyser" + id = "siprog"; + desc = "Lancos SI-Prog "; +; + +# unknown (dasa in uisp) +# reset=rts sck=dtr mosi=txd miso=cts + +programmer + id = "dasa"; + desc = "serial port banging, reset=rts sck=dtr mosi=txd miso=cts"; + type = "serbb"; + connection_type = serial; + reset = 7; + sck = 4; + mosi = 3; + miso = 8; +; + +# unknown (dasa3 in uisp) +# reset=!dtr sck=rts mosi=txd miso=cts + +programmer + id = "dasa3"; + desc = "serial port banging, reset=!dtr sck=rts mosi=txd miso=cts"; + type = "serbb"; + connection_type = serial; + reset = ~4; + sck = 7; + mosi = 3; + miso = 8; +; + +# C2N232i (jumper configuration "auto") +# reset=dtr sck=!rts mosi=!txd miso=!cts + +programmer + id = "c2n232i"; + desc = "serial port banging, reset=dtr sck=!rts mosi=!txd miso=!cts"; + type = "serbb"; + connection_type = serial; + reset = 4; + sck = ~7; + mosi = ~3; + miso = ~8; +; + +# +# PART DEFINITIONS +# + +#------------------------------------------------------------ +# ATtiny11 +#------------------------------------------------------------ + +# This is an HVSP-only device. + +part + id = "t11"; + desc = "ATtiny11"; + stk500_devcode = 0x11; + signature = 0x1e 0x90 0x04; + chip_erase_delay = 20000; + + timeout = 200; + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x00, + 0x68, 0x78, 0x68, 0x68, 0x00, 0x00, 0x68, 0x78, + 0x78, 0x00, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 50; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + memory "eeprom" + size = 64; + blocksize = 64; + readsize = 256; + delay = 5; + ; + + memory "flash" + size = 1024; + blocksize = 128; + readsize = 256; + delay = 3; + ; + + memory "signature" + size = 3; + ; + + memory "lock" + size = 1; + ; + + memory "calibration" + size = 1; + ; + + memory "fuse" + size = 1; + ; +; + +#------------------------------------------------------------ +# ATtiny12 +#------------------------------------------------------------ + +part + id = "t12"; + desc = "ATtiny12"; + stk500_devcode = 0x12; + avr910_devcode = 0x55; + signature = 0x1e 0x90 0x05; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x00, + 0x68, 0x78, 0x68, 0x68, 0x00, 0x00, 0x68, 0x78, + 0x78, 0x00, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 50; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + memory "eeprom" + size = 64; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 x x x x x x x x", + "x x a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 x x x x x x x x", + "x x a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 8; + blocksize = 64; + readsize = 256; + ; + + memory "flash" + size = 1024; + min_write_delay = 4500; + max_write_delay = 20000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 5; + blocksize = 128; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x o o x"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "fuse" + size = 1; + read = "0 1 0 1 0 0 0 0 x x x x x x x x", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 x x x x x", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; +; + +#------------------------------------------------------------ +# ATtiny13 +#------------------------------------------------------------ + +part + id = "t13"; + desc = "ATtiny13"; + has_debugwire = yes; + flash_instr = 0xB4, 0x0E, 0x1E; + eeprom_instr = 0xBB, 0xFE, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x0E, 0xB4, 0x0E, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; + stk500_devcode = 0x14; + signature = 0x1e 0x90 0x07; + chip_erase_delay = 4000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x66, + 0x68, 0x78, 0x68, 0x68, 0x7A, 0x6A, 0x68, 0x78, + 0x78, 0x7D, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 90; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + ocdrev = 0; + + memory "eeprom" + size = 64; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "x x a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "x x a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 5; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 1024; + page_size = 32; + num_pages = 32; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 0 0 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 0 0 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 0 0 a8", + " a7 a6 a5 a4 x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 2; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + ; + +; + + +#------------------------------------------------------------ +# ATtiny15 +#------------------------------------------------------------ + +part + id = "t15"; + desc = "ATtiny15"; + stk500_devcode = 0x13; + avr910_devcode = 0x56; + signature = 0x1e 0x90 0x06; + chip_erase_delay = 8200; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x00, + 0x68, 0x78, 0x68, 0x68, 0x00, 0x00, 0x68, 0x78, + 0x78, 0x00, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + hvspcmdexedelay = 5; + synchcycles = 6; + latchcycles = 16; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 50; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + memory "eeprom" + size = 64; + min_write_delay = 8200; + max_write_delay = 8200; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 x x x x x x x x", + "x x a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 x x x x x x x x", + "x x a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 10; + blocksize = 64; + readsize = 256; + ; + + memory "flash" + size = 1024; + min_write_delay = 4100; + max_write_delay = 4100; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 5; + blocksize = 128; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x o o x"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "fuse" + size = 1; + read = "0 1 0 1 0 0 0 0 x x x x x x x x", + "x x x x x x x x o o o o x x o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 x x x x x", + "x x x x x x x x i i i i 1 1 i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; +; + +#------------------------------------------------------------ +# AT90s1200 +#------------------------------------------------------------ + +part + id = "1200"; + desc = "AT90S1200"; + is_at90s1200 = yes; + stk500_devcode = 0x33; + avr910_devcode = 0x13; + signature = 0x1e 0x90 0x01; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 1; + bytedelay = 0; + pollindex = 0; + pollvalue = 0xFF; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 1; + + memory "eeprom" + size = 64; + min_write_delay = 4000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 x x x x x x x x", + "x x a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 x x x x x x x x", + "x x a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 20; + blocksize = 32; + readsize = 256; + ; + memory "flash" + size = 1024; + min_write_delay = 4000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x02; + delay = 15; + blocksize = 128; + readsize = 256; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + ; + memory "lock" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + ; + ; + +#------------------------------------------------------------ +# AT90s4414 +#------------------------------------------------------------ + +part + id = "4414"; + desc = "AT90S4414"; + stk500_devcode = 0x50; + avr910_devcode = 0x28; + signature = 0x1e 0x92 0x01; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 1; + + memory "eeprom" + size = 256; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0x80; + readback_p2 = 0x7f; + read = " 1 0 1 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 64; + readsize = 256; + ; + memory "flash" + size = 4096; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0x7f; + readback_p2 = 0x7f; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 64; + readsize = 256; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + ; + +#------------------------------------------------------------ +# AT90s2313 +#------------------------------------------------------------ + +part + id = "2313"; + desc = "AT90S2313"; + stk500_devcode = 0x40; + avr910_devcode = 0x20; + signature = 0x1e 0x91 0x01; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 1; + + memory "eeprom" + size = 128; + min_write_delay = 4000; + max_write_delay = 9000; + readback_p1 = 0x80; + readback_p2 = 0x7f; + read = "1 0 1 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 64; + readsize = 256; + ; + memory "flash" + size = 2048; + min_write_delay = 4000; + max_write_delay = 9000; + readback_p1 = 0x7f; + readback_p2 = 0x7f; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x i i x", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + ; + +#------------------------------------------------------------ +# AT90s2333 +#------------------------------------------------------------ + +part + id = "2333"; +##### WARNING: No XML file for device 'AT90S2333'! ##### + desc = "AT90S2333"; + stk500_devcode = 0x42; + avr910_devcode = 0x34; + signature = 0x1e 0x91 0x05; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 1; + + memory "eeprom" + size = 128; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0x00; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + + memory "flash" + size = 2048; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + pwroff_after_write = yes; + read = "0 1 0 1 0 0 0 0 x x x x x x x x", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 i i i i i", + "x x x x x x x x x x x x x x x x"; + ; + memory "lock" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x o o x"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + ; + ; + + +#------------------------------------------------------------ +# AT90s2343 (also AT90s2323 and ATtiny22) +#------------------------------------------------------------ + +part + id = "2343"; + desc = "AT90S2343"; + stk500_devcode = 0x43; + avr910_devcode = 0x4c; + signature = 0x1e 0x91 0x03; + chip_erase_delay = 18000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x00, + 0x68, 0x78, 0x68, 0x68, 0x00, 0x00, 0x68, 0x78, + 0x78, 0x00, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 0; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 50; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + memory "eeprom" + size = 128; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0x00; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 64; + readsize = 256; + ; + memory "flash" + size = 2048; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 128; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x o o o x x x x o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 1 1 1 1 i", + "x x x x x x x x x x x x x x x x"; + ; + memory "lock" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x o o o x x x x o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + ; + ; + + +#------------------------------------------------------------ +# AT90s4433 +#------------------------------------------------------------ + +part + id = "4433"; + desc = "AT90S4433"; + stk500_devcode = 0x51; + avr910_devcode = 0x30; + signature = 0x1e 0x92 0x03; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 1; + + memory "eeprom" + size = 256; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0x00; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0 x x x x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0 x x x x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + memory "flash" + size = 4096; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + pwroff_after_write = yes; + read = "0 1 0 1 0 0 0 0 x x x x x x x x", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 i i i i i", + "x x x x x x x x x x x x x x x x"; + ; + memory "lock" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x o o x"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + ; + ; + +#------------------------------------------------------------ +# AT90s4434 +#------------------------------------------------------------ + +part + id = "4434"; +##### WARNING: No XML file for device 'AT90S4434'! ##### + desc = "AT90S4434"; + stk500_devcode = 0x52; + avr910_devcode = 0x6c; + signature = 0x1e 0x92 0x02; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + memory "eeprom" + size = 256; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0x00; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0 x x x x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0 x x x x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + ; + memory "flash" + size = 4096; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + read = "0 1 0 1 0 0 0 0 x x x x x x x x", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 i i i i i", + "x x x x x x x x x x x x x x x x"; + ; + memory "lock" + size = 1; + min_write_delay = 9000; + max_write_delay = 20000; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x o o x"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + ; + ; + +#------------------------------------------------------------ +# AT90s8515 +#------------------------------------------------------------ + +part + id = "8515"; + desc = "AT90S8515"; + stk500_devcode = 0x60; + avr910_devcode = 0x38; + signature = 0x1e 0x93 0x01; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 1; + + memory "eeprom" + size = 512; + min_write_delay = 4000; + max_write_delay = 9000; + readback_p1 = 0x80; + readback_p2 = 0x7f; + read = " 1 0 1 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + memory "flash" + size = 8192; + min_write_delay = 4000; + max_write_delay = 9000; + readback_p1 = 0x7f; + readback_p2 = 0x7f; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + ; + +#------------------------------------------------------------ +# AT90s8535 +#------------------------------------------------------------ + +part + id = "8535"; + desc = "AT90S8535"; + stk500_devcode = 0x61; + avr910_devcode = 0x68; + signature = 0x1e 0x93 0x03; + chip_erase_delay = 20000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 1; + + memory "eeprom" + size = 512; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0x00; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + memory "flash" + size = 8192; + min_write_delay = 9000; + max_write_delay = 20000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write_lo = " 0 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + write_hi = " 0 1 0 0 1 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 128; + readsize = 256; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "fuse" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x x x o"; + write = "1 0 1 0 1 1 0 0 1 0 1 1 1 1 1 i", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x o o x x x x x x"; + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + ; + +#------------------------------------------------------------ +# ATmega103 +#------------------------------------------------------------ + +part + id = "m103"; + desc = "ATmega103"; + stk500_devcode = 0xB1; + avr910_devcode = 0x41; + signature = 0x1e 0x97 0x01; + chip_erase_delay = 112000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x8E, 0x9E, 0x2E, 0x3E, 0xAE, 0xBE, + 0x4E, 0x5E, 0xCE, 0xDE, 0x6E, 0x7E, 0xEE, 0xDE, + 0x66, 0x76, 0xE6, 0xF6, 0x6A, 0x7A, 0xEA, 0x7A, + 0x7F, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 15; + chiperasepolltimeout = 0; + programfusepulsewidth = 2; + programfusepolltimeout = 0; + programlockpulsewidth = 0; + programlockpolltimeout = 10; + + memory "eeprom" + size = 4096; + min_write_delay = 4000; + max_write_delay = 9000; + readback_p1 = 0x80; + readback_p2 = 0x7f; + read = " 1 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 64; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 131072; + page_size = 256; + num_pages = 512; + min_write_delay = 22000; + max_write_delay = 56000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x11; + delay = 70; + blocksize = 256; + readsize = 256; + ; + + memory "fuse" + size = 1; + read = "0 1 0 1 0 0 0 0 x x x x x x x x", + "x x x x x x x x x x o x o 1 o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 1 i 1 i i", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x o o x"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 i i 1", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + + +#------------------------------------------------------------ +# ATmega64 +#------------------------------------------------------------ + +part + id = "m64"; + desc = "ATmega64"; + has_jtag = yes; + stk500_devcode = 0xA0; + avr910_devcode = 0x45; + signature = 0x1e 0x96 0x02; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x22; + spmcr = 0x68; + allowfullpagebitstream = yes; + + ocdrev = 2; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 2048; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 20; + blocksize = 64; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 4; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + + + + +#------------------------------------------------------------ +# ATmega128 +#------------------------------------------------------------ + +part + id = "m128"; + desc = "ATmega128"; + has_jtag = yes; + stk500_devcode = 0xB2; + avr910_devcode = 0x43; + signature = 0x1e 0x97 0x02; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x22; + spmcr = 0x68; + rampz = 0x3b; + allowfullpagebitstream = yes; + + ocdrev = 1; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 4096; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 12; + blocksize = 64; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 131072; + page_size = 256; + num_pages = 512; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 4; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90CAN128 +#------------------------------------------------------------ + +part + id = "c128"; + desc = "AT90CAN128"; + has_jtag = yes; + stk500_devcode = 0xB3; +# avr910_devcode = 0x43; + signature = 0x1e 0x97 0x81; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + eecr = 0x3f; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 4096; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + + mode = 0x41; + delay = 20; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 131072; + page_size = 256; + num_pages = 512; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90CAN64 +#------------------------------------------------------------ + +part + id = "c64"; + desc = "AT90CAN64"; + has_jtag = yes; + stk500_devcode = 0xB3; +# avr910_devcode = 0x43; + signature = 0x1e 0x96 0x81; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + eecr = 0x3f; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 2048; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + + mode = 0x41; + delay = 20; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90CAN32 +#------------------------------------------------------------ + +part + id = "c32"; + desc = "AT90CAN32"; + has_jtag = yes; + stk500_devcode = 0xB3; +# avr910_devcode = 0x43; + signature = 0x1e 0x95 0x81; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + eecr = 0x3f; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 1024; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + + mode = 0x41; + delay = 20; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 256; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + + +#------------------------------------------------------------ +# ATmega16 +#------------------------------------------------------------ + +part + id = "m16"; + desc = "ATmega16"; + has_jtag = yes; + stk500_devcode = 0x82; + avr910_devcode = 0x74; + signature = 0x1e 0x94 0x03; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 100; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + allowfullpagebitstream = yes; + + ocdrev = 2; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 512; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x04; + delay = 10; + blocksize = 128; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "calibration" + size = 4; + + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + ; + + +#------------------------------------------------------------ +# ATmega164P +#------------------------------------------------------------ + +# close to ATmega16 + +part parent "m16" + id = "m164p"; + desc = "ATmega164P"; + signature = 0x1e 0x94 0x0a; + + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + allowfullpagebitstream = no; + chip_erase_delay = 55000; + + ocdrev = 3; + ; + + +#------------------------------------------------------------ +# ATmega324P +#------------------------------------------------------------ + +# similar to ATmega164P + +part + id = "m324p"; + desc = "ATmega324P"; + has_jtag = yes; + stk500_devcode = 0x82; # no STK500v1 support, use the ATmega16 one + avr910_devcode = 0x74; + signature = 0x1e 0x95 0x08; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 55000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 1024; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 128; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 128; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x 1 1 1 1 1 i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + + +#------------------------------------------------------------ +# ATmega324PA +#------------------------------------------------------------ + +# similar to ATmega324P + +part parent "m324p" + id = "m324pa"; + desc = "ATmega324PA"; + signature = 0x1e 0x95 0x11; + + ocdrev = 3; + ; + + +#------------------------------------------------------------ +# ATmega644 +#------------------------------------------------------------ + +# similar to ATmega164 + +part + id = "m644"; + desc = "ATmega644"; + has_jtag = yes; + stk500_devcode = 0x82; # no STK500v1 support, use the ATmega16 one + avr910_devcode = 0x74; + signature = 0x1e 0x96 0x09; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 55000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 2048; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 128; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x 1 1 1 1 1 i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega644P +#------------------------------------------------------------ + +# similar to ATmega164p + +part parent "m644" + id = "m644p"; + desc = "ATmega644P"; + signature = 0x1e 0x96 0x0a; + + ocdrev = 3; + ; + + + +#------------------------------------------------------------ +# ATmega1284P +#------------------------------------------------------------ + +# similar to ATmega164p + +part + id = "m1284p"; + desc = "ATmega1284P"; + has_jtag = yes; + stk500_devcode = 0x82; # no STK500v1 support, use the ATmega16 one + avr910_devcode = 0x74; + signature = 0x1e 0x97 0x05; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 55000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 4096; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 128; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 131072; + page_size = 256; + num_pages = 512; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 256; + readsize = 256; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x 1 1 1 1 1 i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + + + +#------------------------------------------------------------ +# ATmega162 +#------------------------------------------------------------ + +part + id = "m162"; + desc = "ATmega162"; + has_jtag = yes; + stk500_devcode = 0x83; + avr910_devcode = 0x63; + signature = 0x1e 0x94 0x04; + chip_erase_delay = 9000; + pagel = 0xd7; + bs2 = 0xa0; + + idr = 0x04; + spmcr = 0x57; + allowfullpagebitstream = yes; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + ocdrev = 2; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + mode = 0x41; + delay = 10; + blocksize = 128; + readsize = 256; + + ; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 512; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 16000; + max_write_delay = 16000; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 16000; + max_write_delay = 16000; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 16000; + max_write_delay = 16000; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x 1 1 1 1 1 i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 16000; + max_write_delay = 16000; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "signature" + size = 3; + + read = "0 0 1 1 0 0 0 0 0 0 x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + + read = "0 0 1 1 1 0 0 0 0 0 x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; +; + + + +#------------------------------------------------------------ +# ATmega163 +#------------------------------------------------------------ + +part + id = "m163"; + desc = "ATmega163"; + stk500_devcode = 0x81; + avr910_devcode = 0x64; + signature = 0x1e 0x94 0x02; + chip_erase_delay = 32000; + pagel = 0xd7; + bs2 = 0xa0; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 30; + programfusepulsewidth = 0; + programfusepolltimeout = 2; + programlockpulsewidth = 0; + programlockpolltimeout = 2; + + + memory "eeprom" + size = 512; + min_write_delay = 4000; + max_write_delay = 4000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 16000; + max_write_delay = 16000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x11; + delay = 20; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o x x o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i 1 1 i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x 1 o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x 1 1 1 1 1 i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x 0 x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega169 +#------------------------------------------------------------ + +part + id = "m169"; + desc = "ATmega169"; + has_jtag = yes; + stk500_devcode = 0x85; + avr910_devcode = 0x78; + signature = 0x1e 0x94 0x05; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + + ocdrev = 2; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 512; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + ; + + memory "lock" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega329 +#------------------------------------------------------------ + +part + id = "m329"; + desc = "ATmega329"; + has_jtag = yes; +# stk500_devcode = 0x85; # no STK500 support, only STK500v2 +# avr910_devcode = 0x?; # try the ATmega169 one: + avr910_devcode = 0x75; + signature = 0x1e 0x95 0x03; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 1024; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 128; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega329P +#------------------------------------------------------------ +# Identical to ATmega329 except of the signature + +part parent "m329" + id = "m329p"; + desc = "ATmega329P"; + signature = 0x1e 0x95 0x0b; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# ATmega3290 +#------------------------------------------------------------ + +# identical to ATmega329 + +part parent "m329" + id = "m3290"; + desc = "ATmega3290"; + signature = 0x1e 0x95 0x04; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# ATmega3290P +#------------------------------------------------------------ + +# identical to ATmega3290 except of the signature + +part parent "m3290" + id = "m3290p"; + desc = "ATmega3290P"; + signature = 0x1e 0x95 0x0c; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# ATmega649 +#------------------------------------------------------------ + +part + id = "m649"; + desc = "ATmega649"; + has_jtag = yes; +# stk500_devcode = 0x85; # no STK500 support, only STK500v2 +# avr910_devcode = 0x?; # try the ATmega169 one: + avr910_devcode = 0x75; + signature = 0x1e 0x96 0x03; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 2048; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega6490 +#------------------------------------------------------------ + +# identical to ATmega649 + +part parent "m649" + id = "m6490"; + desc = "ATmega6490"; + signature = 0x1e 0x96 0x04; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# ATmega32 +#------------------------------------------------------------ + +part + id = "m32"; + desc = "ATmega32"; + has_jtag = yes; + stk500_devcode = 0x91; + avr910_devcode = 0x72; + signature = 0x1e 0x95 0x02; + chip_erase_delay = 9000; + pagel = 0xd7; + bs2 = 0xa0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + allowfullpagebitstream = yes; + + ocdrev = 2; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 1024; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x04; + delay = 10; + blocksize = 64; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 128; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 4; + read = "0 0 1 1 1 0 0 0 0 0 x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega161 +#------------------------------------------------------------ + +part + id = "m161"; + desc = "ATmega161"; + stk500_devcode = 0x80; + avr910_devcode = 0x60; + signature = 0x1e 0x94 0x01; + chip_erase_delay = 28000; + pagel = 0xd7; + bs2 = 0xa0; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 30; + programfusepulsewidth = 0; + programfusepolltimeout = 2; + programlockpulsewidth = 0; + programlockpolltimeout = 2; + + memory "eeprom" + size = 512; + min_write_delay = 3400; + max_write_delay = 3400; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 5; + blocksize = 128; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 14000; + max_write_delay = 14000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 16; + blocksize = 128; + readsize = 256; + ; + + memory "fuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 0 0 0 0 x x x x x x x x", + "x x x x x x x x x o x o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 x x x x x", + "x x x x x x x x 1 i 1 i i i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + + +#------------------------------------------------------------ +# ATmega8 +#------------------------------------------------------------ + +part + id = "m8"; + desc = "ATmega8"; + stk500_devcode = 0x70; + avr910_devcode = 0x76; + signature = 0x1e 0x93 0x07; + pagel = 0xd7; + bs2 = 0xc2; + chip_erase_delay = 10000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 2; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + memory "eeprom" + size = 512; + page_size = 4; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 20; + blocksize = 128; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 0 x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 0 x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 10; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 4; + read = "0 0 1 1 1 0 0 0 0 0 x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + + + +#------------------------------------------------------------ +# ATmega8515 +#------------------------------------------------------------ + +part + id = "m8515"; + desc = "ATmega8515"; + stk500_devcode = 0x63; + avr910_devcode = 0x3A; + signature = 0x1e 0x93 0x06; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + memory "eeprom" + size = 512; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 20; + blocksize = 128; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 0 x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 0 x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 4; + read = "0 0 1 1 1 0 0 0 0 0 x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + + + + +#------------------------------------------------------------ +# ATmega8535 +#------------------------------------------------------------ + +part + id = "m8535"; + desc = "ATmega8535"; + stk500_devcode = 0x64; + avr910_devcode = 0x69; + signature = 0x1e 0x93 0x08; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + memory "eeprom" + size = 512; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + mode = 0x04; + delay = 20; + blocksize = 128; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 0 x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 0 x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 2000; + max_write_delay = 2000; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 4; + read = "0 0 1 1 1 0 0 0 0 0 x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + + +#------------------------------------------------------------ +# ATtiny26 +#------------------------------------------------------------ + +part + id = "t26"; + desc = "ATtiny26"; + stk500_devcode = 0x21; + avr910_devcode = 0x5e; + signature = 0x1e 0x91 0x09; + pagel = 0xb3; + bs2 = 0xb2; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0xC4, 0xE4, 0xC4, 0xE4, 0xCC, 0xEC, 0xCC, 0xEC, + 0xD4, 0xF4, 0xD4, 0xF4, 0xDC, 0xFC, 0xDC, 0xFC, + 0xC8, 0xE8, 0xD8, 0xF8, 0x4C, 0x6C, 0x5C, 0x7C, + 0xEC, 0xBC, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 2; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + memory "eeprom" + size = 128; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + mode = 0x04; + delay = 10; + blocksize = 64; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 2048; + page_size = 32; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 16; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x x o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 1 i i", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x x x x i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 4; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + +; + + +#------------------------------------------------------------ +# ATtiny261 +#------------------------------------------------------------ +# Close to ATtiny26 + +part + id = "t261"; + desc = "ATtiny261"; + has_debugwire = yes; + flash_instr = 0xB4, 0x00, 0x10; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x00, 0xB4, 0x00, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +# stk500_devcode = 0x21; +# avr910_devcode = 0x5e; + signature = 0x1e 0x91 0x0c; + pagel = 0xb3; + bs2 = 0xb2; + chip_erase_delay = 4000; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0xC4, 0xE4, 0xC4, 0xE4, 0xCC, 0xEC, 0xCC, 0xEC, + 0xD4, 0xF4, 0xD4, 0xF4, 0xDC, 0xFC, 0xDC, 0xFC, + 0xC8, 0xE8, 0xD8, 0xF8, 0x4C, 0x6C, 0x5C, 0x7C, + 0xEC, 0xBC, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 2; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + size = 128; + page_size = 4; + num_pages = 32; + min_write_delay = 4000; + max_write_delay = 4000; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read = "1 0 1 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 x x x x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 2048; + page_size = 32; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x x x x a9 a8", + " a7 a6 a5 a4 x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x x o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 1 i i", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x x x o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + +; + + +#------------------------------------------------------------ +# ATtiny461 +#------------------------------------------------------------ +# Close to ATtiny261 + +part + id = "t461"; + desc = "ATtiny461"; + has_debugwire = yes; + flash_instr = 0xB4, 0x00, 0x10; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x00, 0xB4, 0x00, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +# stk500_devcode = 0x21; +# avr910_devcode = 0x5e; + signature = 0x1e 0x92 0x08; + pagel = 0xb3; + bs2 = 0xb2; + chip_erase_delay = 4000; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0xC4, 0xE4, 0xC4, 0xE4, 0xCC, 0xEC, 0xCC, 0xEC, + 0xD4, 0xF4, 0xD4, 0xF4, 0xDC, 0xFC, 0xDC, 0xFC, + 0xC8, 0xE8, 0xD8, 0xF8, 0x4C, 0x6C, 0x5C, 0x7C, + 0xEC, 0xBC, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 2; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + size = 256; + page_size = 4; + num_pages = 64; + min_write_delay = 4000; + max_write_delay = 4000; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read = " 1 0 1 0 0 0 0 0 x x x x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0 x x x x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 4096; + page_size = 64; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x x o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 1 i i", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x x x o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + +; + + +#------------------------------------------------------------ +# ATtiny861 +#------------------------------------------------------------ +# Close to ATtiny461 + +part + id = "t861"; + desc = "ATtiny861"; + has_debugwire = yes; + flash_instr = 0xB4, 0x00, 0x10; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x00, 0xB4, 0x00, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +# stk500_devcode = 0x21; +# avr910_devcode = 0x5e; + signature = 0x1e 0x93 0x0d; + pagel = 0xb3; + bs2 = 0xb2; + chip_erase_delay = 4000; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 0; + + pp_controlstack = + 0xC4, 0xE4, 0xC4, 0xE4, 0xCC, 0xEC, 0xCC, 0xEC, + 0xD4, 0xF4, 0xD4, 0xF4, 0xDC, 0xFC, 0xDC, 0xFC, + 0xC8, 0xE8, 0xD8, 0xF8, 0x4C, 0x6C, 0x5C, 0x7C, + 0xEC, 0xBC, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 2; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + size = 512; + num_pages = 128; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4000; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read = " 1 0 1 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0 x x x x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read_lo = " 0 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 x x x x x x x x", + "x x x x x x x x x x x x x x o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 1 1 1 i i", + "x x x x x x x x x x x x x x x x"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x x x o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + +; + + +#------------------------------------------------------------ +# ATmega48 +#------------------------------------------------------------ + +part + id = "m48"; + desc = "ATmega48"; + has_debugwire = yes; + flash_instr = 0xB6, 0x01, 0x11; + eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, + 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, + 0x99, 0xF9, 0xBB, 0xAF; + stk500_devcode = 0x59; +# avr910_devcode = 0x; + signature = 0x1e 0x92 0x05; + pagel = 0xd7; + bs2 = 0xc2; + chip_erase_delay = 45000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + page_size = 4; + size = 256; + min_write_delay = 3600; + max_write_delay = 3600; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x x x x", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 4096; + page_size = 64; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x x x o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega48P +#------------------------------------------------------------ + +part parent "m48" + id = "m48p"; + desc = "ATmega48P"; + signature = 0x1e 0x92 0x0a; + + ocdrev = 1; + ; + +#------------------------------------------------------------ +# ATmega88 +#------------------------------------------------------------ + +part + id = "m88"; + desc = "ATmega88"; + has_debugwire = yes; + flash_instr = 0xB6, 0x01, 0x11; + eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, + 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, + 0x99, 0xF9, 0xBB, 0xAF; + stk500_devcode = 0x73; +# avr910_devcode = 0x; + signature = 0x1e 0x93 0x0a; + pagel = 0xd7; + bs2 = 0xc2; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + page_size = 4; + size = 512; + min_write_delay = 3600; + max_write_delay = 3600; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega88P +#------------------------------------------------------------ + +part parent "m88" + id = "m88p"; + desc = "ATmega88P"; + signature = 0x1e 0x93 0x0f; + + ocdrev = 1; + ; + +#------------------------------------------------------------ +# ATmega168 +#------------------------------------------------------------ + +part + id = "m168"; + desc = "ATmega168"; + has_debugwire = yes; + flash_instr = 0xB6, 0x01, 0x11; + eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, + 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, + 0x99, 0xF9, 0xBB, 0xAF; + stk500_devcode = 0x86; + # avr910_devcode = 0x; + signature = 0x1e 0x94 0x06; + pagel = 0xd7; + bs2 = 0xc2; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + page_size = 4; + size = 512; + min_write_delay = 3600; + max_write_delay = 3600; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; +; + +#------------------------------------------------------------ +# ATmega168P +#------------------------------------------------------------ + +part parent "m168" + id = "m168p"; + desc = "ATmega168P"; + signature = 0x1e 0x94 0x0b; + + ocdrev = 1; +; + +#------------------------------------------------------------ +# ATtiny88 +#------------------------------------------------------------ + +part + id = "t88"; + desc = "ATtiny88"; + has_debugwire = yes; + flash_instr = 0xB6, 0x01, 0x11; + eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, + 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, + 0x99, 0xF9, 0xBB, 0xAF; + stk500_devcode = 0x73; +# avr910_devcode = 0x; + signature = 0x1e 0x93 0x11; + pagel = 0xd7; + bs2 = 0xc2; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + page_size = 4; + size = 64; + min_write_delay = 3600; + max_write_delay = 3600; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 64; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega328 +#------------------------------------------------------------ + +part + id = "m328"; + desc = "ATmega328"; + has_debugwire = yes; + flash_instr = 0xB6, 0x01, 0x11; + eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, + 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, + 0x99, 0xF9, 0xBB, 0xAF; + stk500_devcode = 0x86; + # avr910_devcode = 0x; + signature = 0x1e 0x95 0x14; + pagel = 0xd7; + bs2 = 0xc2; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; + page_size = 4; + size = 1024; + min_write_delay = 3600; + max_write_delay = 3600; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x x a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 128; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x x x o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; +; + +part parent "m328" + id = "m328p"; + desc = "ATmega328P"; + signature = 0x1e 0x95 0x0F; + + ocdrev = 1; +; + +#------------------------------------------------------------ +# ATtiny2313 +#------------------------------------------------------------ + +part + id = "t2313"; + desc = "ATtiny2313"; + has_debugwire = yes; + flash_instr = 0xB2, 0x0F, 0x1F; + eeprom_instr = 0xBB, 0xFE, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBA, 0x0F, 0xB2, 0x0F, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; + stk500_devcode = 0x23; +## Use the ATtiny26 devcode: + avr910_devcode = 0x5e; + signature = 0x1e 0x91 0x0a; + pagel = 0xD4; + bs2 = 0xD6; + reset = io; + chip_erase_delay = 9000; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0E, 0x1E, 0x2E, 0x3E, 0x2E, 0x3E, + 0x4E, 0x5E, 0x4E, 0x5E, 0x6E, 0x7E, 0x6E, 0x7E, + 0x26, 0x36, 0x66, 0x76, 0x2A, 0x3A, 0x6A, 0x7A, + 0x2E, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 0; + + memory "eeprom" + size = 128; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 2048; + page_size = 32; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + +# The information in the data sheet of April/2004 is wrong, this works: + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + +# The information in the data sheet of April/2004 is wrong, this works: + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + +# The information in the data sheet of April/2004 is wrong, this works: + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny2313 has Signature Bytes: 0x1E 0x91 0x0A. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; +# The Tiny2313 has calibration data for both 4 MHz and 8 MHz. +# The information in the data sheet of April/2004 is wrong, this works: + + memory "calibration" + size = 2; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATtiny4313 +#------------------------------------------------------------ + +part + id = "t4313"; + desc = "ATtiny4313"; + has_debugwire = yes; + flash_instr = 0xB2, 0x0F, 0x1F; + eeprom_instr = 0xBB, 0xFE, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBA, 0x0F, 0xB2, 0x0F, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; + stk500_devcode = 0x23; +## Use the ATtiny26 devcode: + avr910_devcode = 0x5e; + signature = 0x1e 0x92 0x0d; + pagel = 0xD4; + bs2 = 0xD6; + reset = io; + chip_erase_delay = 9000; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0E, 0x1E, 0x2E, 0x3E, 0x2E, 0x3E, + 0x4E, 0x5E, 0x4E, 0x5E, 0x6E, 0x7E, 0x6E, 0x7E, + 0x26, 0x36, 0x66, 0x76, 0x2A, 0x3A, 0x6A, 0x7A, + 0x2E, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 0; + + memory "eeprom" + size = 256; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 4096; + page_size = 64; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny4313 has Signature Bytes: 0x1E 0x92 0x0D. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 2; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90PWM2 +#------------------------------------------------------------ + +part + id = "pwm2"; + desc = "AT90PWM2"; + has_debugwire = yes; + flash_instr = 0xB6, 0x01, 0x11; + eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, + 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, + 0x99, 0xF9, 0xBB, 0xAF; + stk500_devcode = 0x65; +## avr910_devcode = ?; + signature = 0x1e 0x93 0x81; + pagel = 0xD8; + bs2 = 0xE2; + reset = io; + chip_erase_delay = 9000; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + memory "eeprom" + size = 512; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 64; + readsize = 256; + ; +# AT90PWM2 has Signature Bytes: 0x1E 0x93 0x81. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90PWM3 +#------------------------------------------------------------ + +# Completely identical to AT90PWM2 (including the signature!) + +part parent "pwm2" + id = "pwm3"; + desc = "AT90PWM3"; + ; + +#------------------------------------------------------------ +# AT90PWM2B +#------------------------------------------------------------ +# Same as AT90PWM2 but different signature. + +part parent "pwm2" + id = "pwm2b"; + desc = "AT90PWM2B"; + signature = 0x1e 0x93 0x83; + + ocdrev = 1; + ; + +#------------------------------------------------------------ +# AT90PWM3B +#------------------------------------------------------------ + +# Completely identical to AT90PWM2B (including the signature!) + +part parent "pwm2b" + id = "pwm3b"; + desc = "AT90PWM3B"; + + ocdrev = 1; + ; + +#------------------------------------------------------------ +# AT90PWM316 +#------------------------------------------------------------ + +# Similar to AT90PWM3B, but with 16 kiB flash, 512 B EEPROM, and 1024 B SRAM. + +part parent "pwm3b" + id = "pwm316"; + desc = "AT90PWM316"; + signature = 0x1e 0x94 0x83; + + ocdrev = 1; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x21; + delay = 6; + blocksize = 128; + readsize = 256; + ; + ; + +#------------------------------------------------------------ +# ATtiny25 +#------------------------------------------------------------ + +part + id = "t25"; + desc = "ATtiny25"; + has_debugwire = yes; + flash_instr = 0xB4, 0x02, 0x12; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x02, 0xB4, 0x02, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +## no STK500 devcode in XML file, use the ATtiny45 one + stk500_devcode = 0x14; +## avr910_devcode = ?; +## Try the AT90S2313 devcode: + avr910_devcode = 0x20; + signature = 0x1e 0x91 0x08; + reset = io; + chip_erase_delay = 4500; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x66, + 0x68, 0x78, 0x68, 0x68, 0x7A, 0x6A, 0x68, 0x78, + 0x78, 0x7D, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + ocdrev = 1; + + memory "eeprom" + size = 128; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 2048; + page_size = 32; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny25 has Signature Bytes: 0x1E 0x91 0x08. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 2; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATtiny45 +#------------------------------------------------------------ + +part + id = "t45"; + desc = "ATtiny45"; + has_debugwire = yes; + flash_instr = 0xB4, 0x02, 0x12; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x02, 0xB4, 0x02, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; + stk500_devcode = 0x14; +## avr910_devcode = ?; +## Try the AT90S2313 devcode: + avr910_devcode = 0x20; + signature = 0x1e 0x92 0x06; + reset = io; + chip_erase_delay = 4500; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x66, + 0x68, 0x78, 0x68, 0x68, 0x7A, 0x6A, 0x68, 0x78, + 0x78, 0x7D, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + ocdrev = 1; + + memory "eeprom" + size = 256; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 4096; + page_size = 64; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny45 has Signature Bytes: 0x1E 0x92 0x08. (Data sheet 2586C-AVR-06/05 (doc2586.pdf) indicates otherwise!) + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 2; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATtiny85 +#------------------------------------------------------------ + +part + id = "t85"; + desc = "ATtiny85"; + has_debugwire = yes; + flash_instr = 0xB4, 0x02, 0x12; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x02, 0xB4, 0x02, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +## no STK500 devcode in XML file, use the ATtiny45 one + stk500_devcode = 0x14; +## avr910_devcode = ?; +## Try the AT90S2313 devcode: + avr910_devcode = 0x20; + signature = 0x1e 0x93 0x0b; + reset = io; + chip_erase_delay = 4500; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x66, + 0x68, 0x78, 0x68, 0x68, 0x7A, 0x6A, 0x68, 0x78, + 0x78, 0x7D, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x00; + hventerstabdelay = 100; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + ocdrev = 1; + + memory "eeprom" + size = 512; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny85 has Signature Bytes: 0x1E 0x93 0x08. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 2; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega640 +#------------------------------------------------------------ +# Almost same as ATmega1280, except for different memory sizes + +part + id = "m640"; + desc = "ATmega640"; + signature = 0x1e 0x96 0x08; + has_jtag = yes; +# stk500_devcode = 0xB2; +# avr910_devcode = 0x43; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 4096; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega1280 +#------------------------------------------------------------ + +part + id = "m1280"; + desc = "ATmega1280"; + signature = 0x1e 0x97 0x03; + has_jtag = yes; +# stk500_devcode = 0xB2; +# avr910_devcode = 0x43; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 4096; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 131072; + page_size = 256; + num_pages = 512; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega1281 +#------------------------------------------------------------ +# Identical to ATmega1280 + +part parent "m1280" + id = "m1281"; + desc = "ATmega1281"; + signature = 0x1e 0x97 0x04; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# ATmega2560 +#------------------------------------------------------------ + +part + id = "m2560"; + desc = "ATmega2560"; + signature = 0x1e 0x98 0x01; + has_jtag = yes; +# stk500_devcode = 0xB2; +# avr910_devcode = 0x43; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + allowfullpagebitstream = no; + + ocdrev = 4; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 4096; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 262144; + page_size = 256; + num_pages = 1024; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + load_ext_addr = " 0 1 0 0 1 1 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 0 a16", + " 0 0 0 0 0 0 0 0"; + + mode = 0x41; + delay = 10; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega2561 +#------------------------------------------------------------ + +part parent "m2560" + id = "m2561"; + desc = "ATmega2561"; + signature = 0x1e 0x98 0x02; + + ocdrev = 4; + ; + +#------------------------------------------------------------ +# ATmega128RFA1 +#------------------------------------------------------------ +# Identical to ATmega2561 but half the ROM + +part parent "m2561" + id = "m128rfa1"; + desc = "ATmega128RFA1"; + signature = 0x1e 0xa7 0x01; + chip_erase_delay = 55000; + bs2 = 0xE2; + + ocdrev = 3; + + memory "flash" + paged = yes; + size = 131072; + page_size = 256; + num_pages = 512; + min_write_delay = 50000; + max_write_delay = 50000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 256; + readsize = 256; + ; + ; + +#------------------------------------------------------------ +# ATmega256RFR2 +#------------------------------------------------------------ + +part parent "m2561" + id = "m256rfr2"; + desc = "ATmega256RFR2"; + signature = 0x1e 0xa8 0x02; + chip_erase_delay = 55000; + bs2 = 0xE2; + + + ocdrev = 4; + ; + +#------------------------------------------------------------ +# ATmega128RFR2 +#------------------------------------------------------------ + +part parent "m128rfa1" + id = "m128rfr2"; + desc = "ATmega128RFR2"; + signature = 0x1e 0xa7 0x02; + + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# ATmega64RFR2 +#------------------------------------------------------------ + +part parent "m128rfa1" + id = "m64rfr2"; + desc = "ATmega64RFR2"; + signature = 0x1e 0xa6 0x02; + + + ocdrev = 3; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 50000; + max_write_delay = 50000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 256; + readsize = 256; + ; + ; + +#------------------------------------------------------------ +# ATmega2564RFR2 +#------------------------------------------------------------ + +part parent "m256rfr2" + id = "m2564rfr2"; + desc = "ATmega2564RFR2"; + signature = 0x1e 0xa8 0x03; + ; + +#------------------------------------------------------------ +# ATmega1284RFR2 +#------------------------------------------------------------ + +part parent "m128rfr2" + id = "m1284rfr2"; + desc = "ATmega1284RFR2"; + signature = 0x1e 0xa7 0x03; + ; + +#------------------------------------------------------------ +# ATmega644RFR2 +#------------------------------------------------------------ + +part parent "m64rfr2" + id = "m644rfr2"; + desc = "ATmega644RFR2"; + signature = 0x1e 0xa6 0x03; + ; + +#------------------------------------------------------------ +# ATtiny24 +#------------------------------------------------------------ + +part + id = "t24"; + desc = "ATtiny24"; + has_debugwire = yes; + flash_instr = 0xB4, 0x07, 0x17; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x07, 0xB4, 0x07, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +## no STK500 devcode in XML file, use the ATtiny45 one + stk500_devcode = 0x14; +## avr910_devcode = ?; +## Try the AT90S2313 devcode: + avr910_devcode = 0x20; + signature = 0x1e 0x91 0x0b; + reset = io; + chip_erase_delay = 4500; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x66, + 0x68, 0x78, 0x68, 0x68, 0x7A, 0x6A, 0x68, 0x78, + 0x78, 0x7D, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x0F; + hventerstabdelay = 100; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 70; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + ocdrev = 1; + + memory "eeprom" + size = 128; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "x a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 2048; + page_size = 32; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x x a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny24 has Signature Bytes: 0x1E 0x91 0x0B. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x x x x x x x i i"; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATtiny44 +#------------------------------------------------------------ + +part + id = "t44"; + desc = "ATtiny44"; + has_debugwire = yes; + flash_instr = 0xB4, 0x07, 0x17; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x07, 0xB4, 0x07, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +## no STK500 devcode in XML file, use the ATtiny45 one + stk500_devcode = 0x14; +## avr910_devcode = ?; +## Try the AT90S2313 devcode: + avr910_devcode = 0x20; + signature = 0x1e 0x92 0x07; + reset = io; + chip_erase_delay = 4500; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x66, + 0x68, 0x78, 0x68, 0x68, 0x7A, 0x6A, 0x68, 0x78, + 0x78, 0x7D, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x0F; + hventerstabdelay = 100; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 70; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + ocdrev = 1; + + memory "eeprom" + size = 256; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 4096; + page_size = 64; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny44 has Signature Bytes: 0x1E 0x92 0x07. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x x x x x x x i i"; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATtiny84 +#------------------------------------------------------------ + +part + id = "t84"; + desc = "ATtiny84"; + has_debugwire = yes; + flash_instr = 0xB4, 0x07, 0x17; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x07, 0xB4, 0x07, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; +## no STK500 devcode in XML file, use the ATtiny45 one + stk500_devcode = 0x14; +## avr910_devcode = ?; +## Try the AT90S2313 devcode: + avr910_devcode = 0x20; + signature = 0x1e 0x93 0x0c; + reset = io; + chip_erase_delay = 4500; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + hvsp_controlstack = + 0x4C, 0x0C, 0x1C, 0x2C, 0x3C, 0x64, 0x74, 0x66, + 0x68, 0x78, 0x68, 0x68, 0x7A, 0x6A, 0x68, 0x78, + 0x78, 0x7D, 0x6D, 0x0C, 0x80, 0x40, 0x20, 0x10, + 0x11, 0x08, 0x04, 0x02, 0x03, 0x08, 0x04, 0x0F; + hventerstabdelay = 100; + hvspcmdexedelay = 0; + synchcycles = 6; + latchcycles = 1; + togglevtg = 1; + poweroffdelay = 25; + resetdelayms = 0; + resetdelayus = 70; + hvleavestabdelay = 100; + resetdelay = 25; + chiperasepolltimeout = 40; + chiperasetime = 0; + programfusepolltimeout = 25; + programlockpolltimeout = 25; + + ocdrev = 1; + + memory "eeprom" + size = 512; + paged = no; + page_size = 4; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x a8", + "a7 a6 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " x a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 32; + readsize = 256; + ; +# ATtiny84 has Signature Bytes: 0x1E 0x93 0x0C. + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x x x x x x x i i"; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATtiny43U +#------------------------------------------------------------ + +part + id = "t43u"; + desc = "ATtiny43u"; + has_debugwire = yes; + flash_instr = 0xB4, 0x07, 0x17; + eeprom_instr = 0xBB, 0xFF, 0xBB, 0xEE, 0xBB, 0xCC, 0xB2, 0x0D, + 0xBC, 0x07, 0xB4, 0x07, 0xBA, 0x0D, 0xBB, 0xBC, + 0x99, 0xE1, 0xBB, 0xAC; + stk500_devcode = 0x14; +## avr910_devcode = ?; +## Try the AT90S2313 devcode: + avr910_devcode = 0x20; + signature = 0x1e 0x92 0x0C; + reset = io; + chip_erase_delay = 1000; + + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + pp_controlstack = 0x0E, 0x1E, 0x0E, 0x1E, 0x2E, 0x3E, 0x2E, 0x3E, 0x4E, 0x5E, + 0x4E, 0x5E, 0x6E, 0x7E, 0x6E, 0x7E, 0x06, 0x16, 0x46, 0x56, + 0x0A, 0x1A, 0x4A, 0x5A, 0x1E, 0x7C, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + hvspcmdexedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 20; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + memory "eeprom" + size = 64; + paged = yes; + page_size = 4; + num_pages = 16; + min_write_delay = 4000; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = "1 0 1 0 0 0 0 0 0 0 0 x x x x x", + "0 0 a4 a3 a2 a1 a0 o o o o o o o o"; + + write = "1 1 0 0 0 0 0 0 0 0 0 x x x x x", + "0 0 a5 a4 a3 a2 a1 a0 i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x x", + " 0 0 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 5; + blocksize = 4; + readsize = 256; + ; + memory "flash" + paged = yes; + size = 4096; + page_size = 64; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x x a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 64; + readsize = 256; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + memory "lock" + size = 1; + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x x x x i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 4500; + max_write_delay = 4500; + ; + + memory "calibration" + size = 2; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 a0 o o o o o o o o"; + ; +; + +#------------------------------------------------------------ +# ATmega32u4 +#------------------------------------------------------------ + +part + id = "m32u4"; + desc = "ATmega32U4"; + signature = 0x1e 0x95 0x87; + has_jtag = yes; +# stk500_devcode = 0xB2; +# avr910_devcode = 0x43; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 1024; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 128; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90USB646 +#------------------------------------------------------------ + +part + id = "usb646"; + desc = "AT90USB646"; + signature = 0x1e 0x96 0x82; + has_jtag = yes; +# stk500_devcode = 0xB2; +# avr910_devcode = 0x43; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 2048; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x x a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90USB647 +#------------------------------------------------------------ +# identical to AT90USB646 + +part parent "usb646" + id = "usb647"; + desc = "AT90USB647"; + signature = 0x1e 0x96 0x82; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# AT90USB1286 +#------------------------------------------------------------ + +part + id = "usb1286"; + desc = "AT90USB1286"; + signature = 0x1e 0x97 0x82; + has_jtag = yes; +# stk500_devcode = 0xB2; +# avr910_devcode = 0x43; + chip_erase_delay = 9000; + pagel = 0xD7; + bs2 = 0xA0; + reset = dedicated; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + rampz = 0x3b; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 4096; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " x x x x a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 131072; + page_size = 256; + num_pages = 512; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 x x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 256; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x x i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 x x x x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 x x x x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90USB1287 +#------------------------------------------------------------ +# identical to AT90USB1286 + +part parent "usb1286" + id = "usb1287"; + desc = "AT90USB1287"; + signature = 0x1e 0x97 0x82; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# AT90USB162 +#------------------------------------------------------------ + +part + id = "usb162"; + desc = "AT90USB162"; + has_jtag = no; + has_debugwire = yes; + signature = 0x1e 0x94 0x82; + chip_erase_delay = 9000; + reset = io; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + pagel = 0xD7; + bs2 = 0xC6; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 512; + num_pages = 128; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# AT90USB82 +#------------------------------------------------------------ +# Changes against AT90USB162 (beside IDs) +# memory "flash" +# size = 8192; +# num_pages = 64; + +part + id = "usb82"; + desc = "AT90USB82"; + has_jtag = no; + has_debugwire = yes; + signature = 0x1e 0x93 0x82; + chip_erase_delay = 9000; + reset = io; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + pagel = 0xD7; + bs2 = 0xC6; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 512; + num_pages = 128; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 8192; + page_size = 128; + num_pages = 64; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega32U2 +#------------------------------------------------------------ +# Changes against AT90USB162 (beside IDs) +# memory "flash" +# size = 32768; +# num_pages = 256; +# memory "eeprom" +# size = 1024; +# num_pages = 256; +part + id = "m32u2"; + desc = "ATmega32U2"; + has_jtag = no; + has_debugwire = yes; + signature = 0x1e 0x95 0x8a; + chip_erase_delay = 9000; + reset = io; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + pagel = 0xD7; + bs2 = 0xC6; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 1024; + num_pages = 256; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 128; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; +#------------------------------------------------------------ +# ATmega16U2 +#------------------------------------------------------------ +# Changes against ATmega32U2 (beside IDs) +# memory "flash" +# size = 16384; +# num_pages = 128; +# memory "eeprom" +# size = 512; +# num_pages = 128; +part + id = "m16u2"; + desc = "ATmega16U2"; + has_jtag = no; + has_debugwire = yes; + signature = 0x1e 0x94 0x89; + chip_erase_delay = 9000; + reset = io; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + pagel = 0xD7; + bs2 = 0xC6; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 512; + num_pages = 128; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 128; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega8U2 +#------------------------------------------------------------ +# Changes against ATmega16U2 (beside IDs) +# memory "flash" +# size = 8192; +# page_size = 64; +# blocksize = 64; + +part + id = "m8u2"; + desc = "ATmega8U2"; + has_jtag = no; + has_debugwire = yes; + signature = 0x1e 0x93 0x89; + chip_erase_delay = 9000; + reset = io; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + pagel = 0xD7; + bs2 = 0xC6; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + ocdrev = 1; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 512; + num_pages = 128; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0x00; + readback_p2 = 0x00; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 0 0 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 20; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 8192; + page_size = 64; + num_pages = 128; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0x00; + readback_p2 = 0x00; + read_lo = " 0 0 1 0 0 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " x x x x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + "a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 64; + readsize = 256; + ; + + memory "lfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x i i i i i i i i"; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; + ; +#------------------------------------------------------------ +# ATmega325 +#------------------------------------------------------------ + +part + id = "m325"; + desc = "ATmega325"; + signature = 0x1e 0x95 0x05; + has_jtag = yes; +# stk500_devcode = 0x??; # No STK500v1 support? +# avr910_devcode = 0x??; # Try the ATmega16 one + avr910_devcode = 0x74; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 4; /* for parallel programming */ + size = 1024; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 0 0 0 0 a9 a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 32768; + page_size = 128; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 0 0 0 0 0", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 0 0 0 0 0", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 128; + readsize = 256; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 0 0 0 0 0", + "0 0 0 0 0 0 0 0 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "0 0 0 0 0 0 0 0 i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "0 0 0 0 0 0 0 0 i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "0 0 0 0 0 0 0 0 1 1 1 1 1 i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + + read = "0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega645 +#------------------------------------------------------------ + +part + id = "m645"; + desc = "ATmega645"; + signature = 0x1E 0x96 0x05; + has_jtag = yes; +# stk500_devcode = 0x??; # No STK500v1 support? +# avr910_devcode = 0x??; # Try the ATmega16 one + avr910_devcode = 0x74; + pagel = 0xd7; + bs2 = 0xa0; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 5; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + idr = 0x31; + spmcr = 0x57; + allowfullpagebitstream = no; + + ocdrev = 3; + + memory "eeprom" + paged = no; /* leave this "no" */ + page_size = 8; /* for parallel programming */ + size = 2048; + min_write_delay = 9000; + max_write_delay = 9000; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 0 0 0 a10 a9 a8", + " a7 a6 a5 a4 a3 0 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 10; + blocksize = 8; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 65536; + page_size = 256; + num_pages = 256; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 0 0 0 0 0", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 0 0 0 0 0", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " a15 a14 a13 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " 0 0 0 0 0 0 0 0"; + + mode = 0x41; + delay = 10; + blocksize = 128; + readsize = 256; + ; + + memory "lock" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 0 0 0 0 0", + "0 0 0 0 0 0 0 0 1 1 i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "lfuse" + size = 1; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "0 0 0 0 0 0 0 0 i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "hfuse" + size = 1; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "0 0 0 0 0 0 0 0 i i i i i i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "efuse" + size = 1; + + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "0 0 0 0 0 0 0 0 1 1 1 1 1 i i i"; + min_write_delay = 9000; + max_write_delay = 9000; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 a1 a0 o o o o o o o o"; + ; + + memory "calibration" + size = 1; + + read = "0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + ; + +#------------------------------------------------------------ +# ATmega3250 +#------------------------------------------------------------ + +part parent "m325" + id = "m3250"; + desc = "ATmega3250"; + signature = 0x1E 0x95 0x06; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# ATmega6450 +#------------------------------------------------------------ + +part parent "m645" + id = "m6450"; + desc = "ATmega6450"; + signature = 0x1E 0x96 0x06; + + ocdrev = 3; + ; + +#------------------------------------------------------------ +# AVR XMEGA family common values +#------------------------------------------------------------ + +part + id = ".xmega"; + desc = "AVR XMEGA family common values"; + has_pdi = yes; + nvm_base = 0x01c0; + mcu_base = 0x0090; + + memory "signature" + size = 3; + offset = 0x1000090; + ; + + memory "prodsig" + size = 0x32; + offset = 0x8e0200; + page_size = 0x32; + readsize = 0x32; + ; + + memory "fuse1" + size = 1; + offset = 0x8f0021; + ; + + memory "fuse2" + size = 1; + offset = 0x8f0022; + ; + + memory "fuse4" + size = 1; + offset = 0x8f0024; + ; + + memory "fuse5" + size = 1; + offset = 0x8f0025; + ; + + memory "lock" + size = 1; + offset = 0x8f0027; + ; + + memory "data" + # SRAM, only used to supply the offset + offset = 0x1000000; + ; +; + +#------------------------------------------------------------ +# ATxmega16A4U +#------------------------------------------------------------ + +part parent ".xmega" + id = "x16a4u"; + desc = "ATxmega16A4U"; + signature = 0x1e 0x94 0x41; + + memory "eeprom" + size = 0x400; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x4000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "apptable" + size = 0x1000; + offset = 0x803000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "boot" + size = 0x1000; + offset = 0x804000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "flash" + size = 0x5000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "usersig" + size = 0x100; + offset = 0x8e0400; + page_size = 0x100; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega16C4 +#------------------------------------------------------------ + +part parent "x16a4u" + id = "x16c4"; + desc = "ATxmega16C4"; + signature = 0x1e 0x95 0x44; +; + +#------------------------------------------------------------ +# ATxmega16D4 +#------------------------------------------------------------ + +part parent "x16a4u" + id = "x16d4"; + desc = "ATxmega16D4"; + signature = 0x1e 0x94 0x42; +; + +#------------------------------------------------------------ +# ATxmega16A4 +#------------------------------------------------------------ + +part parent "x16a4u" + id = "x16a4"; + desc = "ATxmega16A4"; + signature = 0x1e 0x94 0x41; + has_jtag = yes; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega32A4U +#------------------------------------------------------------ + +part parent ".xmega" + id = "x32a4u"; + desc = "ATxmega32A4U"; + signature = 0x1e 0x95 0x41; + + memory "eeprom" + size = 0x400; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x8000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "apptable" + size = 0x1000; + offset = 0x807000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "boot" + size = 0x1000; + offset = 0x808000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "flash" + size = 0x9000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "usersig" + size = 0x100; + offset = 0x8e0400; + page_size = 0x100; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega32C4 +#------------------------------------------------------------ + +part parent "x32a4u" + id = "x32c4"; + desc = "ATxmega32C4"; + signature = 0x1e 0x94 0x43; +; + +#------------------------------------------------------------ +# ATxmega32D4 +#------------------------------------------------------------ + +part parent "x32a4u" + id = "x32d4"; + desc = "ATxmega32D4"; + signature = 0x1e 0x95 0x42; +; + +#------------------------------------------------------------ +# ATxmega32A4 +#------------------------------------------------------------ + +part parent "x32a4u" + id = "x32a4"; + desc = "ATxmega32A4"; + signature = 0x1e 0x95 0x41; + has_jtag = yes; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega64A4U +#------------------------------------------------------------ + +part parent ".xmega" + id = "x64a4u"; + desc = "ATxmega64A4U"; + signature = 0x1e 0x96 0x46; + + memory "eeprom" + size = 0x800; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x10000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "apptable" + size = 0x1000; + offset = 0x80f000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "boot" + size = 0x1000; + offset = 0x810000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "flash" + size = 0x11000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "usersig" + size = 0x100; + offset = 0x8e0400; + page_size = 0x100; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega64C3 +#------------------------------------------------------------ + +part parent "x64a4u" + id = "x64c3"; + desc = "ATxmega64C3"; + signature = 0x1e 0x96 0x49; +; + +#------------------------------------------------------------ +# ATxmega64D3 +#------------------------------------------------------------ + +part parent "x64a4u" + id = "x64d3"; + desc = "ATxmega64D3"; + signature = 0x1e 0x96 0x4a; +; + +#------------------------------------------------------------ +# ATxmega64D4 +#------------------------------------------------------------ + +part parent "x64a4u" + id = "x64d4"; + desc = "ATxmega64D4"; + signature = 0x1e 0x96 0x47; +; + +#------------------------------------------------------------ +# ATxmega64A1 +#------------------------------------------------------------ + +part parent "x64a4u" + id = "x64a1"; + desc = "ATxmega64A1"; + signature = 0x1e 0x96 0x4e; + has_jtag = yes; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega64A1U +#------------------------------------------------------------ + +part parent "x64a1" + id = "x64a1u"; + desc = "ATxmega64A1U"; + signature = 0x1e 0x96 0x4e; +; + +#------------------------------------------------------------ +# ATxmega64A3 +#------------------------------------------------------------ + +part parent "x64a1" + id = "x64a3"; + desc = "ATxmega64A3"; + signature = 0x1e 0x96 0x42; +; + +#------------------------------------------------------------ +# ATxmega64A3U +#------------------------------------------------------------ + +part parent "x64a1" + id = "x64a3u"; + desc = "ATxmega64A3U"; + signature = 0x1e 0x96 0x42; +; + +#------------------------------------------------------------ +# ATxmega64A4 +#------------------------------------------------------------ + +part parent "x64a1" + id = "x64a4"; + desc = "ATxmega64A4"; + signature = 0x1e 0x96 0x46; +; + +#------------------------------------------------------------ +# ATxmega64B1 +#------------------------------------------------------------ + +part parent "x64a1" + id = "x64b1"; + desc = "ATxmega64B1"; + signature = 0x1e 0x96 0x52; +; + +#------------------------------------------------------------ +# ATxmega64B3 +#------------------------------------------------------------ + +part parent "x64a1" + id = "x64b3"; + desc = "ATxmega64B3"; + signature = 0x1e 0x96 0x51; +; + +#------------------------------------------------------------ +# ATxmega128C3 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x128c3"; + desc = "ATxmega128C3"; + signature = 0x1e 0x97 0x52; + + memory "eeprom" + size = 0x800; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x20000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "apptable" + size = 0x2000; + offset = 0x81e000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "boot" + size = 0x2000; + offset = 0x820000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "flash" + size = 0x22000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "usersig" + size = 0x200; + offset = 0x8e0400; + page_size = 0x200; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega128D3 +#------------------------------------------------------------ + +part parent "x128c3" + id = "x128d3"; + desc = "ATxmega128D3"; + signature = 0x1e 0x97 0x48; +; + +#------------------------------------------------------------ +# ATxmega128D4 +#------------------------------------------------------------ + +part parent "x128c3" + id = "x128d4"; + desc = "ATxmega128D4"; + signature = 0x1e 0x97 0x47; +; + +#------------------------------------------------------------ +# ATxmega128A1 +#------------------------------------------------------------ + +part parent "x128c3" + id = "x128a1"; + desc = "ATxmega128A1"; + signature = 0x1e 0x97 0x4c; + has_jtag = yes; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega128A1 revision D +#------------------------------------------------------------ + +part parent "x128a1" + id = "x128a1d"; + desc = "ATxmega128A1revD"; + signature = 0x1e 0x97 0x41; +; + +#------------------------------------------------------------ +# ATxmega128A1U +#------------------------------------------------------------ + +part parent "x128a1" + id = "x128a1u"; + desc = "ATxmega128A1U"; + signature = 0x1e 0x97 0x4c; +; + +#------------------------------------------------------------ +# ATxmega128A3 +#------------------------------------------------------------ + +part parent "x128a1" + id = "x128a3"; + desc = "ATxmega128A3"; + signature = 0x1e 0x97 0x42; +; + +#------------------------------------------------------------ +# ATxmega128A3U +#------------------------------------------------------------ + +part parent "x128a1" + id = "x128a3u"; + desc = "ATxmega128A3U"; + signature = 0x1e 0x97 0x42; +; + +#------------------------------------------------------------ +# ATxmega128A4 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x128a4"; + desc = "ATxmega128A4"; + signature = 0x1e 0x97 0x46; + has_jtag = yes; + + memory "eeprom" + size = 0x800; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x20000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "apptable" + size = 0x1000; + offset = 0x81f000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "boot" + size = 0x2000; + offset = 0x820000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "flash" + size = 0x22000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "usersig" + size = 0x200; + offset = 0x8e0400; + page_size = 0x200; + readsize = 0x100; + ; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega128A4U +#------------------------------------------------------------ + +part parent ".xmega" + id = "x128a4u"; + desc = "ATxmega128A4U"; + signature = 0x1e 0x97 0x46; + + memory "eeprom" + size = 0x800; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x20000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "apptable" + size = 0x1000; + offset = 0x81f000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "boot" + size = 0x2000; + offset = 0x820000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "flash" + size = 0x22000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "usersig" + size = 0x100; + offset = 0x8e0400; + page_size = 0x100; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega128B1 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x128b1"; + desc = "ATxmega128B1"; + signature = 0x1e 0x97 0x4d; + has_jtag = yes; + + memory "eeprom" + size = 0x800; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x20000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "apptable" + size = 0x2000; + offset = 0x81e000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "boot" + size = 0x2000; + offset = 0x820000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "flash" + size = 0x22000; + offset = 0x800000; + page_size = 0x100; + readsize = 0x100; + ; + + memory "usersig" + size = 0x100; + offset = 0x8e0400; + page_size = 0x100; + readsize = 0x100; + ; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega128B3 +#------------------------------------------------------------ + +part parent "x128b1" + id = "x128b3"; + desc = "ATxmega128B3"; + signature = 0x1e 0x97 0x4b; +; + +#------------------------------------------------------------ +# ATxmega192C3 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x192c3"; + desc = "ATxmega192C3"; + signature = 0x1e 0x97 0x51; + + memory "eeprom" + size = 0x800; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x30000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "apptable" + size = 0x2000; + offset = 0x82e000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "boot" + size = 0x2000; + offset = 0x830000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "flash" + size = 0x32000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "usersig" + size = 0x200; + offset = 0x8e0400; + page_size = 0x200; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega192D3 +#------------------------------------------------------------ + +part parent "x192c3" + id = "x192d3"; + desc = "ATxmega192D3"; + signature = 0x1e 0x97 0x49; +; + +#------------------------------------------------------------ +# ATxmega192A1 +#------------------------------------------------------------ + +part parent "x192c3" + id = "x192a1"; + desc = "ATxmega192A1"; + signature = 0x1e 0x97 0x4e; + has_jtag = yes; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega192A3 +#------------------------------------------------------------ + +part parent "x192a1" + id = "x192a3"; + desc = "ATxmega192A3"; + signature = 0x1e 0x97 0x44; +; + +#------------------------------------------------------------ +# ATxmega192A3U +#------------------------------------------------------------ + +part parent "x192a1" + id = "x192a3u"; + desc = "ATxmega192A3U"; + signature = 0x1e 0x97 0x44; +; + +#------------------------------------------------------------ +# ATxmega256C3 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x256c3"; + desc = "ATxmega256C3"; + signature = 0x1e 0x98 0x46; + + memory "eeprom" + size = 0x1000; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x40000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "apptable" + size = 0x2000; + offset = 0x83e000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "boot" + size = 0x2000; + offset = 0x840000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "flash" + size = 0x42000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "usersig" + size = 0x200; + offset = 0x8e0400; + page_size = 0x200; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega256D3 +#------------------------------------------------------------ + +part parent "x256c3" + id = "x256d3"; + desc = "ATxmega256D3"; + signature = 0x1e 0x98 0x44; +; + +#------------------------------------------------------------ +# ATxmega256A1 +#------------------------------------------------------------ + +part parent "x256c3" + id = "x256a1"; + desc = "ATxmega256A1"; + signature = 0x1e 0x98 0x46; + has_jtag = yes; + + memory "fuse0" + size = 1; + offset = 0x8f0020; + ; +; + +#------------------------------------------------------------ +# ATxmega256A3 +#------------------------------------------------------------ + +part parent "x256a1" + id = "x256a3"; + desc = "ATxmega256A3"; + signature = 0x1e 0x98 0x42; +; + +#------------------------------------------------------------ +# ATxmega256A3U +#------------------------------------------------------------ + +part parent "x256a1" + id = "x256a3u"; + desc = "ATxmega256A3U"; + signature = 0x1e 0x98 0x42; +; + +#------------------------------------------------------------ +# ATxmega256A3B +#------------------------------------------------------------ + +part parent "x256a1" + id = "x256a3b"; + desc = "ATxmega256A3B"; + signature = 0x1e 0x98 0x43; +; + +#------------------------------------------------------------ +# ATxmega256A3BU +#------------------------------------------------------------ + +part parent "x256a1" + id = "x256a3bu"; + desc = "ATxmega256A3BU"; + signature = 0x1e 0x98 0x43; +; + +#------------------------------------------------------------ +# ATxmega384C3 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x384c3"; + desc = "ATxmega384C3"; + signature = 0x1e 0x98 0x45; + + memory "eeprom" + size = 0x1000; + offset = 0x8c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x60000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "apptable" + size = 0x2000; + offset = 0x85e000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "boot" + size = 0x2000; + offset = 0x860000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "flash" + size = 0x62000; + offset = 0x800000; + page_size = 0x200; + readsize = 0x100; + ; + + memory "usersig" + size = 0x200; + offset = 0x8e0400; + page_size = 0x200; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega384D3 +#------------------------------------------------------------ + +part parent "x384c3" + id = "x384d3"; + desc = "ATxmega384D3"; + signature = 0x1e 0x98 0x47; +; + +#------------------------------------------------------------ +# ATxmega8E5 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x8e5"; + desc = "ATxmega8E5"; + signature = 0x1e 0x93 0x41; + + memory "eeprom" + size = 0x0200; + offset = 0x08c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x2000; + offset = 0x0800000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "apptable" + size = 0x800; + offset = 0x00801800; + page_size = 0x80; + readsize = 0x100; + ; + + memory "boot" + size = 0x800; + offset = 0x00804000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "flash" + size = 0x2800; + offset = 0x0800000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "usersig" + size = 0x80; + offset = 0x8e0400; + page_size = 0x80; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega16E5 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x16e5"; + desc = "ATxmega16E5"; + signature = 0x1e 0x94 0x45; + + memory "eeprom" + size = 0x0200; + offset = 0x08c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x4000; + offset = 0x0800000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "apptable" + size = 0x1000; + offset = 0x00803000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "boot" + size = 0x1000; + offset = 0x00804000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "flash" + size = 0x5000; + offset = 0x0800000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "usersig" + size = 0x80; + offset = 0x8e0400; + page_size = 0x80; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# ATxmega32E5 +#------------------------------------------------------------ + +part parent ".xmega" + id = "x32e5"; + desc = "ATxmega32E5"; + signature = 0x1e 0x95 0x4c; + + memory "eeprom" + size = 0x0400; + offset = 0x08c0000; + page_size = 0x20; + readsize = 0x100; + ; + + memory "application" + size = 0x8000; + offset = 0x0800000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "apptable" + size = 0x1000; + offset = 0x00807000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "boot" + size = 0x1000; + offset = 0x00804000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "flash" + size = 0x9000; + offset = 0x0800000; + page_size = 0x80; + readsize = 0x100; + ; + + memory "usersig" + size = 0x80; + offset = 0x8e0400; + page_size = 0x80; + readsize = 0x100; + ; +; + +#------------------------------------------------------------ +# AVR32UC3A0512 +#------------------------------------------------------------ + +part + id = "uc3a0512"; + desc = "AT32UC3A0512"; + signature = 0xED 0xC0 0x3F; + has_jtag = yes; + is_avr32 = yes; + + memory "flash" + paged = yes; + page_size = 512; # bytes + readsize = 512; # bytes + num_pages = 1024; # could be set dynamicly + size = 0x00080000; # could be set dynamicly + offset = 0x80000000; + ; +; + +part parent "uc3a0512" + id = "ucr2"; + desc = "deprecated, use 'uc3a0512'"; +; + +#------------------------------------------------------------ +# ATtiny1634. +#------------------------------------------------------------ + +part + id = "t1634"; + desc = "ATtiny1634"; + has_debugwire = yes; + flash_instr = 0xB6, 0x01, 0x11; + eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, + 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, + 0x99, 0xF9, 0xBB, 0xAF; + stk500_devcode = 0x86; + # avr910_devcode = 0x; + signature = 0x1e 0x94 0x12; + pagel = 0xB3; + bs2 = 0xB1; + reset = io; + chip_erase_delay = 9000; + pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1", + "x x x x x x x x x x x x x x x x"; + + chip_erase = "1 0 1 0 1 1 0 0 1 0 0 x x x x x", + "x x x x x x x x x x x x x x x x"; + + timeout = 200; + stabdelay = 100; + cmdexedelay = 25; + synchloops = 32; + bytedelay = 0; + pollindex = 3; + pollvalue = 0x53; + predelay = 1; + postdelay = 1; + pollmethod = 1; + + pp_controlstack = + 0x0E, 0x1E, 0x0E, 0x1E, 0x2E, 0x3E, 0x2E, 0x3E, + 0x4E, 0x5E, 0x4E, 0x5E, 0x6E, 0x7E, 0x6E, 0x7E, + 0x26, 0x36, 0x66, 0x76, 0x2A, 0x3A, 0x6A, 0x7A, + 0x2E, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + hventerstabdelay = 100; + progmodedelay = 0; + latchcycles = 0; + togglevtg = 1; + poweroffdelay = 15; + resetdelayms = 1; + resetdelayus = 0; + hvleavestabdelay = 15; + resetdelay = 15; + chiperasepulsewidth = 0; + chiperasepolltimeout = 10; + programfusepulsewidth = 0; + programfusepolltimeout = 5; + programlockpulsewidth = 0; + programlockpolltimeout = 5; + + memory "eeprom" + paged = no; + page_size = 4; + size = 256; + min_write_delay = 3600; + max_write_delay = 3600; + readback_p1 = 0xff; + readback_p2 = 0xff; + read = " 1 0 1 0 0 0 0 0", + " 0 0 0 x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + write = " 1 1 0 0 0 0 0 0", + " 0 0 0 x x x x a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_lo = " 1 1 0 0 0 0 0 1", + " 0 0 0 0 0 0 0 0", + " 0 0 0 0 0 0 a1 a0", + " i i i i i i i i"; + + writepage = " 1 1 0 0 0 0 1 0", + " 0 0 x x x x x a8", + " a7 a6 a5 a4 a3 a2 0 0", + " x x x x x x x x"; + + mode = 0x41; + delay = 5; + blocksize = 4; + readsize = 256; + ; + + memory "flash" + paged = yes; + size = 16384; + page_size = 32; + num_pages = 512; + min_write_delay = 4500; + max_write_delay = 4500; + readback_p1 = 0xff; + readback_p2 = 0xff; + read_lo = " 0 0 1 0 0 0 0 0", + " 0 0 0 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + read_hi = " 0 0 1 0 1 0 0 0", + " 0 0 0 a12 a11 a10 a9 a8", + " a7 a6 a5 a4 a3 a2 a1 a0", + " o o o o o o o o"; + + loadpage_lo = " 0 1 0 0 0 0 0 0", + " 0 0 0 x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + loadpage_hi = " 0 1 0 0 1 0 0 0", + " 0 0 0 x x x x x", + " x x a5 a4 a3 a2 a1 a0", + " i i i i i i i i"; + + writepage = " 0 1 0 0 1 1 0 0", + " 0 0 0 a12 a11 a10 a9 a8", + " a7 a6 x x x x x x", + " x x x x x x x x"; + + mode = 0x41; + delay = 6; + blocksize = 128; + readsize = 256; + + ; + + memory "lfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "hfuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x o o o o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0", + "x x x x x x x x i i i i i i i i"; + ; + + memory "efuse" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", + "x x x x x x x x x x x o o o o o"; + + write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", + "x x x x x x x x x x x i i i i i"; + ; + + memory "lock" + size = 1; + min_write_delay = 4500; + max_write_delay = 4500; + read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0", + "x x x x x x x x x x x x x x o o"; + + write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x", + "x x x x x x x x 1 1 1 1 1 1 i i"; + ; + + memory "calibration" + size = 1; + read = "0 0 1 1 1 0 0 0 0 0 0 x x x x x", + "0 0 0 0 0 0 0 0 o o o o o o o o"; + ; + + memory "signature" + size = 3; + read = "0 0 1 1 0 0 0 0 0 0 0 x x x x x", + "x x x x x x a1 a0 o o o o o o o o"; + ; +; + +#------------------------------------------------------------ +# Common values for reduced core tinys (4/5/9/10/20/40) +#------------------------------------------------------------ + +part + id = ".reduced_core_tiny"; + desc = "Common values for reduced core tinys"; + has_tpi = yes; + + memory "signature" + size = 3; + offset = 0x3fc0; + page_size = 16; + ; + + memory "fuse" + size = 1; + offset = 0x3f40; + page_size = 16; + blocksize = 4; + ; + + memory "calibration" + size = 1; + offset = 0x3f80; + page_size = 16; + ; + + memory "lockbits" + size = 1; + offset = 0x3f00; + page_size = 16; + ; +; + +#------------------------------------------------------------ +# ATtiny4 +#------------------------------------------------------------ + +part parent ".reduced_core_tiny" + id = "t4"; + desc = "ATtiny4"; + signature = 0x1e 0x8f 0x0a; + + memory "flash" + size = 512; + offset = 0x4000; + page_size = 16; + blocksize = 128; + ; +; + +#------------------------------------------------------------ +# ATtiny5 +#------------------------------------------------------------ + +part parent "t4" + id = "t5"; + desc = "ATtiny5"; + signature = 0x1e 0x8f 0x09; +; + +#------------------------------------------------------------ +# ATtiny9 +#------------------------------------------------------------ + +part parent ".reduced_core_tiny" + id = "t9"; + desc = "ATtiny9"; + signature = 0x1e 0x90 0x08; + + memory "flash" + size = 1024; + offset = 0x4000; + page_size = 16; + blocksize = 128; + ; +; + +#------------------------------------------------------------ +# ATtiny10 +#------------------------------------------------------------ + +part parent "t9" + id = "t10"; + desc = "ATtiny10"; + signature = 0x1e 0x90 0x03; +; + +#------------------------------------------------------------ +# ATtiny20 +#------------------------------------------------------------ + +part parent ".reduced_core_tiny" + id = "t20"; + desc = "ATtiny20"; + signature = 0x1e 0x91 0x0F; + + memory "flash" + size = 2048; + offset = 0x4000; + page_size = 16; + blocksize = 128; + ; +; + +#------------------------------------------------------------ +# ATtiny40 +#------------------------------------------------------------ + +part parent ".reduced_core_tiny" + id = "t40"; + desc = "ATtiny40"; + signature = 0x1e 0x92 0x0E; + + memory "flash" + size = 4096; + offset = 0x4000; + page_size = 64; + blocksize = 128; + ; +; + +#------------------------------------------------------------ +# ATmega406 +#------------------------------------------------------------ + +part + id = "m406"; + desc = "ATMEGA406"; + has_jtag = yes; + signature = 0x1e 0x95 0x07; + + # STK500 parameters (parallel programming IO lines) + pagel = 0xa7; + bs2 = 0xa0; + serial = no; + parallel = yes; + + # STK500v2 HV programming parameters, from XML + pp_controlstack = 0x0e, 0x1e, 0x0f, 0x1f, 0x2e, 0x3e, 0x2f, 0x3f, + 0x4e, 0x5e, 0x4f, 0x5f, 0x6e, 0x7e, 0x6f, 0x7f, + 0x66, 0x76, 0x67, 0x77, 0x6a, 0x7a, 0x6b, 0x7b, + 0xbe, 0xfd, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + + # JTAG ICE mkII parameters, also from XML files + allowfullpagebitstream = no; + enablepageprogramming = yes; + idr = 0x51; + rampz = 0x00; + spmcr = 0x57; + eecr = 0x3f; + + memory "eeprom" + paged = no; + size = 512; + page_size = 4; + blocksize = 4; + readsize = 4; + num_pages = 128; + ; + + memory "flash" + paged = yes; + size = 40960; + page_size = 128; + blocksize = 128; + readsize = 128; + num_pages = 320; + ; + + memory "hfuse" + size = 1; + ; + + memory "lfuse" + size = 1; + ; + + memory "lockbits" + size = 1; + ; + + memory "signature" + size = 3; + ; +; + + + +programmer + id = "peapod"; + desc = "Use the Linux sysfs interface to bitbang GPIO lines"; + type = "linuxgpio"; + reset = 5; + sck = 6; + mosi = 12; + miso = 13; +; \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/platformio.ini b/software/PeaPodOS-Arduino/platformio.ini new file mode 100644 index 00000000..c8148046 --- /dev/null +++ b/software/PeaPodOS-Arduino/platformio.ini @@ -0,0 +1,12 @@ +[platformio] +default_envs = peapod + +[env:peapod] +platform = atmelavr +board = nanoatmega328 +framework = arduino +upload_protocol = custom +upload_command = sudo avrdude -p $BOARD_MCU -C avrdude_gpio.conf -c peapod -v -U flash:w:$SOURCE:i +test_port = /dev/ttyS0 +test_speed = 115200 +test_build_project_src = true \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/actuator.cc b/software/PeaPodOS-Arduino/src/actuators/actuator.cc similarity index 95% rename from software/PeaPodOS-Arduino/src/actuator.cc rename to software/PeaPodOS-Arduino/src/actuators/actuator.cc index 82ab4da7..cd906f73 100644 --- a/software/PeaPodOS-Arduino/src/actuator.cc +++ b/software/PeaPodOS-Arduino/src/actuators/actuator.cc @@ -1,8 +1,8 @@ -#include "actuator.h" +#include -#include "Arduino.h" +#include -#include "base.h" +#include Actuator::Actuator(actuatorid_t actuatorid, float failtarget) { this->actuatorid = actuatorid; diff --git a/software/PeaPodOS-Arduino/src/actuator.h b/software/PeaPodOS-Arduino/src/actuators/actuator.h similarity index 94% rename from software/PeaPodOS-Arduino/src/actuator.h rename to software/PeaPodOS-Arduino/src/actuators/actuator.h index b17b0ff6..bb9425a0 100644 --- a/software/PeaPodOS-Arduino/src/actuator.h +++ b/software/PeaPodOS-Arduino/src/actuators/actuator.h @@ -1,10 +1,11 @@ -#ifndef PEAPOD_ACTUATOR_H_ -#define PEAPOD_ACTUATOR_H_ +#ifndef PEAPOD_ACTUATORS_ACTUATOR_H_ +#define PEAPOD_ACTUATORS_ACTUATOR_H_ -#include "base.h" +#include // Actuator identifiers typedef enum actuatorid_t { + ACTUATOR_NULL, ACTUATOR_LED, } actuatorid_t; diff --git a/software/PeaPodOS-Arduino/src/actuators/led.cc b/software/PeaPodOS-Arduino/src/actuators/led.cc index eeedfe0b..e355069d 100644 --- a/software/PeaPodOS-Arduino/src/actuators/led.cc +++ b/software/PeaPodOS-Arduino/src/actuators/led.cc @@ -1,9 +1,9 @@ -#include "led.h" +#include -#include "Arduino.h" +#include -#include "../base.h" -#include "../actuator.h" +#include +#include LED::LED(uint8_t pin, led_color_t color) : Actuator(ACTUATOR_LED, 0) { this->pin = pin; diff --git a/software/PeaPodOS-Arduino/src/actuators/led.h b/software/PeaPodOS-Arduino/src/actuators/led.h index 9c52e8ec..477763aa 100644 --- a/software/PeaPodOS-Arduino/src/actuators/led.h +++ b/software/PeaPodOS-Arduino/src/actuators/led.h @@ -1,10 +1,10 @@ #ifndef PEAPOD_ACTUATORS_LED_H_ #define PEAPOD_ACTUATORS_LED_H_ -#include "Arduino.h" +#include -#include "../base.h" -#include "../actuator.h" +#include +#include typedef enum { LEDCOLOR_BLUE, diff --git a/software/PeaPodOS-Arduino/src/main.cc b/software/PeaPodOS-Arduino/src/main.cc new file mode 100644 index 00000000..063c5f01 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/main.cc @@ -0,0 +1,115 @@ +#ifndef UNIT_TEST + +#include + +#include + +#if ENABLE_WATCHDOG + #include +#endif + +#include +#include +#include +#include + +bool post(void); + +void setup(void) { + // Status LED + pinMode(PIN_STATUS, OUTPUT); + digitalWrite(PIN_STATUS, LOW); + + // Await serial start + Serial.begin(BAUDRATE); + while (!Serial) { + delay(1); + } + + // Send software revision first + JSONMessenger::sendRevision(REVISION); + + // Serial communications established, initialize sensors and actuators + if (!post()) { + // Failed POST, blink + while (true) { + delay(250); + digitalWrite(PIN_STATUS, HIGH); + delay(250); + digitalWrite(PIN_STATUS, LOW); + } + } + + // Await valid initial instruction set + String in; + do { + // Await instructions + while (!Serial.available()); + in = Serial.readStringUntil('\n'); + + // Trim whitespace, newline terminator + in.trim(); + } while (handleInstructions(in, &matrix) != ERR_NONE); + + // Enable the watchdog timer + #if ENABLE_WATCHDOG + wdt_enable(WDTO_1S); + #endif + digitalWrite(PIN_STATUS, HIGH); +} + +void loop(void) { + // Check for instructions + if (Serial.available()) { + String in = Serial.readStringUntil('\n'); + + in.trim(); + handleInstructions(in, &matrix); + } + + for (int i = 0; i < NUM_SENSORS; ++i) { + SensorState* state = sensors[i]->update(); + if (state->error == ERR_NONE) { + if (state->debug == DS_SUCCESS) { + for (int j = 0; j < state->numdata; ++j) { + JSONMessenger::sendData(state->data[j].label, state->data[j].value); + } + } // else do nothing + } else if (state->error == ERR_WARNING) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[i]->getID()) + " (non-fatal).")); + } else if (state->error == ERR_FATAL) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[i]->getID()) + " (FATAL, SENSOR DISABLED!).")); + } + } + + #if ENABLE_WATCHDOG + wdt_enable(WDTO_1S); + #endif +} + +bool post(void) { + bool success = true; + for (int i = 0; i < NUM_SENSORS; ++i) { + SensorState* state = sensors[i]->begin(); + bool latest = (state->debug == DS_INITIALIZED && state->error == ERR_NONE); + if (latest) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Sensor " + String(sensors[i]->getID()) + " initialized successfully.")); + } else { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Failed to initialize sensor " + String(sensors[i]->getID()) + ". Check wiring.")); + } + success &= latest; + } + for (int i = 0; i < NUM_ACTUATORS; ++i) { + ActuatorState* state = actuators[i]->begin(); + bool latest = (state->debug == DS_INITIALIZED && state->error == ERR_NONE); + if (latest) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Actuator " + String(actuators[i]->getID()) + " initialized successfully.")); + } else { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Failed to initialize actuator " + String(actuators[i]->getID()) + ". Check wiring.")); + } + success &= latest; + } + return success; +} + +#endif UNIT_TEST \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/main.h b/software/PeaPodOS-Arduino/src/main.h new file mode 100644 index 00000000..0634f940 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/main.h @@ -0,0 +1,55 @@ +#include + +#include +#include +#include +#include +#include +#include + +// Settings +#define ENABLE_WATCHDOG false + +// Constants +#define NUM_SENSORS 1 +#define NUM_ACTUATORS 0 +#define REVISION 0 +#define BAUDRATE 115200 + +// Pins +#define PIN_STATUS 13 +// #define PIN_LEDBLUE 3 +// #define PIN_LEDCOOL 5 +// #define PIN_LEDWARM 6 +// #define PIN_LEDRED 9 +// #define PIN_LEDFAR 10 + +// Sensors +SHT31 sht31 = SHT31(); +// K30 k30 = K30(); + +Sensor* sensors[NUM_SENSORS] = { + &sht31, + // &k30, +}; + +// Actuators +// LED led_blue(LEDPIN_BLUE, LEDCOLOR_BLUE); +// LED led_cool(LEDPIN_COOL, LEDCOLOR_COOL); +// LED led_warm(LEDPIN_WARM, LEDCOLOR_WARM); +// LED led_red(LEDPIN_RED, LEDCOLOR_RED); +// LED led_far(LEDPIN_FAR, LEDCOLOR_FAR); + +Actuator* actuators [NUM_ACTUATORS] = { +// &led_blue, +// &led_cool, +// &led_warm, +// &led_red, +// &led_far, +}; + +static const InstructionActuatorMatrix matrix = { + .numActuators = NUM_ACTUATORS, + .actuators = actuators, + .instructions = {} +} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/sensors/k30.cc b/software/PeaPodOS-Arduino/src/sensors/k30.cc index 1a00637b..a8474b06 100644 --- a/software/PeaPodOS-Arduino/src/sensors/k30.cc +++ b/software/PeaPodOS-Arduino/src/sensors/k30.cc @@ -1,16 +1,16 @@ -#include "k30.h" +#include -#include "Arduino.h" -#include "Wire.h" +#include +#include -#include "../base.h" -#include "../sensor.h" +#include +#include // Data setup static const char* labels[1] = { "co2_ppm" }; -static const t_sensordatasetup datasetup = { - 1, - labels +static const SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels }; K30::K30(TwoWire* wire, uint8_t address) : Sensor(SENSOR_K30, &datasetup, K30_DELTA) { diff --git a/software/PeaPodOS-Arduino/src/sensors/k30.h b/software/PeaPodOS-Arduino/src/sensors/k30.h index b285477d..aeae8205 100644 --- a/software/PeaPodOS-Arduino/src/sensors/k30.h +++ b/software/PeaPodOS-Arduino/src/sensors/k30.h @@ -1,11 +1,11 @@ #ifndef PEAPOD_SENSORS_K30_H_ #define PEAPOD_SENSORS_K30_H_ -#include "Arduino.h" -#include "Wire.h" +#include +#include -#include "../base.h" -#include "../sensor.h" +#include +#include #define K30_DEFAULT_ADDR 0x68 #define K30_DELTA 2000 diff --git a/software/PeaPodOS-Arduino/src/sensor.cc b/software/PeaPodOS-Arduino/src/sensors/sensor.cc similarity index 89% rename from software/PeaPodOS-Arduino/src/sensor.cc rename to software/PeaPodOS-Arduino/src/sensors/sensor.cc index 51ca5f21..a281415a 100644 --- a/software/PeaPodOS-Arduino/src/sensor.cc +++ b/software/PeaPodOS-Arduino/src/sensors/sensor.cc @@ -1,12 +1,12 @@ -#include "sensor.h" +#include #include -#include "Arduino.h" +#include -#include "base.h" +#include -Sensor::Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t delta) { +Sensor::Sensor(sensorid_t sensorid, const SensorDataSetup* setup, uint32_t delta) { this->sensorid = sensorid; this->delta = delta; @@ -15,7 +15,7 @@ Sensor::Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t del state.debug = DS_DISABLED; // Allocate data state - state.data = (t_datapoint*)malloc(sizeof(t_datapoint)*(setup->numdata)); + state.data = (DataPoint*)malloc(sizeof(DataPoint)*(setup->numdata)); state.numdata = setup->numdata; for (int i = 0; i < setup->numdata; ++i) { // Since setup->labels[i] is a const char*, we can just reassign our pointer diff --git a/software/PeaPodOS-Arduino/src/sensor.h b/software/PeaPodOS-Arduino/src/sensors/sensor.h similarity index 89% rename from software/PeaPodOS-Arduino/src/sensor.h rename to software/PeaPodOS-Arduino/src/sensors/sensor.h index 4e99346e..3293f0c5 100644 --- a/software/PeaPodOS-Arduino/src/sensor.h +++ b/software/PeaPodOS-Arduino/src/sensors/sensor.h @@ -1,24 +1,25 @@ -#ifndef PEAPOD_SENSOR_H_ -#define PEAPOD_SENSOR_H_ +#ifndef PEAPOD_SENSORS_SENSOR_H_ +#define PEAPOD_SENSORS_SENSOR_H_ -#include "Arduino.h" +#include -#include "base.h" +#include // Sensor identifiers typedef enum sensorid_t { + SENSOR_NULL, SENSOR_SHT31, SENSOR_K30, } sensorid_t; // Single datapoint -typedef struct t_datapoint { +typedef struct DataPoint { // Value float value; // Dataset label (string literal) const char* label; -} t_datapoint; +} DataPoint; // All sensor state info + latest datum - used as the "cache" in the sensor object. typedef struct SensorState { @@ -32,19 +33,19 @@ typedef struct SensorState { uint32_t timestamp; // Array of datapoints - t_datapoint* data; + DataPoint* data; // Length of `data` array uint8_t numdata; } SensorState; -typedef struct t_sensordatasetup { +typedef struct SensorDataSetup { // Number of datasets recorded by this sensor const uint8_t numdata; // Array of string literals corresponding to the labels for each dataset const char** labels; -} t_sensordatasetup; +} SensorDataSetup; class Sensor { public: @@ -54,7 +55,7 @@ class Sensor { * @param setup Pointer to datasets setup * @param delta Minimum delay (in milliseconds) between sensor read attempts */ - Sensor(sensorid_t sensorid, const t_sensordatasetup* setup, uint32_t delta); + Sensor(sensorid_t sensorid, const SensorDataSetup* setup, uint32_t delta); /** Wrapper function for `initialize()`. Sets debug state to indicate initialization success or failure. * @return Pointer to sensor state diff --git a/software/PeaPodOS-Arduino/src/sensors/sht31.cc b/software/PeaPodOS-Arduino/src/sensors/sht31.cc index d546348d..2cda093b 100644 --- a/software/PeaPodOS-Arduino/src/sensors/sht31.cc +++ b/software/PeaPodOS-Arduino/src/sensors/sht31.cc @@ -1,18 +1,18 @@ -#include "sht31.h" +#include -#include "Arduino.h" -#include "Wire.h" +#include +#include -#include "../base.h" -#include "../sensor.h" +#include +#include static uint8_t crc8(const uint8_t *data, int len); // Data setup static const char* labels[2] = { "air_temperature", "air_humidity" }; -static const t_sensordatasetup datasetup = { - 2, - labels +static const SensorDataSetup datasetup = { + .numdata = 2, + .labels = labels }; // CONSTRUCTOR @@ -48,7 +48,7 @@ errorlevel_t SHT31::read(float* data, uint8_t numdata) { } // Read into buffer - for (size_t i = 0; i < sizeof(buffer); i++) { + for (size_t i = 0; i < sizeof(buffer); ++i) { buffer[i] = wire->read(); } diff --git a/software/PeaPodOS-Arduino/src/sensors/sht31.h b/software/PeaPodOS-Arduino/src/sensors/sht31.h index cfb68070..770a7929 100644 --- a/software/PeaPodOS-Arduino/src/sensors/sht31.h +++ b/software/PeaPodOS-Arduino/src/sensors/sht31.h @@ -1,11 +1,11 @@ #ifndef PEAPOD_SENSORS_SHT31_H_ #define PEAPOD_SENSORS_SHT31_H_ -#include "Arduino.h" -#include "Wire.h" +#include +#include -#include "../base.h" -#include "../sensor.h" +#include +#include #define SHT31_DEFAULT_ADDR 0x44 // SHT31 Default Address #define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Measurement High Repeatability with Clock Stretch Enabled diff --git a/software/PeaPodOS-Arduino/src/base.h b/software/PeaPodOS-Arduino/src/utils/base.h similarity index 84% rename from software/PeaPodOS-Arduino/src/base.h rename to software/PeaPodOS-Arduino/src/utils/base.h index ed9d1b44..32a3d0b0 100644 --- a/software/PeaPodOS-Arduino/src/base.h +++ b/software/PeaPodOS-Arduino/src/utils/base.h @@ -1,5 +1,5 @@ -#ifndef PEAPOD_BASE_H_ -#define PEAPOD_BASE_H_ +#ifndef PEAPOD_UTILS_BASE_H_ +#define PEAPOD_UTILS_BASE_H_ // Degree of error occurred typedef enum errorlevel_t { diff --git a/software/PeaPodOS-Arduino/src/utils/instructions.cc b/software/PeaPodOS-Arduino/src/utils/instructions.cc new file mode 100644 index 00000000..802e2aa0 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/utils/instructions.cc @@ -0,0 +1,86 @@ +#include + +#include + +#include +#include +#include + +/** + * Checks a non-empty instruction set for formatting validity + * @param instructionSet JSON-formatted instruction set + * @return Validity + */ +bool isValidSet(String instructionSet) { + return (instructionSet.charAt(0) != '{' || instructionSet.charAt(instructionSet.length()-1) != '}' || instructionSet.indexOf(':') == -1); +} + +/** + * Handles incoming instruction set according to an instruction-actuator matrix + * @param instructionSet JSON-formatted instruction set + * @param matrix Instruction-actuator matrix + * @return Error level - `ERR_FATAL`: invalid instruction set formatting; `ERR_WARNING`: one or more instruction labels were unknown + */ +errorlevel_t handleInstructions(String instructionSet, const InstructionActuatorMatrix* matrix) { + // If empty, by default handled + if(instructionSet.equals("{}")){ + return ERR_NONE; + } + + // If invalid, throw error + if (!isValidSet(instructionSet)) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction set '" + instructionSet + "', bad formatting.")); + return ERR_FATAL; + } + + // Valid and non-empty, strip curly braces + instructionSet = instructionSet.substring(1, instructionSet.length()-1); + + errorlevel_t result = ERR_NONE; + // While there are instructions: + while (instructionSet.indexOf(':') != -1) { + // Find end of leftmost instruction + int stop = instructionSet.indexOf(','); + stop = (stop == -1) ? instructionSet.indexOf('}') : stop; + + // Handle leftmost instruction + String instruction = instructionSet.substring(0, stop); + errorlevel_t latest = handleInstruction(instruction, matrix); + + // Raise result errorlevel? + result = max(result, latest); + + // Truncate leftmost instruction + instructionSet = instructionSet.substring(stop+1); + } + return result; +} + +errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatrix* matrix) { + uint8_t split = instruction.indexOf(":"); + + // Check formatting + if (split != -1 && instruction.charAt(0) == '"' && instruction.charAt(split-1) == '"') { + // Get label (remove quotations) + String label = instruction.substring(1, split-1); + + // Get value + float value = instruction.substring(split+1).toFloat(); + + if(value == 0 && !(instruction.charAt(split+1) == '0' || (instruction.charAt(split+1) == '.' && instruction.charAt(split+2) == '0'))) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid target value '" + instruction.substring(split+1) + "'.")); + return ERR_FATAL; + } + + for (int i = 0; i < matrix->numActuators; ++i) { + if (label.equals(matrix->instructions[i])) { + matrix->actuators[i]->setTarget(value); + return ERR_NONE; + } + } + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Unknown instruction label '" + label + "'.")); + return ERR_WARNING; + } + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction '" + instruction + "', bad formatting.")); + return ERR_FATAL; +} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/utils/instructions.h b/software/PeaPodOS-Arduino/src/utils/instructions.h new file mode 100644 index 00000000..183ce88c --- /dev/null +++ b/software/PeaPodOS-Arduino/src/utils/instructions.h @@ -0,0 +1,37 @@ +#ifndef PEAPOD_UTILS_INSTRUCTIONS_H_ +#define PEAPOD_UTILS_INSTRUCTIONS_H_ + +#include + +#include +#include + +// Instruction label matching `instructions[i]` indicates that the target of actuator `actuators[i]` should be set to the instruction value +typedef struct InstructionActuatorMatrix { + // Number of actuators + uint8_t numActuators; + + // Array of pointers to actuators + Actuator** actuators; + + // Array of instruction label strings + const char** instructions; +} InstructionActuatorMatrix; + +/** + * Handles incoming instruction set according to an instruction-actuator matrix + * @param instructionSet JSON-formatted instruction set + * @param matrix Instruction-actuator matrix + * @return Error level - `ERR_FATAL`: invalid instruction set formatting; `ERR_WARNING`: one or more instruction labels were unknown + */ +errorlevel_t handleInstructions(String instructionSet, const InstructionActuatorMatrix* matrix); + +/** + * Handles incoming instruction label-target pair. + * @param instruction Label-target pair (i.e. `"\"actuator_label\":value_float"`) + * @param matrix Instruction-actuator matrix + * @return Error level - `ERR_FATAL`: invalid instruction formatting; `ERR_WARNING`: instruction label was unknown + */ +errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatrix* matrix); + +#endif diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc new file mode 100644 index 00000000..688d92db --- /dev/null +++ b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc @@ -0,0 +1,56 @@ +#include + +#include + +String parseMessageLevel(JSONMessenger::jsonmessagetype_t type) { + switch (type) { + case JSONMessenger::MESSAGE_DEBUG: + return String("debug"); + case JSONMessenger::MESSAGE_ERROR: + return String("error"); + } + return String("msg"); +} + +namespace JSONMessenger { + + #ifndef UNIT_TEST + + void sendMessage(jsonmessagetype_t type, String message) { + // Replace quotes with escaped quotes + message.replace("\"", "\\\""); + String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); + Serial.print(out); + } + + void sendRevision(uint8_t revision) { + String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); + Serial.print(out); + } + + void sendData(const char* label, float value) { + String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); + Serial.print(out); + } + + #elif + + String sendMessage(jsonmessagetype_t type, String message) { + // Replace quotes with escaped quotes + message.replace("\"", "\\\""); + String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); + return out; + } + + String sendRevision(uint8_t revision) { + String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); + return out; + } + + String sendData(const char* label, float value) { + String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); + return out; + } + + #endif +} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h new file mode 100644 index 00000000..a2e7fb30 --- /dev/null +++ b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h @@ -0,0 +1,48 @@ +#ifndef PEAPOD_UTILS_JSONMESSENGER_H_ +#define PEAPOD_UTILS_JSONMESSENGER_H_ + +#include + +#define DATADECIMALS 2 + +;namespace JSONMessenger { + + typedef enum jsonmessagetype_t { + MESSAGE_DEBUG, + MESSAGE_ERROR, + } jsonmessagetype_t; + + #ifndef UNIT_TEST + + /** + * Send a JSON-formatted message over Serial. + * @param type Message type + * @param message Message content + */ + void sendMessage(jsonmessagetype_t type, String message); + + /** + * Send a JSON-formatted revision message over Serial. + * @param revision Software revision + */ + void sendRevision(uint8_t revision); + + /** + * Send a JSON-formatted message over Serial containing sensor data. + * @param label Environment variable label + * @param value Sensor data value + */ + void sendData(const char* label, float value); + + #elif + + String sendMessage(jsonmessagetype_t type, String message); + + String sendRevision(uint8_t revision); + + String sendData(const char* label, float value); + + #endif +} + +#endif \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/test/README.md b/software/PeaPodOS-Arduino/test/README.md new file mode 100644 index 00000000..120e3927 --- /dev/null +++ b/software/PeaPodOS-Arduino/test/README.md @@ -0,0 +1,9 @@ +# Arduino Software Testing Suite + +### Arduino Compilation & Upload Test - `blink.ino` + +Test the functionality of the `arduino-cli` and `avrdude` utilities (and their custom configurations) by compilation the script and flashing the binary. + +### Communications Protocol Test - `revision.ino` + +Test communications between the Arduino and Raspberry Pi including timeout and revision checking. Essentially a skeleton of the main `PeaPodOS-Arduino.ino` script, with no sensors or actuators. \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc b/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc new file mode 100644 index 00000000..18ac7ce3 --- /dev/null +++ b/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc @@ -0,0 +1,40 @@ +#include +#include + +void test_led_builtin_pin_number(void) { + TEST_ASSERT_EQUAL(13, LED_BUILTIN); +} + +void test_led_state_high(void) { + digitalWrite(LED_BUILTIN, HIGH); + TEST_ASSERT_EQUAL(HIGH, digitalRead(LED_BUILTIN)); +} + +void test_led_state_low(void) { + digitalWrite(LED_BUILTIN, LOW); + TEST_ASSERT_EQUAL(LOW, digitalRead(LED_BUILTIN)); +} + +void setup() { + delay(2000); + + UNITY_BEGIN(); + RUN_TEST(test_led_builtin_pin_number); + + pinMode(LED_BUILTIN, OUTPUT); +} + +uint8_t i = 0; +uint8_t max_blinks = 5; + +void loop() { + if (i < max_blinks) { + RUN_TEST(test_led_state_high); + delay(500); + RUN_TEST(test_led_state_low); + delay(500); + i++; + } else if (i == max_blinks) { + UNITY_END(); + } +} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc new file mode 100644 index 00000000..17fc0c56 --- /dev/null +++ b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc @@ -0,0 +1,82 @@ +#include + +#include +#include +#include + +class TestActuator : public Actuator { + public: + TestActuator(void); + private: + errorlevel_t initialize(void) override; + errorlevel_t set(float target) override; +} + +TestActuator::TestActuator(void) : Actuator(ACTUATOR_NULL, 0) { } + +errorlevel_t TestActuator::initialize(void) { + return ERR_NONE; +} + +errorlevel_t TestActuator::set(float target) { + return ERR_NONE; +} + +TestActuator actuatorA(); +TestActuator actuatorB(); + +static const InstructionActuatorMatrix matrix = { + .numActuators = 2, + .actuators = {&actuatorA, &actuatorB}, + .instructions = {"A", "B"} +} + +void test_non_json(void) { + TEST_ASSERT_EQUAL(handleInstructions("what"), ERR_FATAL); +} + +void test_incomplete_json(void) { + TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete"), ERR_FATAL); +} + +void test_invalid_target(void) { + TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete\":abc"), ERR_FATAL); +} + +void test_bad_instruction(void) { + TEST_ASSERT_EQUAL(handleInstructions("{\"C\":0}"), ERR_WARNING); +} + +void test_empty_instruction_set(void) { + TEST_ASSERT_EQUAL(handleInstructions("{}"), ERR_NONE); + TEST_ASSERT_EQUAL(handleInstructions("{ }"), ERR_NONE); +} + +void test_one_instruction(void) { + TEST_ASSERT_EQUAL(handleInstructions("{\"A\":1.3}"), ERR_NONE); + TEST_ASSERT_EQUAL(actuatorA.update()->lasttarget, 1.3); +} + +void test_multiple_instructions(void) { + TEST_ASSERT_EQUAL(handleInstructions("{\"A\":2.4,\"B\":4.3}"), ERR_NONE); + TEST_ASSERT_EQUAL(actuatorA.update()->lasttarget, 2.4); + TEST_ASSERT_EQUAL(actuatorB.update()->lasttarget, 4.3); +} + +void setup(void) { + delay(2000); + + UNITY_BEGIN(); + + RUN_TEST(test_non_json); + RUN_TEST(test_incomplete_json); + RUN_TEST(test_invalid_target); + RUN_TEST(test_bad_instruction); + RUN_TEST(test_empty_instruction_set); + RUN_TEST(test_one_instruction); + RUN_TEST(test_multiple_instructions); + + UNITY_END(); +} + +void loop(void) { } \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc new file mode 100644 index 00000000..20070980 --- /dev/null +++ b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc @@ -0,0 +1,35 @@ +#include + +#include +#include + +void test_revision(void) { + TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendRevision(63).c_str(), "{\"type\":\"revision\",\"data\":63}\n") +} + +void test_debug(void) { + TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, "hello world").c_str(), "{\"type\":\"debug\",\"data\":\"hello world\"}\n"); +} + +void test_error(void) { + TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, "oh no").c_str(), "{\"type\":\"error\",\"data\":\"oh no\"}\n"); +} + +void test_data(void) { + TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendData("testlabel", 32.4).c_str(), "{\"type\":\"data\",\"data\":{\"label\":\"testlabel\",\"value\":32.40}}\n"); +} + +void setup(void) { + delay(2000); + + UNITY_BEGIN(); + + RUN_TEST(test_revision); + RUN_TEST(test_debug); + RUN_TEST(test_error); + RUN_TEST(test_data); + + UNITY_END(); +} + +void loop(void) { } \ No newline at end of file diff --git a/software/README.md b/software/README.md index 0178139c..21d2d4e2 100644 --- a/software/README.md +++ b/software/README.md @@ -16,7 +16,7 @@ Designed as both a hassle-free food production system and a research tool for pr - [Production](#production) - [Setting Up Raspberry Pi](#setting-up-raspberry-pi) - [Development](#development) - - [Arduino CLI - Compilation and Upload](#arduino-cli---compilation-and-upload) + - [Arduino Test Suite](#arduino-test-suite) - [Raspberry Pi Serial Testing](#raspberry-pi-serial-testing) - [Build from Source](#build-from-source) *** @@ -37,7 +37,7 @@ The following are performed on a computer: 2. Download the Raspberry Pi Imager [(Download)](https://www.raspberrypi.com/software/). 3. Flash the SD card with a *Raspberry Pi OS Lite* image. -> Note: In Future, a custom PeaPod image will be released with steps 5, 6, and 12 already complete. +> Note: In Future, a custom PeaPod Raspberry Pi OS Lite image will be released with steps 5, 6, and 12 already complete. 4. Plug in a keyboard and display, insert the microSD card, and power the Raspberry Pi device. @@ -59,13 +59,13 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: > You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@peapod.local`, or with VS Code) -> Note: In Future, steps 7-11 will be automated at startup. +> Note: In Future, a PeaPodOS Docker container will be released with steps 7-11 already complete. 7. Update Packages: 1. Update package listings, upgrade existing packages: `sudo apt-get update && sudo apt-get upgrade -y` 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude` (could take a while) 3. Install main software package: `sudo npm i -g @peapodtech/peapodos --save` -8. Install and configure the Arduino CLI: + + +8. Install PlatformIO Core with `python3 -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)"` 9. Create a custom configuration file for the AVR flash utility *avrdude* to be able to program the Arduino Nano via ICSP over the Raspberry Pi's GPIO pins: 1. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/avrdude_gpio.conf`, then modify your copy with `nano ~/avrdude_gpio.conf`. Copy the following to the end of the file: @@ -179,12 +181,48 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: # Development -### Arduino CLI - Compilation and Upload +### Arduino Test Suite To manually compile and upload the test sketch `./tests/blink.ino` to an Arduino Nano (ATMega328P aka `m328p`): -1. `arduino-cli compile -b arduino:avr:nano tests/blink -e` + + +1. `~/.platformio/penv/bin/platformio run -d tests/` + + Successful output should look like this: + + ``` + Processing peapod (platform: atmelavr; board: nanoatmega328; framework: arduino) + ---------------------------------------------------------------------------------- + Verbose mode can be enabled via `-v, --verbose` option + CONFIGURATION: https://docs.platformio.org/page/boards/atmelavr/nanoatmega328.html + PLATFORM: Atmel AVR (3.4.0) > Arduino Nano ATmega328 + HARDWARE: ATMEGA328P 16MHz, 2KB RAM, 30KB Flash + DEBUG: Current (avr-stub) On-board (avr-stub, simavr) + PACKAGES: + - framework-arduino-avr 5.1.0 + - toolchain-atmelavr 1.70300.191015 (7.3.0) + LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf + LDF Modes: Finder ~ chain, Compatibility ~ soft + Found 5 compatible libraries + Scanning dependencies... + Dependency Graph + |-- 1.0 + Building in release mode + Compiling .pio/build/peapod/src/actuators/actuator.cc.o + Compiling .pio/build/peapod/src/actuators/led.cc.o + Compiling .pio/build/peapod/src/main.cpp.o + ... + Indexing .pio/build/peapod/libFrameworkArduino.a + Linking .pio/build/peapod/firmware.elf + Checking size .pio/build/peapod/firmware.elf + Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" + RAM: [==== ] 41.3% (used 846 bytes from 2048 bytes) + Flash: [===== ] 45.0% (used 13832 bytes from 30720 bytes) + Building .pio/build/peapod/firmware.hex + ========================= [SUCCESS] Took 1.49 seconds ========================= + ``` 2. `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v -U flash:w:tests/blink/build/arduino.avr.nano/blink.ino.hex:i` diff --git a/software/lib/utils.ts b/software/lib/utils.ts index 66f56bde..6f35d2c3 100644 --- a/software/lib/utils.ts +++ b/software/lib/utils.ts @@ -87,39 +87,32 @@ export const fetchServerCert = async (): Promise => { return (await axios.get('https://pki.goog/roots.pem')).data as string; } -export const checkArduino = async (processor: string = 'm328p') => { +export const checkArduino = async () => { // Create log folder if (!existsSync('logs/')) { mkdirSync('logs/', { recursive: true }); } - execute(`avrdude -p ${processor} -C ~/avrdude_gpio.conf -c peapod -v`, [1]).catch(err => { - writeFileSync('logs/arduinoComms.log', err); - throw new Error(`Failed to communicate with the Arduino. See logs/arduinoComms.log`); + execute(`avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`, [1]).catch(err => { + writeFileSync('logs/checkArduino.log', err); + throw new Error(`Failed to communicate with the Arduino. See logs/checkArduino.log`); }).then(log1 => { - writeFileSync('logs/arduinoComms.log', log1); + writeFileSync('logs/checkArduino.log', log1); }); } -export const updateArduino = (sketch: string = 'PeaPodOS-Arduino', fqbn: string = 'arduino:avr:nano', processor: string = 'm328p'): Promise => { +export const updateArduino = (): Promise => { return new Promise((res, rej) => { // Create log folder if (!existsSync('logs/')) { mkdirSync('logs/', { recursive: true }); } - execute(`arduino-cli compile -b ${fqbn} ${sketch} -e`, [1]) + execute(`${process.env.HOME}/.platformio/penv/bin/platformio run -d PeaPodOS-Arduino/ -e peapod -t upload`, [1]) .then(log1 => { - if (log1) writeFileSync('logs/arduinoCompile.log', log1); - // execute('arduino-cli', ['upload', '-p', serialport, '-b', fqbn, script], { sudo: true, failureCodes: [1] }) - execute(`sudo avrdude -p ${processor} -C ${process.env.HOME}/avrdude_gpio.conf -c peapod -v -U flash:w:${sketch}/build/${fqbn.replace(/:/g, '.')}/${sketch}.ino.hex:i`, [1]) - .then(log2 => { - writeFileSync('logs/arduinoUpload.log', log2); - }).catch(err => { - writeFileSync('logs/arduinoUpload.log', err); - rej(new Error(`Failed to flash the '${sketch}' binary to the Arduino. See logs/arduinoUpload.log`)); - }); + if (log1) writeFileSync('logs/updateArduino.log', log1); + res(); }).catch(err => { - writeFileSync('logs/arduinoCompile.log', err); - rej(new Error(`Failed to compile '${sketch}'. See logs/arduinoCompile.log`)); + writeFileSync('logs/updateArduino.log', err); + rej(new Error(`Failed to update the Arduino software. See logs/updateArduino.log`)); }); }); } diff --git a/software/tests/README.md b/software/tests/README.md index 99f7a38a..d9e600a0 100644 --- a/software/tests/README.md +++ b/software/tests/README.md @@ -1,13 +1,5 @@ -# Software Testing Suite +# Raspberry Pi Software Testing Suite ### Serial Port Test - `serialTest.py` -Test the funcionality of the Raspberry Pi Zero 2 W GPIO mini-UART serial port by tying the RX and TX pins together. - -### Arduino Compilation & Upload Test - `blink.ino` - -Test the functionality of the `arduino-cli` and `avrdude` utilities (and their custom configurations) by compilation the script and flashing the binary. - -### Communications Protocol Test - `revision.ino` - -Test communications between the Arduino and Raspberry Pi including timeout and revision checking. Essentially a skeleton of the main `PeaPodOS-Arduino.ino` script, with no sensors or actuators. \ No newline at end of file +Test the funcionality of the Raspberry Pi Zero 2 W GPIO mini-UART serial port by tying the RX and TX pins together. \ No newline at end of file diff --git a/software/tests/blink/blink.ino b/software/tests/blink/blink.ino deleted file mode 100644 index 8ee9681c..00000000 --- a/software/tests/blink/blink.ino +++ /dev/null @@ -1,10 +0,0 @@ -void setup() { - pinMode(13, OUTPUT); -} - -void loop() { - digitalWrite(13, HIGH); - delay(250); - digitalWrite(13, LOW); - delay(250); -} \ No newline at end of file diff --git a/software/tests/revision/revision.ino b/software/tests/revision/revision.ino deleted file mode 100644 index 8ffba18a..00000000 --- a/software/tests/revision/revision.ino +++ /dev/null @@ -1,100 +0,0 @@ -#define REVISION 0 - -void setup() { - pinMode(13, OUTPUT); - digitalWrite(13, LOW); - if (!post()) { - // Failed POST - while(true) { - delay(250); - digitalWrite(13, HIGH); - delay(250); - digitalWrite(13, LOW); - } - } - - // Tell computer 'ready', wait to receive valid program. - Serial.print("{\"type\":\"revision\",\"data\":"); - Serial.print(REVISION); - Serial.print("}\n"); - - String ins; - do { - while(!Serial.available()); - ins = Serial.readString(); - ins.trim(); - } while(!handleInstructions(ins)); - digitalWrite(13, HIGH); -} - -void loop() { - if (Serial.available()) { - String in = Serial.readString(); - in.trim(); //Whitespace or newlines - handleInstructions(in); - } - - delay(100); -} - -/** - * Handles incoming instruction "var":value string. - * @param s - Instruction string - * @return was the instruction handled in a valid way? - **/ -bool handleInstruction(String in) { - uint8_t split = in.indexOf(":"); - if (split!=-1 && in.charAt(0) == '"' && in.charAt(split-1) == '"') { - String var = in.substring(1, split-1); //Assumes surrounding "" - float value = in.substring(split+1).toFloat(); - - Serial.print("{\"type\":\"error\",\"data\":\"Unknown instruction target '"); - Serial.print(var); - Serial.print("'\"}\n"); - return false; - } - Serial.print("{\"type\":\"error\",\"data\":\"Failed to handle instruction '"); - in.replace("\"", "\\\""); - Serial.print(in); - Serial.print("'\"}\n"); - return false; -} - -/** - * Handles incoming instruction dictionary {"var":val,...} - * Continues regardless of failure of a single instruction, alerts only - * @param s - Instructions dictionary string - * @return were all instructions handled in a valid way? - * */ -bool handleInstructions(String ins) { - if (ins.equals("{}")) { - return true; //Empty dictionary, by default handled - } - if (ins.charAt(0) != '{' || ins.charAt(ins.length()-1) != '}') { - Serial.print("{\"type\":\"error\",\"data\":\"Invalid instructions dictionary '"); - Serial.print(ins); - Serial.print("'\"}\n"); - return false; - } - ins = ins.substring(1, ins.length()-1); //Strips surrounding {} - - //Handles each individual instruction - - bool result = true; - //While there are multiple instructions - while (ins.indexOf(',') != -1) { - //Did the leftmost one succeed? - result &= handleInstruction(ins.substring(0, ins.indexOf(','))); - //Truncate leftmost instruction - ins = ins.substring(ins.indexOf(',')+1); - } - //Handle last/only instruction (there are no commas left) - result &= handleInstruction(ins); - return result; -} - -bool post() { - Serial.begin(115200); - while(!Serial); //Waits until serial opens - return true; -} From 027eee2c540fa56eb6cdf329f840195d4cdd629d Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Wed, 16 Mar 2022 16:08:47 -0400 Subject: [PATCH 11/85] Sensor Unit Tests --- software/PeaPodOS-Arduino/src/sensors/sht31.h | 2 +- .../test_instructions/test_instructions.cc | 1 + .../test/test_k30/test_k30.cc | 57 +++++++++++++++++++ .../test/test_messenger/test_messenger.cc | 1 + .../test/test_sht31/test_sht31.cc | 57 +++++++++++++++++++ software/README.md | 4 +- 6 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 software/PeaPodOS-Arduino/test/test_k30/test_k30.cc create mode 100644 software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc diff --git a/software/PeaPodOS-Arduino/src/sensors/sht31.h b/software/PeaPodOS-Arduino/src/sensors/sht31.h index 770a7929..eca62efd 100644 --- a/software/PeaPodOS-Arduino/src/sensors/sht31.h +++ b/software/PeaPodOS-Arduino/src/sensors/sht31.h @@ -25,7 +25,7 @@ // Import the default `Wire` interface provided by Arduino. extern TwoWire Wire; -// Interface for the Adafruit SHT31-D Temperature and Humidity breakout board. Does NOT inherit from `Sensor`. +// Interface for the Adafruit SHT31-D Temperature and Humidity breakout board. class SHT31 : public Sensor { public: /** diff --git a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc index 17fc0c56..ba4b660a 100644 --- a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc +++ b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc @@ -1,4 +1,5 @@ #include +#include #include #include diff --git a/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc b/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc new file mode 100644 index 00000000..24245725 --- /dev/null +++ b/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc @@ -0,0 +1,57 @@ +#include +#include + +#include +#include +#include + +K30 sensor = K30(); + +void test_begin(void) { + SensorState* state = sensor.begin(); + TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); +} + +void test_read(void) { + SensorState* state = sensor.update(); + TEST_ASSERT_EQUAL(state->debug, DS_SUCCESS); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); +} + +void test_delta(void) { + SensorState* state = sensor.update(); + TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); + + // No delay + + state = sensor.update(); + TEST_ASSERT_EQUAL(state->debug, DS_WAITING); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); +} + +void setup(void) { + delay(2000); + + UNITY_BEGIN(); + + RUN_TEST(test_begin); +} + +int i = 0; + +void loop(void) { + if (i < 5) { + delay(2000); + + RUN_TEST(test_read); + + delay(2000); + + RUN_TEST(test_delta); + i++; + } else { + UNITY_END(); + } +} \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc index 20070980..549af74a 100644 --- a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc +++ b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc @@ -1,4 +1,5 @@ #include +#include #include #include diff --git a/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc b/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc new file mode 100644 index 00000000..a47657be --- /dev/null +++ b/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc @@ -0,0 +1,57 @@ +#include +#include + +#include +#include +#include + +SHT31 sensor = SHT31(); + +void test_begin(void) { + SensorState* state = sensor.begin(); + TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); +} + +void test_read(void) { + SensorState* state = sensor.update(); + TEST_ASSERT_EQUAL(state->debug, DS_SUCCESS); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); +} + +void test_delta(void) { + SensorState* state = sensor.update(); + TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); + + // No delay + + state = sensor.update(); + TEST_ASSERT_EQUAL(state->debug, DS_WAITING); + TEST_ASSERT_EQUAL(state->error, ERR_NONE); +} + +void setup(void) { + delay(2000); + + UNITY_BEGIN(); + + RUN_TEST(test_begin); +} + +int i = 0; + +void loop(void) { + if (i < 5) { + delay(2000); + + RUN_TEST(test_read); + + delay(2000); + + RUN_TEST(test_delta); + i++; + } else { + UNITY_END(); + } +} \ No newline at end of file diff --git a/software/README.md b/software/README.md index 21d2d4e2..9d9ab5a8 100644 --- a/software/README.md +++ b/software/README.md @@ -59,8 +59,8 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: > You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@peapod.local`, or with VS Code) -> Note: In Future, a PeaPodOS Docker container will be released with steps 7-11 already complete. -7. Update Packages: +> Note: In Future, steps 7-11 will be performed at runtime. +1. Update Packages: 1. Update package listings, upgrade existing packages: `sudo apt-get update && sudo apt-get upgrade -y` 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude` (could take a while) 3. Install main software package: `sudo npm i -g @peapodtech/peapodos --save` From c5b3161b26576f44cf271e89ef28752156f76b61 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 18 Mar 2022 02:17:29 -0400 Subject: [PATCH 12/85] TypeScript Unit Testing * Fixed PIO unit testing * JSON messenger returns instead of printing to serial during unit tests * main.cc ignored during unit tests * Moved lib/ to src/ + Setup jest + Utils unit tests + Arduino unit testing from TS - Removed python tests, test readme --- software/.npmignore | 2 + software/PeaPodOS-Arduino/src/actuators/led.h | 16 +- software/PeaPodOS-Arduino/src/main.cc | 2 +- software/PeaPodOS-Arduino/src/main.h | 48 +- .../src/utils/jsonmessenger.cc | 28 +- .../src/utils/jsonmessenger.h | 18 +- .../test_instructions/test_instructions.cc | 31 +- .../test/test_messenger/test_messenger.cc | 2 +- software/index.ts | 12 +- software/package-lock.json | 10108 +++++++++++++--- software/package.json | 18 +- software/{lib => src}/PeaPodArduino.ts | 0 software/{lib => src}/PeaPodCamera.ts | 0 software/{lib => src}/PeaPodPublisher.ts | 0 software/{lib => src}/PeaPodSimulator.ts | 0 software/{lib => src}/errors.ts | 0 software/{lib => src}/ui.ts | 0 software/{lib => src}/utils.ts | 25 +- software/tests/README.md | 5 - software/tests/serialMonitor.py | 13 - software/tests/serialTest.py | 18 - software/tests/testArduino.test.ts | 35 + software/tests/testUtils.test.ts | 6 + software/tests/utils.ts | 11 + software/tsconfig.json | 8 +- 25 files changed, 8394 insertions(+), 2012 deletions(-) rename software/{lib => src}/PeaPodArduino.ts (100%) rename software/{lib => src}/PeaPodCamera.ts (100%) rename software/{lib => src}/PeaPodPublisher.ts (100%) rename software/{lib => src}/PeaPodSimulator.ts (100%) rename software/{lib => src}/errors.ts (100%) rename software/{lib => src}/ui.ts (100%) rename software/{lib => src}/utils.ts (87%) delete mode 100644 software/tests/README.md delete mode 100644 software/tests/serialMonitor.py delete mode 100644 software/tests/serialTest.py create mode 100644 software/tests/testArduino.test.ts create mode 100644 software/tests/testUtils.test.ts create mode 100644 software/tests/utils.ts diff --git a/software/.npmignore b/software/.npmignore index 99ab6e20..545657d8 100644 --- a/software/.npmignore +++ b/software/.npmignore @@ -23,11 +23,13 @@ tests/ *.test.js.map *.test.d.ts *.test.ts +jest.config.js # Arduino Binaries *.elf *.hex *.eep *.bin +*.oo **/.vscode/ **/.pio/ \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/actuators/led.h b/software/PeaPodOS-Arduino/src/actuators/led.h index 477763aa..77568d58 100644 --- a/software/PeaPodOS-Arduino/src/actuators/led.h +++ b/software/PeaPodOS-Arduino/src/actuators/led.h @@ -6,16 +6,16 @@ #include #include -typedef enum { - LEDCOLOR_BLUE, - LEDCOLOR_COOL, - LEDCOLOR_WARM, - LEDCOLOR_RED, - LEDCOLOR_FAR -} led_color_t; - class LED : public Actuator { public: + typedef enum { + LEDCOLOR_BLUE, + LEDCOLOR_COOL, + LEDCOLOR_WARM, + LEDCOLOR_RED, + LEDCOLOR_FAR + } led_color_t; + /** * Constructor. * @param pin PWM pin for controlling this LED. diff --git a/software/PeaPodOS-Arduino/src/main.cc b/software/PeaPodOS-Arduino/src/main.cc index 063c5f01..5788a957 100644 --- a/software/PeaPodOS-Arduino/src/main.cc +++ b/software/PeaPodOS-Arduino/src/main.cc @@ -112,4 +112,4 @@ bool post(void) { return success; } -#endif UNIT_TEST \ No newline at end of file +#endif \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/main.h b/software/PeaPodOS-Arduino/src/main.h index 0634f940..9a50ff50 100644 --- a/software/PeaPodOS-Arduino/src/main.h +++ b/software/PeaPodOS-Arduino/src/main.h @@ -11,45 +11,47 @@ #define ENABLE_WATCHDOG false // Constants -#define NUM_SENSORS 1 -#define NUM_ACTUATORS 0 +#define NUM_SENSORS 2 +#define NUM_ACTUATORS 5 #define REVISION 0 #define BAUDRATE 115200 // Pins #define PIN_STATUS 13 -// #define PIN_LEDBLUE 3 -// #define PIN_LEDCOOL 5 -// #define PIN_LEDWARM 6 -// #define PIN_LEDRED 9 -// #define PIN_LEDFAR 10 +#define PIN_LEDBLUE 3 +#define PIN_LEDCOOL 5 +#define PIN_LEDWARM 6 +#define PIN_LEDRED 9 +#define PIN_LEDFAR 10 // Sensors SHT31 sht31 = SHT31(); -// K30 k30 = K30(); +K30 k30 = K30(); Sensor* sensors[NUM_SENSORS] = { &sht31, - // &k30, + &k30, }; // Actuators -// LED led_blue(LEDPIN_BLUE, LEDCOLOR_BLUE); -// LED led_cool(LEDPIN_COOL, LEDCOLOR_COOL); -// LED led_warm(LEDPIN_WARM, LEDCOLOR_WARM); -// LED led_red(LEDPIN_RED, LEDCOLOR_RED); -// LED led_far(LEDPIN_FAR, LEDCOLOR_FAR); +LED led_blue = LED(PIN_LEDBLUE, LED::LEDCOLOR_BLUE); +LED led_cool = LED(PIN_LEDCOOL, LED::LEDCOLOR_COOL); +LED led_warm = LED(PIN_LEDWARM, LED::LEDCOLOR_WARM); +LED led_red = LED(PIN_LEDRED, LED::LEDCOLOR_RED); +LED led_far = LED(PIN_LEDFAR, LED::LEDCOLOR_FAR); Actuator* actuators [NUM_ACTUATORS] = { -// &led_blue, -// &led_cool, -// &led_warm, -// &led_red, -// &led_far, + &led_blue, + &led_cool, + &led_warm, + &led_red, + &led_far, }; +const char* instr [NUM_ACTUATORS] = { "led_blue", "led_cool", "led_warm", "led_red", "led_far" }; + static const InstructionActuatorMatrix matrix = { - .numActuators = NUM_ACTUATORS, - .actuators = actuators, - .instructions = {} -} \ No newline at end of file + NUM_ACTUATORS, + actuators, + instr +}; \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc index 688d92db..309b8094 100644 --- a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc +++ b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc @@ -14,42 +14,42 @@ String parseMessageLevel(JSONMessenger::jsonmessagetype_t type) { namespace JSONMessenger { - #ifndef UNIT_TEST + #ifdef UNIT_TEST - void sendMessage(jsonmessagetype_t type, String message) { + String sendMessage(jsonmessagetype_t type, String message) { // Replace quotes with escaped quotes message.replace("\"", "\\\""); String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); - Serial.print(out); + return out; } - void sendRevision(uint8_t revision) { + String sendRevision(uint8_t revision) { String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); - Serial.print(out); + return out; } - void sendData(const char* label, float value) { + String sendData(const char* label, float value) { String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); - Serial.print(out); + return out; } - #elif + #else - String sendMessage(jsonmessagetype_t type, String message) { + void sendMessage(jsonmessagetype_t type, String message) { // Replace quotes with escaped quotes message.replace("\"", "\\\""); String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); - return out; + Serial.print(out); } - String sendRevision(uint8_t revision) { + void sendRevision(uint8_t revision) { String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); - return out; + Serial.print(out); } - String sendData(const char* label, float value) { + void sendData(const char* label, float value) { String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); - return out; + Serial.print(out); } #endif diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h index a2e7fb30..c789a6d0 100644 --- a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h +++ b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h @@ -12,7 +12,15 @@ MESSAGE_ERROR, } jsonmessagetype_t; - #ifndef UNIT_TEST + #ifdef UNIT_TEST + + String sendMessage(jsonmessagetype_t type, String message); + + String sendRevision(uint8_t revision); + + String sendData(const char* label, float value); + + #else /** * Send a JSON-formatted message over Serial. @@ -34,14 +42,6 @@ */ void sendData(const char* label, float value); - #elif - - String sendMessage(jsonmessagetype_t type, String message); - - String sendRevision(uint8_t revision); - - String sendData(const char* label, float value); - #endif } diff --git a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc index ba4b660a..972a4e0b 100644 --- a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc +++ b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc @@ -11,7 +11,7 @@ class TestActuator : public Actuator { private: errorlevel_t initialize(void) override; errorlevel_t set(float target) override; -} +}; TestActuator::TestActuator(void) : Actuator(ACTUATOR_NULL, 0) { } @@ -23,43 +23,46 @@ errorlevel_t TestActuator::set(float target) { return ERR_NONE; } -TestActuator actuatorA(); -TestActuator actuatorB(); +TestActuator actuatorA = TestActuator(); +TestActuator actuatorB = TestActuator(); + +const char* ins[2] = { "A", "B" }; +Actuator* acts[2] = { &actuatorA, &actuatorB }; static const InstructionActuatorMatrix matrix = { .numActuators = 2, - .actuators = {&actuatorA, &actuatorB}, - .instructions = {"A", "B"} -} + .actuators = acts, + .instructions = ins +}; void test_non_json(void) { - TEST_ASSERT_EQUAL(handleInstructions("what"), ERR_FATAL); + TEST_ASSERT_EQUAL(handleInstructions("what", &matrix), ERR_FATAL); } void test_incomplete_json(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete"), ERR_FATAL); + TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete", &matrix), ERR_FATAL); } void test_invalid_target(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete\":abc"), ERR_FATAL); + TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete\":abc", &matrix), ERR_FATAL); } void test_bad_instruction(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"C\":0}"), ERR_WARNING); + TEST_ASSERT_EQUAL(handleInstructions("{\"C\":0}", &matrix), ERR_WARNING); } void test_empty_instruction_set(void) { - TEST_ASSERT_EQUAL(handleInstructions("{}"), ERR_NONE); - TEST_ASSERT_EQUAL(handleInstructions("{ }"), ERR_NONE); + TEST_ASSERT_EQUAL(handleInstructions("{}", &matrix), ERR_NONE); + TEST_ASSERT_EQUAL(handleInstructions("{ }", &matrix), ERR_NONE); } void test_one_instruction(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"A\":1.3}"), ERR_NONE); + TEST_ASSERT_EQUAL(handleInstructions("{\"A\":1.3}", &matrix), ERR_NONE); TEST_ASSERT_EQUAL(actuatorA.update()->lasttarget, 1.3); } void test_multiple_instructions(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"A\":2.4,\"B\":4.3}"), ERR_NONE); + TEST_ASSERT_EQUAL(handleInstructions("{\"A\":2.4,\"B\":4.3}", &matrix), ERR_NONE); TEST_ASSERT_EQUAL(actuatorA.update()->lasttarget, 2.4); TEST_ASSERT_EQUAL(actuatorB.update()->lasttarget, 4.3); } diff --git a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc index 549af74a..0a6f58b3 100644 --- a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc +++ b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc @@ -5,7 +5,7 @@ #include void test_revision(void) { - TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendRevision(63).c_str(), "{\"type\":\"revision\",\"data\":63}\n") + TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendRevision(63).c_str(), "{\"type\":\"revision\",\"data\":63}\n"); } void test_debug(void) { diff --git a/software/index.ts b/software/index.ts index 985f9759..e2f954b9 100644 --- a/software/index.ts +++ b/software/index.ts @@ -5,13 +5,13 @@ import { initializeApp, getApps } from 'firebase/app'; import { getAuth } from 'firebase/auth'; // import PiCamera from 'pi-camera'; -import PeaPodArduinoInterface, { IPeaPodArduino, ArduinoInstructions } from './lib/PeaPodArduino'; -import PeaPodPubSub, { IoTConfig, IPeaPodPublisher, PeaPodDataBatch } from './lib/PeaPodPublisher'; -import { ArduinoSimulator, PeaPodLogger } from './lib/PeaPodSimulator'; -// import PeaPodCamera, { IPeaPodCamera } from './lib/PeaPodCamera'; +import PeaPodArduinoInterface, { IPeaPodArduino } from './src/PeaPodArduino'; +import PeaPodPubSub, { IoTConfig, IPeaPodPublisher, PeaPodDataBatch } from './src/PeaPodPublisher'; +import { ArduinoSimulator, PeaPodLogger } from './src/PeaPodSimulator'; +// import PeaPodCamera, { IPeaPodCamera } from './src/PeaPodCamera'; -import { checkInternet, sleep, loadDotenv, findSerialPath } from './lib/utils'; //Utilities -import Spinner from './lib/ui'; //UI utils +import { checkInternet, sleep, loadDotenv, findSerialPath } from './src/utils'; //Utilities +import Spinner from './src/ui'; //UI utils // Global State diff --git a/software/package-lock.json b/software/package-lock.json index 9ea4995c..7f9f51a3 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -1,12 +1,12 @@ { "name": "@peapodtech/peapodos", - "version": "0.0.8", + "version": "0.0.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@peapodtech/peapodos", - "version": "0.0.8", + "version": "0.0.9", "license": "MIT", "dependencies": { "@mapbox/node-pre-gyp": "^1.0.8", @@ -30,397 +30,592 @@ }, "devDependencies": { "@serialport/parser-readline": "^9.0.1", + "@types/jest": "^27.4.1", "@types/jsonwebtoken": "^8.5.5", "@types/pi-camera": "^1.5.3", "@types/project-name-generator": "^2.1.0", "@types/serialport": "^8.0.2", - "@types/uuid": "^8.3.0" + "@types/uuid": "^8.3.0", + "jest": "^27.5.1", + "ts-jest": "^27.1.3" } }, - "node_modules/@firebase/analytics": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.4.tgz", - "integrity": "sha512-AU3XMwHW7SFGCNeUKKNW2wXGTdmS164ackt/Epu2bDXCT1OcauPE1AVd+ofULSIDCaDUAQVmvw3JrobgogEU7Q==", + "node_modules/@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@jridgewell/trace-mapping": "^0.3.0" }, - "peerDependencies": { - "@firebase/app": "0.x" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@firebase/analytics-compat": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.5.tgz", - "integrity": "sha512-5cfr0uWwlhoHQYAr6UtQCHwnGjs/3J/bWrfA3INNtzaN4/tTTLTD02iobbccRcM7dM5TR0sZFWS5orfAU3OBFg==", + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, "dependencies": { - "@firebase/analytics": "0.7.4", - "@firebase/analytics-types": "0.7.0", - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/highlight": "^7.16.7" }, - "peerDependencies": { - "@firebase/app-compat": "0.x" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/analytics-types": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.7.0.tgz", - "integrity": "sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ==" + "node_modules/@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@firebase/app": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.7.tgz", - "integrity": "sha512-3yEJDg814CnYIODgLCr4vrIP5Of78WDdikJVE5LS7MN1MWDFeJpQ4n88BdjO2X4Dp22+UFkw7FiuduwfUJJYYQ==", + "node_modules/@babel/core": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", + "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.7", + "@babel/parser": "^7.17.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@firebase/app-check": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.1.tgz", - "integrity": "sha512-5TYzIM7lhvxt8kB98iULOCrRgI8/qu7LEdsJNm8jEymk3x4DBL3lK0oRw5nHbyUy+lK7cq9D1NmZZnLA3Snt4w==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" }, - "peerDependencies": { - "@firebase/app": "0.x" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/app-check-compat": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.1.tgz", - "integrity": "sha512-nB34OoU0icJM0iVrSf7oRVVzrceSvKYdcwlqitrN9JaB+36KwQ0FiQ4saI/rE4DLjcNsviV2ojJ/PRPdv+P0QQ==", + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, "dependencies": { - "@firebase/app-check": "0.5.1", - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { - "@firebase/app-compat": "0.x" + "@babel/core": "^7.0.0" } }, - "node_modules/@firebase/app-check-interop-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.1.0.tgz", - "integrity": "sha512-uZfn9s4uuRsaX5Lwx+gFP3B6YsyOKUE+Rqa6z9ojT4VSRAsZFko9FRn6OxQUA1z5t5d08fY4pf+/+Dkd5wbdbA==" + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/@firebase/app-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.8.tgz", - "integrity": "sha512-tDjJOoCHYXdswyci0UAPV9vMWp2Guxm8B2jw2A+A1DEcMBcL2z3dDp1JnAwFNbqvE9JHuMBVfEKq/fO0GgDXOg==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, "dependencies": { - "@firebase/app": "0.7.7", - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/app-types": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", - "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" - }, - "node_modules/@firebase/auth": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.2.tgz", - "integrity": "sha512-TH6v+wi3cHNdcshAjaWsAPYw/JmY5MYU8xCtZMQQaJdf+c/X+uCWv23s7Xs1fzda5+jecjVmENoXa+i/Onxeeg==", + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", - "selenium-webdriver": "4.0.0-rc-1", - "tslib": "^2.1.0" + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" }, - "peerDependencies": { - "@firebase/app": "0.x" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/auth-compat": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.2.tgz", - "integrity": "sha512-ywYVs/GdBGHTfIKh65IHkge9kyUqOBd24jCI1mmUeQjO3ChVZfdOJk2JvhegLwaRnPYiuzzrWo7wp87YXVL+TQ==", + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, "dependencies": { - "@firebase/auth": "0.19.2", - "@firebase/auth-types": "0.11.0", - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", - "selenium-webdriver": "^4.0.0-beta.2", - "tslib": "^2.1.0" + "@babel/types": "^7.16.7" }, - "peerDependencies": { - "@firebase/app-compat": "0.x" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/auth-interop-types": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", - "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/auth-types": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.11.0.tgz", - "integrity": "sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/component": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.9.tgz", - "integrity": "sha512-oLCY3x9WbM5rn06qmUvbtJuPj4dIw/C9T4Th52IiHF5tiCRC5k6YthvhfUVcTwfoUhK0fOgtwuKJKA/LpCPjgA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, "dependencies": { - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/database": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.4.tgz", - "integrity": "sha512-XkrL1kXELRNkqKcltuT4hfG1gWmFiGvjFY+z7Lhb//12MqdkLjwa9YMK8c6Lo+Ro+IkWcJArQaOQYe3GkU5Wgg==", - "dependencies": { - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "faye-websocket": "0.11.4", - "tslib": "^2.1.0" + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/database-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.4.tgz", - "integrity": "sha512-dIJiZLDFF3U+MoEwoPBy7zxWmBUro1KefmwSHlpOoxmPv76tuoPm85NumpW/HmMrtTcTkC2qowtb6NjGE8X7mw==", + "node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/database": "0.12.4", - "@firebase/database-types": "0.9.3", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/types": "^7.17.0" }, - "peerDependencies": { - "@firebase/app-compat": "0.x" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/database-types": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.3.tgz", - "integrity": "sha512-R+YXLWy/Q7mNUxiUYiMboTwvVoprrgfyvf1Viyevskw6IoH1q8HV1UjlkLSgmRsOT9HPWt7XZUEStVZJFknHwg==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, "dependencies": { - "@firebase/app-types": "0.7.0", - "@firebase/util": "1.4.2" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/firestore": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.3.0.tgz", - "integrity": "sha512-QMCwmBlUUFldszKtVqIlqwjZYY0eODI2R7F9lkPxiANw8F853bSyBY6wqN85657vfDS7Ij6i6s+1qWMCqFvHHA==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", + "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "@firebase/webchannel-wrapper": "0.6.1", - "@grpc/grpc-js": "^1.3.2", - "@grpc/proto-loader": "^0.6.0", - "node-fetch": "2.6.5", - "tslib": "^2.1.0" + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" }, - "peerDependencies": { - "@firebase/app": "0.x" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@firebase/firestore-compat": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.7.tgz", - "integrity": "sha512-34n9PxdenKRNqZRrr+SfjAcrPUvbfggLnRrADz7iVFYlDo9X1Jj6+fimzo0xC/p+2KZkPAiRYbT60WhjBLYUcg==", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/firestore": "3.3.0", - "@firebase/firestore-types": "2.5.0", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "color-convert": "^1.9.0" }, - "peerDependencies": { - "@firebase/app-compat": "0.x" + "engines": { + "node": ">=4" } }, - "node_modules/@firebase/firestore-types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.5.0.tgz", - "integrity": "sha512-I6c2m1zUhZ5SH0cWPmINabDyH5w0PPFHk2UHsjBpKdZllzJZ2TwTkXbDtpHUZNmnc/zAa0WNMNMvcvbb/xJLKA==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", + "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/functions": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.7.5.tgz", - "integrity": "sha512-eEA8WhvNqahbepl0DF1vPc8Ml8oPMkDUQr+HQFQSqVvhYaGc1r6yP+Xe5QChifGfrAd5s/AanchNDvkS86Dg9g==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, "dependencies": { - "@firebase/app-check-interop-types": "0.1.0", - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.9", - "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@firebase/app": "0.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/functions-compat": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.1.6.tgz", - "integrity": "sha512-YZZmToY5Psp0fH8IMdqb00RXsI+dT/+YHKjNNZ1mO/MQS1Uwk40rqaOwZa00xYVeiEyfMnT2ciXyHPEYD7nxPQ==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/functions": "0.7.5", - "@firebase/functions-types": "0.5.0", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.12.13" }, "peerDependencies": { - "@firebase/app-compat": "0.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/functions-types": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.5.0.tgz", - "integrity": "sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA==" + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@firebase/installations": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.4.tgz", - "integrity": "sha512-rYb6Ju/tIBhojmM8FsgS96pErKl6gPgJFnffMO4bKH7HilXhOfgLfKU9k51ZDcps8N0npDx9+AJJ6pL1aYuYZQ==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "idb": "3.0.2", - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@firebase/app": "0.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/logger": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.2.tgz", - "integrity": "sha512-lzLrcJp9QBWpo40OcOM9B8QEtBw2Fk1zOZQdvv+rWS6gKmhQBCEMc4SMABQfWdjsylBcDfniD1Q+fUX1dcBTXA==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, "dependencies": { - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/messaging": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.4.tgz", - "integrity": "sha512-OvYV4MLPfDpdP/yltLqZXZRx6rXWz52bEilS2jL2B4sGiuTaXSkR6BIHB54EPTblu32nbyZYdlER4fssz4TfXw==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", - "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.4.2", - "idb": "3.0.2", - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@firebase/app": "0.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/messaging-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.4.tgz", - "integrity": "sha512-6477jBw7w7hk0uhnTUMsPoukalpcwbxTTo9kMguHVSXe0t3OdoxeXEaapaNJlOmU4Kgc8j3rsms8IDLdKVpvlA==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/messaging": "0.9.4", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { - "@firebase/app-compat": "0.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/messaging-interop-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.1.0.tgz", - "integrity": "sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ==" + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@firebase/performance": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.4.tgz", - "integrity": "sha512-ES6aS4eoMhf9CczntBADDsXhaFea/3a0FADwy/VpWXXBxVb8tqc5tPcoTwd9L5M/aDeSiQMy344rhrSsTbIZEg==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@firebase/app": "0.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/performance-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.4.tgz", - "integrity": "sha512-YuGfmpC0o+YvEBlEZCbPdNbT4Nn2qhi5uMXjqKnNIUepmXUsgOYDiAqM9nxHPoE/6IkvoFMdCj5nTUYVLCFXgg==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/performance": "0.5.4", - "@firebase/performance-types": "0.1.0", - "@firebase/util": "1.4.2", - "tslib": "^2.1.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@firebase/app-compat": "0.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/performance-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.1.0.tgz", - "integrity": "sha512-6p1HxrH0mpx+622Ql6fcxFxfkYSBpE3LSuwM7iTtYU2nw91Hj6THC8Bc8z4nboIq7WvgsT/kOTYVVZzCSlXl8w==" + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@firebase/polyfill": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.36.tgz", - "integrity": "sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, "dependencies": { - "core-js": "3.6.5", - "promise-polyfill": "8.1.3", - "whatwg-fetch": "2.0.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@firebase/remote-config": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.3.tgz", - "integrity": "sha512-9hZWfB3k3IYsjHbWeUfhv/SDCcOgv/JMJpLXlUbTppXPm1IZ3X9ZW4I9bS86gGYr7m/kSv99U0oxQ7N9PoR8Iw==", + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@firebase/analytics": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.4.tgz", + "integrity": "sha512-AU3XMwHW7SFGCNeUKKNW2wXGTdmS164ackt/Epu2bDXCT1OcauPE1AVd+ofULSIDCaDUAQVmvw3JrobgogEU7Q==", "dependencies": { "@firebase/component": "0.5.9", "@firebase/installations": "0.5.4", @@ -432,15 +627,14 @@ "@firebase/app": "0.x" } }, - "node_modules/@firebase/remote-config-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.4.tgz", - "integrity": "sha512-6WeKR7E9KJ1RIF9GZiyle1uD4IsIPUBKUnUnFkQhj3FV6cGvQwbeG0rbh7QQLvd0IWuh9lABYjHXWp+rGHQk8A==", + "node_modules/@firebase/analytics-compat": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.5.tgz", + "integrity": "sha512-5cfr0uWwlhoHQYAr6UtQCHwnGjs/3J/bWrfA3INNtzaN4/tTTLTD02iobbccRcM7dM5TR0sZFWS5orfAU3OBFg==", "dependencies": { + "@firebase/analytics": "0.7.4", + "@firebase/analytics-types": "0.7.0", "@firebase/component": "0.5.9", - "@firebase/logger": "0.3.2", - "@firebase/remote-config": "0.3.3", - "@firebase/remote-config-types": "0.2.0", "@firebase/util": "1.4.2", "tslib": "^2.1.0" }, @@ -448,33 +642,44 @@ "@firebase/app-compat": "0.x" } }, - "node_modules/@firebase/remote-config-types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.2.0.tgz", - "integrity": "sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw==" + "node_modules/@firebase/analytics-types": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.7.0.tgz", + "integrity": "sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ==" }, - "node_modules/@firebase/storage": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.8.6.tgz", - "integrity": "sha512-ltZinJHC+gvBwmq2MB9phOC5mOBcLE1LkHGjt2LfRyH4SFYdZ6QPwchQfJTvOPKWazsS/y9fud1TliLs7/ZEqQ==", + "node_modules/@firebase/app": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.7.tgz", + "integrity": "sha512-3yEJDg814CnYIODgLCr4vrIP5Of78WDdikJVE5LS7MN1MWDFeJpQ4n88BdjO2X4Dp22+UFkw7FiuduwfUJJYYQ==", "dependencies": { "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.1.tgz", + "integrity": "sha512-5TYzIM7lhvxt8kB98iULOCrRgI8/qu7LEdsJNm8jEymk3x4DBL3lK0oRw5nHbyUy+lK7cq9D1NmZZnLA3Snt4w==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, - "node_modules/@firebase/storage-compat": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.6.tgz", - "integrity": "sha512-4uzrDWAQpJ2wwjDBdWwhmy3F3T4KTV98qHlhXaquX3KRo3VxgZLcNwWYvD4fZwxi66COn2oZb70zMztcNmn7BA==", + "node_modules/@firebase/app-check-compat": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.1.tgz", + "integrity": "sha512-nB34OoU0icJM0iVrSf7oRVVzrceSvKYdcwlqitrN9JaB+36KwQ0FiQ4saI/rE4DLjcNsviV2ojJ/PRPdv+P0QQ==", "dependencies": { + "@firebase/app-check": "0.5.1", "@firebase/component": "0.5.9", - "@firebase/storage": "0.8.6", - "@firebase/storage-types": "0.6.0", + "@firebase/logger": "0.3.2", "@firebase/util": "1.4.2", "tslib": "^2.1.0" }, @@ -482,622 +687,4175 @@ "@firebase/app-compat": "0.x" } }, - "node_modules/@firebase/storage-types": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.6.0.tgz", - "integrity": "sha512-1LpWhcCb1ftpkP/akhzjzeFxgVefs6eMD2QeKiJJUGH1qOiows2w5o0sKCUSQrvrRQS1lz3SFGvNR1Ck/gqxeA==", - "peerDependencies": { - "@firebase/app-types": "0.x", + "node_modules/@firebase/app-check-interop-types": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.1.0.tgz", + "integrity": "sha512-uZfn9s4uuRsaX5Lwx+gFP3B6YsyOKUE+Rqa6z9ojT4VSRAsZFko9FRn6OxQUA1z5t5d08fY4pf+/+Dkd5wbdbA==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.8.tgz", + "integrity": "sha512-tDjJOoCHYXdswyci0UAPV9vMWp2Guxm8B2jw2A+A1DEcMBcL2z3dDp1JnAwFNbqvE9JHuMBVfEKq/fO0GgDXOg==", + "dependencies": { + "@firebase/app": "0.7.7", + "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", + "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" + }, + "node_modules/@firebase/auth": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.2.tgz", + "integrity": "sha512-TH6v+wi3cHNdcshAjaWsAPYw/JmY5MYU8xCtZMQQaJdf+c/X+uCWv23s7Xs1fzda5+jecjVmENoXa+i/Onxeeg==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "node-fetch": "2.6.5", + "selenium-webdriver": "4.0.0-rc-1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.2.tgz", + "integrity": "sha512-ywYVs/GdBGHTfIKh65IHkge9kyUqOBd24jCI1mmUeQjO3ChVZfdOJk2JvhegLwaRnPYiuzzrWo7wp87YXVL+TQ==", + "dependencies": { + "@firebase/auth": "0.19.2", + "@firebase/auth-types": "0.11.0", + "@firebase/component": "0.5.9", + "@firebase/util": "1.4.2", + "node-fetch": "2.6.5", + "selenium-webdriver": "^4.0.0-beta.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", + "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", + "peerDependencies": { + "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, - "node_modules/@firebase/util": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.2.tgz", - "integrity": "sha512-JMiUo+9QE9lMBvEtBjqsOFdmJgObFvi7OL1A0uFGwTmlCI1ZeNPOEBrwXkgTOelVCdiMO15mAebtEyxFuQ6FsA==", + "node_modules/@firebase/auth-types": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.11.0.tgz", + "integrity": "sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.9.tgz", + "integrity": "sha512-oLCY3x9WbM5rn06qmUvbtJuPj4dIw/C9T4Th52IiHF5tiCRC5k6YthvhfUVcTwfoUhK0fOgtwuKJKA/LpCPjgA==", "dependencies": { + "@firebase/util": "1.4.2", "tslib": "^2.1.0" } }, - "node_modules/@firebase/webchannel-wrapper": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.6.1.tgz", - "integrity": "sha512-9FqhNjKQWpQ3fGnSOCovHOm+yhhiorKEqYLAfd525jWavunDJcx8rOW6i6ozAh+FbwcYMkL7b+3j4UR/30MpoQ==" + "node_modules/@firebase/database": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.4.tgz", + "integrity": "sha512-XkrL1kXELRNkqKcltuT4hfG1gWmFiGvjFY+z7Lhb//12MqdkLjwa9YMK8c6Lo+Ro+IkWcJArQaOQYe3GkU5Wgg==", + "dependencies": { + "@firebase/auth-interop-types": "0.1.6", + "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } }, - "node_modules/@grpc/grpc-js": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.4.tgz", - "integrity": "sha512-a6222b7Dl6fIlMgzVl7e+NiRoLiZFbpcwvBH2Oli56Bn7W4/3Ld+86hK4ffPn5rx2DlDidmIcvIJiOQXyhv9gA==", + "node_modules/@firebase/database-compat": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.4.tgz", + "integrity": "sha512-dIJiZLDFF3U+MoEwoPBy7zxWmBUro1KefmwSHlpOoxmPv76tuoPm85NumpW/HmMrtTcTkC2qowtb6NjGE8X7mw==", "dependencies": { - "@grpc/proto-loader": "^0.6.4", - "@types/node": ">=12.12.47" + "@firebase/component": "0.5.9", + "@firebase/database": "0.12.4", + "@firebase/database-types": "0.9.3", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/database-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.3.tgz", + "integrity": "sha512-R+YXLWy/Q7mNUxiUYiMboTwvVoprrgfyvf1Viyevskw6IoH1q8HV1UjlkLSgmRsOT9HPWt7XZUEStVZJFknHwg==", + "dependencies": { + "@firebase/app-types": "0.7.0", + "@firebase/util": "1.4.2" + } + }, + "node_modules/@firebase/firestore": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.3.0.tgz", + "integrity": "sha512-QMCwmBlUUFldszKtVqIlqwjZYY0eODI2R7F9lkPxiANw8F853bSyBY6wqN85657vfDS7Ij6i6s+1qWMCqFvHHA==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "@firebase/webchannel-wrapper": "0.6.1", + "@grpc/grpc-js": "^1.3.2", + "@grpc/proto-loader": "^0.6.0", + "node-fetch": "2.6.5", + "tslib": "^2.1.0" }, "engines": { - "node": "^8.13.0 || >=10.10.0" + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" } }, - "node_modules/@grpc/proto-loader": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.6.tgz", - "integrity": "sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==", + "node_modules/@firebase/firestore-compat": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.7.tgz", + "integrity": "sha512-34n9PxdenKRNqZRrr+SfjAcrPUvbfggLnRrADz7iVFYlDo9X1Jj6+fimzo0xC/p+2KZkPAiRYbT60WhjBLYUcg==", "dependencies": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^6.10.0", - "yargs": "^16.1.1" + "@firebase/component": "0.5.9", + "@firebase/firestore": "3.3.0", + "@firebase/firestore-types": "2.5.0", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.5.0.tgz", + "integrity": "sha512-I6c2m1zUhZ5SH0cWPmINabDyH5w0PPFHk2UHsjBpKdZllzJZ2TwTkXbDtpHUZNmnc/zAa0WNMNMvcvbb/xJLKA==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.7.5.tgz", + "integrity": "sha512-eEA8WhvNqahbepl0DF1vPc8Ml8oPMkDUQr+HQFQSqVvhYaGc1r6yP+Xe5QChifGfrAd5s/AanchNDvkS86Dg9g==", + "dependencies": { + "@firebase/app-check-interop-types": "0.1.0", + "@firebase/auth-interop-types": "0.1.6", + "@firebase/component": "0.5.9", + "@firebase/messaging-interop-types": "0.1.0", + "@firebase/util": "1.4.2", + "node-fetch": "2.6.5", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.1.6.tgz", + "integrity": "sha512-YZZmToY5Psp0fH8IMdqb00RXsI+dT/+YHKjNNZ1mO/MQS1Uwk40rqaOwZa00xYVeiEyfMnT2ciXyHPEYD7nxPQ==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/functions": "0.7.5", + "@firebase/functions-types": "0.5.0", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.5.0.tgz", + "integrity": "sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA==" + }, + "node_modules/@firebase/installations": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.4.tgz", + "integrity": "sha512-rYb6Ju/tIBhojmM8FsgS96pErKl6gPgJFnffMO4bKH7HilXhOfgLfKU9k51ZDcps8N0npDx9+AJJ6pL1aYuYZQ==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/util": "1.4.2", + "idb": "3.0.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.2.tgz", + "integrity": "sha512-lzLrcJp9QBWpo40OcOM9B8QEtBw2Fk1zOZQdvv+rWS6gKmhQBCEMc4SMABQfWdjsylBcDfniD1Q+fUX1dcBTXA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.4.tgz", + "integrity": "sha512-OvYV4MLPfDpdP/yltLqZXZRx6rXWz52bEilS2jL2B4sGiuTaXSkR6BIHB54EPTblu32nbyZYdlER4fssz4TfXw==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/installations": "0.5.4", + "@firebase/messaging-interop-types": "0.1.0", + "@firebase/util": "1.4.2", + "idb": "3.0.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.4.tgz", + "integrity": "sha512-6477jBw7w7hk0uhnTUMsPoukalpcwbxTTo9kMguHVSXe0t3OdoxeXEaapaNJlOmU4Kgc8j3rsms8IDLdKVpvlA==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/messaging": "0.9.4", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.1.0.tgz", + "integrity": "sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ==" + }, + "node_modules/@firebase/performance": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.4.tgz", + "integrity": "sha512-ES6aS4eoMhf9CczntBADDsXhaFea/3a0FADwy/VpWXXBxVb8tqc5tPcoTwd9L5M/aDeSiQMy344rhrSsTbIZEg==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/installations": "0.5.4", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.4.tgz", + "integrity": "sha512-YuGfmpC0o+YvEBlEZCbPdNbT4Nn2qhi5uMXjqKnNIUepmXUsgOYDiAqM9nxHPoE/6IkvoFMdCj5nTUYVLCFXgg==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", + "@firebase/performance": "0.5.4", + "@firebase/performance-types": "0.1.0", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.1.0.tgz", + "integrity": "sha512-6p1HxrH0mpx+622Ql6fcxFxfkYSBpE3LSuwM7iTtYU2nw91Hj6THC8Bc8z4nboIq7WvgsT/kOTYVVZzCSlXl8w==" + }, + "node_modules/@firebase/polyfill": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.36.tgz", + "integrity": "sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg==", + "dependencies": { + "core-js": "3.6.5", + "promise-polyfill": "8.1.3", + "whatwg-fetch": "2.0.4" + } + }, + "node_modules/@firebase/remote-config": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.3.tgz", + "integrity": "sha512-9hZWfB3k3IYsjHbWeUfhv/SDCcOgv/JMJpLXlUbTppXPm1IZ3X9ZW4I9bS86gGYr7m/kSv99U0oxQ7N9PoR8Iw==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/installations": "0.5.4", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.4.tgz", + "integrity": "sha512-6WeKR7E9KJ1RIF9GZiyle1uD4IsIPUBKUnUnFkQhj3FV6cGvQwbeG0rbh7QQLvd0IWuh9lABYjHXWp+rGHQk8A==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/logger": "0.3.2", + "@firebase/remote-config": "0.3.3", + "@firebase/remote-config-types": "0.2.0", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.2.0.tgz", + "integrity": "sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw==" + }, + "node_modules/@firebase/storage": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.8.6.tgz", + "integrity": "sha512-ltZinJHC+gvBwmq2MB9phOC5mOBcLE1LkHGjt2LfRyH4SFYdZ6QPwchQfJTvOPKWazsS/y9fud1TliLs7/ZEqQ==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/util": "1.4.2", + "node-fetch": "2.6.5", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.6.tgz", + "integrity": "sha512-4uzrDWAQpJ2wwjDBdWwhmy3F3T4KTV98qHlhXaquX3KRo3VxgZLcNwWYvD4fZwxi66COn2oZb70zMztcNmn7BA==", + "dependencies": { + "@firebase/component": "0.5.9", + "@firebase/storage": "0.8.6", + "@firebase/storage-types": "0.6.0", + "@firebase/util": "1.4.2", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.6.0.tgz", + "integrity": "sha512-1LpWhcCb1ftpkP/akhzjzeFxgVefs6eMD2QeKiJJUGH1qOiows2w5o0sKCUSQrvrRQS1lz3SFGvNR1Ck/gqxeA==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.2.tgz", + "integrity": "sha512-JMiUo+9QE9lMBvEtBjqsOFdmJgObFvi7OL1A0uFGwTmlCI1ZeNPOEBrwXkgTOelVCdiMO15mAebtEyxFuQ6FsA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.6.1.tgz", + "integrity": "sha512-9FqhNjKQWpQ3fGnSOCovHOm+yhhiorKEqYLAfd525jWavunDJcx8rOW6i6ozAh+FbwcYMkL7b+3j4UR/30MpoQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.4.tgz", + "integrity": "sha512-a6222b7Dl6fIlMgzVl7e+NiRoLiZFbpcwvBH2Oli56Bn7W4/3Ld+86hK4ffPn5rx2DlDidmIcvIJiOQXyhv9gA==", + "dependencies": { + "@grpc/proto-loader": "^0.6.4", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.6.tgz", + "integrity": "sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==", + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.10.0", + "yargs": "^16.1.1" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@peapodtech/firebasedeviceflow": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@peapodtech/firebasedeviceflow/-/firebasedeviceflow-0.2.0.tgz", + "integrity": "sha512-IOkEZt2P7hcMZX7a7URSvtOu4K3Bw8QhA2REsLHqBLywHkslm1dXx0PCZY12Dk2dBdMdY9iUWAfJtjANPWs5og==", + "dependencies": { + "@types/inquirer": "^7.3.1", + "axios": "^0.21.1", + "chalk": "^4.1.0", + "firebase": "^9.4.0", + "inquirer": "^7.3.3", + "ora": "^5.2.0", + "typescript": "^4.1.3" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "dependencies": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "10.6.3", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.6.3.tgz", + "integrity": "sha512-jmALQ62JvIlCIx8osWifVXX1GoftlTPiX/HuEYeCYlRmtwXrbBzoD8dXu48YfGoZ/dwaYYKTsJarHqFoPq809Q==", + "hasInstallScript": true, + "dependencies": { + "@serialport/bindings-interface": "1.2.1", + "@serialport/parser-readline": "^10.2.1", + "debug": "^4.3.2", + "node-addon-api": "^4.3.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=12.17.0 <13.0 || >=14.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", + "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", + "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", + "dependencies": { + "@serialport/parser-delimiter": "10.3.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.1.tgz", + "integrity": "sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@serialport/parser-byte-length": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.3.0.tgz", + "integrity": "sha512-pJ/VoFemzKRRNDHLhFfPThwP40QrGaEnm9TtwL7o2GihEPwzBg3T0bN13ew5TpbbUYZdMpUtpm3CGfl6av9rUQ==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-cctalk": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.3.0.tgz", + "integrity": "sha512-8ujmk8EvVbDPrNF4mM33bWvUYJOZ0wXbY3WCRazHRWvyCdL0VO0DQvW81ZqgoTpiDQZm5r8wQu9rmuemahF6vQ==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-delimiter": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-9.2.4.tgz", + "integrity": "sha512-4nvTAoYAgkxFiXrkI+3CA49Yd43CODjeszh89EK+I9c8wOZ+etZduRCzINYPiy26g7zO+GRAb9FoPCsY+sYcbQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-inter-byte-timeout": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.3.0.tgz", + "integrity": "sha512-wKP0QK85NHgvT6BBB1qBfKBBU4pf8kespNXAZBUYmFT+P4n8r8IZE2mqigCD+AiZcfWNQoAizwOsT/Jx/qeVig==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-packet-length": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.3.0.tgz", + "integrity": "sha512-bj0cWzt8YSQj/E5fRQVYdi4TsfTlZQrXlXrUwjyTsCONv8IPOHzsz+yY0fw5SEMiJtaLyqvPkCHLsttOd/zFsg==", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@serialport/parser-readline": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-9.2.4.tgz", + "integrity": "sha512-Z1/qrZTQUVhNSJP1hd9YfDvq0o7d87rNwAjjRKbVpa7Qi51tG5BnKt43IV3NFMyBlVcRe0rnIb3tJu57E0SOwg==", + "dev": true, + "dependencies": { + "@serialport/parser-delimiter": "9.2.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-ready": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.3.0.tgz", + "integrity": "sha512-1owywJ4p592dJyVrEJZPIh6pUZ3/y/LN6kGTDH2wxdewRUITo/sGvDy0er5i2+dJD3yuowiAz0dOHSdz8tevJA==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.3.0.tgz", + "integrity": "sha512-tIogTs7CvTH+UUFnsvE7i33MSISyTPTGPWlglWYH2/5coipXY503jlaYS1YGe818wWNcSx6YAjMZRdhTWwM39w==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.3.0.tgz", + "integrity": "sha512-JI0ILF5sylWn8f0MuMzHFBix/iMUTa79/Z95KaPZYnVaEdA7h7hh/o21Jmon/26P3RJwL1SNJCjZ81zfan+LtQ==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.3.0.tgz", + "integrity": "sha512-PDF73ClEPsClD1FEJZHNuBevDKsJCkqy/XD5+S5eA6+tY5D4HLrVgSWsg+3qqB6+dlpwf2CzHe+uO8D3teuKHA==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.3.0.tgz", + "integrity": "sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==", + "dependencies": { + "@serialport/bindings-interface": "1.2.1", + "debug": "^4.3.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.18", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", + "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dependencies": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.5.tgz", + "integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, + "node_modules/@types/node": { + "version": "16.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", + "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==" + }, + "node_modules/@types/pi-camera": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/pi-camera/-/pi-camera-1.5.3.tgz", + "integrity": "sha512-9CGFEc7Cr0QDWdhqL+Bjb8+0xcYX8L9iv7YlQw+F5ntBh9GwZImDbaeJsFZg76TLGsTMLDTrowfHA7WWMX0csQ==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", + "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", + "dev": true + }, + "node_modules/@types/project-name-generator": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/project-name-generator/-/project-name-generator-2.1.1.tgz", + "integrity": "sha512-VLd5FEVTJs8hNa/WF4pZRcFvv0OAcIGeTyki4RDcaID0TUhTc5/Xe/btYM2XIHwVCb67ila8wUJYKJNa5dVABw==", + "dev": true + }, + "node_modules/@types/serialport": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@types/serialport/-/serialport-8.0.2.tgz", + "integrity": "sha512-z4b1I8/vdZE3upgCcAL9VAWlVVFUVn5uo3faAHavkVfK/Hb1LUxKwp9YCtA5AZqEUCWoSWl20SRTOvAI/5fQWQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001317", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", + "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", + "dependencies": { + "leven": "^2.1.0", + "minimist": "^1.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "optional": true, + "dependencies": { + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "optional": true + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.86", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz", + "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/firebase": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.4.0.tgz", + "integrity": "sha512-VcdMVYf02OZtDWp6IYnlcostwqWZHlumxgnY5K87EKBHIjGcjkpdb39mguXZCVfQ44TRid2yaBouGDKAacnFwQ==", + "dependencies": { + "@firebase/analytics": "0.7.4", + "@firebase/analytics-compat": "0.1.5", + "@firebase/app": "0.7.7", + "@firebase/app-check": "0.5.1", + "@firebase/app-check-compat": "0.2.1", + "@firebase/app-compat": "0.1.8", + "@firebase/app-types": "0.7.0", + "@firebase/auth": "0.19.2", + "@firebase/auth-compat": "0.2.2", + "@firebase/database": "0.12.4", + "@firebase/database-compat": "0.1.4", + "@firebase/firestore": "3.3.0", + "@firebase/firestore-compat": "0.1.7", + "@firebase/functions": "0.7.5", + "@firebase/functions-compat": "0.1.6", + "@firebase/installations": "0.5.4", + "@firebase/messaging": "0.9.4", + "@firebase/messaging-compat": "0.1.4", + "@firebase/performance": "0.5.4", + "@firebase/performance-compat": "0.1.4", + "@firebase/polyfill": "0.3.36", + "@firebase/remote-config": "0.3.3", + "@firebase/remote-config-compat": "0.1.4", + "@firebase/storage": "0.8.6", + "@firebase/storage-compat": "0.1.6", + "@firebase/util": "1.4.2" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "node_modules/help-me": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", + "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", + "dependencies": { + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/idb": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", + "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", - "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "dependencies": { - "detect-libc": "^1.0.3", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.5", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { - "node-pre-gyp": "bin/node-pre-gyp" + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" }, "engines": { "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/@peapodtech/firebasedeviceflow": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@peapodtech/firebasedeviceflow/-/firebasedeviceflow-0.2.0.tgz", - "integrity": "sha512-IOkEZt2P7hcMZX7a7URSvtOu4K3Bw8QhA2REsLHqBLywHkslm1dXx0PCZY12Dk2dBdMdY9iUWAfJtjANPWs5og==", + "node_modules/jsdom/node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, "dependencies": { - "@types/inquirer": "^7.3.1", - "axios": "^0.21.1", - "chalk": "^4.1.0", - "firebase": "^9.4.0", - "inquirer": "^7.3.3", - "ora": "^5.2.0", - "typescript": "^4.1.3" + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + "node_modules/jsdom/node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "node_modules/jsdom/node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, - "node_modules/@serialport/binding-mock": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", - "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, "dependencies": { - "@serialport/bindings-interface": "^1.2.1", - "debug": "^4.3.3" + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=6" } }, - "node_modules/@serialport/bindings-cpp": { - "version": "10.6.3", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.6.3.tgz", - "integrity": "sha512-jmALQ62JvIlCIx8osWifVXX1GoftlTPiX/HuEYeCYlRmtwXrbBzoD8dXu48YfGoZ/dwaYYKTsJarHqFoPq809Q==", - "hasInstallScript": true, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", "dependencies": { - "@serialport/bindings-interface": "1.2.1", - "@serialport/parser-readline": "^10.2.1", - "debug": "^4.3.2", - "node-addon-api": "^4.3.0", - "node-gyp-build": "^4.3.0" + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" }, "engines": { - "node": ">=12.17.0 <13.0 || >=14.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node": ">=4", + "npm": ">=1.4.28" } }, - "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", - "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", - "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "dependencies": { - "@serialport/parser-delimiter": "10.3.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" } }, - "node_modules/@serialport/bindings-interface": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.1.tgz", - "integrity": "sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==", - "engines": { - "node": "^12.22 || ^14.13 || >=16" + "node_modules/jszip": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", + "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" } }, - "node_modules/@serialport/parser-byte-length": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.3.0.tgz", - "integrity": "sha512-pJ/VoFemzKRRNDHLhFfPThwP40QrGaEnm9TtwL7o2GihEPwzBg3T0bN13ew5TpbbUYZdMpUtpm3CGfl6av9rUQ==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/@serialport/parser-cctalk": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.3.0.tgz", - "integrity": "sha512-8ujmk8EvVbDPrNF4mM33bWvUYJOZ0wXbY3WCRazHRWvyCdL0VO0DQvW81ZqgoTpiDQZm5r8wQu9rmuemahF6vQ==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "node_modules/@serialport/parser-delimiter": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-9.2.4.tgz", - "integrity": "sha512-4nvTAoYAgkxFiXrkI+3CA49Yd43CODjeszh89EK+I9c8wOZ+etZduRCzINYPiy26g7zO+GRAb9FoPCsY+sYcbQ==", + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "engines": { - "node": ">=10.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-inter-byte-timeout": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.3.0.tgz", - "integrity": "sha512-wKP0QK85NHgvT6BBB1qBfKBBU4pf8kespNXAZBUYmFT+P4n8r8IZE2mqigCD+AiZcfWNQoAizwOsT/Jx/qeVig==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node": ">=6" } }, - "node_modules/@serialport/parser-packet-length": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.3.0.tgz", - "integrity": "sha512-bj0cWzt8YSQj/E5fRQVYdi4TsfTlZQrXlXrUwjyTsCONv8IPOHzsz+yY0fw5SEMiJtaLyqvPkCHLsttOd/zFsg==", + "node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", "engines": { - "node": ">=8.6.0" + "node": ">=0.10.0" } }, - "node_modules/@serialport/parser-readline": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-9.2.4.tgz", - "integrity": "sha512-Z1/qrZTQUVhNSJP1hd9YfDvq0o7d87rNwAjjRKbVpa7Qi51tG5BnKt43IV3NFMyBlVcRe0rnIb3tJu57E0SOwg==", + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "dependencies": { - "@serialport/parser-delimiter": "9.2.4" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" }, "engines": { - "node": ">=10.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node": ">= 0.8.0" } }, - "node_modules/@serialport/parser-ready": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.3.0.tgz", - "integrity": "sha512-1owywJ4p592dJyVrEJZPIh6pUZ3/y/LN6kGTDH2wxdewRUITo/sGvDy0er5i2+dJD3yuowiAz0dOHSdz8tevJA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" } }, - "node_modules/@serialport/parser-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.3.0.tgz", - "integrity": "sha512-tIogTs7CvTH+UUFnsvE7i33MSISyTPTGPWlglWYH2/5coipXY503jlaYS1YGe818wWNcSx6YAjMZRdhTWwM39w==", - "engines": { - "node": ">=12.0.0" + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "engines": { + "node": ">=8" } }, - "node_modules/@serialport/parser-slip-encoder": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.3.0.tgz", - "integrity": "sha512-JI0ILF5sylWn8f0MuMzHFBix/iMUTa79/Z95KaPZYnVaEdA7h7hh/o21Jmon/26P3RJwL1SNJCjZ81zfan+LtQ==", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, "engines": { - "node": ">=12.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@serialport/parser-spacepacket": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.3.0.tgz", - "integrity": "sha512-PDF73ClEPsClD1FEJZHNuBevDKsJCkqy/XD5+S5eA6+tY5D4HLrVgSWsg+3qqB6+dlpwf2CzHe+uO8D3teuKHA==", - "engines": { - "node": ">=12.0.0" + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "engines": { + "node": ">=10" } }, - "node_modules/@serialport/stream": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.3.0.tgz", - "integrity": "sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==", + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dependencies": { - "@serialport/bindings-interface": "1.2.1", - "debug": "^4.3.2" + "semver": "^6.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@types/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", - "dependencies": { - "@types/through": "*", - "rxjs": "^6.4.0" + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@types/jsonwebtoken": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.5.tgz", - "integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "dependencies": { - "@types/node": "*" + "tmpl": "1.0.5" } }, - "node_modules/@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "node_modules/@types/node": { - "version": "16.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", - "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==" - }, - "node_modules/@types/pi-camera": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@types/pi-camera/-/pi-camera-1.5.3.tgz", - "integrity": "sha512-9CGFEc7Cr0QDWdhqL+Bjb8+0xcYX8L9iv7YlQw+F5ntBh9GwZImDbaeJsFZg76TLGsTMLDTrowfHA7WWMX0csQ==", - "dev": true - }, - "node_modules/@types/project-name-generator": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/project-name-generator/-/project-name-generator-2.1.1.tgz", - "integrity": "sha512-VLd5FEVTJs8hNa/WF4pZRcFvv0OAcIGeTyki4RDcaID0TUhTc5/Xe/btYM2XIHwVCb67ila8wUJYKJNa5dVABw==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "node_modules/@types/serialport": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@types/serialport/-/serialport-8.0.2.tgz", - "integrity": "sha512-z4b1I8/vdZE3upgCcAL9VAWlVVFUVn5uo3faAHavkVfK/Hb1LUxKwp9YCtA5AZqEUCWoSWl20SRTOvAI/5fQWQ==", + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "dependencies": { - "@types/node": "*" + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" } }, - "node_modules/@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", - "dependencies": { - "@types/node": "*" + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@types/uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dependencies": { - "debug": "4" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 6.0.0" + "node": "*" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "dependencies": { - "type-fest": "^0.21.3" + "yallist": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=10" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "node_modules/mqtt": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", + "integrity": "sha512-DJYjlXODVXtSDecN8jnNzi6ItX3+ufGsEs9OB3YV24HtkRrh7kpx8L5M1LuyF0KzaiGtWr2PzDcMGAY60KGOSA==", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "commist": "^1.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.1.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", + "inherits": "^2.0.3", + "minimist": "^1.2.5", + "mqtt-packet": "^6.8.0", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "reinterval": "^1.1.0", + "split2": "^3.1.0", + "ws": "^7.5.0", + "xtend": "^4.0.2" + }, + "bin": { + "mqtt": "bin/mqtt.js", + "mqtt_pub": "bin/pub.js", + "mqtt_sub": "bin/sub.js" }, "engines": { - "node": ">=10" + "node": ">=10.0.0" } }, - "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "node_modules/mqtt-packet": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", + "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", "dependencies": { - "follow-redirects": "^1.14.0" + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + }, + "node_modules/node-fetch": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "node_modules/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" }, "engines": { - "node": ">=10" + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" + "wrappy": "1" } }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, "engines": { "node": ">=6" }, @@ -1105,426 +4863,437 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, "engines": { - "node": ">= 10" + "node": ">= 0.8.0" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dependencies": { - "string-width": "^4.2.0", + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "engines": { - "node": ">=0.8" + "node": ">=0.10.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "p-try": "^2.0.0" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/commist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", - "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", - "dependencies": { - "leven": "^2.1.0", - "minimist": "^1.1.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "node_modules/core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, - "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=6.0" + "node": ">=8" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dependencies": { - "clone": "^1.0.2" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/domexception": { + "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "dependencies": { - "webidl-conversions": "^4.0.2" + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" + "node_modules/pi-camera": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.6.0.tgz", + "integrity": "sha512-WrnJ4R1X/5qA5/vXeou/Ty97c7urrnla03j5FhctF9nZy1lXnfYkPOsL+qDZAZcvg40cbkTE217WlHNnMMftaw==", + "engines": { + "node": ">=6.10.0" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">= 6" } }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "find-up": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">= 0.8.0" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/firebase": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.4.0.tgz", - "integrity": "sha512-VcdMVYf02OZtDWp6IYnlcostwqWZHlumxgnY5K87EKBHIjGcjkpdb39mguXZCVfQ44TRid2yaBouGDKAacnFwQ==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise-polyfill": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", + "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "dependencies": { - "@firebase/analytics": "0.7.4", - "@firebase/analytics-compat": "0.1.5", - "@firebase/app": "0.7.7", - "@firebase/app-check": "0.5.1", - "@firebase/app-check-compat": "0.2.1", - "@firebase/app-compat": "0.1.8", - "@firebase/app-types": "0.7.0", - "@firebase/auth": "0.19.2", - "@firebase/auth-compat": "0.2.2", - "@firebase/database": "0.12.4", - "@firebase/database-compat": "0.1.4", - "@firebase/firestore": "3.3.0", - "@firebase/firestore-compat": "0.1.7", - "@firebase/functions": "0.7.5", - "@firebase/functions-compat": "0.1.6", - "@firebase/installations": "0.5.4", - "@firebase/messaging": "0.9.4", - "@firebase/messaging-compat": "0.1.4", - "@firebase/performance": "0.5.4", - "@firebase/performance-compat": "0.1.4", - "@firebase/polyfill": "0.3.36", - "@firebase/remote-config": "0.3.3", - "@firebase/remote-config-compat": "0.1.4", - "@firebase/storage": "0.8.6", - "@firebase/storage-compat": "0.1.6", - "@firebase/util": "1.4.2" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" + "node_modules/protobufjs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", + "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dependencies": { - "minipass": "^3.0.0" - }, + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { - "node": ">= 8" + "node": ">=6" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=0.10.0" } }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": "*" + "bin": { + "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "node_modules/help-me": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", - "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", - "dependencies": { - "glob": "^7.1.6", - "readable-stream": "^3.6.0" + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dependencies": { - "agent-base": "6", - "debug": "4" + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "engines": { - "node": ">= 6" + "node": ">=0.12.0" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "tslib": "^1.9.0" }, "engines": { - "node": ">=0.10.0" + "npm": ">=2.0.0" } }, - "node_modules/idb": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", - "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -1540,1060 +5309,1310 @@ } ] }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "node_modules/selenium-webdriver": { + "version": "4.0.0-rc-1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-rc-1.tgz", + "integrity": "sha512-bcrwFPRax8fifRP60p7xkWDGSJJoMkPAzufMlk5K2NyLPht/YZzR2WcIk1+3gR8VOCLlst1P2PI+MXACaFzpIw==", "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "jszip": "^3.6.0", + "rimraf": "^3.0.2", + "tmp": "^0.2.1", + "ws": ">=7.4.6" }, "engines": { - "node": ">=8.0.0" + "node": ">= 10.15.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/selenium-webdriver/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { + "rimraf": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": ">=8.17.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/serialport": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.3.0.tgz", + "integrity": "sha512-MZMRlQgWOG7boLkJwm200Z3W39GcFmGo3tWxQrvyrZpH7W1p3t16M1VBglVxR6wXRF8qm01VxXBA+rNV7fPMVA==", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "10.6.3", + "@serialport/parser-byte-length": "10.3.0", + "@serialport/parser-cctalk": "10.3.0", + "@serialport/parser-delimiter": "10.3.0", + "@serialport/parser-inter-byte-timeout": "10.3.0", + "@serialport/parser-packet-length": "10.3.0", + "@serialport/parser-readline": "10.3.0", + "@serialport/parser-ready": "10.3.0", + "@serialport/parser-regex": "10.3.0", + "@serialport/parser-slip-encoder": "10.3.0", + "@serialport/parser-spacepacket": "10.3.0", + "@serialport/stream": "10.3.0", + "debug": "^4.3.3" + }, "engines": { - "node": ">=10" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/serialport/donate" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "node_modules/serialport/node_modules/@serialport/parser-delimiter": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", + "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } }, - "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "node_modules/serialport/node_modules/@serialport/parser-readline": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", + "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" + "@serialport/parser-delimiter": "10.3.0" }, "engines": { - "node": ">=4", - "npm": ">=1.4.28" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" } }, - "node_modules/jsonwebtoken/node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "node_modules/jsonwebtoken/node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" + "node_modules/set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jszip": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", - "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/jszip/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "dependencies": { - "immediate": "~3.0.5" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dependencies": { + "readable-stream": "^3.0.0" + } }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "yallist": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "semver": "^6.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, "engines": { "node": ">=6" } }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "brace-expansion": "^1.1.7" + "has-flag": "^4.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 10" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mqtt": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", - "integrity": "sha512-DJYjlXODVXtSDecN8jnNzi6ItX3+ufGsEs9OB3YV24HtkRrh7kpx8L5M1LuyF0KzaiGtWr2PzDcMGAY60KGOSA==", + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, "dependencies": { - "commist": "^1.0.0", - "concat-stream": "^2.0.0", - "debug": "^4.1.1", - "duplexify": "^4.1.1", - "help-me": "^3.0.0", - "inherits": "^2.0.3", - "minimist": "^1.2.5", - "mqtt-packet": "^6.8.0", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "reinterval": "^1.1.0", - "split2": "^3.1.0", - "ws": "^7.5.0", - "xtend": "^4.0.2" - }, - "bin": { - "mqtt": "bin/mqtt.js", - "mqtt_pub": "bin/pub.js", - "mqtt_sub": "bin/sub.js" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=8" } }, - "node_modules/mqtt-packet": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", - "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", + "node_modules/throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } }, - "node_modules/node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { - "whatwg-url": "^5.0.0" + "is-number": "^7.0.0" }, "engines": { - "node": "4.x || >=6.0.0" + "node": ">=8.0" } }, - "node_modules/node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" } }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/ts-jest": { + "version": "27.1.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", + "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "dev": true, "dependencies": { - "abbrev": "1" + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" }, "bin": { - "nopt": "bin/nopt.js" + "ts-jest": "cli.js" }, "engines": { - "node": ">=6" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@types/jest": "^27.0.0", + "babel-jest": ">=27.0.0 <28", + "esbuild": "~0.14.0", + "jest": "^27.0.0", + "typescript": ">=3.8 <5.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } } }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "node_modules/ts-jest/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.2.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 4.0.0" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/pi-camera": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.6.0.tgz", - "integrity": "sha512-WrnJ4R1X/5qA5/vXeou/Ty97c7urrnla03j5FhctF9nZy1lXnfYkPOsL+qDZAZcvg40cbkTE217WlHNnMMftaw==", + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, "engines": { - "node": ">=6.10.0" + "node": ">=10.12.0" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } }, - "node_modules/promise-polyfill": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", - "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==" + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } }, - "node_modules/protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", - "hasInstallScript": true, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" + "xml-name-validator": "^3.0.0" }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" } }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "defaults": "^1.0.3" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" }, "engines": { - "node": ">= 6" + "node": ">=0.8.0" } }, - "node_modules/reinterval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", - "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" + "iconv-lite": "0.4.24" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "dependencies": { - "glob": "^7.1.3" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" }, "bin": { - "rimraf": "bin.js" + "node-which": "bin/node-which" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 8" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=0.10.0" } }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": { - "tslib": "^1.9.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "npm": ">=2.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } }, - "node_modules/selenium-webdriver": { - "version": "4.0.0-rc-1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-rc-1.tgz", - "integrity": "sha512-bcrwFPRax8fifRP60p7xkWDGSJJoMkPAzufMlk5K2NyLPht/YZzR2WcIk1+3gR8VOCLlst1P2PI+MXACaFzpIw==", + "node_modules/wrtc": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz", + "integrity": "sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g==", + "bundleDependencies": [ + "node-pre-gyp" + ], + "hasInstallScript": true, "dependencies": { - "jszip": "^3.6.0", - "rimraf": "^3.0.2", - "tmp": "^0.2.1", - "ws": ">=7.4.6" + "node-pre-gyp": "^0.13.0" }, "engines": { - "node": ">= 10.15.0" + "node": "^8.11.2 || >=10.0.0" + }, + "optionalDependencies": { + "domexception": "^1.0.1" } }, - "node_modules/selenium-webdriver/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dependencies": { - "rimraf": "^3.0.0" + "node_modules/ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "engines": { - "node": ">=8.17.0" + "node": ">=0.4" } }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" } }, - "node_modules/serialport": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.3.0.tgz", - "integrity": "sha512-MZMRlQgWOG7boLkJwm200Z3W39GcFmGo3tWxQrvyrZpH7W1p3t16M1VBglVxR6wXRF8qm01VxXBA+rNV7fPMVA==", + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dependencies": { - "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "10.6.3", - "@serialport/parser-byte-length": "10.3.0", - "@serialport/parser-cctalk": "10.3.0", - "@serialport/parser-delimiter": "10.3.0", - "@serialport/parser-inter-byte-timeout": "10.3.0", - "@serialport/parser-packet-length": "10.3.0", - "@serialport/parser-readline": "10.3.0", - "@serialport/parser-ready": "10.3.0", - "@serialport/parser-regex": "10.3.0", - "@serialport/parser-slip-encoder": "10.3.0", - "@serialport/parser-spacepacket": "10.3.0", - "@serialport/stream": "10.3.0", - "debug": "^4.3.3" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node": ">=10" } }, - "node_modules/serialport/node_modules/@serialport/parser-delimiter": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", - "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "node": ">=10" } - }, - "node_modules/serialport/node_modules/@serialport/parser-readline": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", - "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", - "dependencies": { - "@serialport/parser-delimiter": "10.3.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "engines": { - "node": ">=0.10.0" + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" } }, - "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + "@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "@babel/core": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", + "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.7", + "@babel/parser": "^7.17.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, "dependencies": { - "readable-stream": "^3.0.0" + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, - "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, "dependencies": { - "safe-buffer": "~5.2.0" + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" }, - "engines": { - "node": ">=8" + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" } }, - "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" + "@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true }, - "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true }, - "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + }, + "@babel/helpers": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", + "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + } + }, + "@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" }, - "engines": { - "node": ">=4.2.0" + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "@babel/parser": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", + "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==", + "dev": true }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dependencies": { - "defaults": "^1.0.3" + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" } }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "engines": { - "node": ">=0.8.0" + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" } }, - "node_modules/whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } }, - "node_modules/wrtc": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz", - "integrity": "sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g==", - "bundleDependencies": [ - "node-pre-gyp" - ], - "hasInstallScript": true, - "dependencies": { - "node-pre-gyp": "^0.13.0" - }, - "engines": { - "node": "^8.11.2 || >=10.0.0" - }, - "optionalDependencies": { - "domexception": "^1.0.1" + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" + "@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" } - } - }, - "dependencies": { + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@firebase/analytics": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.4.tgz", @@ -3007,6 +7026,238 @@ "yargs": "^16.1.1" } }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "requires": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "@mapbox/node-pre-gyp": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", @@ -3202,17 +7453,125 @@ "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.3.0.tgz", "integrity": "sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==", "requires": { - "@serialport/bindings-interface": "1.2.1", - "debug": "^4.3.2" + "@serialport/bindings-interface": "1.2.1", + "debug": "^4.3.2" + } + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.18", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", + "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" } }, - "@types/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" } }, "@types/jsonwebtoken": { @@ -3240,6 +7599,12 @@ "integrity": "sha512-9CGFEc7Cr0QDWdhqL+Bjb8+0xcYX8L9iv7YlQw+F5ntBh9GwZImDbaeJsFZg76TLGsTMLDTrowfHA7WWMX0csQ==", "dev": true }, + "@types/prettier": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", + "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", + "dev": true + }, "@types/project-name-generator": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@types/project-name-generator/-/project-name-generator-2.1.1.tgz", @@ -3255,6 +7620,12 @@ "@types/node": "*" } }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, "@types/through": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", @@ -3282,11 +7653,47 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -3316,6 +7723,16 @@ "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -3330,6 +7747,21 @@ "readable-stream": "^3.6.0" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "axios": { "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", @@ -3338,6 +7770,77 @@ "follow-redirects": "^1.14.0" } }, + "babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "requires": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3367,6 +7870,52 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -3386,6 +7935,24 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001317", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", + "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3395,6 +7962,12 @@ "supports-color": "^7.1.0" } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -3405,6 +7978,18 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, + "ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -3438,6 +8023,18 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3456,6 +8053,15 @@ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", @@ -3486,6 +8092,23 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "core-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -3496,6 +8119,79 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "dependencies": { + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + } + } + }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -3504,6 +8200,30 @@ "ms": "2.1.2" } }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -3512,6 +8232,12 @@ "clone": "^1.0.2" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -3522,6 +8248,18 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -3563,6 +8301,18 @@ "safe-buffer": "^5.0.1" } }, + "electron-to-chromium": { + "version": "1.4.86", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz", + "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==", + "dev": true + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -3576,6 +8326,15 @@ "once": "^1.4.0" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3586,6 +8345,72 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3596,6 +8421,18 @@ "tmp": "^0.0.33" } }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -3604,6 +8441,15 @@ "websocket-driver": ">=0.5.1" } }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -3612,6 +8458,25 @@ "escape-string-regexp": "^1.0.5" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "firebase": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.4.0.tgz", @@ -3650,6 +8515,17 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -3663,6 +8539,19 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -3679,11 +8568,29 @@ "wide-align": "^1.1.2" } }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -3697,6 +8604,27 @@ "path-is-absolute": "^1.0.0" } }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3716,11 +8644,37 @@ "readable-stream": "^3.6.0" } }, - "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" - }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -3730,6 +8684,12 @@ "debug": "4" } }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3753,6 +8713,22 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3787,16 +8763,61 @@ "through": "^2.3.6" } }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, "is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -3807,6 +8828,684 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "requires": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + } + }, + "jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + } + }, + "jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "requires": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + } + }, + "jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "requires": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + } + }, + "jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "requires": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + } + }, + "jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "requires": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -3885,11 +9584,27 @@ } } }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -3898,6 +9613,21 @@ "immediate": "~3.0.5" } }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -3938,6 +9668,12 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -3980,6 +9716,52 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -4061,6 +9843,12 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "node-addon-api": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", @@ -4079,6 +9867,18 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -4087,6 +9887,21 @@ "abbrev": "1" } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, "npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", @@ -4098,6 +9913,12 @@ "set-blocking": "^2.0.0" } }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4119,6 +9940,20 @@ "mimic-fn": "^2.1.0" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -4140,21 +9975,133 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "pi-camera": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.6.0.tgz", "integrity": "sha512-WrnJ4R1X/5qA5/vXeou/Ty97c7urrnla03j5FhctF9nZy1lXnfYkPOsL+qDZAZcvg40cbkTE217WlHNnMMftaw==" }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4165,6 +10112,16 @@ "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==" }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "protobufjs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", @@ -4185,6 +10142,12 @@ "long": "^4.0.0" } }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -4194,6 +10157,18 @@ "once": "^1.3.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -4214,6 +10189,38 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -4261,6 +10268,15 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, "selenium-webdriver": { "version": "4.0.0-rc-1", "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-rc-1.tgz", @@ -4333,11 +10349,54 @@ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, "signal-exit": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -4346,6 +10405,29 @@ "readable-stream": "^3.0.0" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -4359,6 +10441,16 @@ "safe-buffer": "~5.2.0" } }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4377,6 +10469,24 @@ "ansi-regex": "^5.0.1" } }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4385,6 +10495,28 @@ "has-flag": "^4.0.0" } }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", @@ -4398,6 +10530,33 @@ "yallist": "^4.0.0" } }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4411,16 +10570,90 @@ "os-tmpdir": "~1.0.2" } }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "ts-jest": { + "version": "27.1.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", + "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -4431,11 +10664,26 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typescript": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==" }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4446,6 +10694,52 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -4474,11 +10768,26 @@ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, "whatwg-fetch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -4488,6 +10797,15 @@ "webidl-conversions": "^3.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -4496,6 +10814,12 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -4511,6 +10835,18 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, "wrtc": { "version": "0.4.7", "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz", @@ -4526,6 +10862,18 @@ "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", "requires": {} }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/software/package.json b/software/package.json index 74336cb2..2137c1f2 100644 --- a/software/package.json +++ b/software/package.json @@ -7,10 +7,11 @@ "lib": "lib" }, "scripts": { - "test": "node . simulate offline", - "offline": "node . offline", - "simulate": "node . simulate", - "build": "npm i && tsc", + "test": "npm run build && jest", + "dryrun": "npm run build && node . simulate offline", + "offline": "npm run build && node . offline", + "simulate": "npm run build && node . simulate", + "build": "npm i && tsc && clear", "start": "node ." }, "repository": { @@ -47,14 +48,19 @@ }, "devDependencies": { "@serialport/parser-readline": "^9.0.1", + "@types/jest": "^27.4.1", "@types/jsonwebtoken": "^8.5.5", "@types/pi-camera": "^1.5.3", "@types/project-name-generator": "^2.1.0", "@types/serialport": "^8.0.2", - "@types/uuid": "^8.3.0" + "@types/uuid": "^8.3.0", + "jest": "^27.5.1", + "ts-jest": "^27.1.3" }, "publishConfig": { "access": "public" }, - "bin": "index.js" + "bin": { + "peapodos": "./index.js" + } } diff --git a/software/lib/PeaPodArduino.ts b/software/src/PeaPodArduino.ts similarity index 100% rename from software/lib/PeaPodArduino.ts rename to software/src/PeaPodArduino.ts diff --git a/software/lib/PeaPodCamera.ts b/software/src/PeaPodCamera.ts similarity index 100% rename from software/lib/PeaPodCamera.ts rename to software/src/PeaPodCamera.ts diff --git a/software/lib/PeaPodPublisher.ts b/software/src/PeaPodPublisher.ts similarity index 100% rename from software/lib/PeaPodPublisher.ts rename to software/src/PeaPodPublisher.ts diff --git a/software/lib/PeaPodSimulator.ts b/software/src/PeaPodSimulator.ts similarity index 100% rename from software/lib/PeaPodSimulator.ts rename to software/src/PeaPodSimulator.ts diff --git a/software/lib/errors.ts b/software/src/errors.ts similarity index 100% rename from software/lib/errors.ts rename to software/src/errors.ts diff --git a/software/lib/ui.ts b/software/src/ui.ts similarity index 100% rename from software/lib/ui.ts rename to software/src/ui.ts diff --git a/software/lib/utils.ts b/software/src/utils.ts similarity index 87% rename from software/lib/utils.ts rename to software/src/utils.ts index 6f35d2c3..8187243d 100644 --- a/software/lib/utils.ts +++ b/software/src/utils.ts @@ -87,16 +87,19 @@ export const fetchServerCert = async (): Promise => { return (await axios.get('https://pki.goog/roots.pem')).data as string; } -export const checkArduino = async () => { - // Create log folder - if (!existsSync('logs/')) { - mkdirSync('logs/', { recursive: true }); - } - execute(`avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`, [1]).catch(err => { - writeFileSync('logs/checkArduino.log', err); - throw new Error(`Failed to communicate with the Arduino. See logs/checkArduino.log`); - }).then(log1 => { - writeFileSync('logs/checkArduino.log', log1); +export const checkArduino = (): Promise => { + return new Promise((res, rej) => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute(`avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`, [1]).catch(err => { + writeFileSync('logs/checkArduino.log', err); + rej(new Error(`Failed to communicate with the Arduino. See logs/checkArduino.log`)); + }).then(log1 => { + log1 ? writeFileSync('logs/checkArduino.log', log1) : null; + res(); + }); }); } @@ -132,9 +135,9 @@ export const execute = (command: string, failureCodes: number[] = []): Promise { - eprocess.kill(); log += error.message; rej(log); + eprocess.kill(); }); eprocess.on('close', code => { if (code) { diff --git a/software/tests/README.md b/software/tests/README.md deleted file mode 100644 index d9e600a0..00000000 --- a/software/tests/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Raspberry Pi Software Testing Suite - -### Serial Port Test - `serialTest.py` - -Test the funcionality of the Raspberry Pi Zero 2 W GPIO mini-UART serial port by tying the RX and TX pins together. \ No newline at end of file diff --git a/software/tests/serialMonitor.py b/software/tests/serialMonitor.py deleted file mode 100644 index 9ba45f1a..00000000 --- a/software/tests/serialMonitor.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import serial -port = "/dev/ttyS0" -try: - serialPort = serial.Serial(port, 115200) - print ("Serial port '", port, "' open:", sep='') - while True: - if serialPort.in_waiting > 0: - print(serialPort.read(serialPort.in_waiting).decode('unicode_escape'), end='') -except IOError: - print ("Error on ", port,"\n") - serialPort.close() \ No newline at end of file diff --git a/software/tests/serialTest.py b/software/tests/serialTest.py deleted file mode 100644 index fff3e217..00000000 --- a/software/tests/serialTest.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import serial -test_string = "Testing Raspberry Pi GPIO serial port ...".encode('utf-8') -port = "/dev/ttyS0" -try: - serialPort = serial.Serial(port, 9600, timeout = 2) - print ("Serial port '", port, "' ready for test:", sep='') - bytes_sent = serialPort.write(test_string) - print ("Sent ", bytes_sent, " bytes: '", test_string, "'", sep='') - loopback = serialPort.read(bytes_sent) - if loopback == test_string: - print ("Received ",len(loopback), " bytes: '", test_string, "'\nPort ", port," is setup correctly!\n", sep='') - else: - print ("Received incorrect data: '", loopback, "' on serial port '", port, "' loopback\n", sep='') - serialPort.close() -except IOError: - print ("Error on ", port,"\n") diff --git a/software/tests/testArduino.test.ts b/software/tests/testArduino.test.ts new file mode 100644 index 00000000..7f044eab --- /dev/null +++ b/software/tests/testArduino.test.ts @@ -0,0 +1,35 @@ +import { promiseToReject } from './utils'; +import { execute, checkArduino } from '../src/utils'; +import { mkdirSync, writeFileSync, existsSync } from 'fs'; + +/** + * Run Arduino unit tests, powered by PlatformIO. Log results to file. + * @returns Resolves with void, rejects with error. + */ +const arduinoUnitTests = () => { + return new Promise((res, rej) => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute(`pio test -d PeaPodOS-Arduino`, [1]).catch(err => { + writeFileSync('logs/arduinoUnitTests.log', err); + rej(new Error(`One or more unit tests failed. See logs/arduinoUnitTests.log`)); + }).then(log1 => { + log1 ? writeFileSync('logs/arduinoUnitTests.log', log1) : null; + res(); + }); + }); +} + +jest.setTimeout(60000); + +test('avrdude can talk to the Arduino', () => { + expect.assertions(1); + return expect(promiseToReject(checkArduino())).resolves.toBe(false); +}); + +test('Arduino unit tests', () => { + expect.assertions(1); + return expect(promiseToReject(arduinoUnitTests())).resolves.toBe(false); +}); \ No newline at end of file diff --git a/software/tests/testUtils.test.ts b/software/tests/testUtils.test.ts new file mode 100644 index 00000000..c4a9556e --- /dev/null +++ b/software/tests/testUtils.test.ts @@ -0,0 +1,6 @@ +import { execute } from '../src/utils' + +test('code execution works', () => { + expect.assertions(1); + return expect(execute('echo hello world!')).resolves.toEqual('> echo hello world!\nhello world!\n'); +}); \ No newline at end of file diff --git a/software/tests/utils.ts b/software/tests/utils.ts new file mode 100644 index 00000000..27470e37 --- /dev/null +++ b/software/tests/utils.ts @@ -0,0 +1,11 @@ +/** + * Checks if a Promise rejects. If so, RESOLVES with the error (or, if falsy or empty `{}`, with `true`), and otherwise resolves with false (no error thrown). + * Never rejects. + * @param f Promise to check. + * @returns False if resolved, error (or true) if rejected. + */ +export const promiseToReject = (f: Promise): Promise => { + return new Promise(res => { + f.catch((err)=>{ res((err === {} || !err) ? true : err) }).then(()=>{ res(false) }); + }); +}; \ No newline at end of file diff --git a/software/tsconfig.json b/software/tsconfig.json index 39be999a..f29caf80 100644 --- a/software/tsconfig.json +++ b/software/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "lib": ["es2021"], + "target": "es2021", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ "removeComments": true, /* Do not emit comments to output. */ "strict": true, /* Enable all strict type-checking options. */ @@ -8,7 +9,8 @@ "skipLibCheck": true, /* Skip type checking of declaration files. */ "declaration": true, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + // No outDir: keep index.js at package root, only the binary gets published }, - "include": ["index.ts", "./lib/"], - "exclude": ["node_modules"] + "include": ["index.ts", "./src/"], + "exclude": ["node_modules", "PeaPodOS-Arduino", "**/*.spec.ts"], } \ No newline at end of file From 202846b18a4008c65d63a4a8e75813f5970fc7e1 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 18 Mar 2022 21:21:14 -0400 Subject: [PATCH 13/85] Fixed Unit Tests * Fixed TS compiling for RPi Node version (12.22.x) * No longer running test_instructions or test_messenger (awaiting fix) --- software/PeaPodOS-Arduino/.gitignore | 3 +- software/PeaPodOS-Arduino/platformio.ini | 5 +- .../src/utils/instructions.cc | 10 +- .../src/utils/jsonmessenger.cc | 18 +-- .../test/test_blink/test_blink.cc | 5 +- .../test_instructions/test_instructions.cc | 31 ++-- .../test/test_k30/test_k30.cc | 22 +-- .../test/test_messenger/test_messenger.cc | 12 +- .../test/test_sht31/test_sht31.cc | 22 +-- software/README.md | 151 ++---------------- software/tsconfig.json | 4 +- 11 files changed, 86 insertions(+), 197 deletions(-) diff --git a/software/PeaPodOS-Arduino/.gitignore b/software/PeaPodOS-Arduino/.gitignore index 3b8da3a4..ecb27f0a 100644 --- a/software/PeaPodOS-Arduino/.gitignore +++ b/software/PeaPodOS-Arduino/.gitignore @@ -1,2 +1,3 @@ .pio -.vscode \ No newline at end of file +.vscode +test/tmp_pio_test_transport.cpp \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/platformio.ini b/software/PeaPodOS-Arduino/platformio.ini index c8148046..b092c82e 100644 --- a/software/PeaPodOS-Arduino/platformio.ini +++ b/software/PeaPodOS-Arduino/platformio.ini @@ -9,4 +9,7 @@ upload_protocol = custom upload_command = sudo avrdude -p $BOARD_MCU -C avrdude_gpio.conf -c peapod -v -U flash:w:$SOURCE:i test_port = /dev/ttyS0 test_speed = 115200 -test_build_project_src = true \ No newline at end of file +test_build_project_src = true +monitor_port = /dev/ttyS0 +monitor_speed = 115200 +test_ignore = test_instructions, test_messenger \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/utils/instructions.cc b/software/PeaPodOS-Arduino/src/utils/instructions.cc index 802e2aa0..0706ef84 100644 --- a/software/PeaPodOS-Arduino/src/utils/instructions.cc +++ b/software/PeaPodOS-Arduino/src/utils/instructions.cc @@ -12,7 +12,7 @@ * @return Validity */ bool isValidSet(String instructionSet) { - return (instructionSet.charAt(0) != '{' || instructionSet.charAt(instructionSet.length()-1) != '}' || instructionSet.indexOf(':') == -1); + return (instructionSet.charAt(0) == '{' && instructionSet.charAt(instructionSet.length()-1) == '}' && instructionSet.indexOf(':') != -1); } /** @@ -29,7 +29,7 @@ errorlevel_t handleInstructions(String instructionSet, const InstructionActuator // If invalid, throw error if (!isValidSet(instructionSet)) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction set '" + instructionSet + "', bad formatting.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Invalid instruction set '")) + instructionSet + String(F("', bad formatting."))); return ERR_FATAL; } @@ -68,7 +68,7 @@ errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatr float value = instruction.substring(split+1).toFloat(); if(value == 0 && !(instruction.charAt(split+1) == '0' || (instruction.charAt(split+1) == '.' && instruction.charAt(split+2) == '0'))) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid target value '" + instruction.substring(split+1) + "'.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Invalid target value '")) + instruction.substring(split+1) + "'."); return ERR_FATAL; } @@ -78,9 +78,9 @@ errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatr return ERR_NONE; } } - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Unknown instruction label '" + label + "'.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Unknown instruction label '")) + label + "'."); return ERR_WARNING; } - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction '" + instruction + "', bad formatting.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Invalid instruction '")) + instruction + String(F("', bad formatting."))); return ERR_FATAL; } \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc index 309b8094..bc9a13d2 100644 --- a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc +++ b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc @@ -5,11 +5,11 @@ String parseMessageLevel(JSONMessenger::jsonmessagetype_t type) { switch (type) { case JSONMessenger::MESSAGE_DEBUG: - return String("debug"); + return String(F("debug")); case JSONMessenger::MESSAGE_ERROR: - return String("error"); + return String(F("error")); } - return String("msg"); + return String(F("msg")); } namespace JSONMessenger { @@ -19,17 +19,17 @@ namespace JSONMessenger { String sendMessage(jsonmessagetype_t type, String message) { // Replace quotes with escaped quotes message.replace("\"", "\\\""); - String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); + String out = String(F("{\"type\":\"")) + parseMessageLevel(type) + String(F("\",\"data\":\"")) + message + "\"}\n"; return out; } String sendRevision(uint8_t revision) { - String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); + String out = String(F("{\"type\":\"revision\",\"data\":")) + String(revision) + "}\n"; return out; } String sendData(const char* label, float value) { - String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); + String out = String(F("{\"type\":\"data\",\"data\":{\"label\":\"")) + String(label) + String(F("\",\"value\":")) + String(value, DATADECIMALS) + "}}\n"; return out; } @@ -38,17 +38,17 @@ namespace JSONMessenger { void sendMessage(jsonmessagetype_t type, String message) { // Replace quotes with escaped quotes message.replace("\"", "\\\""); - String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); + String out = String(F("{\"type\":\"")) + parseMessageLevel(type) + String(F("\",\"data\":\"")) + message + "\"}\n"; Serial.print(out); } void sendRevision(uint8_t revision) { - String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); + String out = String(F("{\"type\":\"revision\",\"data\":")) + String(revision) + "}\n"; Serial.print(out); } void sendData(const char* label, float value) { - String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); + String out = String(F("{\"type\":\"data\",\"data\":{\"label\":\"")) + String(label) + String(F("\",\"value\":")) + String(value, DATADECIMALS) + "}}\n"; Serial.print(out); } diff --git a/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc b/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc index 18ac7ce3..e22285cc 100644 --- a/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc +++ b/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc @@ -25,16 +25,15 @@ void setup() { } uint8_t i = 0; -uint8_t max_blinks = 5; void loop() { - if (i < max_blinks) { + if (i < 3) { RUN_TEST(test_led_state_high); delay(500); RUN_TEST(test_led_state_low); delay(500); i++; - } else if (i == max_blinks) { + } else { UNITY_END(); } } \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc index 972a4e0b..7c0401ad 100644 --- a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc +++ b/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc @@ -36,35 +36,44 @@ static const InstructionActuatorMatrix matrix = { }; void test_non_json(void) { - TEST_ASSERT_EQUAL(handleInstructions("what", &matrix), ERR_FATAL); + uint8_t result = handleInstructions("hello world", &matrix); + TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); } void test_incomplete_json(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete", &matrix), ERR_FATAL); + uint8_t result = handleInstructions("{\"incomplete", &matrix); + TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); } void test_invalid_target(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"incomplete\":abc", &matrix), ERR_FATAL); + uint8_t result = handleInstructions("{\"incomplete\":abc", &matrix); + TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); } void test_bad_instruction(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"C\":0}", &matrix), ERR_WARNING); + uint8_t result = handleInstructions("{\"C\":0}", &matrix); + TEST_ASSERT_EQUAL_UINT8(ERR_WARNING, result); } void test_empty_instruction_set(void) { - TEST_ASSERT_EQUAL(handleInstructions("{}", &matrix), ERR_NONE); - TEST_ASSERT_EQUAL(handleInstructions("{ }", &matrix), ERR_NONE); + uint8_t result = handleInstructions("{}"), &matrix); + TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); } void test_one_instruction(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"A\":1.3}", &matrix), ERR_NONE); - TEST_ASSERT_EQUAL(actuatorA.update()->lasttarget, 1.3); + uint8_t result = handleInstructions("{\"A\":1.3}", &matrix); + TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); + float target = actuatorA.update()->lasttarget; + TEST_ASSERT_EQUAL_FLOAT(1.3, target); } void test_multiple_instructions(void) { - TEST_ASSERT_EQUAL(handleInstructions("{\"A\":2.4,\"B\":4.3}", &matrix), ERR_NONE); - TEST_ASSERT_EQUAL(actuatorA.update()->lasttarget, 2.4); - TEST_ASSERT_EQUAL(actuatorB.update()->lasttarget, 4.3); + uint8_t result = handleInstructions("{\"A\":2.4,\"B\":4.3}", &matrix); + TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); + float target = actuatorA.update()->lasttarget; + TEST_ASSERT_EQUAL_FLOAT(2.4, target); + target = actuatorB.update()->lasttarget; + TEST_ASSERT_EQUAL_FLOAT(4.3, target); } void setup(void) { diff --git a/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc b/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc index 24245725..1c56a3c7 100644 --- a/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc +++ b/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc @@ -9,26 +9,26 @@ K30 sensor = K30(); void test_begin(void) { SensorState* state = sensor.begin(); - TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_INITIALIZED, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); } void test_read(void) { SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(state->debug, DS_SUCCESS); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); } void test_delta(void) { SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); // No delay state = sensor.update(); - TEST_ASSERT_EQUAL(state->debug, DS_WAITING); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_WAITING, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); } void setup(void) { @@ -42,12 +42,12 @@ void setup(void) { int i = 0; void loop(void) { - if (i < 5) { - delay(2000); + if (i < 3) { + delay(3000); RUN_TEST(test_read); - delay(2000); + delay(3000); RUN_TEST(test_delta); i++; diff --git a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc index 0a6f58b3..06a16c53 100644 --- a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc +++ b/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc @@ -5,19 +5,23 @@ #include void test_revision(void) { - TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendRevision(63).c_str(), "{\"type\":\"revision\",\"data\":63}\n"); + const char* result = JSONMessenger::sendRevision(63).c_str(); + TEST_ASSERT_EQUAL_STRING("{\"type\":\"revision\",\"data\":63}\n", result); } void test_debug(void) { - TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, "hello world").c_str(), "{\"type\":\"debug\",\"data\":\"hello world\"}\n"); + const char* result = JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, "hello world").c_str(); + TEST_ASSERT_EQUAL_STRING("{\"type\":\"debug\",\"data\":\"hello world\"}\n", result); } void test_error(void) { - TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, "oh no").c_str(), "{\"type\":\"error\",\"data\":\"oh no\"}\n"); + const char* result = JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, "oh no").c_str(); + TEST_ASSERT_EQUAL_STRING("{\"type\":\"error\",\"data\":\"oh no\"}\n", result); } void test_data(void) { - TEST_ASSERT_EQUAL_STRING(JSONMessenger::sendData("testlabel", 32.4).c_str(), "{\"type\":\"data\",\"data\":{\"label\":\"testlabel\",\"value\":32.40}}\n"); + const char* result = JSONMessenger::sendData("testlabel", 32.4).c_str(); + TEST_ASSERT_EQUAL_STRING("{\"type\":\"data\",\"data\":{\"label\":\"testlabel\",\"value\":32.40}}\n", result); } void setup(void) { diff --git a/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc b/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc index a47657be..db77acec 100644 --- a/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc +++ b/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc @@ -9,26 +9,26 @@ SHT31 sensor = SHT31(); void test_begin(void) { SensorState* state = sensor.begin(); - TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_INITIALIZED, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); } void test_read(void) { SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(state->debug, DS_SUCCESS); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); } void test_delta(void) { SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(state->debug, DS_INITIALIZED); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); // No delay state = sensor.update(); - TEST_ASSERT_EQUAL(state->debug, DS_WAITING); - TEST_ASSERT_EQUAL(state->error, ERR_NONE); + TEST_ASSERT_EQUAL(DS_WAITING, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); } void setup(void) { @@ -42,12 +42,12 @@ void setup(void) { int i = 0; void loop(void) { - if (i < 5) { - delay(2000); + if (i < 3) { + delay(1500); RUN_TEST(test_read); - delay(2000); + delay(1500); RUN_TEST(test_delta); i++; diff --git a/software/README.md b/software/README.md index 9d9ab5a8..aeba96fd 100644 --- a/software/README.md +++ b/software/README.md @@ -60,9 +60,9 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: > You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@peapod.local`, or with VS Code) > Note: In Future, steps 7-11 will be performed at runtime. -1. Update Packages: +7. Update Packages: 1. Update package listings, upgrade existing packages: `sudo apt-get update && sudo apt-get upgrade -y` - 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude` (could take a while) + 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude python3-venv` (could take a while) 3. Install main software package: `sudo npm i -g @peapodtech/peapodos --save` # Development ### Arduino Test Suite -To manually compile and upload the test sketch `./tests/blink.ino` to an Arduino Nano (ATMega328P aka `m328p`): - - -1. `~/.platformio/penv/bin/platformio run -d tests/` - - Successful output should look like this: - - ``` - Processing peapod (platform: atmelavr; board: nanoatmega328; framework: arduino) - ---------------------------------------------------------------------------------- - Verbose mode can be enabled via `-v, --verbose` option - CONFIGURATION: https://docs.platformio.org/page/boards/atmelavr/nanoatmega328.html - PLATFORM: Atmel AVR (3.4.0) > Arduino Nano ATmega328 - HARDWARE: ATMEGA328P 16MHz, 2KB RAM, 30KB Flash - DEBUG: Current (avr-stub) On-board (avr-stub, simavr) - PACKAGES: - - framework-arduino-avr 5.1.0 - - toolchain-atmelavr 1.70300.191015 (7.3.0) - LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf - LDF Modes: Finder ~ chain, Compatibility ~ soft - Found 5 compatible libraries - Scanning dependencies... - Dependency Graph - |-- 1.0 - Building in release mode - Compiling .pio/build/peapod/src/actuators/actuator.cc.o - Compiling .pio/build/peapod/src/actuators/led.cc.o - Compiling .pio/build/peapod/src/main.cpp.o - ... - Indexing .pio/build/peapod/libFrameworkArduino.a - Linking .pio/build/peapod/firmware.elf - Checking size .pio/build/peapod/firmware.elf - Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" - RAM: [==== ] 41.3% (used 846 bytes from 2048 bytes) - Flash: [===== ] 45.0% (used 13832 bytes from 30720 bytes) - Building .pio/build/peapod/firmware.hex - ========================= [SUCCESS] Took 1.49 seconds ========================= - ``` - -2. `sudo avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v -U flash:w:tests/blink/build/arduino.avr.nano/blink.ino.hex:i` - - (`-p`: microprocessor ID, `-C`: custom configuration file path, `-c`: programmer ID, `-v`: verify sketch after upload, `-U`: upload options and path to binary) - - Successful output should look like this: - - ``` - avrdude: Version 6.3-20171130 - Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ - Copyright (c) 2007-2014 Joerg Wunsch - - System wide configuration file is "/home/pi/avrdude_gpio.conf" - User configuration file is "/root/.avrduderc" - User configuration file does not exist or is not a regular file, skipping - - Using Port : unknown - Using Programmer : peapod - AVR Part : ATmega328P - Chip Erase delay : 9000 us - PAGEL : PD7 - BS2 : PC2 - RESET disposition : dedicated - RETRY pulse : SCK - serial program mode : yes - parallel program mode : yes - Timeout : 200 - StabDelay : 100 - CmdexeDelay : 25 - SyncLoops : 32 - ByteDelay : 0 - PollIndex : 3 - PollValue : 0x53 - Memory Detail : - - Block Poll Page Polled - Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack - ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- - eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff - flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff - lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 - hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 - efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 - lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 - calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 - signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 - - Programmer Type : linuxgpio - Description : Use the Linux sysfs interface to bitbang GPIO lines - Pin assignment : /sys/class/gpio/gpio{n} - RESET = 8 - SCK = 11 - MOSI = 10 - MISO = 9 - - avrdude: AVR device initialized and ready to accept instructions - - Reading | ################################################## | 100% 0.00s - - avrdude: Device signature = 0x1e950f (probably m328p) - avrdude: safemode: lfuse reads as FF - avrdude: safemode: hfuse reads as DA - avrdude: safemode: efuse reads as FD - avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed - To disable this feature, specify the -D option. - avrdude: erasing chip - avrdude: reading input file "blink/build/arduino.avr.nano/blink.ino.hex" - avrdude: writing flash (922 bytes): - - Writing | ################################################## | 100% 0.31s - - avrdude: 922 bytes of flash written - avrdude: verifying flash memory against blink/build/arduino.avr.nano/blink.ino.hex: - avrdude: load data flash data from input file blink/build/arduino.avr.nano/blink.ino.hex: - avrdude: input file blink/build/arduino.avr.nano/blink.ino.hex contains 922 bytes - avrdude: reading on-chip flash data: - - Reading | ################################################## | 100% 0.28s - - avrdude: verifying ... - avrdude: 922 bytes of flash verified - - avrdude: safemode: lfuse reads as FF - avrdude: safemode: hfuse reads as DA - avrdude: safemode: efuse reads as FD - avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF) - - avrdude done. Thank you. - ``` - +To run the PlatformIO Arduino test suite: `~/.platformio/penv/bin/platformio test -d ~/software/PeaPodOS-Arduino/` ### Raspberry Pi Serial Testing diff --git a/software/tsconfig.json b/software/tsconfig.json index f29caf80..9be506e2 100644 --- a/software/tsconfig.json +++ b/software/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "lib": ["es2021"], - "target": "es2021", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "lib": ["es2020"], + "target": "es2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ "removeComments": true, /* Do not emit comments to output. */ "strict": true, /* Enable all strict type-checking options. */ From 17cf61126e2daa9cfe0811ca52485847a41340eb Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 25 Mar 2022 01:18:06 -0400 Subject: [PATCH 14/85] Actuator Control + Direct Arduino instruction set command via GCP * Fixed actuator target statefulness - no more ignored tests - no more "F" PSTR * Fixed TS serial write error handling --- software/PeaPodOS-Arduino/platformio.ini | 3 +-- .../PeaPodOS-Arduino/src/actuators/actuator.cc | 4 ++-- software/PeaPodOS-Arduino/src/main.cc | 17 ++++++++++++----- .../PeaPodOS-Arduino/src/utils/instructions.cc | 12 +++++++----- .../src/utils/jsonmessenger.cc | 18 +++++++++--------- software/index.ts | 6 ++++-- software/src/PeaPodArduino.ts | 8 ++++---- software/src/PeaPodPublisher.ts | 4 ++++ 8 files changed, 43 insertions(+), 29 deletions(-) diff --git a/software/PeaPodOS-Arduino/platformio.ini b/software/PeaPodOS-Arduino/platformio.ini index b092c82e..6828f650 100644 --- a/software/PeaPodOS-Arduino/platformio.ini +++ b/software/PeaPodOS-Arduino/platformio.ini @@ -11,5 +11,4 @@ test_port = /dev/ttyS0 test_speed = 115200 test_build_project_src = true monitor_port = /dev/ttyS0 -monitor_speed = 115200 -test_ignore = test_instructions, test_messenger \ No newline at end of file +monitor_speed = 115200 \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/actuators/actuator.cc b/software/PeaPodOS-Arduino/src/actuators/actuator.cc index cd906f73..b3d43007 100644 --- a/software/PeaPodOS-Arduino/src/actuators/actuator.cc +++ b/software/PeaPodOS-Arduino/src/actuators/actuator.cc @@ -30,9 +30,9 @@ ActuatorState* Actuator::begin(void) { ActuatorState* Actuator::update(void) { // Check state preconditions, and do not attempt set if the target hasn't changed if (state.error < ERR_FATAL && state.debug >= DS_INITIALIZED) { - if (state.lasttarget != target) { + if (target != state.lasttarget) { // Attempt to set actuator state from target - state.error = set(state.lasttarget); + state.error = set(target); switch (state.error) { case ERR_NONE: diff --git a/software/PeaPodOS-Arduino/src/main.cc b/software/PeaPodOS-Arduino/src/main.cc index 5788a957..7df9d126 100644 --- a/software/PeaPodOS-Arduino/src/main.cc +++ b/software/PeaPodOS-Arduino/src/main.cc @@ -62,7 +62,6 @@ void loop(void) { // Check for instructions if (Serial.available()) { String in = Serial.readStringUntil('\n'); - in.trim(); handleInstructions(in, &matrix); } @@ -81,6 +80,14 @@ void loop(void) { JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[i]->getID()) + " (FATAL, SENSOR DISABLED!).")); } } + for (int i = 0; i < NUM_ACTUATORS; ++i) { + ActuatorState* state = actuators[i]->update(); + if (state->error == ERR_WARNING) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[i]->getID()) + " (non-fatal).")); + } else if (state->error == ERR_FATAL) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[i]->getID()) + " (FATAL, ACTUATOR DISABLED!).")); + } + } #if ENABLE_WATCHDOG wdt_enable(WDTO_1S); @@ -91,7 +98,7 @@ bool post(void) { bool success = true; for (int i = 0; i < NUM_SENSORS; ++i) { SensorState* state = sensors[i]->begin(); - bool latest = (state->debug == DS_INITIALIZED && state->error == ERR_NONE); + bool latest = (state->debug >= DS_INITIALIZED && state->error == ERR_NONE); if (latest) { JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Sensor " + String(sensors[i]->getID()) + " initialized successfully.")); } else { @@ -101,11 +108,11 @@ bool post(void) { } for (int i = 0; i < NUM_ACTUATORS; ++i) { ActuatorState* state = actuators[i]->begin(); - bool latest = (state->debug == DS_INITIALIZED && state->error == ERR_NONE); + bool latest = (state->debug >= DS_INITIALIZED && state->error == ERR_NONE); if (latest) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Actuator " + String(actuators[i]->getID()) + " initialized successfully.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Actuator " + String(actuators[i]->getID()) + " initialized successfully.")); } else { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Failed to initialize actuator " + String(actuators[i]->getID()) + ". Check wiring.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to initialize actuator " + String(actuators[i]->getID()) + ". Check wiring.")); } success &= latest; } diff --git a/software/PeaPodOS-Arduino/src/utils/instructions.cc b/software/PeaPodOS-Arduino/src/utils/instructions.cc index 0706ef84..e275a505 100644 --- a/software/PeaPodOS-Arduino/src/utils/instructions.cc +++ b/software/PeaPodOS-Arduino/src/utils/instructions.cc @@ -29,7 +29,7 @@ errorlevel_t handleInstructions(String instructionSet, const InstructionActuator // If invalid, throw error if (!isValidSet(instructionSet)) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Invalid instruction set '")) + instructionSet + String(F("', bad formatting."))); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction set '" + instructionSet + "', bad formatting.")); return ERR_FATAL; } @@ -41,7 +41,9 @@ errorlevel_t handleInstructions(String instructionSet, const InstructionActuator while (instructionSet.indexOf(':') != -1) { // Find end of leftmost instruction int stop = instructionSet.indexOf(','); - stop = (stop == -1) ? instructionSet.indexOf('}') : stop; + if (stop == -1) { + stop = instructionSet.length(); + } // Handle leftmost instruction String instruction = instructionSet.substring(0, stop); @@ -68,7 +70,7 @@ errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatr float value = instruction.substring(split+1).toFloat(); if(value == 0 && !(instruction.charAt(split+1) == '0' || (instruction.charAt(split+1) == '.' && instruction.charAt(split+2) == '0'))) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Invalid target value '")) + instruction.substring(split+1) + "'."); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid target value '" + instruction.substring(split+1) + "'.")); return ERR_FATAL; } @@ -78,9 +80,9 @@ errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatr return ERR_NONE; } } - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Unknown instruction label '")) + label + "'."); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Unknown instruction label '" + label + "'.")); return ERR_WARNING; } - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String(F("Invalid instruction '")) + instruction + String(F("', bad formatting."))); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction '" + instruction + String("', bad formatting."))); return ERR_FATAL; } \ No newline at end of file diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc index bc9a13d2..309b8094 100644 --- a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc +++ b/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc @@ -5,11 +5,11 @@ String parseMessageLevel(JSONMessenger::jsonmessagetype_t type) { switch (type) { case JSONMessenger::MESSAGE_DEBUG: - return String(F("debug")); + return String("debug"); case JSONMessenger::MESSAGE_ERROR: - return String(F("error")); + return String("error"); } - return String(F("msg")); + return String("msg"); } namespace JSONMessenger { @@ -19,17 +19,17 @@ namespace JSONMessenger { String sendMessage(jsonmessagetype_t type, String message) { // Replace quotes with escaped quotes message.replace("\"", "\\\""); - String out = String(F("{\"type\":\"")) + parseMessageLevel(type) + String(F("\",\"data\":\"")) + message + "\"}\n"; + String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); return out; } String sendRevision(uint8_t revision) { - String out = String(F("{\"type\":\"revision\",\"data\":")) + String(revision) + "}\n"; + String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); return out; } String sendData(const char* label, float value) { - String out = String(F("{\"type\":\"data\",\"data\":{\"label\":\"")) + String(label) + String(F("\",\"value\":")) + String(value, DATADECIMALS) + "}}\n"; + String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); return out; } @@ -38,17 +38,17 @@ namespace JSONMessenger { void sendMessage(jsonmessagetype_t type, String message) { // Replace quotes with escaped quotes message.replace("\"", "\\\""); - String out = String(F("{\"type\":\"")) + parseMessageLevel(type) + String(F("\",\"data\":\"")) + message + "\"}\n"; + String out = String("{\"type\":\"" + parseMessageLevel(type) + "\",\"data\":\"" + message + "\"}\n"); Serial.print(out); } void sendRevision(uint8_t revision) { - String out = String(F("{\"type\":\"revision\",\"data\":")) + String(revision) + "}\n"; + String out = String("{\"type\":\"revision\",\"data\":" + String(revision) + "}\n"); Serial.print(out); } void sendData(const char* label, float value) { - String out = String(F("{\"type\":\"data\",\"data\":{\"label\":\"")) + String(label) + String(F("\",\"value\":")) + String(value, DATADECIMALS) + "}}\n"; + String out = String("{\"type\":\"data\",\"data\":{\"label\":\"" + String(label) + "\",\"value\":" + String(value, DATADECIMALS) + "}}\n"); Serial.print(out); } diff --git a/software/index.ts b/software/index.ts index e2f954b9..8e35b2a8 100644 --- a/software/index.ts +++ b/software/index.ts @@ -154,7 +154,9 @@ function main(): Promise { }, command => { Spinner.log("[COMMAND] - "+command); // TODO: Respond to commands (immediate actions) - if (command.type == 'livestreamoffer') { + if (command.type == 'instructions') { + arduino.write(command.data); + } else if (command.type == 'livestreamoffer') { // Assumes webcam hardware has been initialized @@ -174,7 +176,7 @@ function main(): Promise { } }).then(({projectid, projectname, run}) => { // TODO: Get program - let initialinstructions = {} + let initialinstructions = {}; // START Spinner.log(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); diff --git a/software/src/PeaPodArduino.ts b/software/src/PeaPodArduino.ts index 5bac17ff..c9f4ee5d 100644 --- a/software/src/PeaPodArduino.ts +++ b/software/src/PeaPodArduino.ts @@ -147,11 +147,11 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { }) await sleep(3000); } - write(msg : any) { + write(msg : ArduinoInstructions) { Spinner.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); - if(!this.serial?.write(JSON.stringify(msg)+'\n')){ - throw new ArduinoInstructionsError(JSON.stringify(msg)); - } + this.serial.write(JSON.stringify(msg)+'\n', undefined, (err)=>{ + if(err) throw new ArduinoInstructionsError(JSON.stringify(msg)); + }); } stop() { this.serial.close(); diff --git a/software/src/PeaPodPublisher.ts b/software/src/PeaPodPublisher.ts index 8a41cdce..066413fb 100644 --- a/software/src/PeaPodPublisher.ts +++ b/software/src/PeaPodPublisher.ts @@ -10,6 +10,7 @@ import { fetchServerCert } from './utils'; import * as inquirer from 'inquirer'; import {v4 as uuid} from 'uuid'; import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; //Firebase Auth via OAuth2 'Device Flow' +import { ArduinoInstructions } from './PeaPodArduino'; import chalk from 'chalk'; export type PeaPodDataBatch = { @@ -36,6 +37,9 @@ export type PeaPodMessage = { } export type PeaPodCommand = { + type: 'instructions', + data: ArduinoInstructions +} | { type: 'livestreamoffer', data: any // TODO: import types from WebRTC From 715a369ccbab065048ebdae21726dd16d5b359dc Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Tue, 3 May 2022 16:08:30 -0400 Subject: [PATCH 15/85] GPIO Utils, Arduino Reset on Start + Utils for exporting, unexporting, writing to, and reading from GPIO pins via sysfs + Testing suite for new utils * Arduino reset pin pulled high on start --- software/index.ts | 36 +- software/package-lock.json | 905 ++++++++++++++++--------------- software/package.json | 5 +- software/src/PeaPodArduino.ts | 14 +- software/src/PeaPodPublisher.ts | 9 +- software/src/utils.ts | 51 +- software/tests/testUtils.test.ts | 24 +- 7 files changed, 574 insertions(+), 470 deletions(-) diff --git a/software/index.ts b/software/index.ts index 8e35b2a8..3c3528b1 100644 --- a/software/index.ts +++ b/software/index.ts @@ -5,7 +5,7 @@ import { initializeApp, getApps } from 'firebase/app'; import { getAuth } from 'firebase/auth'; // import PiCamera from 'pi-camera'; -import PeaPodArduinoInterface, { IPeaPodArduino } from './src/PeaPodArduino'; +import PeaPodArduinoInterface, { ArduinoInstructions, IPeaPodArduino } from './src/PeaPodArduino'; import PeaPodPubSub, { IoTConfig, IPeaPodPublisher, PeaPodDataBatch } from './src/PeaPodPublisher'; import { ArduinoSimulator, PeaPodLogger } from './src/PeaPodSimulator'; // import PeaPodCamera, { IPeaPodCamera } from './src/PeaPodCamera'; @@ -125,23 +125,25 @@ function main(): Promise { await sleep(1500); } - let batch: PeaPodDataBatch = { }; - let batchInterval: NodeJS.Timer; + let batch: PeaPodDataBatch = { }, batchInterval: NodeJS.Timer, instructions: ArduinoInstructions = {}; // Initialize Arduino communications interface first arduino.start((msg) => { - if(msg.type == 'data') { - // Initialize batch array - if (batch[msg.data.label] === undefined) batch[msg.data.label] = {batch: []}; - // Accumulate data into batches - batch[msg.data.label].batch.push({ - timestamp: Date.now(), - value: msg.data.value - }); - - // TODO: Plan, act - } else { - Spinner.log(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) + switch (msg.type) { + case "data": + // Initialize batch array + if (batch[msg.data.label] === undefined) batch[msg.data.label] = {batch: []}; + // Accumulate data into batches + batch[msg.data.label].batch.push({ + timestamp: Date.now(), + value: msg.data.value + }); + break; + case "revision": + arduino.write(instructions); + break; + default: + Spinner.log(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) } // TODO: publish other message types }).then(() => { @@ -175,13 +177,11 @@ function main(): Promise { } }).then(({projectid, projectname, run}) => { - // TODO: Get program - let initialinstructions = {}; + // Todo: get instructions // START Spinner.log(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); - arduino.write(initialinstructions); // Reset batch = { }; diff --git a/software/package-lock.json b/software/package-lock.json index 7f9f51a3..c15b67dc 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -1,12 +1,12 @@ { "name": "@peapodtech/peapodos", - "version": "0.0.9", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@peapodtech/peapodos", - "version": "0.0.9", + "version": "0.1.0", "license": "MIT", "dependencies": { "@mapbox/node-pre-gyp": "^1.0.8", @@ -30,8 +30,9 @@ }, "devDependencies": { "@serialport/parser-readline": "^9.0.1", - "@types/jest": "^27.4.1", + "@types/jest": "^27.5.0", "@types/jsonwebtoken": "^8.5.5", + "@types/node": "^17.0.31", "@types/pi-camera": "^1.5.3", "@types/project-name-generator": "^2.1.0", "@types/serialport": "^8.0.2", @@ -613,14 +614,14 @@ "dev": true }, "node_modules/@firebase/analytics": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.4.tgz", - "integrity": "sha512-AU3XMwHW7SFGCNeUKKNW2wXGTdmS164ackt/Epu2bDXCT1OcauPE1AVd+ofULSIDCaDUAQVmvw3JrobgogEU7Q==", + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.8.tgz", + "integrity": "sha512-W38Zy/jf64LKpPi+mGNNETIjz4eq/YXBE0Uu2bzstqUwlhvFn1WlRBK4vzgtZMRaGW04CQp9FXYv6ZTRo/Xbyw==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -628,14 +629,14 @@ } }, "node_modules/@firebase/analytics-compat": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.5.tgz", - "integrity": "sha512-5cfr0uWwlhoHQYAr6UtQCHwnGjs/3J/bWrfA3INNtzaN4/tTTLTD02iobbccRcM7dM5TR0sZFWS5orfAU3OBFg==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.9.tgz", + "integrity": "sha512-HYKMAZvfU589WVvK5XKY9Pl+axXFISabouAFw2VHpJm/TO1mAXAy0+eIjqQ3j8z3L1OEfCeOV/oY9eh8rpJZ5w==", "dependencies": { - "@firebase/analytics": "0.7.4", + "@firebase/analytics": "0.7.8", "@firebase/analytics-types": "0.7.0", - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -648,24 +649,24 @@ "integrity": "sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ==" }, "node_modules/@firebase/app": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.7.tgz", - "integrity": "sha512-3yEJDg814CnYIODgLCr4vrIP5Of78WDdikJVE5LS7MN1MWDFeJpQ4n88BdjO2X4Dp22+UFkw7FiuduwfUJJYYQ==", + "version": "0.7.22", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.22.tgz", + "integrity": "sha512-v3AXSCwAvZyIFzOGgPAYtzjltm1M9R4U4yqsIBPf5B4ryaT1EGK+3ETZUOckNl5y2YwdKRJVPDDore+B2xg0Ug==", "dependencies": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "node_modules/@firebase/app-check": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.1.tgz", - "integrity": "sha512-5TYzIM7lhvxt8kB98iULOCrRgI8/qu7LEdsJNm8jEymk3x4DBL3lK0oRw5nHbyUy+lK7cq9D1NmZZnLA3Snt4w==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.7.tgz", + "integrity": "sha512-ByfjzbWCg+f42TeS++70pmEmYBtouJbHem/yH0vgF8+E90LeZugMx18oZxa/+4GVJRmDrMyhJHzWYQlqsG4q2Q==", "dependencies": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -673,14 +674,15 @@ } }, "node_modules/@firebase/app-check-compat": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.1.tgz", - "integrity": "sha512-nB34OoU0icJM0iVrSf7oRVVzrceSvKYdcwlqitrN9JaB+36KwQ0FiQ4saI/rE4DLjcNsviV2ojJ/PRPdv+P0QQ==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.7.tgz", + "integrity": "sha512-RK3JViHfaIfwLLWETJWY7STYsegXJXK1GACu06tv2WLEJGMXbvWftxpVi7VMSVjgCFDRDnUEhim5Exn4Z73sOg==", "dependencies": { - "@firebase/app-check": "0.5.1", - "@firebase/component": "0.5.9", + "@firebase/app-check": "0.5.7", + "@firebase/app-check-types": "0.4.0", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -692,15 +694,20 @@ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.1.0.tgz", "integrity": "sha512-uZfn9s4uuRsaX5Lwx+gFP3B6YsyOKUE+Rqa6z9ojT4VSRAsZFko9FRn6OxQUA1z5t5d08fY4pf+/+Dkd5wbdbA==" }, + "node_modules/@firebase/app-check-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.4.0.tgz", + "integrity": "sha512-SsWafqMABIOu7zLgWbmwvHGOeQQVQlwm42kwwubsmfLmL4Sf5uGpBfDhQ0CAkpi7bkJ/NwNFKafNDL9prRNP0Q==" + }, "node_modules/@firebase/app-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.8.tgz", - "integrity": "sha512-tDjJOoCHYXdswyci0UAPV9vMWp2Guxm8B2jw2A+A1DEcMBcL2z3dDp1JnAwFNbqvE9JHuMBVfEKq/fO0GgDXOg==", + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.23.tgz", + "integrity": "sha512-c0QOhU2UVxZ7N5++nLQgKZ899ZC8+/ESa8VCzsQDwBw1T3MFAD1cG40KhB+CGtp/uYk/w6Jtk8k0xyZu6O2LOg==", "dependencies": { - "@firebase/app": "0.7.7", - "@firebase/component": "0.5.9", + "@firebase/app": "0.7.22", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -710,14 +717,14 @@ "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" }, "node_modules/@firebase/auth": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.2.tgz", - "integrity": "sha512-TH6v+wi3cHNdcshAjaWsAPYw/JmY5MYU8xCtZMQQaJdf+c/X+uCWv23s7Xs1fzda5+jecjVmENoXa+i/Onxeeg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.12.tgz", + "integrity": "sha512-39/eJBmq5Ne+HoCJuQXlhaOH2e8qySxYUa5Z25mhcam8nmAMrBh7Ph1yZjUeSfLsSJiSXANMHK5dnVE+1TROXw==", "dependencies": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "selenium-webdriver": "4.0.0-rc-1", "tslib": "^2.1.0" }, @@ -726,15 +733,15 @@ } }, "node_modules/@firebase/auth-compat": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.2.tgz", - "integrity": "sha512-ywYVs/GdBGHTfIKh65IHkge9kyUqOBd24jCI1mmUeQjO3ChVZfdOJk2JvhegLwaRnPYiuzzrWo7wp87YXVL+TQ==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.12.tgz", + "integrity": "sha512-LKeKylktRj03xgW5ilSOW1c4AsMig15ogf5hDKa820t6Bp6MNabj8yq2TV0/Q4SP4Ox/yrTISJGVvk+TJuBecQ==", "dependencies": { - "@firebase/auth": "0.19.2", + "@firebase/auth": "0.19.12", "@firebase/auth-types": "0.11.0", - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "selenium-webdriver": "^4.0.0-beta.2", "tslib": "^2.1.0" }, @@ -761,37 +768,37 @@ } }, "node_modules/@firebase/component": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.9.tgz", - "integrity": "sha512-oLCY3x9WbM5rn06qmUvbtJuPj4dIw/C9T4Th52IiHF5tiCRC5k6YthvhfUVcTwfoUhK0fOgtwuKJKA/LpCPjgA==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.13.tgz", + "integrity": "sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w==", "dependencies": { - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "node_modules/@firebase/database": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.4.tgz", - "integrity": "sha512-XkrL1kXELRNkqKcltuT4hfG1gWmFiGvjFY+z7Lhb//12MqdkLjwa9YMK8c6Lo+Ro+IkWcJArQaOQYe3GkU5Wgg==", + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.8.tgz", + "integrity": "sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA==", "dependencies": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "faye-websocket": "0.11.4", "tslib": "^2.1.0" } }, "node_modules/@firebase/database-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.4.tgz", - "integrity": "sha512-dIJiZLDFF3U+MoEwoPBy7zxWmBUro1KefmwSHlpOoxmPv76tuoPm85NumpW/HmMrtTcTkC2qowtb6NjGE8X7mw==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.8.tgz", + "integrity": "sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/database": "0.12.4", - "@firebase/database-types": "0.9.3", + "@firebase/component": "0.5.13", + "@firebase/database": "0.12.8", + "@firebase/database-types": "0.9.7", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -799,26 +806,26 @@ } }, "node_modules/@firebase/database-types": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.3.tgz", - "integrity": "sha512-R+YXLWy/Q7mNUxiUYiMboTwvVoprrgfyvf1Viyevskw6IoH1q8HV1UjlkLSgmRsOT9HPWt7XZUEStVZJFknHwg==", + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.7.tgz", + "integrity": "sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw==", "dependencies": { "@firebase/app-types": "0.7.0", - "@firebase/util": "1.4.2" + "@firebase/util": "1.5.2" } }, "node_modules/@firebase/firestore": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.3.0.tgz", - "integrity": "sha512-QMCwmBlUUFldszKtVqIlqwjZYY0eODI2R7F9lkPxiANw8F853bSyBY6wqN85657vfDS7Ij6i6s+1qWMCqFvHHA==", + "version": "3.4.8", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.4.8.tgz", + "integrity": "sha512-qjrI22TrqSGsOVBkYpRcpY48eSFj+hvleWEaFn3bDxy+QNUiZS08cicSlBOxdosKi5LRMQVGyHKcqHExup02+A==", "dependencies": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "@firebase/webchannel-wrapper": "0.6.1", "@grpc/grpc-js": "^1.3.2", "@grpc/proto-loader": "^0.6.0", - "node-fetch": "2.6.5", + "node-fetch": "2.6.7", "tslib": "^2.1.0" }, "engines": { @@ -829,14 +836,14 @@ } }, "node_modules/@firebase/firestore-compat": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.7.tgz", - "integrity": "sha512-34n9PxdenKRNqZRrr+SfjAcrPUvbfggLnRrADz7iVFYlDo9X1Jj6+fimzo0xC/p+2KZkPAiRYbT60WhjBLYUcg==", + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.17.tgz", + "integrity": "sha512-hTLgq2WXUE6bb3/IqYlwY0Q6FdbZB2JwDoZHexIQmK69XuuK3j+JbE/NixV3mBo232tNSU+QeamfbAd6A1Agfw==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/firestore": "3.3.0", + "@firebase/component": "0.5.13", + "@firebase/firestore": "3.4.8", "@firebase/firestore-types": "2.5.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -853,16 +860,16 @@ } }, "node_modules/@firebase/functions": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.7.5.tgz", - "integrity": "sha512-eEA8WhvNqahbepl0DF1vPc8Ml8oPMkDUQr+HQFQSqVvhYaGc1r6yP+Xe5QChifGfrAd5s/AanchNDvkS86Dg9g==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.8.0.tgz", + "integrity": "sha512-QewfP/QY4jifjqLV6xrAnksZz+BwJe4XfMNoohhz7etu403yu+ZxAatev7brq1XtgvHePwa+vpQ8ppSeX6TIpA==", "dependencies": { "@firebase/app-check-interop-types": "0.1.0", "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "tslib": "^2.1.0" }, "peerDependencies": { @@ -870,14 +877,14 @@ } }, "node_modules/@firebase/functions-compat": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.1.6.tgz", - "integrity": "sha512-YZZmToY5Psp0fH8IMdqb00RXsI+dT/+YHKjNNZ1mO/MQS1Uwk40rqaOwZa00xYVeiEyfMnT2ciXyHPEYD7nxPQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.2.0.tgz", + "integrity": "sha512-jbrjTNgWRgcRegZlA3cQV1NLhvMBIlG0JFc8o34OhSvFAurraOX0uPBOCHruR8Pk+lJaP61Gqk3eeRhenVdX5w==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/functions": "0.7.5", + "@firebase/component": "0.5.13", + "@firebase/functions": "0.8.0", "@firebase/functions-types": "0.5.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -890,13 +897,12 @@ "integrity": "sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA==" }, "node_modules/@firebase/installations": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.4.tgz", - "integrity": "sha512-rYb6Ju/tIBhojmM8FsgS96pErKl6gPgJFnffMO4bKH7HilXhOfgLfKU9k51ZDcps8N0npDx9+AJJ6pL1aYuYZQ==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.8.tgz", + "integrity": "sha512-u/lAOVhgYFg1e38rNrVzFrWxdKzTOIromx574Hi2AccFA230hSlXFY7pRaCpgs11VDzmpt4lhhOrQOX7886cKw==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "idb": "3.0.2", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -912,15 +918,14 @@ } }, "node_modules/@firebase/messaging": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.4.tgz", - "integrity": "sha512-OvYV4MLPfDpdP/yltLqZXZRx6rXWz52bEilS2jL2B4sGiuTaXSkR6BIHB54EPTblu32nbyZYdlER4fssz4TfXw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.12.tgz", + "integrity": "sha512-qfLW7SZRZVKscI1GSyWc3WPtjAUDUk3gcEfPkdz9fzzQwj98V8xF++g4wL+9cuEuRzYf8ki2kCN/aqKRYUrxag==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.4.2", - "idb": "3.0.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -928,13 +933,13 @@ } }, "node_modules/@firebase/messaging-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.4.tgz", - "integrity": "sha512-6477jBw7w7hk0uhnTUMsPoukalpcwbxTTo9kMguHVSXe0t3OdoxeXEaapaNJlOmU4Kgc8j3rsms8IDLdKVpvlA==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.12.tgz", + "integrity": "sha512-Cfv4ZQaxiMx4DcpDkFX1yKHFGQtnyMA6pcLplcC3uHkSVCyNRW6pFYSoO0/Uae03ixxIYNwle1ZVaVUZ2L5ddA==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/messaging": "0.9.4", - "@firebase/util": "1.4.2", + "@firebase/component": "0.5.13", + "@firebase/messaging": "0.9.12", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -947,14 +952,14 @@ "integrity": "sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ==" }, "node_modules/@firebase/performance": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.4.tgz", - "integrity": "sha512-ES6aS4eoMhf9CczntBADDsXhaFea/3a0FADwy/VpWXXBxVb8tqc5tPcoTwd9L5M/aDeSiQMy344rhrSsTbIZEg==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.8.tgz", + "integrity": "sha512-IN5MWdGRn0jglSdv1UHEDMklm1SOfF1IZ1pGNxVyO5CpF3a08I54I60fuwEfMUcsU6OAfzMl3zI+bnW5IgKdPg==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -962,15 +967,15 @@ } }, "node_modules/@firebase/performance-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.4.tgz", - "integrity": "sha512-YuGfmpC0o+YvEBlEZCbPdNbT4Nn2qhi5uMXjqKnNIUepmXUsgOYDiAqM9nxHPoE/6IkvoFMdCj5nTUYVLCFXgg==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.8.tgz", + "integrity": "sha512-lMLKFcOB99+tb6dVHJlJ8s19JFjxqpAqPGXCG8evTODPUW3BluBbfG4YS7JRESVA7wc/6kkuQIOx9q7l+bBZtQ==", "dependencies": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/performance": "0.5.4", + "@firebase/performance": "0.5.8", "@firebase/performance-types": "0.1.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -993,14 +998,14 @@ } }, "node_modules/@firebase/remote-config": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.3.tgz", - "integrity": "sha512-9hZWfB3k3IYsjHbWeUfhv/SDCcOgv/JMJpLXlUbTppXPm1IZ3X9ZW4I9bS86gGYr7m/kSv99U0oxQ7N9PoR8Iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.7.tgz", + "integrity": "sha512-gQaGzQCBOkS35b/aXC5Y9/zsPenqs6+axnChYYyfU7CqMG5FGfNbVi2rezYwB4G3+fH4rGO1s6xqcI535Fvy/A==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1008,15 +1013,15 @@ } }, "node_modules/@firebase/remote-config-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.4.tgz", - "integrity": "sha512-6WeKR7E9KJ1RIF9GZiyle1uD4IsIPUBKUnUnFkQhj3FV6cGvQwbeG0rbh7QQLvd0IWuh9lABYjHXWp+rGHQk8A==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.8.tgz", + "integrity": "sha512-lU9t7PMVpgE6q1vG8AuFenFhfUnx0H+eeiIQTi4dtuLDMx9BsI14c9VuiVjRIi7xC2DCDRNQCRL1kRD8bzgJNg==", "dependencies": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/remote-config": "0.3.3", + "@firebase/remote-config": "0.3.7", "@firebase/remote-config-types": "0.2.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1029,13 +1034,13 @@ "integrity": "sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw==" }, "node_modules/@firebase/storage": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.8.6.tgz", - "integrity": "sha512-ltZinJHC+gvBwmq2MB9phOC5mOBcLE1LkHGjt2LfRyH4SFYdZ6QPwchQfJTvOPKWazsS/y9fud1TliLs7/ZEqQ==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.9.5.tgz", + "integrity": "sha512-+nCDNIT2pNovlHnLOQPofn8jdOyJ4akUWPGn4ydAoFrfVt1/lINx5Qe+jS3/tKLROfYabqBYxfFUjHQKZBYwvg==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1043,14 +1048,14 @@ } }, "node_modules/@firebase/storage-compat": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.6.tgz", - "integrity": "sha512-4uzrDWAQpJ2wwjDBdWwhmy3F3T4KTV98qHlhXaquX3KRo3VxgZLcNwWYvD4fZwxi66COn2oZb70zMztcNmn7BA==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.13.tgz", + "integrity": "sha512-MdubKh+xe3Xpi34WaXBKtim8H2aauO5sqqmATTc2WgSmSAqTmNSjQfNqIdf139Mp9ZCnpZAxiwiwzQtfckLYWg==", "dependencies": { - "@firebase/component": "0.5.9", - "@firebase/storage": "0.8.6", + "@firebase/component": "0.5.13", + "@firebase/storage": "0.9.5", "@firebase/storage-types": "0.6.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1067,9 +1072,9 @@ } }, "node_modules/@firebase/util": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.2.tgz", - "integrity": "sha512-JMiUo+9QE9lMBvEtBjqsOFdmJgObFvi7OL1A0uFGwTmlCI1ZeNPOEBrwXkgTOelVCdiMO15mAebtEyxFuQ6FsA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.2.tgz", + "integrity": "sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==", "dependencies": { "tslib": "^2.1.0" } @@ -1080,9 +1085,9 @@ "integrity": "sha512-9FqhNjKQWpQ3fGnSOCovHOm+yhhiorKEqYLAfd525jWavunDJcx8rOW6i6ozAh+FbwcYMkL7b+3j4UR/30MpoQ==" }, "node_modules/@grpc/grpc-js": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.4.tgz", - "integrity": "sha512-a6222b7Dl6fIlMgzVl7e+NiRoLiZFbpcwvBH2Oli56Bn7W4/3Ld+86hK4ffPn5rx2DlDidmIcvIJiOQXyhv9gA==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz", + "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==", "dependencies": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" @@ -1092,15 +1097,15 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.6.tgz", - "integrity": "sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==", + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.11.tgz", + "integrity": "sha512-MRiPjTjNgKxMupQ0M8mM9Mcljb2aZvE3Y/oEv+dacozIs2TwTdiPbvfkZpMeghfjGtoDJhDjyCtmFzJcjdDTUQ==", "dependencies": { "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", + "long": "^4.0.0 || ^5.2.0", "protobufjs": "^6.10.0", - "yargs": "^16.1.1" + "yargs": "^16.2.0" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" @@ -1802,9 +1807,9 @@ } }, "node_modules/@types/jest": { - "version": "27.4.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", - "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "version": "27.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.0.tgz", + "integrity": "sha512-9RBFx7r4k+msyj/arpfaa0WOOEcaAZNmN+j80KFbFCoSqCJGHTz7YMAMGQW9Xmqm5w6l5c25vbSjMwlikJi5+g==", "dev": true, "dependencies": { "jest-matcher-utils": "^27.0.0", @@ -1821,14 +1826,14 @@ } }, "node_modules/@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "node_modules/@types/node": { - "version": "16.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", - "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==" + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", + "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==" }, "node_modules/@types/pi-camera": { "version": "1.5.3", @@ -2984,42 +2989,42 @@ } }, "node_modules/firebase": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.4.0.tgz", - "integrity": "sha512-VcdMVYf02OZtDWp6IYnlcostwqWZHlumxgnY5K87EKBHIjGcjkpdb39mguXZCVfQ44TRid2yaBouGDKAacnFwQ==", - "dependencies": { - "@firebase/analytics": "0.7.4", - "@firebase/analytics-compat": "0.1.5", - "@firebase/app": "0.7.7", - "@firebase/app-check": "0.5.1", - "@firebase/app-check-compat": "0.2.1", - "@firebase/app-compat": "0.1.8", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.7.0.tgz", + "integrity": "sha512-IsBZ11b8MbB2YyulNii+vhNMpqmfNwI9iGWG9GVgn+Pzdi+Ta+eaGI3+VbeSZoqC/nkKOzSR89cpz7kXd4+mgg==", + "dependencies": { + "@firebase/analytics": "0.7.8", + "@firebase/analytics-compat": "0.1.9", + "@firebase/app": "0.7.22", + "@firebase/app-check": "0.5.7", + "@firebase/app-check-compat": "0.2.7", + "@firebase/app-compat": "0.1.23", "@firebase/app-types": "0.7.0", - "@firebase/auth": "0.19.2", - "@firebase/auth-compat": "0.2.2", - "@firebase/database": "0.12.4", - "@firebase/database-compat": "0.1.4", - "@firebase/firestore": "3.3.0", - "@firebase/firestore-compat": "0.1.7", - "@firebase/functions": "0.7.5", - "@firebase/functions-compat": "0.1.6", - "@firebase/installations": "0.5.4", - "@firebase/messaging": "0.9.4", - "@firebase/messaging-compat": "0.1.4", - "@firebase/performance": "0.5.4", - "@firebase/performance-compat": "0.1.4", + "@firebase/auth": "0.19.12", + "@firebase/auth-compat": "0.2.12", + "@firebase/database": "0.12.8", + "@firebase/database-compat": "0.1.8", + "@firebase/firestore": "3.4.8", + "@firebase/firestore-compat": "0.1.17", + "@firebase/functions": "0.8.0", + "@firebase/functions-compat": "0.2.0", + "@firebase/installations": "0.5.8", + "@firebase/messaging": "0.9.12", + "@firebase/messaging-compat": "0.1.12", + "@firebase/performance": "0.5.8", + "@firebase/performance-compat": "0.1.8", "@firebase/polyfill": "0.3.36", - "@firebase/remote-config": "0.3.3", - "@firebase/remote-config-compat": "0.1.4", - "@firebase/storage": "0.8.6", - "@firebase/storage-compat": "0.1.6", - "@firebase/util": "1.4.2" + "@firebase/remote-config": "0.3.7", + "@firebase/remote-config-compat": "0.1.8", + "@firebase/storage": "0.9.5", + "@firebase/storage-compat": "0.1.13", + "@firebase/util": "1.5.2" } }, "node_modules/follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", "funding": [ { "type": "individual", @@ -3229,9 +3234,9 @@ "dev": true }, "node_modules/http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==" }, "node_modules/http-proxy-agent": { "version": "4.0.1", @@ -3279,11 +3284,6 @@ "node": ">=0.10.0" } }, - "node_modules/idb": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", - "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4378,9 +4378,9 @@ } }, "node_modules/jszip": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", - "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz", + "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==", "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -4538,9 +4538,9 @@ } }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" }, "node_modules/lru-cache": { "version": "6.0.0", @@ -4650,9 +4650,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/minipass": { "version": "3.1.6", @@ -4749,14 +4749,22 @@ "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" }, "node_modules/node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/node-gyp-build": { @@ -5137,6 +5145,11 @@ "pbts": "bin/pbts" } }, + "node_modules/protobufjs/node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -5815,9 +5828,9 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/type-check": { "version": "0.3.2", @@ -6614,26 +6627,26 @@ "dev": true }, "@firebase/analytics": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.4.tgz", - "integrity": "sha512-AU3XMwHW7SFGCNeUKKNW2wXGTdmS164ackt/Epu2bDXCT1OcauPE1AVd+ofULSIDCaDUAQVmvw3JrobgogEU7Q==", + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.8.tgz", + "integrity": "sha512-W38Zy/jf64LKpPi+mGNNETIjz4eq/YXBE0Uu2bzstqUwlhvFn1WlRBK4vzgtZMRaGW04CQp9FXYv6ZTRo/Xbyw==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/analytics-compat": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.5.tgz", - "integrity": "sha512-5cfr0uWwlhoHQYAr6UtQCHwnGjs/3J/bWrfA3INNtzaN4/tTTLTD02iobbccRcM7dM5TR0sZFWS5orfAU3OBFg==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.9.tgz", + "integrity": "sha512-HYKMAZvfU589WVvK5XKY9Pl+axXFISabouAFw2VHpJm/TO1mAXAy0+eIjqQ3j8z3L1OEfCeOV/oY9eh8rpJZ5w==", "requires": { - "@firebase/analytics": "0.7.4", + "@firebase/analytics": "0.7.8", "@firebase/analytics-types": "0.7.0", - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6643,36 +6656,37 @@ "integrity": "sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ==" }, "@firebase/app": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.7.tgz", - "integrity": "sha512-3yEJDg814CnYIODgLCr4vrIP5Of78WDdikJVE5LS7MN1MWDFeJpQ4n88BdjO2X4Dp22+UFkw7FiuduwfUJJYYQ==", + "version": "0.7.22", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.22.tgz", + "integrity": "sha512-v3AXSCwAvZyIFzOGgPAYtzjltm1M9R4U4yqsIBPf5B4ryaT1EGK+3ETZUOckNl5y2YwdKRJVPDDore+B2xg0Ug==", "requires": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/app-check": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.1.tgz", - "integrity": "sha512-5TYzIM7lhvxt8kB98iULOCrRgI8/qu7LEdsJNm8jEymk3x4DBL3lK0oRw5nHbyUy+lK7cq9D1NmZZnLA3Snt4w==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.7.tgz", + "integrity": "sha512-ByfjzbWCg+f42TeS++70pmEmYBtouJbHem/yH0vgF8+E90LeZugMx18oZxa/+4GVJRmDrMyhJHzWYQlqsG4q2Q==", "requires": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/app-check-compat": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.1.tgz", - "integrity": "sha512-nB34OoU0icJM0iVrSf7oRVVzrceSvKYdcwlqitrN9JaB+36KwQ0FiQ4saI/rE4DLjcNsviV2ojJ/PRPdv+P0QQ==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.7.tgz", + "integrity": "sha512-RK3JViHfaIfwLLWETJWY7STYsegXJXK1GACu06tv2WLEJGMXbvWftxpVi7VMSVjgCFDRDnUEhim5Exn4Z73sOg==", "requires": { - "@firebase/app-check": "0.5.1", - "@firebase/component": "0.5.9", + "@firebase/app-check": "0.5.7", + "@firebase/app-check-types": "0.4.0", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6681,15 +6695,20 @@ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.1.0.tgz", "integrity": "sha512-uZfn9s4uuRsaX5Lwx+gFP3B6YsyOKUE+Rqa6z9ojT4VSRAsZFko9FRn6OxQUA1z5t5d08fY4pf+/+Dkd5wbdbA==" }, + "@firebase/app-check-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.4.0.tgz", + "integrity": "sha512-SsWafqMABIOu7zLgWbmwvHGOeQQVQlwm42kwwubsmfLmL4Sf5uGpBfDhQ0CAkpi7bkJ/NwNFKafNDL9prRNP0Q==" + }, "@firebase/app-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.8.tgz", - "integrity": "sha512-tDjJOoCHYXdswyci0UAPV9vMWp2Guxm8B2jw2A+A1DEcMBcL2z3dDp1JnAwFNbqvE9JHuMBVfEKq/fO0GgDXOg==", + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.23.tgz", + "integrity": "sha512-c0QOhU2UVxZ7N5++nLQgKZ899ZC8+/ESa8VCzsQDwBw1T3MFAD1cG40KhB+CGtp/uYk/w6Jtk8k0xyZu6O2LOg==", "requires": { - "@firebase/app": "0.7.7", - "@firebase/component": "0.5.9", + "@firebase/app": "0.7.22", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6699,28 +6718,28 @@ "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" }, "@firebase/auth": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.2.tgz", - "integrity": "sha512-TH6v+wi3cHNdcshAjaWsAPYw/JmY5MYU8xCtZMQQaJdf+c/X+uCWv23s7Xs1fzda5+jecjVmENoXa+i/Onxeeg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.12.tgz", + "integrity": "sha512-39/eJBmq5Ne+HoCJuQXlhaOH2e8qySxYUa5Z25mhcam8nmAMrBh7Ph1yZjUeSfLsSJiSXANMHK5dnVE+1TROXw==", "requires": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "selenium-webdriver": "4.0.0-rc-1", "tslib": "^2.1.0" } }, "@firebase/auth-compat": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.2.tgz", - "integrity": "sha512-ywYVs/GdBGHTfIKh65IHkge9kyUqOBd24jCI1mmUeQjO3ChVZfdOJk2JvhegLwaRnPYiuzzrWo7wp87YXVL+TQ==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.12.tgz", + "integrity": "sha512-LKeKylktRj03xgW5ilSOW1c4AsMig15ogf5hDKa820t6Bp6MNabj8yq2TV0/Q4SP4Ox/yrTISJGVvk+TJuBecQ==", "requires": { - "@firebase/auth": "0.19.2", + "@firebase/auth": "0.19.12", "@firebase/auth-types": "0.11.0", - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "selenium-webdriver": "^4.0.0-beta.2", "tslib": "^2.1.0" } @@ -6738,73 +6757,73 @@ "requires": {} }, "@firebase/component": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.9.tgz", - "integrity": "sha512-oLCY3x9WbM5rn06qmUvbtJuPj4dIw/C9T4Th52IiHF5tiCRC5k6YthvhfUVcTwfoUhK0fOgtwuKJKA/LpCPjgA==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.13.tgz", + "integrity": "sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w==", "requires": { - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/database": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.4.tgz", - "integrity": "sha512-XkrL1kXELRNkqKcltuT4hfG1gWmFiGvjFY+z7Lhb//12MqdkLjwa9YMK8c6Lo+Ro+IkWcJArQaOQYe3GkU5Wgg==", + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.8.tgz", + "integrity": "sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA==", "requires": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "faye-websocket": "0.11.4", "tslib": "^2.1.0" } }, "@firebase/database-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.4.tgz", - "integrity": "sha512-dIJiZLDFF3U+MoEwoPBy7zxWmBUro1KefmwSHlpOoxmPv76tuoPm85NumpW/HmMrtTcTkC2qowtb6NjGE8X7mw==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.8.tgz", + "integrity": "sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/database": "0.12.4", - "@firebase/database-types": "0.9.3", + "@firebase/component": "0.5.13", + "@firebase/database": "0.12.8", + "@firebase/database-types": "0.9.7", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/database-types": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.3.tgz", - "integrity": "sha512-R+YXLWy/Q7mNUxiUYiMboTwvVoprrgfyvf1Viyevskw6IoH1q8HV1UjlkLSgmRsOT9HPWt7XZUEStVZJFknHwg==", + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.7.tgz", + "integrity": "sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw==", "requires": { "@firebase/app-types": "0.7.0", - "@firebase/util": "1.4.2" + "@firebase/util": "1.5.2" } }, "@firebase/firestore": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.3.0.tgz", - "integrity": "sha512-QMCwmBlUUFldszKtVqIlqwjZYY0eODI2R7F9lkPxiANw8F853bSyBY6wqN85657vfDS7Ij6i6s+1qWMCqFvHHA==", + "version": "3.4.8", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.4.8.tgz", + "integrity": "sha512-qjrI22TrqSGsOVBkYpRcpY48eSFj+hvleWEaFn3bDxy+QNUiZS08cicSlBOxdosKi5LRMQVGyHKcqHExup02+A==", "requires": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "@firebase/webchannel-wrapper": "0.6.1", "@grpc/grpc-js": "^1.3.2", "@grpc/proto-loader": "^0.6.0", - "node-fetch": "2.6.5", + "node-fetch": "2.6.7", "tslib": "^2.1.0" } }, "@firebase/firestore-compat": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.7.tgz", - "integrity": "sha512-34n9PxdenKRNqZRrr+SfjAcrPUvbfggLnRrADz7iVFYlDo9X1Jj6+fimzo0xC/p+2KZkPAiRYbT60WhjBLYUcg==", + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.17.tgz", + "integrity": "sha512-hTLgq2WXUE6bb3/IqYlwY0Q6FdbZB2JwDoZHexIQmK69XuuK3j+JbE/NixV3mBo232tNSU+QeamfbAd6A1Agfw==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/firestore": "3.3.0", + "@firebase/component": "0.5.13", + "@firebase/firestore": "3.4.8", "@firebase/firestore-types": "2.5.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6815,28 +6834,28 @@ "requires": {} }, "@firebase/functions": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.7.5.tgz", - "integrity": "sha512-eEA8WhvNqahbepl0DF1vPc8Ml8oPMkDUQr+HQFQSqVvhYaGc1r6yP+Xe5QChifGfrAd5s/AanchNDvkS86Dg9g==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.8.0.tgz", + "integrity": "sha512-QewfP/QY4jifjqLV6xrAnksZz+BwJe4XfMNoohhz7etu403yu+ZxAatev7brq1XtgvHePwa+vpQ8ppSeX6TIpA==", "requires": { "@firebase/app-check-interop-types": "0.1.0", "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "tslib": "^2.1.0" } }, "@firebase/functions-compat": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.1.6.tgz", - "integrity": "sha512-YZZmToY5Psp0fH8IMdqb00RXsI+dT/+YHKjNNZ1mO/MQS1Uwk40rqaOwZa00xYVeiEyfMnT2ciXyHPEYD7nxPQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.2.0.tgz", + "integrity": "sha512-jbrjTNgWRgcRegZlA3cQV1NLhvMBIlG0JFc8o34OhSvFAurraOX0uPBOCHruR8Pk+lJaP61Gqk3eeRhenVdX5w==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/functions": "0.7.5", + "@firebase/component": "0.5.13", + "@firebase/functions": "0.8.0", "@firebase/functions-types": "0.5.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6846,13 +6865,12 @@ "integrity": "sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA==" }, "@firebase/installations": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.4.tgz", - "integrity": "sha512-rYb6Ju/tIBhojmM8FsgS96pErKl6gPgJFnffMO4bKH7HilXhOfgLfKU9k51ZDcps8N0npDx9+AJJ6pL1aYuYZQ==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.8.tgz", + "integrity": "sha512-u/lAOVhgYFg1e38rNrVzFrWxdKzTOIromx574Hi2AccFA230hSlXFY7pRaCpgs11VDzmpt4lhhOrQOX7886cKw==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "idb": "3.0.2", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6865,26 +6883,25 @@ } }, "@firebase/messaging": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.4.tgz", - "integrity": "sha512-OvYV4MLPfDpdP/yltLqZXZRx6rXWz52bEilS2jL2B4sGiuTaXSkR6BIHB54EPTblu32nbyZYdlER4fssz4TfXw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.12.tgz", + "integrity": "sha512-qfLW7SZRZVKscI1GSyWc3WPtjAUDUk3gcEfPkdz9fzzQwj98V8xF++g4wL+9cuEuRzYf8ki2kCN/aqKRYUrxag==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.4.2", - "idb": "3.0.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/messaging-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.4.tgz", - "integrity": "sha512-6477jBw7w7hk0uhnTUMsPoukalpcwbxTTo9kMguHVSXe0t3OdoxeXEaapaNJlOmU4Kgc8j3rsms8IDLdKVpvlA==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.12.tgz", + "integrity": "sha512-Cfv4ZQaxiMx4DcpDkFX1yKHFGQtnyMA6pcLplcC3uHkSVCyNRW6pFYSoO0/Uae03ixxIYNwle1ZVaVUZ2L5ddA==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/messaging": "0.9.4", - "@firebase/util": "1.4.2", + "@firebase/component": "0.5.13", + "@firebase/messaging": "0.9.12", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6894,27 +6911,27 @@ "integrity": "sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ==" }, "@firebase/performance": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.4.tgz", - "integrity": "sha512-ES6aS4eoMhf9CczntBADDsXhaFea/3a0FADwy/VpWXXBxVb8tqc5tPcoTwd9L5M/aDeSiQMy344rhrSsTbIZEg==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.8.tgz", + "integrity": "sha512-IN5MWdGRn0jglSdv1UHEDMklm1SOfF1IZ1pGNxVyO5CpF3a08I54I60fuwEfMUcsU6OAfzMl3zI+bnW5IgKdPg==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/performance-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.4.tgz", - "integrity": "sha512-YuGfmpC0o+YvEBlEZCbPdNbT4Nn2qhi5uMXjqKnNIUepmXUsgOYDiAqM9nxHPoE/6IkvoFMdCj5nTUYVLCFXgg==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.8.tgz", + "integrity": "sha512-lMLKFcOB99+tb6dVHJlJ8s19JFjxqpAqPGXCG8evTODPUW3BluBbfG4YS7JRESVA7wc/6kkuQIOx9q7l+bBZtQ==", "requires": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/performance": "0.5.4", + "@firebase/performance": "0.5.8", "@firebase/performance-types": "0.1.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6934,27 +6951,27 @@ } }, "@firebase/remote-config": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.3.tgz", - "integrity": "sha512-9hZWfB3k3IYsjHbWeUfhv/SDCcOgv/JMJpLXlUbTppXPm1IZ3X9ZW4I9bS86gGYr7m/kSv99U0oxQ7N9PoR8Iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.7.tgz", + "integrity": "sha512-gQaGzQCBOkS35b/aXC5Y9/zsPenqs6+axnChYYyfU7CqMG5FGfNbVi2rezYwB4G3+fH4rGO1s6xqcI535Fvy/A==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/installations": "0.5.4", + "@firebase/component": "0.5.13", + "@firebase/installations": "0.5.8", "@firebase/logger": "0.3.2", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, "@firebase/remote-config-compat": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.4.tgz", - "integrity": "sha512-6WeKR7E9KJ1RIF9GZiyle1uD4IsIPUBKUnUnFkQhj3FV6cGvQwbeG0rbh7QQLvd0IWuh9lABYjHXWp+rGHQk8A==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.8.tgz", + "integrity": "sha512-lU9t7PMVpgE6q1vG8AuFenFhfUnx0H+eeiIQTi4dtuLDMx9BsI14c9VuiVjRIi7xC2DCDRNQCRL1kRD8bzgJNg==", "requires": { - "@firebase/component": "0.5.9", + "@firebase/component": "0.5.13", "@firebase/logger": "0.3.2", - "@firebase/remote-config": "0.3.3", + "@firebase/remote-config": "0.3.7", "@firebase/remote-config-types": "0.2.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6964,25 +6981,25 @@ "integrity": "sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw==" }, "@firebase/storage": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.8.6.tgz", - "integrity": "sha512-ltZinJHC+gvBwmq2MB9phOC5mOBcLE1LkHGjt2LfRyH4SFYdZ6QPwchQfJTvOPKWazsS/y9fud1TliLs7/ZEqQ==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.9.5.tgz", + "integrity": "sha512-+nCDNIT2pNovlHnLOQPofn8jdOyJ4akUWPGn4ydAoFrfVt1/lINx5Qe+jS3/tKLROfYabqBYxfFUjHQKZBYwvg==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/util": "1.4.2", - "node-fetch": "2.6.5", + "@firebase/component": "0.5.13", + "@firebase/util": "1.5.2", + "node-fetch": "2.6.7", "tslib": "^2.1.0" } }, "@firebase/storage-compat": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.6.tgz", - "integrity": "sha512-4uzrDWAQpJ2wwjDBdWwhmy3F3T4KTV98qHlhXaquX3KRo3VxgZLcNwWYvD4fZwxi66COn2oZb70zMztcNmn7BA==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.13.tgz", + "integrity": "sha512-MdubKh+xe3Xpi34WaXBKtim8H2aauO5sqqmATTc2WgSmSAqTmNSjQfNqIdf139Mp9ZCnpZAxiwiwzQtfckLYWg==", "requires": { - "@firebase/component": "0.5.9", - "@firebase/storage": "0.8.6", + "@firebase/component": "0.5.13", + "@firebase/storage": "0.9.5", "@firebase/storage-types": "0.6.0", - "@firebase/util": "1.4.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" } }, @@ -6993,9 +7010,9 @@ "requires": {} }, "@firebase/util": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.2.tgz", - "integrity": "sha512-JMiUo+9QE9lMBvEtBjqsOFdmJgObFvi7OL1A0uFGwTmlCI1ZeNPOEBrwXkgTOelVCdiMO15mAebtEyxFuQ6FsA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.2.tgz", + "integrity": "sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==", "requires": { "tslib": "^2.1.0" } @@ -7006,24 +7023,24 @@ "integrity": "sha512-9FqhNjKQWpQ3fGnSOCovHOm+yhhiorKEqYLAfd525jWavunDJcx8rOW6i6ozAh+FbwcYMkL7b+3j4UR/30MpoQ==" }, "@grpc/grpc-js": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.4.tgz", - "integrity": "sha512-a6222b7Dl6fIlMgzVl7e+NiRoLiZFbpcwvBH2Oli56Bn7W4/3Ld+86hK4ffPn5rx2DlDidmIcvIJiOQXyhv9gA==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz", + "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==", "requires": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" } }, "@grpc/proto-loader": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.6.tgz", - "integrity": "sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==", + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.11.tgz", + "integrity": "sha512-MRiPjTjNgKxMupQ0M8mM9Mcljb2aZvE3Y/oEv+dacozIs2TwTdiPbvfkZpMeghfjGtoDJhDjyCtmFzJcjdDTUQ==", "requires": { "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", + "long": "^4.0.0 || ^5.2.0", "protobufjs": "^6.10.0", - "yargs": "^16.1.1" + "yargs": "^16.2.0" } }, "@istanbuljs/load-nyc-config": { @@ -7565,9 +7582,9 @@ } }, "@types/jest": { - "version": "27.4.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", - "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "version": "27.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.0.tgz", + "integrity": "sha512-9RBFx7r4k+msyj/arpfaa0WOOEcaAZNmN+j80KFbFCoSqCJGHTz7YMAMGQW9Xmqm5w6l5c25vbSjMwlikJi5+g==", "dev": true, "requires": { "jest-matcher-utils": "^27.0.0", @@ -7584,14 +7601,14 @@ } }, "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "@types/node": { - "version": "16.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", - "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==" + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", + "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==" }, "@types/pi-camera": { "version": "1.5.3", @@ -8478,42 +8495,42 @@ } }, "firebase": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.4.0.tgz", - "integrity": "sha512-VcdMVYf02OZtDWp6IYnlcostwqWZHlumxgnY5K87EKBHIjGcjkpdb39mguXZCVfQ44TRid2yaBouGDKAacnFwQ==", - "requires": { - "@firebase/analytics": "0.7.4", - "@firebase/analytics-compat": "0.1.5", - "@firebase/app": "0.7.7", - "@firebase/app-check": "0.5.1", - "@firebase/app-check-compat": "0.2.1", - "@firebase/app-compat": "0.1.8", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.7.0.tgz", + "integrity": "sha512-IsBZ11b8MbB2YyulNii+vhNMpqmfNwI9iGWG9GVgn+Pzdi+Ta+eaGI3+VbeSZoqC/nkKOzSR89cpz7kXd4+mgg==", + "requires": { + "@firebase/analytics": "0.7.8", + "@firebase/analytics-compat": "0.1.9", + "@firebase/app": "0.7.22", + "@firebase/app-check": "0.5.7", + "@firebase/app-check-compat": "0.2.7", + "@firebase/app-compat": "0.1.23", "@firebase/app-types": "0.7.0", - "@firebase/auth": "0.19.2", - "@firebase/auth-compat": "0.2.2", - "@firebase/database": "0.12.4", - "@firebase/database-compat": "0.1.4", - "@firebase/firestore": "3.3.0", - "@firebase/firestore-compat": "0.1.7", - "@firebase/functions": "0.7.5", - "@firebase/functions-compat": "0.1.6", - "@firebase/installations": "0.5.4", - "@firebase/messaging": "0.9.4", - "@firebase/messaging-compat": "0.1.4", - "@firebase/performance": "0.5.4", - "@firebase/performance-compat": "0.1.4", + "@firebase/auth": "0.19.12", + "@firebase/auth-compat": "0.2.12", + "@firebase/database": "0.12.8", + "@firebase/database-compat": "0.1.8", + "@firebase/firestore": "3.4.8", + "@firebase/firestore-compat": "0.1.17", + "@firebase/functions": "0.8.0", + "@firebase/functions-compat": "0.2.0", + "@firebase/installations": "0.5.8", + "@firebase/messaging": "0.9.12", + "@firebase/messaging-compat": "0.1.12", + "@firebase/performance": "0.5.8", + "@firebase/performance-compat": "0.1.8", "@firebase/polyfill": "0.3.36", - "@firebase/remote-config": "0.3.3", - "@firebase/remote-config-compat": "0.1.4", - "@firebase/storage": "0.8.6", - "@firebase/storage-compat": "0.1.6", - "@firebase/util": "1.4.2" + "@firebase/remote-config": "0.3.7", + "@firebase/remote-config-compat": "0.1.8", + "@firebase/storage": "0.9.5", + "@firebase/storage-compat": "0.1.13", + "@firebase/util": "1.5.2" } }, "follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" }, "form-data": { "version": "3.0.1", @@ -8660,9 +8677,9 @@ "dev": true }, "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==" }, "http-proxy-agent": { "version": "4.0.1", @@ -8698,11 +8715,6 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "idb": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", - "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" - }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -9545,9 +9557,9 @@ } }, "jszip": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", - "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz", + "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==", "requires": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -9689,9 +9701,9 @@ } }, "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" }, "lru-cache": { "version": "6.0.0", @@ -9776,9 +9788,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "minipass": { "version": "3.1.6", @@ -9855,9 +9867,9 @@ "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" }, "node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "requires": { "whatwg-url": "^5.0.0" } @@ -10140,6 +10152,13 @@ "@types/long": "^4.0.1", "@types/node": ">=13.7.0", "long": "^4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + } } }, "psl": { @@ -10635,9 +10654,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "type-check": { "version": "0.3.2", diff --git a/software/package.json b/software/package.json index 2137c1f2..9bbbe623 100644 --- a/software/package.json +++ b/software/package.json @@ -1,6 +1,6 @@ { "name": "@peapodtech/peapodos", - "version": "0.0.9", + "version": "0.1.0", "description": "Main software for PeaPod.", "main": "index.js", "directories": { @@ -48,8 +48,9 @@ }, "devDependencies": { "@serialport/parser-readline": "^9.0.1", - "@types/jest": "^27.4.1", + "@types/jest": "^27.5.0", "@types/jsonwebtoken": "^8.5.5", + "@types/node": "^17.0.31", "@types/pi-camera": "^1.5.3", "@types/project-name-generator": "^2.1.0", "@types/serialport": "^8.0.2", diff --git a/software/src/PeaPodArduino.ts b/software/src/PeaPodArduino.ts index c9f4ee5d..64eded95 100644 --- a/software/src/PeaPodArduino.ts +++ b/software/src/PeaPodArduino.ts @@ -3,11 +3,12 @@ import chalk from 'chalk'; import { ReadlineParser, SerialPort } from 'serialport'; import { ArduinoInstructionsError } from './errors'; import Spinner from './ui'; -import { sleep, updateArduino } from './utils'; +import { gpioUnexport, gpioWrite, sleep, updateArduino } from './utils'; const BAUDRATE = 115200; const ARDUINO_REVISION = 0; // const TIMEOUT_SECONDS = 5; +const RESET_PIN = 5; /** * Abstract base type for any PeaPod message source. @@ -74,6 +75,9 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { } start(onMessage : (msg : ArduinoMessage) => void): Promise { return new Promise(async (res, rej) => { + // Reset the Arduino + await this.reset(); + // Open the serial port await new Promise((reso, reje) => { Spinner.start('Establishing serial communications with the Arduino...'); @@ -114,10 +118,10 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { // Handle all message types except: 'info', 'data', 'debug', 'error' switch (msg.type) { case 'revision': - onMessage(msg); if(msg.data == ARDUINO_REVISION) { Spinner.succeed('Arduino software up to date!'); res(); + // Do not break } else { Spinner.fail(`Arduino software out of date! Expected ${ARDUINO_REVISION}.`); // Attempt to update the Arduino, and then restart @@ -156,4 +160,10 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { stop() { this.serial.close(); } + async reset() { + gpioWrite(RESET_PIN, 1); + await sleep(100); + gpioWrite(RESET_PIN, 0); + gpioUnexport(5); + } } \ No newline at end of file diff --git a/software/src/PeaPodPublisher.ts b/software/src/PeaPodPublisher.ts index 066413fb..7b0722b3 100644 --- a/software/src/PeaPodPublisher.ts +++ b/software/src/PeaPodPublisher.ts @@ -142,7 +142,14 @@ export default class PeaPodPubSub implements IPeaPodPublisher { if (topic === `/devices/${this.deviceId}/config`) { onConfig(message.toString()); } else if (topic.startsWith(`/devices/${this.deviceId}/commands`)) { - onCommand(JSON.parse(message.toString()) as PeaPodCommand); + try { + const command = JSON.parse(message.toString()) as PeaPodCommand; + onCommand(command); + } catch (e) { + if (e instanceof SyntaxError) { + // TODO: handle invalid command + } + } } }); diff --git a/software/src/utils.ts b/software/src/utils.ts index 8187243d..77a504ad 100644 --- a/software/src/utils.ts +++ b/software/src/utils.ts @@ -1,5 +1,5 @@ import * as dns from 'dns'; -import { existsSync, mkdirSync, writeFileSync } from 'fs'; +import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; import dotenv from 'dotenv'; import { SerialPort } from 'serialport'; import axios from 'axios'; @@ -30,7 +30,7 @@ export const checkInternet = (timeout : number = 5000, url: string = 'www.google * @param millis Number of milliseconds to sleep. * @returns */ -export const sleep = (millis : number) => { +export const sleep = (millis : number): Promise => { return new Promise(resolve => { setTimeout(resolve, millis); }); @@ -41,7 +41,7 @@ export const sleep = (millis : number) => { * * Throws an error if the file does not exist. */ -export const loadDotenv = (path: string = '.env') => { +export const loadDotenv = (path: string = '.env'): void => { // Check for file if(existsSync(path)){ const config = dotenv.config({path}); @@ -151,4 +151,49 @@ export const execute = (command: string, failureCodes: number[] = []): Promise { + if(!existsSync(`/sys/class/gpio/gpio${pin}/`)) { + execute(`echo ${pin} > /sys/class/gpio/export`); + } +} + +/** + * Unexports a pin (if it has been previously exported) + * @param pin The GPIO pin to unexport. + */ +export const gpioUnexport = (pin: number): void => { + if(existsSync(`/sys/class/gpio/gpio${pin}/`)) { + execute(`echo ${pin} > /sys/class/gpio/unexport`); + } +} + +/** + * Exports and sets the direction (output) of a pin, and writes either 1 or 0 to it. + * Does NOT unexport the pin. + * @param pin The GPIO pin to write to. + * @param value 1 or 0. + */ +export const gpioWrite = (pin: number, value: 0 | 1): void => { + gpioExport(pin); + writeFileSync(`/sys/class/gpio/gpio${pin}/direction`, 'out'); + writeFileSync(`/sys/class/gpio/gpio${pin}/value`, `${value}`); +} + +/** + * Exports and sets the direction (input) of a pin, reads from it, and returns the value. + * Does NOT unexport the pin. + * @param pin The GPIO pin to read from. + * @param value + */ +export const gpioRead = (pin: number): number => { + gpioExport(pin); + writeFileSync(`/sys/class/gpio/gpio${pin}/direction`, 'in'); + const value: number = Number.parseInt(readFileSync(`/sys/class/gpio/gpio${pin}/value`).toString()); + return value; } \ No newline at end of file diff --git a/software/tests/testUtils.test.ts b/software/tests/testUtils.test.ts index c4a9556e..6b377fc3 100644 --- a/software/tests/testUtils.test.ts +++ b/software/tests/testUtils.test.ts @@ -1,6 +1,28 @@ -import { execute } from '../src/utils' +import { execute, gpioExport, gpioUnexport, gpioWrite } from '../src/utils' +import { existsSync, readFileSync } from 'fs'; + +const TEST_PIN = 24; test('code execution works', () => { expect.assertions(1); return expect(execute('echo hello world!')).resolves.toEqual('> echo hello world!\nhello world!\n'); +}); + +test('gpio pins can be exported', () => { + expect.assertions(1); + gpioExport(TEST_PIN); + return expect(existsSync(`/sys/class/gpio/gpio${TEST_PIN}/`)).toBe(true); +}); + +test('gpio pins are unexported', () => { + expect.assertions(1); + gpioUnexport(TEST_PIN); + return expect(existsSync(`/sys/class/gpio/gpio${TEST_PIN}/`)).toBe(false); +}); + +test('gpio pins can be written to', () => { + expect.assertions(1); + gpioExport(TEST_PIN); + gpioWrite(TEST_PIN, 1); + return expect(readFileSync(`/sys/class/gpio/gpio${TEST_PIN}/value`).toString()).toMatch(/^1$/); }); \ No newline at end of file From 33c966434470a9d640eda1941ac02abdd6e1a574 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Tue, 3 May 2022 18:03:33 -0400 Subject: [PATCH 16/85] Restructured Data Batches * De-nested, from {"air-temperature":{batch:[...]},...} to {"air-temperature":[...],...} --- software/index.ts | 6 +++--- software/src/PeaPodPublisher.ts | 22 +++++++++++----------- software/src/PeaPodSimulator.ts | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/software/index.ts b/software/index.ts index 3c3528b1..2de86044 100644 --- a/software/index.ts +++ b/software/index.ts @@ -132,9 +132,9 @@ function main(): Promise { switch (msg.type) { case "data": // Initialize batch array - if (batch[msg.data.label] === undefined) batch[msg.data.label] = {batch: []}; + if (batch[msg.data.label] === undefined) batch[msg.data.label] = []; // Accumulate data into batches - batch[msg.data.label].batch.push({ + batch[msg.data.label].push({ timestamp: Date.now(), value: msg.data.value }); @@ -202,7 +202,7 @@ function main(): Promise { return; } - Spinner.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.batch.length }, 0) } datapoints published.`); + Spinner.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.length }, 0) } datapoints published.`); // Reset batch to empty batch = { }; diff --git a/software/src/PeaPodPublisher.ts b/software/src/PeaPodPublisher.ts index 7b0722b3..2ea77a39 100644 --- a/software/src/PeaPodPublisher.ts +++ b/software/src/PeaPodPublisher.ts @@ -1,25 +1,25 @@ -import * as jwt from 'jsonwebtoken'; import * as fs from 'fs'; +import {v4 as uuid} from 'uuid'; +import * as jwt from 'jsonwebtoken'; import * as mqtt from 'mqtt'; -import Spinner from './ui'; //UI utils +import * as inquirer from 'inquirer'; +import chalk from 'chalk'; + import { getApp } from 'firebase/app'; import { getFunctions, httpsCallable } from 'firebase/functions'; import { getFirestore, doc, setDoc, collection, getDocs, query, where, DocumentReference } from 'firebase/firestore'; import { getAuth } from 'firebase/auth'; + +import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; +import Spinner from './ui'; import { fetchServerCert } from './utils'; -import * as inquirer from 'inquirer'; -import {v4 as uuid} from 'uuid'; -import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; //Firebase Auth via OAuth2 'Device Flow' import { ArduinoInstructions } from './PeaPodArduino'; -import chalk from 'chalk'; export type PeaPodDataBatch = { [key: string]: { - batch: { - timestamp: number, - value: number - }[] - } + timestamp: number, + value: number + }[] } // PeaPod Message to Cloud diff --git a/software/src/PeaPodSimulator.ts b/software/src/PeaPodSimulator.ts index 8dd53487..47f6b5e5 100644 --- a/software/src/PeaPodSimulator.ts +++ b/software/src/PeaPodSimulator.ts @@ -59,14 +59,14 @@ export class PeaPodLogger implements IPeaPodPublisher { switch(msg.type) { case 'data': for (const label of Object.keys(msg.data)) { - for (const datum of msg.data[label].batch) { + for (const datum of msg.data[label]) { console.log(`[${chalk.magenta(msg.type.toUpperCase())}] - [${(new Date(datum.timestamp)).toLocaleTimeString()}] - ${label}: ${datum.value}`); } const dir = `./projects/${msg.metadata.project}/runs/${msg.metadata.run}/${label}/`; if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } - writeFileSync(`${dir}${label+'-'+uuid()+'.json'}`, JSON.stringify(msg.data[label].batch, null, 2)) + writeFileSync(`${dir}${label+'-'+uuid()+'.json'}`, JSON.stringify(msg.data[label], null, 2)) } break; default: From 6a57888cf747b3e2df7a531a58ea94f5c1352805 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Tue, 3 May 2022 22:20:11 -0400 Subject: [PATCH 17/85] Minor Fixes + Added Jest config via gitignore "exclude" - Removed camera --- software/.gitignore | 1 + software/index.ts | 132 ++++++++++++-------------------- software/jest.config.js | 5 ++ software/src/PeaPodArduino.ts | 70 ++++++++++------- software/src/PeaPodCamera.ts | 27 ------- software/src/PeaPodPublisher.ts | 47 ++++++------ 6 files changed, 120 insertions(+), 162 deletions(-) create mode 100644 software/jest.config.js delete mode 100644 software/src/PeaPodCamera.ts diff --git a/software/.gitignore b/software/.gitignore index c420b566..beeee6ae 100644 --- a/software/.gitignore +++ b/software/.gitignore @@ -2,6 +2,7 @@ node_modules/ *.js *.d.ts +!jest.config.js # Runtime files deviceInfo.json diff --git a/software/index.ts b/software/index.ts index 2de86044..fa61c7cf 100644 --- a/software/index.ts +++ b/software/index.ts @@ -1,77 +1,57 @@ #!/usr/bin/env node -import chalk from 'chalk'; //Colored CLI text -import { DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; //Firebase Auth via OAuth2 'Device Flow' +import chalk from 'chalk'; +import { DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; import { initializeApp, getApps } from 'firebase/app'; import { getAuth } from 'firebase/auth'; -// import PiCamera from 'pi-camera'; import PeaPodArduinoInterface, { ArduinoInstructions, IPeaPodArduino } from './src/PeaPodArduino'; import PeaPodPubSub, { IoTConfig, IPeaPodPublisher, PeaPodDataBatch } from './src/PeaPodPublisher'; import { ArduinoSimulator, PeaPodLogger } from './src/PeaPodSimulator'; -// import PeaPodCamera, { IPeaPodCamera } from './src/PeaPodCamera'; - -import { checkInternet, sleep, loadDotenv, findSerialPath } from './src/utils'; //Utilities -import Spinner from './src/ui'; //UI utils - -// Global State +import { loadDotenv, checkInternet, sleep, findSerialPath } from './src/utils'; +import Spinner from './src/ui'; + +loadDotenv(); + +// CONSTANTS +const firebaseConfig = { + apiKey: process.env.FIREBASE_APIKEY, + authDomain: process.env.FIREBASE_AUTHDOMAIN, + databaseURL: process.env.FIREBASE_DATABASEURL, + projectId: process.env.FIREBASE_PROJECTID, + storageBucket: process.env.FIREBASE_STORAGEBUCKET, + messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, + appId: process.env.FIREBASE_APPID, + measurementId: process.env.FIREBASE_MEASUREMENTID +}; + +const iotConfig : IoTConfig = { + cloudregion: process.env.IOT_CLOUDREGION, + projectid: process.env.FIREBASE_PROJECTID, + registryid: process.env.IOT_REGISTRY, + jwtexpiryminutes: 1440, +}; + +const authConfig : DeviceFlowUIOptions = { + Google : { + scopes : process.env.GOOGLE_SCOPES?.split(' '), + clientid : process.env.GOOGLE_CLIENTID, + clientsecret : process.env.GOOGLE_CLIENTSECRET + }, + GitHub : { + scopes : process.env.GITHUB_SCOPES?.split(' '), + clientid : process.env.GITHUB_CLIENTID, + clientsecret : process.env.GITHUB_CLIENTSECRET + } +}; + +// Seconds between data batch publications +const batchPublishInterval = 5; +const simulated = process.argv.includes('simulate'); +const offline = process.argv.includes('offline'); function main(): Promise { - // SETUP - - // Load environment variables - loadDotenv(); - - // CONSTANTS - const firebaseConfig = { - apiKey: process.env.FIREBASE_APIKEY, - authDomain: process.env.FIREBASE_AUTHDOMAIN, - databaseURL: process.env.FIREBASE_DATABASEURL, - projectId: process.env.FIREBASE_PROJECTID, - storageBucket: process.env.FIREBASE_STORAGEBUCKET, - messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, - appId: process.env.FIREBASE_APPID, - measurementId: process.env.FIREBASE_MEASUREMENTID - }; - - const iotConfig : IoTConfig = { - cloudregion: 'us-central1', - projectid: 'cloudponics-bc383', - registryid: 'CloudPonics', - jwtexpiryminutes: 1440, - }; - - const authConfig : DeviceFlowUIOptions = { - Google : { - scopes : process.env.GOOGLE_SCOPES?.split(' '), - clientid : process.env.GOOGLE_CLIENTID, - clientsecret : process.env.GOOGLE_CLIENTSECRET - }, - GitHub : { - scopes : process.env.GITHUB_SCOPES?.split(' '), - clientid : process.env.GITHUB_CLIENTID, - clientsecret : process.env.GITHUB_CLIENTSECRET - } - }; - - // const RTCServers = { - // iceServers: [ - // { - // urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'], - // }, - // ], - // iceCandidatePoolSize: 10, - // }; - - // Seconds between data batch publications - const batchPublishInterval = 5; - - // IN PRODUCTION: fetch serial port with findSerialPath - const simulated = process.argv.includes('simulate'); - const offline = process.argv.includes('offline'); - let arduino : IPeaPodArduino, publisher : IPeaPodPublisher; - // let camera: IPeaPodCamera | null; Spinner.log(`Running in ${ chalk.bold(simulated ? 'Simulated' : 'Live') } mode with ${ chalk.bold(offline ? 'Local Filesystem' : 'Google Cloud') } publishing.`); @@ -90,8 +70,6 @@ function main(): Promise { interval: 1000 } }); - - // camera = null; } else { let serialpath; if (process.env.SERIALPORT) { @@ -155,26 +133,10 @@ function main(): Promise { // TODO: Handle this }, command => { Spinner.log("[COMMAND] - "+command); - // TODO: Respond to commands (immediate actions) - if (command.type == 'instructions') { - arduino.write(command.data); - } else if (command.type == 'livestreamoffer') { - - // Assumes webcam hardware has been initialized - - // 1. Create answer from command data - // 2. Call cloud function with answer - // 3. Enable video feed - - // camera?.stream().pipe(); - - /** - * { - * type: 'livestreamoffer', - * data: {...} - * } - */ - + switch (command.type) { + case 'instructions': + arduino.write(command.data); + break; } }).then(({projectid, projectname, run}) => { // Todo: get instructions diff --git a/software/jest.config.js b/software/jest.config.js new file mode 100644 index 00000000..8cbf8940 --- /dev/null +++ b/software/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; \ No newline at end of file diff --git a/software/src/PeaPodArduino.ts b/software/src/PeaPodArduino.ts index 64eded95..d8a3275c 100644 --- a/software/src/PeaPodArduino.ts +++ b/software/src/PeaPodArduino.ts @@ -1,15 +1,17 @@ -// import { PeaPodMessage } from './PeaPodPublisher'; import chalk from 'chalk'; import { ReadlineParser, SerialPort } from 'serialport'; -import { ArduinoInstructionsError } from './errors'; +import { ArduinoInstructionsError, SerialTimeoutError } from './errors'; import Spinner from './ui'; import { gpioUnexport, gpioWrite, sleep, updateArduino } from './utils'; +// CONSTANTS const BAUDRATE = 115200; const ARDUINO_REVISION = 0; -// const TIMEOUT_SECONDS = 5; +const TIMEOUT_SECONDS = 10; const RESET_PIN = 5; +// TYPES + /** * Abstract base type for any PeaPod message source. */ @@ -53,13 +55,19 @@ export type ArduinoInstructions = { [key: string]: number; } +// MAIN CLASS + /** * Interface between this computer and the Arduino. */ export default class PeaPodArduinoInterface implements IPeaPodArduino { + serial : SerialPort; parser : ReadlineParser; + timeout?: NodeJS.Timeout; + constructor(readonly serialpath: string) { + // Create the serial port interface this.serial = new SerialPort({ path: serialpath, @@ -73,6 +81,7 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { includeDelimiter: false })); } + start(onMessage : (msg : ArduinoMessage) => void): Promise { return new Promise(async (res, rej) => { // Reset the Arduino @@ -81,7 +90,7 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { // Open the serial port await new Promise((reso, reje) => { Spinner.start('Establishing serial communications with the Arduino...'); - this.serial.open(err => { + this.serial.open(err => { if (err) { reje(err); } else { @@ -92,22 +101,12 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { }); Spinner.start('Awaiting Arduino software revision number...'); - - // Reusable timeout, enabled at serial open (fails if no messages recieved after starting) - // let timeout: NodeJS.Timeout; - // const resetTimeout = (timeoutSeconds: number = TIMEOUT_SECONDS) => { - // clearTimeout(timeout); - // timeout = setTimeout(() => { - // this.stop(); - // throw new SerialTimeoutError(timeoutSeconds) - // }, timeoutSeconds*1000); - // } - // Initial timeout - // resetTimeout(); + // this.resetTimeout(); // Set up the listener this.parser.on('data', msgtxt => { - // Attempt to parse the raw text as a valid JSON object' + + // Attempt to parse the raw text as a valid JSON object var msg : ArduinoMessage; try { msg = JSON.parse(msgtxt) as ArduinoMessage; @@ -115,34 +114,46 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { rej(err); return; } - // Handle all message types except: 'info', 'data', 'debug', 'error' + + // "Internal" (relevant to arduino only) message handling switch (msg.type) { case 'revision': if(msg.data == ARDUINO_REVISION) { Spinner.succeed('Arduino software up to date!'); - res(); - // Do not break + res(); //Successful start sequence } else { Spinner.fail(`Arduino software out of date! Expected ${ARDUINO_REVISION}.`); // Attempt to update the Arduino, and then restart this.stop(); - // clearTimeout(timeout); + // this.clearTimeout(); this.update().finally(() => { process.exit(); }); - // Do not reset timeout - return; + return; //Do not settle promise } - break; + // Do not break default: onMessage(msg); break; } - // resetTimeout(); + // this.resetTimeout(); }); }); } - async update() { + + private clearTimeout = () => { + if(this.timeout) clearTimeout(this.timeout); + } + + private resetTimeout = (timeoutSeconds: number = TIMEOUT_SECONDS) => { + this.clearTimeout(); + this.timeout = setTimeout(() => { + this.stop(); + throw new SerialTimeoutError(timeoutSeconds); + }, timeoutSeconds*1000); + } + + private async update() { Spinner.start('Compiling latest Arduino software and flashing to board...'); await updateArduino().then(() => { Spinner.succeed('Updated Arduino software successfully! Rebooting in 3 seconds...'); @@ -151,16 +162,21 @@ export default class PeaPodArduinoInterface implements IPeaPodArduino { }) await sleep(3000); } + write(msg : ArduinoInstructions) { Spinner.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); this.serial.write(JSON.stringify(msg)+'\n', undefined, (err)=>{ if(err) throw new ArduinoInstructionsError(JSON.stringify(msg)); }); } + stop() { + this.clearTimeout(); this.serial.close(); + this.reset(); } - async reset() { + + private async reset() { gpioWrite(RESET_PIN, 1); await sleep(100); gpioWrite(RESET_PIN, 0); diff --git a/software/src/PeaPodCamera.ts b/software/src/PeaPodCamera.ts deleted file mode 100644 index 18116505..00000000 --- a/software/src/PeaPodCamera.ts +++ /dev/null @@ -1,27 +0,0 @@ -import child from 'child_process'; -import { Readable } from 'node:stream' - -export type IPeaPodCamera = { - stream(options?: PeaPodCameraOptions): Readable; -} - -export type PeaPodCameraOptions = { - height: number; - width: number; -} - -export default class PeaPodCamera { - stream(options: PeaPodCameraOptions = {height: 480, width: 852}){ - return child.spawn('raspivid', [ - '--nopreview', - '--width', - `${options.width}`, - '--height', - `${options.height}`, - '-o', - '-' - ], { - stdio: ['ignore', 'pipe', 'inherit'] - }).stdout; - } -} \ No newline at end of file diff --git a/software/src/PeaPodPublisher.ts b/software/src/PeaPodPublisher.ts index 2ea77a39..0cc8eeb3 100644 --- a/software/src/PeaPodPublisher.ts +++ b/software/src/PeaPodPublisher.ts @@ -15,6 +15,8 @@ import Spinner from './ui'; import { fetchServerCert } from './utils'; import { ArduinoInstructions } from './PeaPodArduino'; +// TYPES + export type PeaPodDataBatch = { [key: string]: { timestamp: number, @@ -39,10 +41,6 @@ export type PeaPodMessage = { export type PeaPodCommand = { type: 'instructions', data: ArduinoInstructions -} | { - type: 'livestreamoffer', - data: any - // TODO: import types from WebRTC } /** @@ -62,12 +60,14 @@ type RegisterResponse = { export type IoTConfig = { deviceid?: string, - projectid: string, - cloudregion: string, - registryid: string, + projectid?: string, + cloudregion?: string, + registryid?: string, jwtexpiryminutes: number } +// MAIN CLASS + export default class PeaPodPubSub implements IPeaPodPublisher { private tokenRefreshInterval?: NodeJS.Timer; private mqttclient?: mqtt.MqttClient; @@ -94,6 +94,7 @@ export default class PeaPodPubSub implements IPeaPodPublisher { Spinner.info('Welcome!'); } + // Keypair management if(fs.existsSync('./rsa_private.pem') && fs.existsSync('./deviceInfo.json')){ Spinner.succeed('Private key and device info found!'); privatekey = fs.readFileSync('./rsa_private.pem').toString(); @@ -196,22 +197,22 @@ export default class PeaPodPubSub implements IPeaPodPublisher { * Select a run owned by the current user under a given project. * @returns {Promise} */ - async selectRun(project : DocumentReference){ - const myRuns = query(collection(getFirestore(), project.path+'/runs'), where('owner', '==', getAuth().currentUser?.uid)); - const runs = ((await getDocs(myRuns)).docs.map(doc=>({id: doc.id,ref: doc.ref}))); - if(runs.length == 0){ - throw new Error("No runs found! Create one first."); - } - const ref = (await inquirer.prompt<{ref: DocumentReference}>([ - { - type: 'list', - name: 'ref', - message: 'Select a run:', - choices: runs.map(run=>({name: run.id, value: run.ref})) - } - ])).ref; - return ref; - } + // private async selectRun(project : DocumentReference){ + // const myRuns = query(collection(getFirestore(), project.path+'/runs'), where('owner', '==', getAuth().currentUser?.uid)); + // const runs = ((await getDocs(myRuns)).docs.map(doc=>({id: doc.id,ref: doc.ref}))); + // if(runs.length == 0){ + // throw new Error("No runs found! Create one first."); + // } + // const ref = (await inquirer.prompt<{ref: DocumentReference}>([ + // { + // type: 'list', + // name: 'ref', + // message: 'Select a run:', + // choices: runs.map(run=>({name: run.id, value: run.ref})) + // } + // ])).ref; + // return ref; + // } /** * Sign a new JWT. From d8ea56ae0c4c9e4cbf8d4eba53f5e2d3987d78ff Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Wed, 11 May 2022 01:40:13 -0400 Subject: [PATCH 18/85] Overhaul * Simulated interfaces accessible only via unit testing + Util methods for compound .env checking, returning and loading to objects * Moved "simulated" interfaces to be in the same file as the classes they simulate + PeaPod class + More+better variable description comments * Refactored "arduino" to "controller" - Removed instances of "PeaPod" in type names (redundant) - Removed "findSerialPath", useless (should ALWAYS rely on .env) * Controller serial timeout now forces chip reset rather than fail * Moved away from "then", instead using await (better for exception handling) * Followed Google TS style guide :) --- software/index.ts | 204 +++------------ software/src/PeaPodArduino.ts | 185 -------------- software/src/PeaPodPublisher.ts | 284 --------------------- software/src/PeaPodSimulator.ts | 77 ------ software/src/controller.ts | 254 +++++++++++++++++++ software/src/env.ts | 134 ++++++++++ software/src/errors.ts | 28 ++- software/src/peapod.ts | 138 ++++++++++ software/src/publisher.ts | 360 +++++++++++++++++++++++++++ software/src/ui.ts | 194 +++++++-------- software/src/utils.ts | 166 ++++++------ software/tests/testArduino.test.ts | 6 +- software/tests/testPublisher.test.ts | 14 ++ software/tests/utils.ts | 2 +- 14 files changed, 1129 insertions(+), 917 deletions(-) delete mode 100644 software/src/PeaPodArduino.ts delete mode 100644 software/src/PeaPodPublisher.ts delete mode 100644 software/src/PeaPodSimulator.ts create mode 100644 software/src/controller.ts create mode 100644 software/src/env.ts create mode 100644 software/src/peapod.ts create mode 100644 software/src/publisher.ts create mode 100644 software/tests/testPublisher.test.ts diff --git a/software/index.ts b/software/index.ts index fa61c7cf..17fec257 100644 --- a/software/index.ts +++ b/software/index.ts @@ -1,180 +1,46 @@ #!/usr/bin/env node import chalk from 'chalk'; -import { DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; -import { initializeApp, getApps } from 'firebase/app'; -import { getAuth } from 'firebase/auth'; - -import PeaPodArduinoInterface, { ArduinoInstructions, IPeaPodArduino } from './src/PeaPodArduino'; -import PeaPodPubSub, { IoTConfig, IPeaPodPublisher, PeaPodDataBatch } from './src/PeaPodPublisher'; -import { ArduinoSimulator, PeaPodLogger } from './src/PeaPodSimulator'; -import { loadDotenv, checkInternet, sleep, findSerialPath } from './src/utils'; -import Spinner from './src/ui'; - -loadDotenv(); - -// CONSTANTS -const firebaseConfig = { - apiKey: process.env.FIREBASE_APIKEY, - authDomain: process.env.FIREBASE_AUTHDOMAIN, - databaseURL: process.env.FIREBASE_DATABASEURL, - projectId: process.env.FIREBASE_PROJECTID, - storageBucket: process.env.FIREBASE_STORAGEBUCKET, - messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, - appId: process.env.FIREBASE_APPID, - measurementId: process.env.FIREBASE_MEASUREMENTID -}; - -const iotConfig : IoTConfig = { - cloudregion: process.env.IOT_CLOUDREGION, - projectid: process.env.FIREBASE_PROJECTID, - registryid: process.env.IOT_REGISTRY, - jwtexpiryminutes: 1440, -}; - -const authConfig : DeviceFlowUIOptions = { - Google : { - scopes : process.env.GOOGLE_SCOPES?.split(' '), - clientid : process.env.GOOGLE_CLIENTID, - clientsecret : process.env.GOOGLE_CLIENTSECRET - }, - GitHub : { - scopes : process.env.GITHUB_SCOPES?.split(' '), - clientid : process.env.GITHUB_CLIENTID, - clientsecret : process.env.GITHUB_CLIENTSECRET +import * as inquirer from 'inquirer'; + +import { checkInternet } from './src/utils'; +import * as Spinner from './src/ui'; +import { PublishingMode } from './src/publisher'; +import PeaPod from './src/peapod'; + +// MAIN + +async function main(): Promise { + Spinner.start(`Checking for ${ chalk.blue('Internet') } connection...'`); + const internet = await checkInternet(); + if (internet) { + Spinner.succeed(`Connected to the ${ chalk.blue('Internet') }!`); + } else { + Spinner.fail(`Failed to connect to the ${ chalk.blue('Internet') }.`); } -}; - -// Seconds between data batch publications -const batchPublishInterval = 5; -const simulated = process.argv.includes('simulate'); -const offline = process.argv.includes('offline'); -function main(): Promise { - - let arduino : IPeaPodArduino, publisher : IPeaPodPublisher; - - Spinner.log(`Running in ${ chalk.bold(simulated ? 'Simulated' : 'Live') } mode with ${ chalk.bold(offline ? 'Local Filesystem' : 'Google Cloud') } publishing.`); - - return new Promise(async (res, rej) => { - - if (simulated) { - arduino = new ArduinoSimulator({ - air_temperature: { - min: 10, - max: 20, - interval: 2000 - }, - water_level: { - min: 0, - max: 1, - interval: 1000 - } - }); - } else { - let serialpath; - if (process.env.SERIALPORT) { - Spinner.log('Using serial port: '+process.env.SERIALPORT); - serialpath = process.env.SERIALPORT; - } else { - Spinner.start('Finding Arduino serial port...'); - serialpath = await findSerialPath(); - Spinner.succeed('Arduino serial port found: '+serialpath); - } - - arduino = new PeaPodArduinoInterface(serialpath); - // camera = new PeaPodCamera; - } - if (offline) { - publisher = new PeaPodLogger(); - } else { - // Check Internet connection - Spinner.start(`Checking for ${ chalk.blue('Internet') } connection...'`); - if (!(await checkInternet())) { - Spinner.fail(`Could not connect to the ${ chalk.blue('Internet') }! Running in ${ chalk.bold('Offline') } mode.`); - publisher = new PeaPodLogger(); - } else { - Spinner.succeed(`Connected to the ${ chalk.blue('Internet') }!`); - - // Connect to Firebase - initializeApp(firebaseConfig); - - publisher = new PeaPodPubSub(iotConfig, authConfig); - } - await sleep(1500); + // Get desired publishing mode + let publishingmode = (await inquirer.prompt<{pm: PublishingMode}>([ + { + type: 'list', + name: 'pm', + message: 'Select publishing mode:', + choices: Object.entries(PublishingMode).map(pm => { + return { + // Object.entries casts `PublishingMode` values to `string`. This recasts them to PublishingMode enum values (TypeScript magic) + value: PublishingMode[pm[0] as keyof typeof PublishingMode], + name: pm[1] + }; + }) } - - let batch: PeaPodDataBatch = { }, batchInterval: NodeJS.Timer, instructions: ArduinoInstructions = {}; - - // Initialize Arduino communications interface first - arduino.start((msg) => { - switch (msg.type) { - case "data": - // Initialize batch array - if (batch[msg.data.label] === undefined) batch[msg.data.label] = []; - // Accumulate data into batches - batch[msg.data.label].push({ - timestamp: Date.now(), - value: msg.data.value - }); - break; - case "revision": - arduino.write(instructions); - break; - default: - Spinner.log(`[${ chalk.blueBright('ARDUINO') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) - } - // TODO: publish other message types - }).then(() => { - // Initialize publisher - publisher.start(config => { - // Hot-swap programs - Spinner.log("[CONFIG] - "+config); - - // TODO: Handle this - }, command => { - Spinner.log("[COMMAND] - "+command); - switch (command.type) { - case 'instructions': - arduino.write(command.data); - break; - } - }).then(({projectid, projectname, run}) => { - // Todo: get instructions - - // START - Spinner.log(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); + ])).pm; - // Reset - batch = { }; + let peapod: PeaPod = new PeaPod(publishingmode); - batchInterval = setInterval(() => { - // Publish entire batch - try { - publisher.publish({ - type: 'data', - metadata: { - owner: (getApps().length ? (getAuth().currentUser?.uid ?? 'user') : 'user'), - project: projectid, - run - }, - data: batch - }); - } catch { - Spinner.fail('Batch publish failed, will retry...'); - return; - } - - Spinner.log(`[${chalk.magenta('PUBLISH')}] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.length }, 0) } datapoints published.`); - - // Reset batch to empty - batch = { }; - }, batchPublishInterval*1000); - }).catch(e => { rej(e) }); - }).catch(e => { rej(e) }); - }); + peapod.start(); } -main().catch((err: Error) => { - Spinner.fail(err.message); +main().catch((err)=>{ + // Last resort catch + Spinner.fail(JSON.stringify(err)); process.exit(1); }); \ No newline at end of file diff --git a/software/src/PeaPodArduino.ts b/software/src/PeaPodArduino.ts deleted file mode 100644 index d8a3275c..00000000 --- a/software/src/PeaPodArduino.ts +++ /dev/null @@ -1,185 +0,0 @@ -import chalk from 'chalk'; -import { ReadlineParser, SerialPort } from 'serialport'; -import { ArduinoInstructionsError, SerialTimeoutError } from './errors'; -import Spinner from './ui'; -import { gpioUnexport, gpioWrite, sleep, updateArduino } from './utils'; - -// CONSTANTS -const BAUDRATE = 115200; -const ARDUINO_REVISION = 0; -const TIMEOUT_SECONDS = 10; -const RESET_PIN = 5; - -// TYPES - -/** - * Abstract base type for any PeaPod message source. - */ -export type IPeaPodArduino = { - /** - * Establish communications with the Arduino. - * @param onMessage Pipe recieved messages. - */ - start(onMessage : (msg : ArduinoMessage) => void): Promise - - /** - * Write new instructions to the Arduino. - * @param instructions - */ - write(instructions: ArduinoInstructions): void; - - /** - * Halt communications with the Arduino. - */ - stop(): void; -}; - -/** - * Expanded message type to include all types of messages from the Arduino - */ -export type ArduinoMessage = { - type: 'info' | 'debug' | 'error', - data: string -} | { - type: 'data', - data: { - label: string, - value: number - } -} | { - type: 'revision', - data: number -} - -export type ArduinoInstructions = { - [key: string]: number; -} - -// MAIN CLASS - -/** - * Interface between this computer and the Arduino. - */ -export default class PeaPodArduinoInterface implements IPeaPodArduino { - - serial : SerialPort; - parser : ReadlineParser; - timeout?: NodeJS.Timeout; - - constructor(readonly serialpath: string) { - - // Create the serial port interface - this.serial = new SerialPort({ - path: serialpath, - baudRate: BAUDRATE, - autoOpen: false - }); - - // Create the newline parser - this.parser = this.serial.pipe(new ReadlineParser({ - delimiter: '\n', - includeDelimiter: false - })); - } - - start(onMessage : (msg : ArduinoMessage) => void): Promise { - return new Promise(async (res, rej) => { - // Reset the Arduino - await this.reset(); - - // Open the serial port - await new Promise((reso, reje) => { - Spinner.start('Establishing serial communications with the Arduino...'); - this.serial.open(err => { - if (err) { - reje(err); - } else { - Spinner.succeed('Arduino serial communications established!'); - reso(); - } - }); - }); - - Spinner.start('Awaiting Arduino software revision number...'); - // this.resetTimeout(); - - // Set up the listener - this.parser.on('data', msgtxt => { - - // Attempt to parse the raw text as a valid JSON object - var msg : ArduinoMessage; - try { - msg = JSON.parse(msgtxt) as ArduinoMessage; - } catch (err) { - rej(err); - return; - } - - // "Internal" (relevant to arduino only) message handling - switch (msg.type) { - case 'revision': - if(msg.data == ARDUINO_REVISION) { - Spinner.succeed('Arduino software up to date!'); - res(); //Successful start sequence - } else { - Spinner.fail(`Arduino software out of date! Expected ${ARDUINO_REVISION}.`); - // Attempt to update the Arduino, and then restart - this.stop(); - // this.clearTimeout(); - this.update().finally(() => { - process.exit(); - }); - return; //Do not settle promise - } - // Do not break - default: - onMessage(msg); - break; - } - // this.resetTimeout(); - }); - }); - } - - private clearTimeout = () => { - if(this.timeout) clearTimeout(this.timeout); - } - - private resetTimeout = (timeoutSeconds: number = TIMEOUT_SECONDS) => { - this.clearTimeout(); - this.timeout = setTimeout(() => { - this.stop(); - throw new SerialTimeoutError(timeoutSeconds); - }, timeoutSeconds*1000); - } - - private async update() { - Spinner.start('Compiling latest Arduino software and flashing to board...'); - await updateArduino().then(() => { - Spinner.succeed('Updated Arduino software successfully! Rebooting in 3 seconds...'); - }).catch(e => { - Spinner.fail(e); - }) - await sleep(3000); - } - - write(msg : ArduinoInstructions) { - Spinner.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); - this.serial.write(JSON.stringify(msg)+'\n', undefined, (err)=>{ - if(err) throw new ArduinoInstructionsError(JSON.stringify(msg)); - }); - } - - stop() { - this.clearTimeout(); - this.serial.close(); - this.reset(); - } - - private async reset() { - gpioWrite(RESET_PIN, 1); - await sleep(100); - gpioWrite(RESET_PIN, 0); - gpioUnexport(5); - } -} \ No newline at end of file diff --git a/software/src/PeaPodPublisher.ts b/software/src/PeaPodPublisher.ts deleted file mode 100644 index 0cc8eeb3..00000000 --- a/software/src/PeaPodPublisher.ts +++ /dev/null @@ -1,284 +0,0 @@ -import * as fs from 'fs'; -import {v4 as uuid} from 'uuid'; -import * as jwt from 'jsonwebtoken'; -import * as mqtt from 'mqtt'; -import * as inquirer from 'inquirer'; -import chalk from 'chalk'; - -import { getApp } from 'firebase/app'; -import { getFunctions, httpsCallable } from 'firebase/functions'; -import { getFirestore, doc, setDoc, collection, getDocs, query, where, DocumentReference } from 'firebase/firestore'; -import { getAuth } from 'firebase/auth'; - -import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; -import Spinner from './ui'; -import { fetchServerCert } from './utils'; -import { ArduinoInstructions } from './PeaPodArduino'; - -// TYPES - -export type PeaPodDataBatch = { - [key: string]: { - timestamp: number, - value: number - }[] -} - -// PeaPod Message to Cloud -export type PeaPodMessage = { - type: 'info' | 'debug' | 'error', - data: any -} | { - type: 'data', - metadata: { - owner: string, - project: string, - run: string - } - data: PeaPodDataBatch -} - -export type PeaPodCommand = { - type: 'instructions', - data: ArduinoInstructions -} - -/** -* Abstract base class for any PeaPod message destination. -*/ -export type IPeaPodPublisher = { - start(onConfig?: (message: string)=>void, onCommand?: (message: PeaPodCommand)=>void) : Promise<{projectid: string, projectname?: string, run: string}>, - stop(): void; - publish(msg : PeaPodMessage) : void -} - -type RegisterResponse = { - id: string, - name: string, - privateKey: string -} - -export type IoTConfig = { - deviceid?: string, - projectid?: string, - cloudregion?: string, - registryid?: string, - jwtexpiryminutes: number -} - -// MAIN CLASS - -export default class PeaPodPubSub implements IPeaPodPublisher { - private tokenRefreshInterval?: NodeJS.Timer; - private mqttclient?: mqtt.MqttClient; - private deviceId: string = ''; - constructor(readonly iotConfig: IoTConfig, readonly authConfig: DeviceFlowUIOptions){} - publish(msg: PeaPodMessage): void { - if(!this.mqttclient || !this.mqttclient.connected){ - throw new Error('MQTT client not connected!'); - } - // Build topic path - const topic = `/devices/${this.deviceId}/` + (msg.type == 'data' ? 'events/data' : msg.type == 'info' ? 'events' : 'state'); - // Strip type from published object - this.mqttclient.publish(topic, JSON.stringify({...msg, type: undefined}), {qos: 1}); - } - async start(onConfig: (message: string)=>void, onCommand: (message: PeaPodCommand)=>void) { - let privatekey = ''; - - // Authenticate the user with Firebase - const auth = new DeviceFlowUI(getApp(), this.authConfig); - const user = await auth.signIn(); - if(user.displayName){ - Spinner.info(`Welcome, ${chalk.bold(user.displayName)}!`); - } else { - Spinner.info('Welcome!'); - } - - // Keypair management - if(fs.existsSync('./rsa_private.pem') && fs.existsSync('./deviceInfo.json')){ - Spinner.succeed('Private key and device info found!'); - privatekey = fs.readFileSync('./rsa_private.pem').toString(); - let deviceinfo = JSON.parse(fs.readFileSync('./deviceInfo.json').toString()); - this.deviceId = deviceinfo['id']; - if(deviceinfo['owner'] != user.uid){ - throw new Error('This PeaPod is not owned by this user!'); - } - } else { - Spinner.info('Private key and/or device info not found!'); - Spinner.start('Registering device...'); - const registerDevice = httpsCallable(getFunctions(), 'registerDevice'); - let result = (await registerDevice()).data; - Spinner.succeed('Device '+(result.id) + ' registered!'); - - fs.writeFileSync('./rsa_private.pem', result.privateKey); - fs.writeFileSync('./deviceInfo.json', JSON.stringify({name: result.name, id: result.id, owner: getAuth().currentUser?.uid}, null, 2)); - privatekey = result.privateKey; - this.deviceId = result.id; - } - - Spinner.start('Fetching Google root CA certificates...'); - const servercert = await fetchServerCert(); - Spinner.succeed('Certificates fetched!'); - - const [project, projectname] = await this.selectProject(); - const run = await this.createRun(project); - - Spinner.start('Connecting to MQTT broker...'); - await this.connect(servercert, this.refreshToken(privatekey)); - Spinner.succeed('Device connected!'); - - // Token Refresh - this.tokenRefreshInterval = setInterval(async ()=>{ - Spinner.start('Refreshing token...'); - await this.connect(servercert, this.refreshToken(privatekey)); - Spinner.succeed('Token refreshed. Reconnected.'); - }, this.iotConfig.jwtexpiryminutes*60*1000); - - // Message listeners - this.mqttclient?.subscribe(`/devices/${this.deviceId}/config`, {qos: 1}); - this.mqttclient?.subscribe(`/devices/${this.deviceId}/commands/#`, {qos: 0}); - - this.mqttclient?.on('error', err => { throw err }); - this.mqttclient?.on('message', (topic, message) => { - if (topic === `/devices/${this.deviceId}/config`) { - onConfig(message.toString()); - } else if (topic.startsWith(`/devices/${this.deviceId}/commands`)) { - try { - const command = JSON.parse(message.toString()) as PeaPodCommand; - onCommand(command); - } catch (e) { - if (e instanceof SyntaxError) { - // TODO: handle invalid command - } - } - } - }); - - return {projectid: project.id, projectname, run: run.id} - } - - /** - * Select a project owned by the current user - * @returns {Promise<[DocumentReference, string]>} - */ - private async selectProject(): Promise<[DocumentReference, string]> { - const myProjects = query(collection(getFirestore(), 'projects'), where('owners', 'array-contains', getAuth().currentUser?.uid)); - const projects = (await getDocs(myProjects)).docs; - if(projects.length < 1){ - throw new Error("No projects found! Create one first."); - } - const ref = (await inquirer.prompt<{ref: [DocumentReference, string]}>([ - { - type: 'list', - name: 'ref', - message: 'Select a project:', - choices: projects.map(project=>({name: project.get('name')+' - '+project.id, value: [project.ref, project.get('name')]})) - } - ])).ref; - return ref; - } - - /** - * Publish a new project. - * @returns {Promise} The project. - */ - private async createRun(project : DocumentReference) : Promise { - const runid = project.id+'-'+uuid(); - const rundoc = doc(getFirestore(), project.path+'/runs/'+runid); - setDoc(rundoc, { - owner: getAuth().currentUser?.uid, - deviceId: this.deviceId, - }); - // console.log(`New run ${runid} on project ${project.id} generated successfully.`); - return rundoc; - } - - /** - * Select a run owned by the current user under a given project. - * @returns {Promise} - */ - // private async selectRun(project : DocumentReference){ - // const myRuns = query(collection(getFirestore(), project.path+'/runs'), where('owner', '==', getAuth().currentUser?.uid)); - // const runs = ((await getDocs(myRuns)).docs.map(doc=>({id: doc.id,ref: doc.ref}))); - // if(runs.length == 0){ - // throw new Error("No runs found! Create one first."); - // } - // const ref = (await inquirer.prompt<{ref: DocumentReference}>([ - // { - // type: 'list', - // name: 'ref', - // message: 'Select a run:', - // choices: runs.map(run=>({name: run.id, value: run.ref})) - // } - // ])).ref; - // return ref; - // } - - /** - * Sign a new JWT. - * @returns JSON Web Token string payload. - */ - private refreshToken(privatekey: string) : string { - const now = Date.now() / 1000; - const token = { - iat: now, - exp: now + this.iotConfig.jwtexpiryminutes * 60, - aud: this.iotConfig.projectid, - }; - return jwt.sign(token, privatekey, {algorithm: 'RS256'}); - } - - /** - * Connect to the MQTT broker. - * @param servercert Root CA certificate. - * @param deviceid ID of this device. - * @returns The MQTT client. - */ - private async connect(servercert: string, password: string): Promise { - // Disconnect existing client - this.disconnect(); - - let client = mqtt.connect({ - host: 'mqtt.googleapis.com', - port: 8883, - clientId: `projects/${this.iotConfig.projectid}/locations/${this.iotConfig.cloudregion}/registries/${this.iotConfig.registryid}/devices/${this.deviceId}`, - username: 'unused', - password: password, - protocol: 'mqtts', - secureProtocol: 'TLSv1_2_method', - ca: [servercert], - }); - - return new Promise((res)=>{ - client.on('connect', packet => { - if (!packet) { - throw new Error('Could not connect to MQTT broker!'); - } - this.mqttclient = client; - res(); - }); - }); - } - - /** - * If the MQTT client is connected, disconnect it. - */ - private async disconnect(): Promise{ - if(this.mqttclient && this.mqttclient.connected){ - await new Promise(res=>{ - this.mqttclient?.end(true, undefined, (err)=>{ - if(err) { - throw err; - } else { - res(); - } - }); - }); - } - } - - stop() { - if(this.tokenRefreshInterval) clearInterval(this.tokenRefreshInterval); - if(this.mqttclient) this.disconnect(); - } -} \ No newline at end of file diff --git a/software/src/PeaPodSimulator.ts b/software/src/PeaPodSimulator.ts deleted file mode 100644 index 47f6b5e5..00000000 --- a/software/src/PeaPodSimulator.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { IPeaPodArduino, ArduinoMessage } from './PeaPodArduino'; -import { IPeaPodPublisher, PeaPodMessage } from './PeaPodPublisher'; -import { stringsToTuple } from './utils'; -import chalk from 'chalk'; -import { v4 as uuid} from 'uuid'; -import { existsSync, mkdirSync, writeFileSync } from 'fs'; -import Spinner from './ui'; - -// Convert our set of strings to a union type. TypeScript nonsense. -const DataLabels = stringsToTuple('air_temperature', 'water_level'); -type TDataLabels = typeof DataLabels[number]; -type SimulatorParameters = { - [key in TDataLabels]: { - min: number, - max: number, - interval: number - } -} - -function generateData(label: TDataLabels, min : number, max : number) : ArduinoMessage { - return { - type: 'data', - data: { - label: label.replace('_','-'), - value: Math.random()*(max-min)+min - } - }; -} - -/** -* A simulated Arduino for generating random data. -*/ -export class ArduinoSimulator implements IPeaPodArduino { - intervals : NodeJS.Timeout[] = []; - constructor(public parameters : SimulatorParameters) { } - async stop(): Promise { - for (const interval of this.intervals) { - clearInterval(interval); - } - } - async start(onMessage: (msg: ArduinoMessage) => any): Promise { - for(const label in this.parameters) { - this.intervals.push(setInterval(() => { - onMessage(generateData(label as TDataLabels, this.parameters[label as TDataLabels].min, this.parameters[label as TDataLabels].max)); - }, this.parameters[label as TDataLabels].interval)); - } - } - write() {} -} - -export class PeaPodLogger implements IPeaPodPublisher { - async start() { - let config = { projectid: 'testproject', run: 'testrun-'+uuid() }; - Spinner.info(`Logging data to ${chalk.bold('projects/'+config.projectid+'/runs/'+config.run+'/')}`); - return config; - } - stop() { }; - publish(msg: PeaPodMessage): void { - switch(msg.type) { - case 'data': - for (const label of Object.keys(msg.data)) { - for (const datum of msg.data[label]) { - console.log(`[${chalk.magenta(msg.type.toUpperCase())}] - [${(new Date(datum.timestamp)).toLocaleTimeString()}] - ${label}: ${datum.value}`); - } - const dir = `./projects/${msg.metadata.project}/runs/${msg.metadata.run}/${label}/`; - if (!existsSync(dir)) { - mkdirSync(dir, { recursive: true }); - } - writeFileSync(`${dir}${label+'-'+uuid()+'.json'}`, JSON.stringify(msg.data[label], null, 2)) - } - break; - default: - console.log(`[${chalk.yellow(msg.type.toUpperCase())}] - ${JSON.stringify(msg.data)}`); - break; - } - } -} \ No newline at end of file diff --git a/software/src/controller.ts b/software/src/controller.ts new file mode 100644 index 00000000..bac37d1f --- /dev/null +++ b/software/src/controller.ts @@ -0,0 +1,254 @@ +import chalk from 'chalk'; +import { ReadlineParser, SerialPort } from 'serialport'; +import { ControllerInstructionsError } from './errors'; +import * as Spinner from './ui'; +import { gpioUnexport, gpioWrite, sleep, updateMicrocontroller } from './utils'; + +// CONSTANTS + +/** + * Baud rate for serial communication. Must match that found in microcontroller code. + */ +const BAUDRATE = 115200; + +/** + * Microcontroller software revision matching this software. + */ +const CONTROLLER_REVISION = 0; + +/** + * Seconds to wait between messages before timing out. + */ +const SERIAL_TIMEOUT_SECONDS = 10; + +/** + * Linux `sysfs` GPIO pin attached to the active-low reset pin on the microcontroller. + */ +const RESET_PIN = 5; + +// TYPES + +/** + * Base type for any controller. + */ +export type Controller = { + /** + * Establish communications with the Controller. + * @param onMessage Pipe all received messages. + * @throws If received message is invalid (JSON parsing fails). + */ + start(onMessage: (msg : ControllerMessage) => void): Promise, + + /** + * Write instructions to the Controller. + * @param instructions Instruction set. + */ + write(instructions: ControllerInstructions): void, + + /** + * Halt communications with the Controller. + */ + stop(): void, +}; + +/** + * Messages FROM the controller + */ +export type ControllerMessage = { + type: 'info' | 'debug' | 'error', + data: string, +} | { + type: 'data', + data: { + label: string, + value: number, + }, +} | { + type: 'revision', + data: number, +}; + +/** + * Messages TO the controller + */ +export type ControllerInstructions = { + [key: string]: number, +}; + +/** + * Simulated controller parameters + */ +export type SimulatorConfig = { + [key: string]: { + min: number, + max: number, + interval: number, + }, +}; + +// CLASSES + +/** + * Interface between this computer and the microcontroller. + */ +export default class MicroController implements Controller { + + serial: SerialPort; + parser: ReadlineParser; + timeout?: NodeJS.Timeout; + + constructor(readonly serialport: string) { + + // Create the serial port interface + this.serial = new SerialPort({ + path: serialport, + baudRate: BAUDRATE, + autoOpen: false + }); + + // Create the newline parser + this.parser = this.serial.pipe(new ReadlineParser({ + delimiter: '\n', + includeDelimiter: false + })); + } + + start(onMessage: (msg: ControllerMessage) => void): Promise { + // Explicit promise construction so we can resolve only on valid comms AND revision check + return new Promise(async (res, rej) => { + // Reset the microcontroller + await this.reset(); + + // Open the serial port + await new Promise((reso, reje) => { + Spinner.start('Establishing serial communications with the microcontroller...'); + this.serial.open(err => { + if (err) { + reje(err); + } else { + Spinner.succeed('Microcontroller serial communications established!'); + reso(); + } + }); + }); + + Spinner.start('Awaiting microcontroller software revision number...'); + this.resetTimeout(); + + // Set up the listener + this.parser.on('data', async msgtxt => { + + // Attempt to parse the raw text as a valid JSON object + let msg: ControllerMessage; + try { + msg = JSON.parse(msgtxt); + } catch (err) { + rej(err); + return; + } + + // Microcontroller-specific pre-handling + switch (msg.type) { + case 'revision': + // Software update + if(msg.data === CONTROLLER_REVISION) { + Spinner.succeed('Microcontroller software up to date!'); + res(); //Successful start sequence + } else { + Spinner.fail(`Microcontroller software out of date! Got ${msg.data}, expected ${CONTROLLER_REVISION}.`); + // Attempt to update the microcontroller, and then restart + this.stop(); + Spinner.start('Compiling latest microcontroller software and flashing...'); + await updateMicrocontroller(); + Spinner.succeed('Updated microcontroller software successfully!'); + } + // Do not break + default: + onMessage(msg); + break; + } + this.resetTimeout(); + }); + }); + } + + /** + * Clear the serial timeout. + */ + private clearTimeout(): void { + if(this.timeout) clearTimeout(this.timeout); + } + + /** + * Refresh (or start) the serial timeout. + */ + private resetTimeout(timeoutSeconds: number = SERIAL_TIMEOUT_SECONDS): void { + this.clearTimeout(); + this.timeout = setTimeout(() => { + this.reset(); + }, timeoutSeconds*1000); + } + + write(msg: ControllerInstructions): void { + Spinner.info(`[${ chalk.yellow('WRITE') }] - ${ JSON.stringify(msg) }`); + this.serial.write(JSON.stringify(msg)+'\n', undefined, err => { + if (err) throw new ControllerInstructionsError(JSON.stringify(msg)); + }); + } + + stop(): void { + this.clearTimeout(); + this.serial.close(); + this.reset(); + } + + /** + * Resets the microcontroller (active-low). + */ + private async reset(): Promise { + gpioWrite(RESET_PIN, 0); + await sleep(10); + gpioWrite(RESET_PIN, 1); + gpioUnexport(RESET_PIN); + } +} + +/** +* A simulated controller for generating random data. +*/ +export class SimulatedController implements Controller { + + intervals: NodeJS.Timeout[] = []; + + constructor(readonly parameters: SimulatorConfig) { } + + async start(onMessage: (msg: ControllerMessage) => any): Promise { + for(const label of Object.keys(this.parameters)) { + this.intervals.push(setInterval(() => { + onMessage(this.generateData(label, this.parameters[label].min, this.parameters[label].max)); + }, this.parameters[label].interval)); + } + } + write() {} + async stop(): Promise { + for (const interval of this.intervals) { + clearInterval(interval); + } + } + + /** + * Generate a single data point + * @param label Dataset label + * @param min Minimum value + * @param max Maximum value + */ + private generateData(label: string, min: number, max: number): ControllerMessage { + return { + type: 'data', + data: { + label, + value: Math.random()*(max-min)+min + } + }; + } +} \ No newline at end of file diff --git a/software/src/env.ts b/software/src/env.ts new file mode 100644 index 00000000..8ebfff0f --- /dev/null +++ b/software/src/env.ts @@ -0,0 +1,134 @@ +import dotenv from 'dotenv'; +import { existsSync } from 'fs'; +import { FirebaseOptions } from 'firebase/app'; +import { EnvFieldError } from './errors'; +import { IoTConfig } from './publisher'; +import { DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; + +/** + * Env fields required for ALL modes. + */ +const ENV_FIELDS_PEAPOD = ["SERIALPORT"]; + +/** + * Env fields required for Firebase functionality. + */ +const ENV_FIELDS_FIREBASE = ["FIREBASE_APIKEY", "FIREBASE_AUTHDOMAIN", "FIREBASE_PROJECTID", "FIREBASE_STORAGEBUCKET", "FIREBASE_MESSAGINGSENDERID", "FIREBASE_APPID", "FIREBASE_MEASUREMENTID"]; + +/** + * Env fields required for GCP IoT Core functionality. + */ +const ENV_FIELDS_IOT = ["IOT_CLOUDREGION", "FIREBASE_PROJECTID", "IOT_REGISTRY", "IOT_JWT_EXPIRYMINS"]; + +/** + * Env fields required for FirebaseDeviceFlow Auth functionality. + */ +const ENV_FIELDS_AUTH = ["GOOGLE_SCOPES", "GOOGLE_CLIENTID", "GOOGLE_CLIENTSECRET", "GITHUB_SCOPES", "GITHUB_CLIENTID", "GITHUB_CLIENTSECRET"]; + +export type PeaPodEnv = { + serialport: string, +}; + +/** +* Load a `.env`-style file to `process.env`. +* +* Throws an error if the file does not exist. +*/ +function loadDotEnv(path: string = '.env'): void { + // Check for file + if (existsSync(path)) { + const config = dotenv.config({path}); + if (config.error) { + throw config.error + } + } else { + throw new Error('Environment variable file not found.'); + } +} + +/** + * Check which of the given fields are missing (undefined) in `process.env` + * @param fields List of fields to check + * @returns List of missing fields + */ +function getMissingEnvFields(fields: string[]) : string[] { + return fields.filter(field => !Object.keys(process.env).includes(field)); +} + +/** + * Load config for the Firebase app and check for all fields. + * @returns Firebase app config object + */ +export function loadFirebaseEnv(): FirebaseOptions { + const missingFields = getMissingEnvFields(ENV_FIELDS_FIREBASE); + if (missingFields.length) { + throw new EnvFieldError("Firebase", missingFields); + } else { + return { + apiKey: process.env.FIREBASE_APIKEY, + authDomain: process.env.FIREBASE_AUTHDOMAIN, + databaseURL: process.env.FIREBASE_DATABASEURL, + projectId: process.env.FIREBASE_PROJECTID, + storageBucket: process.env.FIREBASE_STORAGEBUCKET, + messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, + appId: process.env.FIREBASE_APPID, + measurementId: process.env.FIREBASE_MEASUREMENTID + }; + } +}; + +/** + * Load config for GCP IoT Core and check for all fields. + * @returns GCP IoT Core config object + */ +export function loadIoTEnv(): IoTConfig { + const missingFields = getMissingEnvFields(ENV_FIELDS_IOT); + if (missingFields.length) { + throw new EnvFieldError("GCP IoT Core", missingFields); + } else { + return { + cloudregion: process.env.IOT_CLOUDREGION!, + projectid: process.env.FIREBASE_PROJECTID!, + registryid: process.env.IOT_REGISTRY!, + jwtexpiryminutes: Number(process.env.IOT_JWT_EXPIRYMINS ?? "1440"), + }; + } +}; + +/** + * Load config for the Firebase app and check for all fields. + * @returns Firebase app config object + */ +export function loadAuthEnv(): DeviceFlowUIOptions { + const missingFields = getMissingEnvFields(ENV_FIELDS_AUTH); + if (missingFields.length) { + throw new EnvFieldError("Firebase Device Flow Auth", missingFields); + } else { + return { + Google : { + scopes : process.env.GOOGLE_SCOPES?.split(' '), + clientid : process.env.GOOGLE_CLIENTID, + clientsecret : process.env.GOOGLE_CLIENTSECRET + }, + GitHub : { + scopes : process.env.GITHUB_SCOPES?.split(' '), + clientid : process.env.GITHUB_CLIENTID, + clientsecret : process.env.GITHUB_CLIENTSECRET + } + }; + } +}; + +export function loadPeaPodEnv(): PeaPodEnv { + const missingFields = getMissingEnvFields(ENV_FIELDS_PEAPOD); + if (missingFields.length) { + throw new EnvFieldError("PeaPod", missingFields); + } else { + return { + serialport: process.env.SERIALPORT! + }; + } +}; + +// MAIN +loadDotEnv(); \ No newline at end of file diff --git a/software/src/errors.ts b/software/src/errors.ts index 9c8dba09..8af88bf4 100644 --- a/software/src/errors.ts +++ b/software/src/errors.ts @@ -1,14 +1,24 @@ -/** -* Error on instruction send. -*/ -export class ArduinoInstructionsError extends Error { +export class ControllerInstructionsError extends Error { constructor(instructions: any) { super("Failed to send instructions to Arduino: '"+JSON.stringify(instructions)+"'"); } -} +}; -export class SerialTimeoutError extends Error { - constructor(timeoutSeconds?: number) { - super(`Arduino serial communication timed out${timeoutSeconds === undefined ? '' : ` after ${timeoutSeconds} seconds`}.`); +// UNUSED +// export class SerialTimeoutError extends Error { +// constructor(timeoutSeconds?: number) { +// super(`Arduino serial communication timed out${ timeoutSeconds === undefined ? '' : ` after ${ timeoutSeconds } seconds` }.`); +// } +// }; + +export class EnvFieldError extends Error { + constructor(mode: string, missingFields: string[]) { + super(`.env file is missing the following fields necessary for ${ mode } functionality: ${ missingFields.join(", ") }`); + } +}; + +export class GPIOError extends Error { + constructor(operation: string, pin: number, value?: string | number) { + super(`GPIO '${ operation }' operation failed (pin ${ pin }${ value === undefined ? '' : `, value '${ value }` }')`); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/software/src/peapod.ts b/software/src/peapod.ts new file mode 100644 index 00000000..e2de99db --- /dev/null +++ b/software/src/peapod.ts @@ -0,0 +1,138 @@ +import * as Spinner from './ui'; +import chalk from 'chalk'; + +import { initializeApp, getApps } from 'firebase/app'; +import { getAuth } from 'firebase/auth'; + +import { loadPeaPodEnv, loadAuthEnv, loadFirebaseEnv, loadIoTEnv } from './env'; +import MicroController, { ControllerInstructions, Controller } from './controller'; +import OnlinePublisher, { DataBatch, Publisher, OfflinePublisher, PublishingMode } from './publisher'; + +// CONSTANTS + +/** + * Seconds between data publications. + */ +const BATCH_PUBLISH_INTERVAL = 5; + +// GLOBAL VARIABLES + +/** + * Batch of data to be published. + */ +let batch: DataBatch = { }; + +/** + * JS interval to trigger batch publishing. + */ +let batchInterval: NodeJS.Timer; + +/** + * Latest controller instruction set. + */ +let instructions: ControllerInstructions = {}; + +/** + * Main driver class. + */ +export default class PeaPod { + + controller: Controller; + publisher: Publisher; + + constructor(pm: PublishingMode) { + const ENV_PEAPOD = loadPeaPodEnv(); + + // CREATE CONTROLLER + this.controller = new MicroController(ENV_PEAPOD.serialport); + + // CREATE PUBLISHER + Spinner.info(`Running ${ chalk.green('PeaPod') } in ${ pm } mode.`) + switch (pm) { + + case PublishingMode.ONLINE: + initializeApp(loadFirebaseEnv()); + if (getApps().length === 0) { + throw new Error("Failed to initialize Firebase app! Check .env fields."); + } else { + Spinner.succeed(`Initialized Firebase app!`); + } + this.publisher = new OnlinePublisher(loadIoTEnv(), loadAuthEnv()); + break; + + case PublishingMode.OFFLINE: + default: + this.publisher = new OfflinePublisher(); + break; + } + } + + async start(): Promise { + // INITIALIZE CONTROLLER + await this.controller.start(msg => { + // Handle different message types + // TODO: Handle error, etc. types + switch (msg.type) { + case "data": + // Initialize batch array + if (batch[msg.data.label] === undefined) batch[msg.data.label] = []; + // Accumulate data into batches + batch[msg.data.label].push({ + timestamp: Date.now(), + value: msg.data.value + }); + break; + case "revision": + this.controller.write(instructions); + break; + default: + Spinner.log(`[${ chalk.blueBright('CONTROLLER') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) + } + }); + + // INITIALIZE PUBLISHER + let {projectid, projectname, run} = await this.publisher.start(config => { + // Hot-swap programs + // TODO: This + Spinner.log(`[${ chalk.yellow('PUBLISHER') } | CONFIG] - ${ config }`); + + }, command => { + switch (command.type) { + case 'instructions': + this.controller.write(command.data); + default: + Spinner.log(`[${ chalk.yellow('PUBLISHER') } | COMMAND] - ${ JSON.stringify(command) }`); + } + }); + // Todo: get instructions + + // START + Spinner.log(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); + + // Reset + batch = { }; + + batchInterval = setInterval(() => { + // Publish entire batch + try { + this.publisher.publish({ + type: 'data', + metadata: { + owner: (getApps().length ? (getAuth().currentUser?.uid ?? 'user') : 'user'), + project: projectid, + run + }, + data: batch + }); + } catch { + Spinner.fail('Batch publish failed, will retry...'); + return; + } + + Spinner.log(`[${ chalk.magenta('PUBLISH') }] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.length }, 0) } datapoints published.`); + + // Reset batch to empty + batch = { }; + }, BATCH_PUBLISH_INTERVAL*1000); + } +} \ No newline at end of file diff --git a/software/src/publisher.ts b/software/src/publisher.ts new file mode 100644 index 00000000..cc161355 --- /dev/null +++ b/software/src/publisher.ts @@ -0,0 +1,360 @@ +import * as fs from 'fs'; +import {v4 as uuid} from 'uuid'; +import * as jwt from 'jsonwebtoken'; +import * as mqtt from 'mqtt'; +import * as inquirer from 'inquirer'; +import chalk from 'chalk'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; + +import { getApp } from 'firebase/app'; +import { getFunctions, httpsCallable } from 'firebase/functions'; +import { getFirestore, doc, setDoc, collection, getDocs, query, where, DocumentReference } from 'firebase/firestore'; +import { getAuth } from 'firebase/auth'; + +import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; +import * as Spinner from './ui'; +import { fetchServerCert } from './utils'; +import { ControllerInstructions } from './controller'; + +// TYPES + +/** + * A single datapoint. + */ +type DataPoint = { + timestamp: number, + value: number +}; + +/** + * Batch of datapoints to be published, organized by dataset label + */ +export type DataBatch = { + [key: string]: DataPoint[], +}; + +/** + * Message TO the publisher. + */ +export type PubSubMessage = { + type: 'info' | 'debug' | 'error', + data: any, +} | { + type: 'data', + metadata: { + owner: string, + project: string, + run: string, + } + data: DataBatch, +}; + +/** + * Message FROM the publisher (command). + */ +export type PubSubCommand = { + type: 'instructions', + data: ControllerInstructions, +}; + +/** + * Message FROM the publisher (config). WIP. + */ +export type PubSubConfig = string; + +/** + * Types of publishers. + */ +export enum PublishingMode { + ONLINE = "Online", + OFFLINE = "Offline", +}; + +/** + * Response from the device registration cloud function. + */ +type RegisterResponse = { + id: string, + name: string, + privateKey: string, +} + +/** + * GCP IoT Core configuration parameters. + */ +export type IoTConfig = { + deviceid?: string, + projectid: string, + cloudregion: string, + registryid: string, + jwtexpiryminutes: number, +} + +/** + * Base type for any publisher. + */ +export type Publisher = { + start(onConfig?: (message: PubSubConfig)=>void, onCommand?: (message: PubSubCommand)=>void) : Promise<{projectid: string, projectname?: string, run: string}>, + stop(): void, + publish(msg: PubSubMessage): void, +} + +// MAIN CLASS + +/** + * Interface for publishing data to and receiving instructions from Google Cloud/Firebase via IoT Core PubSub. + */ +export default class PubSubPublisher implements Publisher { + + private tokenRefreshInterval?: NodeJS.Timer; + private mqttclient?: mqtt.MqttClient; + private deviceid!: string; + + constructor(readonly iotConfig: IoTConfig, readonly authConfig: DeviceFlowUIOptions){ } + + publish(msg: PubSubMessage): void { + if (!this.mqttclient || !this.mqttclient.connected) { + throw new Error('MQTT client not connected!'); + } + + // Build topic path + const topic = `/devices/${ this.deviceid }/` + (msg.type === 'data' ? 'events/data' : msg.type === 'info' ? 'events' : 'state'); + + // Strip type from published object + this.mqttclient.publish(topic, JSON.stringify({ ...msg, type: undefined }), { qos: 1 }); + } + + async start(onConfig: (message: PubSubConfig) => void, onCommand: (message: PubSubCommand) => void) { + + // AUTHENTICATION - FIREBASE DEVICE FLOW + const auth = new DeviceFlowUI(getApp(), this.authConfig); + const user = await auth.signIn(); + if (user.displayName) { + Spinner.info(`Welcome, ${ chalk.bold(user.displayName) }!`); + } else { + Spinner.info('Welcome!'); + } + + // GET DEVICE INFO OR REGISTER + const {privatekey, deviceid} = await this.getDeviceInfoOrRegister(user.uid); + this.deviceid = deviceid; + + // CHOOSE PROJECT, RUN, PROGRAM + // TODO: choose program + const [project, projectname] = await this.selectProject(); + const run = await this.createRun(project); + + // SETUP MQTT + Spinner.start('Fetching Google root CA certificates...'); + const servercert = await fetchServerCert(); + Spinner.succeed('Certificates fetched!'); + + Spinner.start('Connecting to MQTT broker...'); + await this.connect(servercert, this.refreshToken(privatekey)); + Spinner.succeed('Device connected!'); + + // Token Refresh + this.tokenRefreshInterval = setInterval(async () => { + Spinner.start('Refreshing token...'); + await this.connect(servercert, this.refreshToken(privatekey)); + Spinner.succeed('Token refreshed. Reconnected.'); + }, this.iotConfig.jwtexpiryminutes*60*1000); + + // Message listeners + this.mqttclient?.subscribe(`/devices/${ this.deviceid }/config`, { qos: 1 }); + this.mqttclient?.subscribe(`/devices/${ this.deviceid }/commands/#`, { qos: 0 }); + + this.mqttclient?.on('error', err => { throw err }); + this.mqttclient?.on('message', (topic, message) => { + if (topic === `/devices/${this.deviceid}/config`) { + try { + const config: PubSubConfig = JSON.parse(message.toString()); + onConfig(config); + } catch (e) { + if (e instanceof SyntaxError) { + // TODO: handle invalid config + } + } + } else if (topic.startsWith(`/devices/${this.deviceid}/commands`)) { + try { + const command: PubSubCommand = JSON.parse(message.toString()); + onCommand(command); + } catch (e) { + if (e instanceof SyntaxError) { + // TODO: handle invalid command + } + } + } + }); + + return { projectid: project.id, projectname, run: run.id }; + } + + /** + * Select a project owned by the current user + * @returns A tuple containing both the document reference and the project name (if any) + */ + private async selectProject(): Promise<[DocumentReference, string]> { + const myProjects = query(collection(getFirestore(), 'projects'), where('owners', 'array-contains', getAuth().currentUser?.uid)); + const projects = (await getDocs(myProjects)).docs; + if (projects.length < 1) { + throw new Error("No projects found! Create one first."); + } + const ref = (await inquirer.prompt<{ref: [DocumentReference, string]}>([ + { + type: 'list', + name: 'ref', + message: 'Select a project:', + choices: projects.map(project => ({ + name: project.get('name')+' - '+project.id, + value: [project.ref, project.get('name')] + })) + } + ])).ref; + return ref; + } + + /** + * Publish a new project. + * @param project Document reference to the project + * @returns Documetn reference to the new run + */ + private async createRun(project: DocumentReference): Promise { + const runid = project.id+'-'+uuid(); + const rundoc = doc(getFirestore(), project.path+'/runs/'+runid); + setDoc(rundoc, { + owner: getAuth().currentUser?.uid, + deviceId: this.deviceid, + }); + return rundoc; + } + + /** + * Look for the private key and device info in local files. If not found, register the device and store the new key and info. + * @param user The UID of the user to whom this device is to be registered. + * @returns The private key and device ID. + */ + private async getDeviceInfoOrRegister(user: string): Promise<{privatekey: string, deviceid: string}> { + if (fs.existsSync('./rsa_private.pem') && fs.existsSync('./deviceInfo.json')) { + Spinner.succeed('Private key and device info found!'); + const privatekey = fs.readFileSync('./rsa_private.pem').toString(); + + let deviceinfo: {id: string, owner: string} = JSON.parse(fs.readFileSync('./deviceInfo.json').toString()); + + if (deviceinfo['owner'] != user) { + throw new Error('This PeaPod is not owned by this user!'); + } + return { privatekey, deviceid: deviceinfo.id }; + } else { + Spinner.info('Private key and/or device info not found!'); + + Spinner.start('Registering device...'); + const registerDevice = httpsCallable(getFunctions(), 'registerDevice'); + let result = (await registerDevice()).data; + Spinner.succeed('Device '+(result.id) + ' registered!'); + + fs.writeFileSync('./rsa_private.pem', result.privateKey); + fs.writeFileSync('./deviceInfo.json', JSON.stringify({name: result.name, id: result.id, owner: getAuth().currentUser?.uid}, null, 2)); + + return { privatekey: result.privateKey, deviceid: result.id }; + } + } + + /** + * Sign a new JWT. + * @returns JSON Web Token string payload. + */ + private refreshToken(privatekey: string) : string { + const now = Date.now() / 1000; + const token = { + iat: now, + exp: now + this.iotConfig.jwtexpiryminutes * 60, + aud: this.iotConfig.projectid, + }; + return jwt.sign(token, privatekey, {algorithm: 'RS256'}); + } + + /** + * Connect to the MQTT broker. + * @param servercert Root CA certificate. + * @param password JWT + */ + private async connect(servercert: string, password: string): Promise { + // Disconnect existing client + this.disconnect(); + + let client = mqtt.connect({ + host: 'mqtt.googleapis.com', + port: 8883, + clientId: `projects/${ this.iotConfig.projectid }/locations/${ this.iotConfig.cloudregion }/registries/${ this.iotConfig.registryid }/devices/${ this.deviceid }`, + username: 'unused', + password, + protocol: 'mqtts', + secureProtocol: 'TLSv1_2_method', + ca: [servercert], + }); + + return new Promise(res => { + client.on('connect', packet => { + if (!packet) { + throw new Error('Could not connect to MQTT broker!'); + } + this.mqttclient = client; + res(); + }); + }); + } + + /** + * If the MQTT client is connected, disconnect it. + */ + private async disconnect(): Promise{ + if (this.mqttclient && this.mqttclient.connected) { + await new Promise(res => { + this.mqttclient?.end(true, undefined, err => { + if (err) { + throw err; + } else { + res(); + } + }); + }); + } + } + + stop() { + if(this.tokenRefreshInterval) clearInterval(this.tokenRefreshInterval); + if(this.mqttclient) this.disconnect(); + } +} + +/** + * Publishes data batches to local JSON files. + */ +export class OfflinePublisher implements Publisher { + async start() { + let config = { projectid: 'testproject', run: 'testrun-'+uuid() }; + Spinner.info(`Logging data to ${ chalk.bold('projects/'+config.projectid+'/runs/'+config.run+'/') }`); + return config; + } + stop() { }; + publish(msg: PubSubMessage): void { + switch(msg.type) { + case 'data': + for (const label of Object.keys(msg.data)) { + for (const datum of msg.data[label]) { + console.log(`[${ chalk.magenta(msg.type.toUpperCase()) }] - [${ (new Date(datum.timestamp)).toLocaleTimeString() }] - ${ label }: ${ datum.value }`); + } + const dir = `./projects/${ msg.metadata.project }/runs/${ msg.metadata.run }/${ label }/`; + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true }); + } + writeFileSync(`${ dir }${ label+'-'+uuid()+'.json' }`, JSON.stringify(msg.data[label], null, 2)) + } + break; + default: + Spinner.log(`[${ chalk.yellow(msg.type.toUpperCase()) }] - ${ JSON.stringify(msg.data) }`); + break; + } + } +} \ No newline at end of file diff --git a/software/src/ui.ts b/software/src/ui.ts index f910724f..2dea2201 100644 --- a/software/src/ui.ts +++ b/software/src/ui.ts @@ -1,109 +1,103 @@ import ora from 'ora'; -namespace Spinner { - const defaultSpinner : ora.Spinner = { - interval: 50, - frames: [ - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "██▁▁▁▁▁▁▁▁▁▁▁▁▁", - "███▁▁▁▁▁▁▁▁▁▁▁▁", - "████▁▁▁▁▁▁▁▁▁▁▁", - "█████▁▁▁▁▁▁▁▁▁▁", - "▁█████▁▁▁▁▁▁▁▁▁", - "▁▁█████▁▁▁▁▁▁▁▁", - "▁▁▁█████▁▁▁▁▁▁▁", - "▁▁▁▁█████▁▁▁▁▁▁", - "▁▁▁▁▁█████▁▁▁▁▁", - "▁▁▁▁▁▁█████▁▁▁▁", - "▁▁▁▁▁▁▁█████▁▁▁", - "▁▁▁▁▁▁▁▁█████▁▁", - "▁▁▁▁▁▁▁▁▁█████▁", - "▁▁▁▁▁▁▁▁▁▁█████", - "▁▁▁▁▁▁▁▁▁▁▁████", - "▁▁▁▁▁▁▁▁▁▁▁▁███", - "▁▁▁▁▁▁▁▁▁▁▁▁▁██", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", - ] - } - - // Global spinner - export var Spinner: ora.Ora = ora(); - - /** - * Start the loading spinner. - * @param text Text to display. - * @param spinner Spinner to use. Defaults to the default spinner. - */ - export const start = (text: string = "", spinner: ora.Spinner = defaultSpinner) => { - // If it's already spinning, just change the text - if(Spinner.isSpinning){ - Spinner.text = text; - Spinner.spinner = spinner; - } else { - // Otherwise, start a new one - Spinner = ora({text, spinner}).start(); - } - } - - /** - * Fail the loading spinner. - * @param text Text to display. - */ - export const fail = (text: string = "") => { - // If it's spinning, change the text and fail - if(Spinner.isSpinning){ - Spinner.fail(text); - } else { - // Otherwise, start a new one and fail it - Spinner = ora({text}).fail(); - } +const DEFAULT_SPINNER: ora.Spinner = { + interval: 50, + frames: [ + "▁▁▁▁▁▁▁▁▁▁▁", + "█▁▁▁▁▁▁▁▁▁▁", + "██▁▁▁▁▁▁▁▁▁", + "███▁▁▁▁▁▁▁▁", + "████▁▁▁▁▁▁▁", + "█████▁▁▁▁▁▁", + "▁█████▁▁▁▁▁", + "▁▁█████▁▁▁▁", + "▁▁▁█████▁▁▁", + "▁▁▁▁█████▁▁", + "▁▁▁▁▁█████▁", + "▁▁▁▁▁▁█████", + "▁▁▁▁▁▁▁████", + "▁▁▁▁▁▁▁▁███", + "▁▁▁▁▁▁▁▁▁██", + "▁▁▁▁▁▁▁▁▁▁█", + ] +}; + +/** + * Global spinner object. + */ +export let Spinner: ora.Ora = ora(); + +/** +* Start the loading spinner. +* @param text Text to display. +* @param spinner Spinner to use. Defaults to the default spinner. +*/ +export function start(text: string = "", spinner: ora.Spinner = DEFAULT_SPINNER) { + // If it's already spinning, just change the text + if (Spinner.isSpinning) { + Spinner.text = text; + Spinner.spinner = spinner; + } else { + // Otherwise, start a new one + Spinner = ora({ text, spinner }).start(); } - - /** - * Succeed the loading spinner. - * @param text Text to display. - */ - export const succeed = (text: string = "") => { - // If it's spinning, change the text and succeed - if(Spinner.isSpinning){ - Spinner.succeed(text); - } else { - // Otherwise, start a new one and succeed it - Spinner = ora({text}).succeed(); - } +}; + +/** +* Fail the loading spinner. +* @param text Text to display. +*/ +export function fail(text: string = "") { + // If it's spinning, change the text and fail + if (Spinner.isSpinning) { + Spinner.fail(text); + } else { + // Otherwise, start a new one and fail it + Spinner = ora({ text }).fail(); } +}; - /** - * Complete the loading spinner with info (blue `i`) - * @param text Text to display. - */ - export const info = (text: string = "") => { - if(Spinner.isSpinning) { - // If it's spinning, change the text and set to info - Spinner.info(text); - } else { - // Otherwise, start a new one and info it - Spinner = ora({text}).info(); - } +/** +* Succeed the loading spinner. +* @param text Text to display. +*/ +export function succeed(text: string = "") { + // If it's spinning, change the text and succeed + if (Spinner.isSpinning) { + Spinner.succeed(text); + } else { + // Otherwise, start a new one and succeed it + Spinner = ora({ text }).succeed(); } +}; - /** - * If spinning: stop and clear the current spinner, log some text, then restart the spinner - * Else: Just log - * - * @param text Text to log - */ - export const log = (text: string) => { - let oldtext = Spinner.text; - if(Spinner.isSpinning) { - Spinner.stop(); - console.log(text); - Spinner.start(oldtext); - } else { - console.log(text); - } +/** + * Complete the loading spinner with info (blue `i`) + * @param text Text to display. + */ +export function info(text: string = "") { + if (Spinner.isSpinning) { + // If it's spinning, change the text and set to info + Spinner.info(text); + } else { + // Otherwise, start a new one and info it + Spinner = ora({ text }).info(); } -} +}; -export default Spinner; \ No newline at end of file +/** + * If spinning: stop and clear the current spinner, log some text, then restart the spinner + * Else: Just log + * + * @param text Text to log + */ +export function log(text: string) { + let oldtext = Spinner.text; + if (Spinner.isSpinning) { + Spinner.stop(); + console.log(text); + Spinner.start(oldtext); + } else { + console.log(text); + } +}; \ No newline at end of file diff --git a/software/src/utils.ts b/software/src/utils.ts index 77a504ad..04196b55 100644 --- a/software/src/utils.ts +++ b/software/src/utils.ts @@ -1,19 +1,18 @@ import * as dns from 'dns'; import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; -import dotenv from 'dotenv'; -import { SerialPort } from 'serialport'; import axios from 'axios'; import { spawn } from 'child_process'; +import { GPIOError } from './errors'; /** -* Checks the internet connectivity. -* @param {number} timeout Timeout in milliseconds. -* @param {dns.Resolver} resolver DNS Resolver object. -*/ -export const checkInternet = (timeout : number = 5000, url: string = 'www.google.com') : Promise => { - const resolver = new dns.Resolver({timeout}); - return new Promise(ret=>{ - resolver.resolve(url, err=>{ + * Checks the internet connectivity. + * @param timeout Timeout in milliseconds. Default: 5000 + * @param url The URL to ping. Default: 'www.google.com' + */ +export function checkInternet(timeout: number = 5000, url: string = 'www.google.com'): Promise { + const resolver = new dns.Resolver({ timeout }); + return new Promise(ret => { + resolver.resolve(url, err => { if (err) { ret(false); } else { @@ -24,98 +23,60 @@ export const checkInternet = (timeout : number = 5000, url: string = 'www.google }; /** -* Sleep a given number of milliseconds. -* -* Usage: `await sleep(x)` or `sleep(x).then(...)` -* @param millis Number of milliseconds to sleep. -* @returns -*/ -export const sleep = (millis : number): Promise => { + * Sleep a given number of milliseconds. + * + * Usage: `await sleep(x)` + * @param millis Number of milliseconds to sleep. + * @returns + */ +export function sleep(millis: number): Promise { return new Promise(resolve => { setTimeout(resolve, millis); }); }; /** -* Load a `.env`-style file to `process.env`. -* -* Throws an error if the file does not exist. -*/ -export const loadDotenv = (path: string = '.env'): void => { - // Check for file - if(existsSync(path)){ - const config = dotenv.config({path}); - if (config.error) { - throw config.error - } - } else { - throw new Error('Environment variable file not found.'); - } -}; - -/** -* Find the path to the serial port of a given device. -* -* Returns the first port matching the given parameters. -* @param manufacturer Device manufacturer name (or a substring thereof). -* -* TODO: More search fields? -* @returns Path to port. -*/ -export const findSerialPath = async (manufacturer: string = "arduino") : Promise => { - // List all ports - let ports = await SerialPort.list(); - // Search ports and find one that matches our parameters - for(const port of ports){ - if(port.manufacturer?.toLowerCase().includes(manufacturer)){ - return port.path; - } - } - throw new Error('Arduino not found! Check wiring.'); -}; - -/** -* Converts a set of strings to a tuple. Useful TypeScript nonsense. -* @param data -* @returns -*/ -export const stringsToTuple = (...data: T): T => { - return data; -}; - -export const fetchServerCert = async (): Promise => { - return (await axios.get('https://pki.goog/roots.pem')).data as string; + * Fetch the Google Root CA certificate. + * @returns The very same certificate. + */ +export async function fetchServerCert(): Promise { + return String((await axios.get('https://pki.goog/roots.pem')).data); } -export const checkArduino = (): Promise => { +/** + * Checks that the microcontroller is connected properly and accessible for flashing. + */ +export function checkMicrocontroller(): Promise { return new Promise((res, rej) => { // Create log folder if (!existsSync('logs/')) { mkdirSync('logs/', { recursive: true }); } execute(`avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`, [1]).catch(err => { - writeFileSync('logs/checkArduino.log', err); - rej(new Error(`Failed to communicate with the Arduino. See logs/checkArduino.log`)); + writeFileSync('logs/checkMicrocontroller.log', err); + rej(new Error(`Failed to communicate with the microcontroller. See logs/checkMicrocontroller.log`)); }).then(log1 => { - log1 ? writeFileSync('logs/checkArduino.log', log1) : null; + if (log1) writeFileSync('logs/checkMicrocontroller.log', log1); res(); }); }); } -export const updateArduino = (): Promise => { +/** + * Compiles the microcontroller software, and flashes the binary to the chip. + */ +export function updateMicrocontroller(): Promise { return new Promise((res, rej) => { // Create log folder if (!existsSync('logs/')) { mkdirSync('logs/', { recursive: true }); } - execute(`${process.env.HOME}/.platformio/penv/bin/platformio run -d PeaPodOS-Arduino/ -e peapod -t upload`, [1]) - .then(log1 => { - if (log1) writeFileSync('logs/updateArduino.log', log1); + execute(`${ process.env.HOME }/.platformio/penv/bin/platformio run -d PeaPodOS-Arduino/ -e peapod -t upload`, [1]).catch(err => { + writeFileSync('logs/updateMicrocontroller.log', err); + rej(new Error(`Failed to update the microcontroller software. See logs/updateMicrocontroller.log`)); + }).then(log1 => { + if (log1) writeFileSync('logs/updateMicrocontroller.log', log1); res(); - }).catch(err => { - writeFileSync('logs/updateArduino.log', err); - rej(new Error(`Failed to update the Arduino software. See logs/updateArduino.log`)); }); }); } @@ -123,7 +84,7 @@ export const updateArduino = (): Promise => { /** * General purpose command execution and logging. No `sudo` support. */ -export const execute = (command: string, failureCodes: number[] = []): Promise => { +export function execute(command: string, failureCodes: number[] = []): Promise { return new Promise((res, rej) => { const args = command.split(' '); const eprocess = spawn(args[0], args.slice(1)); @@ -157,9 +118,13 @@ export const execute = (command: string, failureCodes: number[] = []): Promise { - if(!existsSync(`/sys/class/gpio/gpio${pin}/`)) { - execute(`echo ${pin} > /sys/class/gpio/export`); +export function gpioExport(pin: number): void { + if (!existsSync(`/sys/class/gpio/gpio${ pin }/`)) { + try{ + execute(`echo ${ pin } > /sys/class/gpio/export`); + } catch (err) { + throw new GPIOError('export', pin); + } } } @@ -167,9 +132,13 @@ export const gpioExport = (pin: number): void => { * Unexports a pin (if it has been previously exported) * @param pin The GPIO pin to unexport. */ -export const gpioUnexport = (pin: number): void => { - if(existsSync(`/sys/class/gpio/gpio${pin}/`)) { - execute(`echo ${pin} > /sys/class/gpio/unexport`); +export function gpioUnexport(pin: number): void { + if (existsSync(`/sys/class/gpio/gpio${ pin }/`)) { + try { + execute(`echo ${ pin } > /sys/class/gpio/unexport`); + } catch (err) { + throw new GPIOError('unexport', pin); + } } } @@ -179,10 +148,18 @@ export const gpioUnexport = (pin: number): void => { * @param pin The GPIO pin to write to. * @param value 1 or 0. */ -export const gpioWrite = (pin: number, value: 0 | 1): void => { +export function gpioWrite(pin: number, value: 0 | 1): void { gpioExport(pin); - writeFileSync(`/sys/class/gpio/gpio${pin}/direction`, 'out'); - writeFileSync(`/sys/class/gpio/gpio${pin}/value`, `${value}`); + try { + writeFileSync(`/sys/class/gpio/gpio${ pin }/direction`, 'out'); + } catch (err) { + throw new GPIOError('direction', pin, 'out'); + } + try { + writeFileSync(`/sys/class/gpio/gpio${ pin }/value`, String(value)); + } catch (err) { + throw new GPIOError('write', pin, value); + } } /** @@ -191,9 +168,20 @@ export const gpioWrite = (pin: number, value: 0 | 1): void => { * @param pin The GPIO pin to read from. * @param value */ -export const gpioRead = (pin: number): number => { +export function gpioRead(pin: number): number { gpioExport(pin); - writeFileSync(`/sys/class/gpio/gpio${pin}/direction`, 'in'); - const value: number = Number.parseInt(readFileSync(`/sys/class/gpio/gpio${pin}/value`).toString()); + try { + writeFileSync(`/sys/class/gpio/gpio${ pin }/direction`, 'in'); + } catch (err) { + throw new GPIOError('direction', pin, 'in'); + } + let raw: string; + try { + raw = readFileSync(`/sys/class/gpio/gpio${ pin }/value`).toString(); + } catch (err) { + throw new GPIOError('read', pin); + } + const value: number = Number(raw); + if (isNaN(value)) throw new GPIOError('read', pin); return value; } \ No newline at end of file diff --git a/software/tests/testArduino.test.ts b/software/tests/testArduino.test.ts index 7f044eab..585d24d7 100644 --- a/software/tests/testArduino.test.ts +++ b/software/tests/testArduino.test.ts @@ -1,12 +1,12 @@ import { promiseToReject } from './utils'; -import { execute, checkArduino } from '../src/utils'; +import { execute, checkMicrocontroller } from '../src/utils'; import { mkdirSync, writeFileSync, existsSync } from 'fs'; /** * Run Arduino unit tests, powered by PlatformIO. Log results to file. * @returns Resolves with void, rejects with error. */ -const arduinoUnitTests = () => { +function arduinoUnitTests() { return new Promise((res, rej) => { // Create log folder if (!existsSync('logs/')) { @@ -26,7 +26,7 @@ jest.setTimeout(60000); test('avrdude can talk to the Arduino', () => { expect.assertions(1); - return expect(promiseToReject(checkArduino())).resolves.toBe(false); + return expect(promiseToReject(checkMicrocontroller())).resolves.toBe(false); }); test('Arduino unit tests', () => { diff --git a/software/tests/testPublisher.test.ts b/software/tests/testPublisher.test.ts new file mode 100644 index 00000000..9cc65874 --- /dev/null +++ b/software/tests/testPublisher.test.ts @@ -0,0 +1,14 @@ +// controller = new SimulatedController({ +// air_temperature: { +// min: 10, +// max: 20, +// interval: 2000 +// }, +// water_level: { +// min: 0, +// max: 1, +// interval: 1000 +// } +// }); + +// publisher = new OfflinePublisher(); \ No newline at end of file diff --git a/software/tests/utils.ts b/software/tests/utils.ts index 27470e37..a2c1a7fe 100644 --- a/software/tests/utils.ts +++ b/software/tests/utils.ts @@ -4,7 +4,7 @@ * @param f Promise to check. * @returns False if resolved, error (or true) if rejected. */ -export const promiseToReject = (f: Promise): Promise => { +export function promiseToReject(f: Promise): Promise { return new Promise(res => { f.catch((err)=>{ res((err === {} || !err) ? true : err) }).then(()=>{ res(false) }); }); From ee55b89476f564679b7a73ee508c0662b8c284be Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Wed, 11 May 2022 14:46:51 -0400 Subject: [PATCH 19/85] Renaming, Removing Deprecated * Renamed PeaPodOS-Arduino to microcontroller - Removed checkArduino and corresponding unit test --- software/.env.template | 3 +++ software/README.md | 8 +++---- .../.gitignore | 0 .../avrdude_gpio.conf | 0 .../platformio.ini | 0 .../src/actuators/actuator.cc | 0 .../src/actuators/actuator.h | 0 .../src/actuators/led.cc | 0 .../src/actuators/led.h | 0 .../src/main.cc | 0 .../src/main.h | 0 .../src/sensors/k30.cc | 0 .../src/sensors/k30.h | 0 .../src/sensors/sensor.cc | 0 .../src/sensors/sensor.h | 0 .../src/sensors/sht31.cc | 0 .../src/sensors/sht31.h | 0 .../src/utils/base.h | 0 .../src/utils/instructions.cc | 0 .../src/utils/instructions.h | 0 .../src/utils/jsonmessenger.cc | 0 .../src/utils/jsonmessenger.h | 0 .../test/README.md | 0 .../test/test_blink/test_blink.cc | 0 .../test_instructions/test_instructions.cc | 0 .../test/test_k30/test_k30.cc | 0 .../test/test_messenger/test_messenger.cc | 0 .../test/test_sht31/test_sht31.cc | 0 software/src/utils.ts | 21 +------------------ software/tests/testArduino.test.ts | 9 ++------ software/tsconfig.json | 2 +- subsystems/automation/README.md | 2 +- 32 files changed, 12 insertions(+), 33 deletions(-) rename software/{PeaPodOS-Arduino => microcontroller}/.gitignore (100%) rename software/{PeaPodOS-Arduino => microcontroller}/avrdude_gpio.conf (100%) rename software/{PeaPodOS-Arduino => microcontroller}/platformio.ini (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/actuators/actuator.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/actuators/actuator.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/actuators/led.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/actuators/led.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/main.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/main.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/sensors/k30.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/sensors/k30.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/sensors/sensor.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/sensors/sensor.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/sensors/sht31.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/sensors/sht31.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/utils/base.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/utils/instructions.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/utils/instructions.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/utils/jsonmessenger.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/src/utils/jsonmessenger.h (100%) rename software/{PeaPodOS-Arduino => microcontroller}/test/README.md (100%) rename software/{PeaPodOS-Arduino => microcontroller}/test/test_blink/test_blink.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/test/test_instructions/test_instructions.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/test/test_k30/test_k30.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/test/test_messenger/test_messenger.cc (100%) rename software/{PeaPodOS-Arduino => microcontroller}/test/test_sht31/test_sht31.cc (100%) diff --git a/software/.env.template b/software/.env.template index 73608751..3bfd86c0 100644 --- a/software/.env.template +++ b/software/.env.template @@ -11,4 +11,7 @@ GOOGLE_CLIENTSECRET="" GITHUB_SCOPES="read:user user:email" GITHUB_CLIENTID="" GITHUB_CLIENTSECRET="" +IOT_CLOUDREGION="" +IOT_REGISTRY="" +IOT_JWT_EXPIRYMINS=1440 SERIALPORT="/dev/ttyS0" \ No newline at end of file diff --git a/software/README.md b/software/README.md index aeba96fd..799c1654 100644 --- a/software/README.md +++ b/software/README.md @@ -79,7 +79,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: 8. Install PlatformIO Core with `python3 -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)"` 9. Create a custom configuration file for the AVR flash utility *avrdude* to be able to program the Arduino Nano via ICSP over the Raspberry Pi's GPIO pins: - 1. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/software/PeaPodOS-Arduino/avrdude_gpio.conf`, then modify your copy with `nano ~/software/PeaPodOS-Arduino/avrdude_gpio.conf`. Copy the following to the end of the file: + 1. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/software/microcontroller/avrdude_gpio.conf`, then modify your copy with `nano ~/software/microcontroller/avrdude_gpio.conf`. Copy the following to the end of the file: ``` # Raspberry Pi GPIO configuration for avrdude. @@ -94,7 +94,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: ; ``` (*Ctrl-O* to save, *Ctrl-X* to exit) - 2. Verify the configuration and connection to the Arduino with `sudo avrdude -p m328p -C ~/software/PeaPodOS-Arduino/avrdude_gpio.conf -c peapod -v`. A successful output should look something like: + 2. Verify the configuration and connection to the Arduino with `sudo avrdude -p m328p -C/avrdude_gpio.conf -c peapod -v`. A successful output should look something like: ``` avrdude: Version 6.3-20171130 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ @@ -160,7 +160,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: avrdude done. Thank you. ``` -10. Perform first-time flashing with `~/.platformio/penv/bin/platformio run -d ~/software/PeaPodOS-Arduino/ --target upload` +10. Perform first-time flashing with `~/.platformio/penv/bin/platformio run -d ~/software/microcontroller/ --target upload` 11. Edit the `sudoers` file to allow `avrdude` to be executed using `sudo` *without a password*: 1. Open the `sudoers` file: `sudo visudo` @@ -185,7 +185,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: ### Arduino Test Suite -To run the PlatformIO Arduino test suite: `~/.platformio/penv/bin/platformio test -d ~/software/PeaPodOS-Arduino/` +To run the PlatformIO Arduino test suite: `~/.platformio/penv/bin/platformio test -d ~/software/microcontroller/` ### Raspberry Pi Serial Testing diff --git a/software/PeaPodOS-Arduino/.gitignore b/software/microcontroller/.gitignore similarity index 100% rename from software/PeaPodOS-Arduino/.gitignore rename to software/microcontroller/.gitignore diff --git a/software/PeaPodOS-Arduino/avrdude_gpio.conf b/software/microcontroller/avrdude_gpio.conf similarity index 100% rename from software/PeaPodOS-Arduino/avrdude_gpio.conf rename to software/microcontroller/avrdude_gpio.conf diff --git a/software/PeaPodOS-Arduino/platformio.ini b/software/microcontroller/platformio.ini similarity index 100% rename from software/PeaPodOS-Arduino/platformio.ini rename to software/microcontroller/platformio.ini diff --git a/software/PeaPodOS-Arduino/src/actuators/actuator.cc b/software/microcontroller/src/actuators/actuator.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/actuators/actuator.cc rename to software/microcontroller/src/actuators/actuator.cc diff --git a/software/PeaPodOS-Arduino/src/actuators/actuator.h b/software/microcontroller/src/actuators/actuator.h similarity index 100% rename from software/PeaPodOS-Arduino/src/actuators/actuator.h rename to software/microcontroller/src/actuators/actuator.h diff --git a/software/PeaPodOS-Arduino/src/actuators/led.cc b/software/microcontroller/src/actuators/led.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/actuators/led.cc rename to software/microcontroller/src/actuators/led.cc diff --git a/software/PeaPodOS-Arduino/src/actuators/led.h b/software/microcontroller/src/actuators/led.h similarity index 100% rename from software/PeaPodOS-Arduino/src/actuators/led.h rename to software/microcontroller/src/actuators/led.h diff --git a/software/PeaPodOS-Arduino/src/main.cc b/software/microcontroller/src/main.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/main.cc rename to software/microcontroller/src/main.cc diff --git a/software/PeaPodOS-Arduino/src/main.h b/software/microcontroller/src/main.h similarity index 100% rename from software/PeaPodOS-Arduino/src/main.h rename to software/microcontroller/src/main.h diff --git a/software/PeaPodOS-Arduino/src/sensors/k30.cc b/software/microcontroller/src/sensors/k30.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/k30.cc rename to software/microcontroller/src/sensors/k30.cc diff --git a/software/PeaPodOS-Arduino/src/sensors/k30.h b/software/microcontroller/src/sensors/k30.h similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/k30.h rename to software/microcontroller/src/sensors/k30.h diff --git a/software/PeaPodOS-Arduino/src/sensors/sensor.cc b/software/microcontroller/src/sensors/sensor.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/sensor.cc rename to software/microcontroller/src/sensors/sensor.cc diff --git a/software/PeaPodOS-Arduino/src/sensors/sensor.h b/software/microcontroller/src/sensors/sensor.h similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/sensor.h rename to software/microcontroller/src/sensors/sensor.h diff --git a/software/PeaPodOS-Arduino/src/sensors/sht31.cc b/software/microcontroller/src/sensors/sht31.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/sht31.cc rename to software/microcontroller/src/sensors/sht31.cc diff --git a/software/PeaPodOS-Arduino/src/sensors/sht31.h b/software/microcontroller/src/sensors/sht31.h similarity index 100% rename from software/PeaPodOS-Arduino/src/sensors/sht31.h rename to software/microcontroller/src/sensors/sht31.h diff --git a/software/PeaPodOS-Arduino/src/utils/base.h b/software/microcontroller/src/utils/base.h similarity index 100% rename from software/PeaPodOS-Arduino/src/utils/base.h rename to software/microcontroller/src/utils/base.h diff --git a/software/PeaPodOS-Arduino/src/utils/instructions.cc b/software/microcontroller/src/utils/instructions.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/utils/instructions.cc rename to software/microcontroller/src/utils/instructions.cc diff --git a/software/PeaPodOS-Arduino/src/utils/instructions.h b/software/microcontroller/src/utils/instructions.h similarity index 100% rename from software/PeaPodOS-Arduino/src/utils/instructions.h rename to software/microcontroller/src/utils/instructions.h diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc b/software/microcontroller/src/utils/jsonmessenger.cc similarity index 100% rename from software/PeaPodOS-Arduino/src/utils/jsonmessenger.cc rename to software/microcontroller/src/utils/jsonmessenger.cc diff --git a/software/PeaPodOS-Arduino/src/utils/jsonmessenger.h b/software/microcontroller/src/utils/jsonmessenger.h similarity index 100% rename from software/PeaPodOS-Arduino/src/utils/jsonmessenger.h rename to software/microcontroller/src/utils/jsonmessenger.h diff --git a/software/PeaPodOS-Arduino/test/README.md b/software/microcontroller/test/README.md similarity index 100% rename from software/PeaPodOS-Arduino/test/README.md rename to software/microcontroller/test/README.md diff --git a/software/PeaPodOS-Arduino/test/test_blink/test_blink.cc b/software/microcontroller/test/test_blink/test_blink.cc similarity index 100% rename from software/PeaPodOS-Arduino/test/test_blink/test_blink.cc rename to software/microcontroller/test/test_blink/test_blink.cc diff --git a/software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc b/software/microcontroller/test/test_instructions/test_instructions.cc similarity index 100% rename from software/PeaPodOS-Arduino/test/test_instructions/test_instructions.cc rename to software/microcontroller/test/test_instructions/test_instructions.cc diff --git a/software/PeaPodOS-Arduino/test/test_k30/test_k30.cc b/software/microcontroller/test/test_k30/test_k30.cc similarity index 100% rename from software/PeaPodOS-Arduino/test/test_k30/test_k30.cc rename to software/microcontroller/test/test_k30/test_k30.cc diff --git a/software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc b/software/microcontroller/test/test_messenger/test_messenger.cc similarity index 100% rename from software/PeaPodOS-Arduino/test/test_messenger/test_messenger.cc rename to software/microcontroller/test/test_messenger/test_messenger.cc diff --git a/software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc b/software/microcontroller/test/test_sht31/test_sht31.cc similarity index 100% rename from software/PeaPodOS-Arduino/test/test_sht31/test_sht31.cc rename to software/microcontroller/test/test_sht31/test_sht31.cc diff --git a/software/src/utils.ts b/software/src/utils.ts index 04196b55..f3ffa5b1 100644 --- a/software/src/utils.ts +++ b/software/src/utils.ts @@ -43,25 +43,6 @@ export async function fetchServerCert(): Promise { return String((await axios.get('https://pki.goog/roots.pem')).data); } -/** - * Checks that the microcontroller is connected properly and accessible for flashing. - */ -export function checkMicrocontroller(): Promise { - return new Promise((res, rej) => { - // Create log folder - if (!existsSync('logs/')) { - mkdirSync('logs/', { recursive: true }); - } - execute(`avrdude -p m328p -C ~/avrdude_gpio.conf -c peapod -v`, [1]).catch(err => { - writeFileSync('logs/checkMicrocontroller.log', err); - rej(new Error(`Failed to communicate with the microcontroller. See logs/checkMicrocontroller.log`)); - }).then(log1 => { - if (log1) writeFileSync('logs/checkMicrocontroller.log', log1); - res(); - }); - }); -} - /** * Compiles the microcontroller software, and flashes the binary to the chip. */ @@ -71,7 +52,7 @@ export function updateMicrocontroller(): Promise { if (!existsSync('logs/')) { mkdirSync('logs/', { recursive: true }); } - execute(`${ process.env.HOME }/.platformio/penv/bin/platformio run -d PeaPodOS-Arduino/ -e peapod -t upload`, [1]).catch(err => { + execute(`${ process.env.HOME }/.platformio/penv/bin/platformio run -d microcontroller/ -e peapod -t upload`, [1]).catch(err => { writeFileSync('logs/updateMicrocontroller.log', err); rej(new Error(`Failed to update the microcontroller software. See logs/updateMicrocontroller.log`)); }).then(log1 => { diff --git a/software/tests/testArduino.test.ts b/software/tests/testArduino.test.ts index 585d24d7..cf1f68cc 100644 --- a/software/tests/testArduino.test.ts +++ b/software/tests/testArduino.test.ts @@ -1,5 +1,5 @@ import { promiseToReject } from './utils'; -import { execute, checkMicrocontroller } from '../src/utils'; +import { execute } from '../src/utils'; import { mkdirSync, writeFileSync, existsSync } from 'fs'; /** @@ -12,7 +12,7 @@ function arduinoUnitTests() { if (!existsSync('logs/')) { mkdirSync('logs/', { recursive: true }); } - execute(`pio test -d PeaPodOS-Arduino`, [1]).catch(err => { + execute(`pio test -d microcontroller`, [1]).catch(err => { writeFileSync('logs/arduinoUnitTests.log', err); rej(new Error(`One or more unit tests failed. See logs/arduinoUnitTests.log`)); }).then(log1 => { @@ -24,11 +24,6 @@ function arduinoUnitTests() { jest.setTimeout(60000); -test('avrdude can talk to the Arduino', () => { - expect.assertions(1); - return expect(promiseToReject(checkMicrocontroller())).resolves.toBe(false); -}); - test('Arduino unit tests', () => { expect.assertions(1); return expect(promiseToReject(arduinoUnitTests())).resolves.toBe(false); diff --git a/software/tsconfig.json b/software/tsconfig.json index 9be506e2..00c8addc 100644 --- a/software/tsconfig.json +++ b/software/tsconfig.json @@ -12,5 +12,5 @@ // No outDir: keep index.js at package root, only the binary gets published }, "include": ["index.ts", "./src/"], - "exclude": ["node_modules", "PeaPodOS-Arduino", "**/*.spec.ts"], + "exclude": ["node_modules", "microcontroller", "**/*.spec.ts"], } \ No newline at end of file diff --git a/subsystems/automation/README.md b/subsystems/automation/README.md index f3d4edd2..7afcb8f6 100644 --- a/subsystems/automation/README.md +++ b/subsystems/automation/README.md @@ -1,6 +1,6 @@ # PeaPod Automation Subsystem ->Note: All Arduino code can be found under `/software/PeaPodOS-Arduino` +>Note: All Arduino code can be found under `/software/microcontroller` ## Purchasing From 74a40c45c7bab6de21347f0d9fc44ad655e12872 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sun, 29 May 2022 22:17:23 -0400 Subject: [PATCH 20/85] BoMs --- .github/BOM_TEMPLATES/bom_template_purchase.csv | 2 -- .../hardware/boms/automation_bom_components.csv | 12 ++++++++++++ .../hardware/boms/automation_bom_purchase.csv | 2 ++ .../hardware/boms/sensors_bom_digikey.csv | 12 ------------ .../hardware/schematics/sensors_breadboard.pdf | Bin 56195 -> 0 bytes 5 files changed, 14 insertions(+), 14 deletions(-) delete mode 100644 .github/BOM_TEMPLATES/bom_template_purchase.csv create mode 100644 subsystems/automation/hardware/boms/automation_bom_components.csv create mode 100644 subsystems/automation/hardware/boms/automation_bom_purchase.csv delete mode 100644 subsystems/automation/hardware/boms/sensors_bom_digikey.csv delete mode 100644 subsystems/automation/hardware/schematics/sensors_breadboard.pdf diff --git a/.github/BOM_TEMPLATES/bom_template_purchase.csv b/.github/BOM_TEMPLATES/bom_template_purchase.csv deleted file mode 100644 index 5d2344cc..00000000 --- a/.github/BOM_TEMPLATES/bom_template_purchase.csv +++ /dev/null @@ -1,2 +0,0 @@ -Part,Description,Quantity,Supplier,Supplier Part Number,Unit Cost (CAD),Reference -E.g. Foam Board,"2"" Polypropylene",6,Home Depot,142-978D,12.64,www.homedepot.ca/foam-board \ No newline at end of file diff --git a/subsystems/automation/hardware/boms/automation_bom_components.csv b/subsystems/automation/hardware/boms/automation_bom_components.csv new file mode 100644 index 00000000..c4fe1162 --- /dev/null +++ b/subsystems/automation/hardware/boms/automation_bom_components.csv @@ -0,0 +1,12 @@ +Index ,Manufacturer Part Number ,Manufacturer Name ,Description ,Quantity ,Digi-Key Part Number ,Unit Price ,Extended Price ,Datasheet ,Reference Designator + 1 ,A000005 ,Arduino ,ARDUINO NANO ATMEGA328 EVAL BRD , 1 ,1050-1001-ND , 28.63000 ,$28.63 ,https://media.digikey.com/pdf/Data%20Sheets/Arduino%20PDFs/A000005.pdf ,U2 + 2 ,S404GSEJ6-U3000-3 ,"Delkin Devices, Inc." ,4GB MLC MICROSD CARD (-25C - +85 , 1 ,3247-S404GSEJ6-U3000-3-ND , 19.33000 ,$19.33 ,https://www.delkin.com/wp-content/uploads/2021/01/microSD-Industrial-Spec-Sheet-Jan-2021.pdf , + 3 ,61304021121 ,Würth Elektronik ,CONN HEADER VERT 40POS 2.54MM , 1 ,732-5310-ND , 2.70000 ,$2.70 ,https://www.we-online.com/katalog/datasheet/6130xx21121.pdf ,"U1, U2" + 4 ,SC0510 ,Raspberry Pi ,ZERO 2 W , 1 , , , ,https://datasheets.raspberrypi.com/rpizero2/raspberry-pi-zero-2-w-product-brief.pdf ,U1 + 5 ,DMN2005K-7 ,Diodes Incorporated ,MOSFET N-CH 20V 300MA SOT23-3 , 2 ,DMN2005K-7DICT-ND , 0.58000 ,$1.16 ,https://www.diodes.com/assets/Datasheets/ds30734.pdf ,"Q1, Q2" + 6 ,RC0603FR-0710KL ,YAGEO ,RES 10K OHM 1% 1/10W 0603 , 5 ,311-10.0KHRCT-ND , 0.15000 ,$0.75 ,https://www.yageo.com/upload/media/product/productsearch/datasheet/rchip/PYu-RC_Group_51_RoHS_L_11.pdf ,R1-5 + 7 ,4484 ,Adafruit Industries LLC ,MINI PITFT 1.3 FOR RASPBERRY PI , 1 ,1528-4484-ND , 22.02000 ,$22.02 ,https://media.digikey.com/pdf/Data%20Sheets/Adafruit%20PDFs/4484_Web.pdf , + 8 ,5055670271 ,Molex ,CONN HEADER SMD R/A 2POS 1.25MM , 2 ,WM17038CT-ND , 1.19000 ,$2.38 ,https://www.molex.com/pdm_docs/ps/5055650000-000.pdf ,"J3, J8" + 9 ,5055670471 ,Molex ,CONN HEADER SMD R/A 4POS 1.25MM , 5 ,WM17040CT-ND , 1.30000 ,$6.50 ,http://www.literature.molex.com/SQLImages/kelmscott/Molex/PDF_Images/987651-4888.pdf ,"J1, J2, J6, J9, J10" + 10 ,5055670871 ,Molex ,CONN HEADER SMD R/A 8POS 1.25MM , 3 ,WM17115CT-ND , 1.77000 ,$5.31 ,https://www.molex.com/pdm_docs/pk/5055679200-200.pdf ,"J4, J5, J7" + 11 ,5055670681 ,Molex ,CONN HEADER SMD R/A 6POS 1.25MM , 3 ,WM22471CT-ND , 1.42000 ,$4.26 ,https://www.molex.com/pdm_docs/ps/5055650002-000.pdf ,J11-13 \ No newline at end of file diff --git a/subsystems/automation/hardware/boms/automation_bom_purchase.csv b/subsystems/automation/hardware/boms/automation_bom_purchase.csv new file mode 100644 index 00000000..1c22ff1e --- /dev/null +++ b/subsystems/automation/hardware/boms/automation_bom_purchase.csv @@ -0,0 +1,2 @@ +Part ,Description ,Quantity ,Supplier ,Supplier Part Number ,Unit Cost (CAD) ,Reference +Automation Motherboard PCB ,"2 Layers, 1 oz. Copper, 1.6mm Thickness, Suggested: HASL Finish, White PCB, Black Silkscreen" ,1 (MOQ 5) ,JLCPCB ,N/A , 0.51 ,../gerber/ \ No newline at end of file diff --git a/subsystems/automation/hardware/boms/sensors_bom_digikey.csv b/subsystems/automation/hardware/boms/sensors_bom_digikey.csv deleted file mode 100644 index 8e3f8a73..00000000 --- a/subsystems/automation/hardware/boms/sensors_bom_digikey.csv +++ /dev/null @@ -1,12 +0,0 @@ -"Manufacturer Part Number" ,"Manufacturer" ,"Digi-Key Part Number" ,"Customer Reference" ,"Reference Designator" ,"Part Status" ,"Quantity" ,"Unit Price" ,"Extended Price" ,"Quantity Available" ,"Description" -"A000053" ,"Arduino" ,"1050-1066-ND" ,"Microcontroller" ,"U1" ,"Active" ,"1" ,"27.39000" ,"$27.39" ,"453" ,"ARDUINO MICRO ATMEGA32U4 EVAL BD" -"59630-1-T-03-A" ,"Littelfuse Inc." ,"F7185-ND" ,"Float Sensor" ,"S2" ,"Active" ,"1" ,"9.64000" ,"$9.64" ,"385" ,"REED SENSOR W/INTEGRAL FLOAT" -"RNMF14FTC4K70" ,"Stackpole Electronics Inc" ,"S4.7KCACT-ND" ,"Pull Down Resistor" ,"R1-3" ,"Active" ,"3" ,"0.14000" ,"$0.42" ,"51538" ,"RES 4.7K OHM 1/4W 1% AXIAL" -"030-8-0011" ,"Senseair North America Inc." ,"2194-030-8-0011-ND" ,"CO2 Sensor" ,"S3" ,"Active" ,"1" ,"107.08000" ,"$107.08" ,"134" ,"K30 EXTENDED RANGE" -"2857" ,"Adafruit Industries LLC" ,"1528-1540-ND" ,"SHT31 Breakout" ,"S1" ,"Active" ,"1" ,"19.30000" ,"$19.30" ,"357" ,"SENSOR HUMID/TEMP 5V I2C 2% MOD" -"NAU7802KGI" ,"Nuvoton Technology Corporation of America" ,"NAU7802KGI-ND" ,"24 Bit ADC" ,"U2" ,"Active" ,"1" ,"3.08000" ,"$3.08" ,"377" ,"IC ADC 24BIT SIGMA-DELTA 16DIP" -"C322C104K5R5TA" ,"KEMET" ,"399-4329-ND" ,"" ,"C1,C2" ,"Active" ,"2" ,"0.33000" ,"$0.66" ,"106126" ,"CAP CER 0.1UF 50V X7R RADIAL" -"FG14X7R1A106KRT06" ,"TDK Corporation" ,"445-173132-1-ND" ,"IC Decoupling" ,"C4,C5" ,"Active" ,"2" ,"0.77000" ,"$1.54" ,"6043" ,"CAP CER 10UF 10V X7R RADIAL" -"PH1-14-UA" ,"Adam Tech" ,"2057-PH1-14-UA-ND" ,"" ,"" ,"Active" ,"1" ,"0.29000" ,"$0.29" ,"2756" ,"CONN HEADER VERT 14POS 2.54MM" -"SEN-14727" ,"SparkFun Electronics" ,"1568-1898-ND" ,"Load Cell" ,"" ,"Active" ,"2" ,"13.83000" ,"$27.66" ,"196" ,"MINI LOAD CELL 100G STRAIGHT BAR" -"K331J15C0GF5TL2" ,"Vishay Beyschlag/Draloric/BC Components" ,"BC1019CT-ND" ,"" ,"" ,"Active" ,"1" ,"0.40000" ,"$0.40" ,"7440" ,"CAP CER 330PF 50V C0G/NP0 RADIAL" \ No newline at end of file diff --git a/subsystems/automation/hardware/schematics/sensors_breadboard.pdf b/subsystems/automation/hardware/schematics/sensors_breadboard.pdf deleted file mode 100644 index bac5a54ceec69abeb5b8dccfc1f42c9a6999c6bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56195 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 From edc5fa9f87a39ec4bb6821422477d242b5844311 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sat, 18 Jun 2022 23:24:44 -0400 Subject: [PATCH 21/85] Minor Fix Made intervals private fields --- software/src/controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/software/src/controller.ts b/software/src/controller.ts index bac37d1f..9ad7df1b 100644 --- a/software/src/controller.ts +++ b/software/src/controller.ts @@ -95,7 +95,7 @@ export default class MicroController implements Controller { serial: SerialPort; parser: ReadlineParser; - timeout?: NodeJS.Timeout; + private timeout?: NodeJS.Timeout; constructor(readonly serialport: string) { @@ -218,7 +218,7 @@ export default class MicroController implements Controller { */ export class SimulatedController implements Controller { - intervals: NodeJS.Timeout[] = []; + private intervals: NodeJS.Timeout[] = []; constructor(readonly parameters: SimulatorConfig) { } From c5a5b25b7435ffcb0ca07f7c8f7767dcd2402ca1 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sun, 26 Jun 2022 14:13:53 -0400 Subject: [PATCH 22/85] PeaPodOS v0.1.1 --- software/package-lock.json | 12 ++++++------ software/package.json | 2 +- software/tsconfig.json | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/software/package-lock.json b/software/package-lock.json index c15b67dc..30c60eaf 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -5121,9 +5121,9 @@ } }, "node_modules/protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -10135,9 +10135,9 @@ } }, "protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", diff --git a/software/package.json b/software/package.json index 9bbbe623..cf27c77b 100644 --- a/software/package.json +++ b/software/package.json @@ -1,6 +1,6 @@ { "name": "@peapodtech/peapodos", - "version": "0.1.0", + "version": "0.1.1", "description": "Main software for PeaPod.", "main": "index.js", "directories": { diff --git a/software/tsconfig.json b/software/tsconfig.json index 00c8addc..d42d79ea 100644 --- a/software/tsconfig.json +++ b/software/tsconfig.json @@ -8,6 +8,7 @@ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "skipLibCheck": true, /* Skip type checking of declaration files. */ "declaration": true, + "moduleResolution": "Node", "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ // No outDir: keep index.js at package root, only the binary gets published }, From bfdd7306430e57919ee07c3bab62a9f82425bd29 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Tue, 5 Jul 2022 13:28:01 -0400 Subject: [PATCH 23/85] Fixed Reset - Removed sysfs GPIO util functions + Implemeneted `onoff` package GPIO --- software/README.md | 55 ++++--------- software/package-lock.json | 136 +++++++++++++++++++++++++------ software/package.json | 3 +- software/src/controller.ts | 58 +++++++------ software/src/errors.ts | 6 -- software/src/peapod.ts | 8 +- software/src/utils.ts | 73 ----------------- software/tests/testUtils.test.ts | 24 +----- 8 files changed, 168 insertions(+), 195 deletions(-) diff --git a/software/README.md b/software/README.md index 799c1654..e0dd6efc 100644 --- a/software/README.md +++ b/software/README.md @@ -17,7 +17,6 @@ Designed as both a hassle-free food production system and a research tool for pr - [Setting Up Raspberry Pi](#setting-up-raspberry-pi) - [Development](#development) - [Arduino Test Suite](#arduino-test-suite) - - [Raspberry Pi Serial Testing](#raspberry-pi-serial-testing) - [Build from Source](#build-from-source) *** @@ -35,7 +34,7 @@ The following are performed on a computer: 1. Format a microSD card (>=4GB) with a single FAT partition. 2. Download the Raspberry Pi Imager [(Download)](https://www.raspberrypi.com/software/). -3. Flash the SD card with a *Raspberry Pi OS Lite* image. +3. Flash the SD card with a *Raspberry Pi OS Lite (64-bit)* image. > Note: In Future, a custom PeaPod Raspberry Pi OS Lite image will be released with steps 5, 6, and 12 already complete. @@ -46,7 +45,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: 5. Login with the default username (`pi`) and password (`raspberry`). 6. Execute `sudo raspi-config` and perform these steps to setup the Pi: 1. *System Options > Wireless Lan* - Setup WiFi and connect to the Internet - 2. *System Options > Hostname* - Set a unique host name (`e.g. john-peapod`) + 2. *System Options > Hostname* - Set a unique hostname (`e.g. peapod`) 3. *System Options > Boot/Auto Login* - Select `Console Autologin` 4. *Interface Options > SSH* - `Enable` SSH Server 5. *Interface Options > Serial Port* - `Disable` serial login shell, but `Enable` the serial port hardware @@ -57,7 +56,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: 10. Optional: *Advanced Options > Expand Filesystem* 11. Reboot to save: `sudo reboot` -> You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@peapod.local`, or with VS Code) +> You can now SSH into the Raspberry Pi to perform the rest of the setup (`ssh pi@{hostname}.local` with your chosen hostname, or with VS Code) > Note: In Future, steps 7-11 will be performed at runtime. 7. Update Packages: @@ -65,21 +64,10 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude python3-venv` (could take a while) 3. Install main software package: `sudo npm i -g @peapodtech/peapodos --save` - - 8. Install PlatformIO Core with `python3 -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)"` 9. Create a custom configuration file for the AVR flash utility *avrdude* to be able to program the Arduino Nano via ICSP over the Raspberry Pi's GPIO pins: - 1. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/software/microcontroller/avrdude_gpio.conf`, then modify your copy with `nano ~/software/microcontroller/avrdude_gpio.conf`. Copy the following to the end of the file: + 1. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/avrdude_gpio.conf`, then modify your copy with `nano ~/avrdude_gpio.conf`. Copy the following to the end of the file: ``` # Raspberry Pi GPIO configuration for avrdude. @@ -160,7 +148,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: avrdude done. Thank you. ``` -10. Perform first-time flashing with `~/.platformio/penv/bin/platformio run -d ~/software/microcontroller/ --target upload` +10. Perform first-time flashing with `~/.platformio/penv/bin/platformio run -d ~/microcontroller/ --target upload` 11. Edit the `sudoers` file to allow `avrdude` to be executed using `sudo` *without a password*: 1. Open the `sudoers` file: `sudo visudo` @@ -170,11 +158,11 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: (*Ctrl-O* to save, *Ctrl-X* to exit; *avrdude* can be located with `whereis avrdude`) -12. Install the *UV4L* camera library: + 13. Populate a `.env` file with Firebase and Google and/or GitHub auth keys (a template is provided as `.env.template`), as well as the field `SERIALPORT="/dev/ttyS0"` (Raspberry Pi Zero 2 W GPIO mini-UART). @@ -185,29 +173,14 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: ### Arduino Test Suite -To run the PlatformIO Arduino test suite: `~/.platformio/penv/bin/platformio test -d ~/software/microcontroller/` - -### Raspberry Pi Serial Testing - -To test the GPIO serial port (`/dev/ttyS0`), connect GPIO pins 8 and 10 (TX and RX) and execute the following: -1. Install the Python package manager *pip*: `sudo apt-get install pip` -2. Install the Serial package *pyserial*: `pip install pyserial` -3. Execute the test script: `python ~/PeaPod/software/tests/serialTest.py` - -Correct output should look like: - -``` -Serial port '/dev/ttyS0' ready for test: -Sent 20 bytes: 'Testing Raspberry Pi GPIO serial port ...' -Received 20 bytes: 'Testing Raspberry Pi GPIO serial port ...' -Port '/dev/ttyS0' is is setup correctly! -``` +To run the PlatformIO test suite: `~/.platformio/penv/bin/platformio test -d ~/microcontroller/` ### Build from Source 1. Install TypeScript language support and compiler, as well as a Node build tool: `sudo npm install -g typescript` -2. Clone source -3. Navigate to `software/` folder -4. Build the `serialport` package from source: `sudo npm install serialport --unsafe-perm --build-from-source` -5. Install all other Node dependencies: `npm i` -6. Compile the source with `tsc` and execute with `node .` \ No newline at end of file +2. Clone this source, copy contents of `software/` (only the essentials: `index.ts`, `package.json`, `tsconfig.json`, `src/`, and `microcontroller/`)to home folder `~/` +3. Build the `serialport` package from source: `sudo npm install serialport --unsafe-perm --build-from-source` +4. Install all other Node dependencies: `npm i` +5. Compile: `tsc` +6. Populate the `.env` file (see `.env.template`) +7. Execute: `node .` \ No newline at end of file diff --git a/software/package-lock.json b/software/package-lock.json index 30c60eaf..f477c46a 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -1,12 +1,12 @@ { "name": "@peapodtech/peapodos", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@peapodtech/peapodos", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "dependencies": { "@mapbox/node-pre-gyp": "^1.0.8", @@ -17,10 +17,11 @@ "firebase": "^9.3.0", "jsonwebtoken": "^8.5.1", "mqtt": "^4.2.8", + "onoff": "^6.0.3", "ora": "^5.3.0", "pi-camera": "^1.6.0", "rxjs": "^6.6.3", - "serialport": "^10.3.0", + "serialport": "^10.4.0", "uuid": "^8.3.2", "wrtc": "^0.4.7", "yargs": "^16.2.0" @@ -1518,9 +1519,9 @@ } }, "node_modules/@serialport/bindings-cpp": { - "version": "10.6.3", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.6.3.tgz", - "integrity": "sha512-jmALQ62JvIlCIx8osWifVXX1GoftlTPiX/HuEYeCYlRmtwXrbBzoD8dXu48YfGoZ/dwaYYKTsJarHqFoPq809Q==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.7.0.tgz", + "integrity": "sha512-Xx1wA2UCG2loS32hxNvWJI4smCzGKhWqE85//fLRzHoGgE1lSLe3Nk7W40/ebrlGFHWRbQZmeaIF4chb2XLliA==", "hasInstallScript": true, "dependencies": { "@serialport/bindings-interface": "1.2.1", @@ -2165,6 +2166,14 @@ } ] }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2779,6 +2788,19 @@ "once": "^1.4.0" } }, + "node_modules/epoll": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/epoll/-/epoll-4.0.1.tgz", + "integrity": "sha512-BgCq0nEsk+XI7y9qjrRtt9uXsyFEdvevvq42xl6t/hKZjxLSDZreD9rTZ0pU40V//c3Zzk2PZGuIsn8YJHSJ4g==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.14.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2963,6 +2985,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4481,6 +4508,11 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -4737,6 +4769,11 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, + "node_modules/nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4768,9 +4805,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -4871,6 +4908,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/onoff": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/onoff/-/onoff-6.0.3.tgz", + "integrity": "sha512-xtVlwRDzswYM69bzzIui/qzu7QHsFnjsQiCV1iYVA/HXt5xdc9utc97SYAlXzK8wAhIN7+H7MaVqh2vpfdKk9A==", + "dependencies": { + "epoll": "^4.0.1", + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -5373,12 +5422,12 @@ } }, "node_modules/serialport": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.3.0.tgz", - "integrity": "sha512-MZMRlQgWOG7boLkJwm200Z3W39GcFmGo3tWxQrvyrZpH7W1p3t16M1VBglVxR6wXRF8qm01VxXBA+rNV7fPMVA==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.4.0.tgz", + "integrity": "sha512-PszPM5SnFMgSXom60PkKS2A9nMlNbHkuoyRBlzdSWw9rmgOn258+V0dYbWMrETJMM+TJV32vqBzjg5MmmUMwMw==", "dependencies": { "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "10.6.3", + "@serialport/bindings-cpp": "10.7.0", "@serialport/parser-byte-length": "10.3.0", "@serialport/parser-cctalk": "10.3.0", "@serialport/parser-delimiter": "10.3.0", @@ -7379,9 +7428,9 @@ } }, "@serialport/bindings-cpp": { - "version": "10.6.3", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.6.3.tgz", - "integrity": "sha512-jmALQ62JvIlCIx8osWifVXX1GoftlTPiX/HuEYeCYlRmtwXrbBzoD8dXu48YfGoZ/dwaYYKTsJarHqFoPq809Q==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.7.0.tgz", + "integrity": "sha512-Xx1wA2UCG2loS32hxNvWJI4smCzGKhWqE85//fLRzHoGgE1lSLe3Nk7W40/ebrlGFHWRbQZmeaIF4chb2XLliA==", "requires": { "@serialport/bindings-interface": "1.2.1", "@serialport/parser-readline": "^10.2.1", @@ -7868,6 +7917,14 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -8343,6 +8400,15 @@ "once": "^1.4.0" } }, + "epoll": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/epoll/-/epoll-4.0.1.tgz", + "integrity": "sha512-BgCq0nEsk+XI7y9qjrRtt9uXsyFEdvevvq42xl6t/hKZjxLSDZreD9rTZ0pU40V//c3Zzk2PZGuIsn8YJHSJ4g==", + "requires": { + "bindings": "^1.5.0", + "nan": "^2.14.2" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -8475,6 +8541,11 @@ "escape-string-regexp": "^1.0.5" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -9650,6 +9721,11 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -9855,6 +9931,11 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, + "nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -9875,9 +9956,9 @@ } }, "node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==" }, "node-int64": { "version": "0.4.0", @@ -9952,6 +10033,15 @@ "mimic-fn": "^2.1.0" } }, + "onoff": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/onoff/-/onoff-6.0.3.tgz", + "integrity": "sha512-xtVlwRDzswYM69bzzIui/qzu7QHsFnjsQiCV1iYVA/HXt5xdc9utc97SYAlXzK8wAhIN7+H7MaVqh2vpfdKk9A==", + "requires": { + "epoll": "^4.0.1", + "lodash.debounce": "^4.0.8" + } + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -10323,12 +10413,12 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "serialport": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.3.0.tgz", - "integrity": "sha512-MZMRlQgWOG7boLkJwm200Z3W39GcFmGo3tWxQrvyrZpH7W1p3t16M1VBglVxR6wXRF8qm01VxXBA+rNV7fPMVA==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.4.0.tgz", + "integrity": "sha512-PszPM5SnFMgSXom60PkKS2A9nMlNbHkuoyRBlzdSWw9rmgOn258+V0dYbWMrETJMM+TJV32vqBzjg5MmmUMwMw==", "requires": { "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "10.6.3", + "@serialport/bindings-cpp": "10.7.0", "@serialport/parser-byte-length": "10.3.0", "@serialport/parser-cctalk": "10.3.0", "@serialport/parser-delimiter": "10.3.0", diff --git a/software/package.json b/software/package.json index cf27c77b..a066f336 100644 --- a/software/package.json +++ b/software/package.json @@ -38,10 +38,11 @@ "firebase": "^9.3.0", "jsonwebtoken": "^8.5.1", "mqtt": "^4.2.8", + "onoff": "^6.0.3", "ora": "^5.3.0", "pi-camera": "^1.6.0", "rxjs": "^6.6.3", - "serialport": "^10.3.0", + "serialport": "^10.4.0", "uuid": "^8.3.2", "wrtc": "^0.4.7", "yargs": "^16.2.0" diff --git a/software/src/controller.ts b/software/src/controller.ts index 9ad7df1b..c721915d 100644 --- a/software/src/controller.ts +++ b/software/src/controller.ts @@ -2,7 +2,8 @@ import chalk from 'chalk'; import { ReadlineParser, SerialPort } from 'serialport'; import { ControllerInstructionsError } from './errors'; import * as Spinner from './ui'; -import { gpioUnexport, gpioWrite, sleep, updateMicrocontroller } from './utils'; +import { sleep, updateMicrocontroller } from './utils'; +import { Gpio } from 'onoff'; // CONSTANTS @@ -22,9 +23,9 @@ const CONTROLLER_REVISION = 0; const SERIAL_TIMEOUT_SECONDS = 10; /** - * Linux `sysfs` GPIO pin attached to the active-low reset pin on the microcontroller. + * GPIO pin attached to the reset grounding circuit */ -const RESET_PIN = 5; + const RESET_PIN = 26; // TYPES @@ -96,8 +97,11 @@ export default class MicroController implements Controller { serial: SerialPort; parser: ReadlineParser; private timeout?: NodeJS.Timeout; + private resetpin: Gpio; constructor(readonly serialport: string) { + // Reset pin GPIO interface + this.resetpin = new Gpio(RESET_PIN, 'out'); // Create the serial port interface this.serial = new SerialPort({ @@ -116,22 +120,9 @@ export default class MicroController implements Controller { start(onMessage: (msg: ControllerMessage) => void): Promise { // Explicit promise construction so we can resolve only on valid comms AND revision check return new Promise(async (res, rej) => { - // Reset the microcontroller + // Reset the microcontroller (opens the serial port) await this.reset(); - // Open the serial port - await new Promise((reso, reje) => { - Spinner.start('Establishing serial communications with the microcontroller...'); - this.serial.open(err => { - if (err) { - reje(err); - } else { - Spinner.succeed('Microcontroller serial communications established!'); - reso(); - } - }); - }); - Spinner.start('Awaiting microcontroller software revision number...'); this.resetTimeout(); @@ -185,6 +176,7 @@ export default class MicroController implements Controller { private resetTimeout(timeoutSeconds: number = SERIAL_TIMEOUT_SECONDS): void { this.clearTimeout(); this.timeout = setTimeout(() => { + Spinner.fail(`Microcontroller serial communications timed out after ${ timeoutSeconds } seconds.`); this.reset(); }, timeoutSeconds*1000); } @@ -198,18 +190,36 @@ export default class MicroController implements Controller { stop(): void { this.clearTimeout(); - this.serial.close(); - this.reset(); + if (this.serial.isOpen) this.serial.close(); } /** - * Resets the microcontroller (active-low). + * Resets the microcontroller by closing and re-opening serial. */ private async reset(): Promise { - gpioWrite(RESET_PIN, 0); - await sleep(10); - gpioWrite(RESET_PIN, 1); - gpioUnexport(RESET_PIN); + // Stop and reset + this.stop(); + this.resetpin.writeSync(1); + + // Wait, then stop resetting + await sleep(1000); + this.resetpin.writeSync(0); + + // (Re-)open serial + await new Promise((reso, reje) => { + Spinner.start('Establishing serial communications with the microcontroller...'); + this.serial.open(err => { + if (err) { + reje(err); + } else { + Spinner.succeed('Microcontroller serial communications established!'); + reso(); + } + }); + }); + + // Restart timeout + this.resetTimeout(); } } diff --git a/software/src/errors.ts b/software/src/errors.ts index 8af88bf4..3a78e441 100644 --- a/software/src/errors.ts +++ b/software/src/errors.ts @@ -15,10 +15,4 @@ export class EnvFieldError extends Error { constructor(mode: string, missingFields: string[]) { super(`.env file is missing the following fields necessary for ${ mode } functionality: ${ missingFields.join(", ") }`); } -}; - -export class GPIOError extends Error { - constructor(operation: string, pin: number, value?: string | number) { - super(`GPIO '${ operation }' operation failed (pin ${ pin }${ value === undefined ? '' : `, value '${ value }` }')`); - } }; \ No newline at end of file diff --git a/software/src/peapod.ts b/software/src/peapod.ts index e2de99db..ba8218b4 100644 --- a/software/src/peapod.ts +++ b/software/src/peapod.ts @@ -47,7 +47,7 @@ export default class PeaPod { this.controller = new MicroController(ENV_PEAPOD.serialport); // CREATE PUBLISHER - Spinner.info(`Running ${ chalk.green('PeaPod') } in ${ pm } mode.`) + Spinner.info(`Running ${ chalk.green('PeaPod') } in ${ pm } mode.`); switch (pm) { case PublishingMode.ONLINE: @@ -79,19 +79,19 @@ export default class PeaPod { // Accumulate data into batches batch[msg.data.label].push({ timestamp: Date.now(), - value: msg.data.value + value: msg.data.value, }); break; case "revision": this.controller.write(instructions); break; default: - Spinner.log(`[${ chalk.blueBright('CONTROLLER') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`) + Spinner.log(`[${ chalk.blueBright('CONTROLLER') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`); } }); // INITIALIZE PUBLISHER - let {projectid, projectname, run} = await this.publisher.start(config => { + let { projectid, projectname, run } = await this.publisher.start(config => { // Hot-swap programs // TODO: This Spinner.log(`[${ chalk.yellow('PUBLISHER') } | CONFIG] - ${ config }`); diff --git a/software/src/utils.ts b/software/src/utils.ts index f3ffa5b1..d7a0fa7c 100644 --- a/software/src/utils.ts +++ b/software/src/utils.ts @@ -2,7 +2,6 @@ import * as dns from 'dns'; import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; import axios from 'axios'; import { spawn } from 'child_process'; -import { GPIOError } from './errors'; /** * Checks the internet connectivity. @@ -93,76 +92,4 @@ export function execute(command: string, failureCodes: number[] = []): Promise /sys/class/gpio/export`); - } catch (err) { - throw new GPIOError('export', pin); - } - } -} - -/** - * Unexports a pin (if it has been previously exported) - * @param pin The GPIO pin to unexport. - */ -export function gpioUnexport(pin: number): void { - if (existsSync(`/sys/class/gpio/gpio${ pin }/`)) { - try { - execute(`echo ${ pin } > /sys/class/gpio/unexport`); - } catch (err) { - throw new GPIOError('unexport', pin); - } - } -} - -/** - * Exports and sets the direction (output) of a pin, and writes either 1 or 0 to it. - * Does NOT unexport the pin. - * @param pin The GPIO pin to write to. - * @param value 1 or 0. - */ -export function gpioWrite(pin: number, value: 0 | 1): void { - gpioExport(pin); - try { - writeFileSync(`/sys/class/gpio/gpio${ pin }/direction`, 'out'); - } catch (err) { - throw new GPIOError('direction', pin, 'out'); - } - try { - writeFileSync(`/sys/class/gpio/gpio${ pin }/value`, String(value)); - } catch (err) { - throw new GPIOError('write', pin, value); - } -} - -/** - * Exports and sets the direction (input) of a pin, reads from it, and returns the value. - * Does NOT unexport the pin. - * @param pin The GPIO pin to read from. - * @param value - */ -export function gpioRead(pin: number): number { - gpioExport(pin); - try { - writeFileSync(`/sys/class/gpio/gpio${ pin }/direction`, 'in'); - } catch (err) { - throw new GPIOError('direction', pin, 'in'); - } - let raw: string; - try { - raw = readFileSync(`/sys/class/gpio/gpio${ pin }/value`).toString(); - } catch (err) { - throw new GPIOError('read', pin); - } - const value: number = Number(raw); - if (isNaN(value)) throw new GPIOError('read', pin); - return value; } \ No newline at end of file diff --git a/software/tests/testUtils.test.ts b/software/tests/testUtils.test.ts index 6b377fc3..c4a9556e 100644 --- a/software/tests/testUtils.test.ts +++ b/software/tests/testUtils.test.ts @@ -1,28 +1,6 @@ -import { execute, gpioExport, gpioUnexport, gpioWrite } from '../src/utils' -import { existsSync, readFileSync } from 'fs'; - -const TEST_PIN = 24; +import { execute } from '../src/utils' test('code execution works', () => { expect.assertions(1); return expect(execute('echo hello world!')).resolves.toEqual('> echo hello world!\nhello world!\n'); -}); - -test('gpio pins can be exported', () => { - expect.assertions(1); - gpioExport(TEST_PIN); - return expect(existsSync(`/sys/class/gpio/gpio${TEST_PIN}/`)).toBe(true); -}); - -test('gpio pins are unexported', () => { - expect.assertions(1); - gpioUnexport(TEST_PIN); - return expect(existsSync(`/sys/class/gpio/gpio${TEST_PIN}/`)).toBe(false); -}); - -test('gpio pins can be written to', () => { - expect.assertions(1); - gpioExport(TEST_PIN); - gpioWrite(TEST_PIN, 1); - return expect(readFileSync(`/sys/class/gpio/gpio${TEST_PIN}/value`).toString()).toMatch(/^1$/); }); \ No newline at end of file From 312039077af844baf5172f01acf5a18e567af3d6 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 15 Jul 2022 17:25:26 -0400 Subject: [PATCH 24/85] Supply Pump, Solenoid Actuators --- software/README.md | 7 ++-- software/microcontroller/platformio.ini | 10 +++++ .../microcontroller/src/actuators/actuator.h | 2 + software/microcontroller/src/actuators/led.cc | 40 ++----------------- software/microcontroller/src/actuators/led.h | 9 +---- .../microcontroller/src/actuators/onoff.cc | 21 ++++++++++ .../microcontroller/src/actuators/onoff.h | 26 ++++++++++++ software/microcontroller/src/actuators/pwm.cc | 22 ++++++++++ software/microcontroller/src/actuators/pwm.h | 26 ++++++++++++ .../microcontroller/src/actuators/solenoid.cc | 9 +++++ .../microcontroller/src/actuators/solenoid.h | 19 +++++++++ .../microcontroller/src/actuators/supply.cc | 9 +++++ .../microcontroller/src/actuators/supply.h | 19 +++++++++ software/microcontroller/src/main.h | 12 +++++- 14 files changed, 182 insertions(+), 49 deletions(-) create mode 100644 software/microcontroller/src/actuators/onoff.cc create mode 100644 software/microcontroller/src/actuators/onoff.h create mode 100644 software/microcontroller/src/actuators/pwm.cc create mode 100644 software/microcontroller/src/actuators/pwm.h create mode 100644 software/microcontroller/src/actuators/solenoid.cc create mode 100644 software/microcontroller/src/actuators/solenoid.h create mode 100644 software/microcontroller/src/actuators/supply.cc create mode 100644 software/microcontroller/src/actuators/supply.h diff --git a/software/README.md b/software/README.md index e0dd6efc..0cce9216 100644 --- a/software/README.md +++ b/software/README.md @@ -148,7 +148,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: avrdude done. Thank you. ``` -10. Perform first-time flashing with `~/.platformio/penv/bin/platformio run -d ~/microcontroller/ --target upload` +10. Perform first-time flashing with `~/.platformio/penv/bin/platformio run -e peapod -d ~/microcontroller/ --target upload` 11. Edit the `sudoers` file to allow `avrdude` to be executed using `sudo` *without a password*: 1. Open the `sudoers` file: `sudo visudo` @@ -173,7 +173,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: ### Arduino Test Suite -To run the PlatformIO test suite: `~/.platformio/penv/bin/platformio test -d ~/microcontroller/` +To run the PlatformIO test suite: `~/.platformio/penv/bin/platformio test -e peapod -d ~/microcontroller/` ### Build from Source @@ -183,4 +183,5 @@ To run the PlatformIO test suite: `~/.platformio/penv/bin/platformio test -d ~/m 4. Install all other Node dependencies: `npm i` 5. Compile: `tsc` 6. Populate the `.env` file (see `.env.template`) -7. Execute: `node .` \ No newline at end of file +7. Compile and upload microcontroller software: `~/.platformio/penv/bin/platformio run -e peapod -d ~/microcontroller/ --target upload` +8. Execute: `node .` \ No newline at end of file diff --git a/software/microcontroller/platformio.ini b/software/microcontroller/platformio.ini index 6828f650..34ac7f42 100644 --- a/software/microcontroller/platformio.ini +++ b/software/microcontroller/platformio.ini @@ -11,4 +11,14 @@ test_port = /dev/ttyS0 test_speed = 115200 test_build_project_src = true monitor_port = /dev/ttyS0 +monitor_speed = 115200 + +[env:direct] +platform = atmelavr +board = nanoatmega328 +framework = arduino +test_port = /dev/ttyS0 +test_speed = 115200 +test_build_project_src = true +monitor_port = /dev/ttyS0 monitor_speed = 115200 \ No newline at end of file diff --git a/software/microcontroller/src/actuators/actuator.h b/software/microcontroller/src/actuators/actuator.h index bb9425a0..e64e70f6 100644 --- a/software/microcontroller/src/actuators/actuator.h +++ b/software/microcontroller/src/actuators/actuator.h @@ -7,6 +7,8 @@ typedef enum actuatorid_t { ACTUATOR_NULL, ACTUATOR_LED, + ACTUATOR_SUPPLY, + ACTUATOR_SOLENOID, } actuatorid_t; // All actuator state info diff --git a/software/microcontroller/src/actuators/led.cc b/software/microcontroller/src/actuators/led.cc index e355069d..609e2d36 100644 --- a/software/microcontroller/src/actuators/led.cc +++ b/software/microcontroller/src/actuators/led.cc @@ -4,42 +4,8 @@ #include #include +#include -LED::LED(uint8_t pin, led_color_t color) : Actuator(ACTUATOR_LED, 0) { - this->pin = pin; +LED::LED(uint8_t pin, led_color_t color) : PWM(pin, ACTUATOR_LED) { this->color = color; -} - -errorlevel_t LED::initialize(void) { - pinMode(this->pin, OUTPUT); - return ERR_NONE; -} - -errorlevel_t LED::set(float target) { - // Clamp to 0 < x < 1 - target = min(max(target, 0), 1); - analogWrite(this->pin, target * 255); - return ERR_NONE; -} - -/** - * UNUSED. - * @param led Enum representation of the LED. - * @return String representation of the LED. - */ -// static String parseLED(led_color_t led) { -// switch (led) { -// case LEDCOLOR_BLUE: -// return String("Royal Blue LED"); -// case LEDCOLOR_COOL: -// return String("Cool White LED"); -// case LEDCOLOR_WARM: -// return String("Warm White LED"); -// case LEDCOLOR_RED: -// return String("Photo Red LED"); -// case LEDCOLOR_FAR: -// return String("Far Red LED"); -// default: -// return String("Unknown LED"); -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/software/microcontroller/src/actuators/led.h b/software/microcontroller/src/actuators/led.h index 77568d58..4e793dcf 100644 --- a/software/microcontroller/src/actuators/led.h +++ b/software/microcontroller/src/actuators/led.h @@ -5,8 +5,9 @@ #include #include +#include -class LED : public Actuator { +class LED : public PWM { public: typedef enum { LEDCOLOR_BLUE, @@ -23,12 +24,6 @@ class LED : public Actuator { */ LED(uint8_t pin, led_color_t color); private: - errorlevel_t initialize(void) override; - errorlevel_t set(float target) override; - - // PWM output pin. - uint8_t pin; - // LED color. led_color_t color; }; diff --git a/software/microcontroller/src/actuators/onoff.cc b/software/microcontroller/src/actuators/onoff.cc new file mode 100644 index 00000000..141f0d33 --- /dev/null +++ b/software/microcontroller/src/actuators/onoff.cc @@ -0,0 +1,21 @@ +#include + +#include + +#include +#include + +OnOff::OnOff(uint8_t pin, actuatorid_t actuatorid) : Actuator(actuatorid, 0) { + this->pin = pin; +} + +errorlevel_t OnOff::initialize(void) { + pinMode(this->pin, OUTPUT); + return ERR_NONE; +} + +errorlevel_t OnOff::set(float target) { + // Round to 0 or 1 + digitalWrite(this->pin, target >= 0.5 ? 1 : 0); + return ERR_NONE; +} \ No newline at end of file diff --git a/software/microcontroller/src/actuators/onoff.h b/software/microcontroller/src/actuators/onoff.h new file mode 100644 index 00000000..b82d2e26 --- /dev/null +++ b/software/microcontroller/src/actuators/onoff.h @@ -0,0 +1,26 @@ +#ifndef PEAPOD_ACTUATORS_ONOFF_H_ +#define PEAPOD_ACTUATORS_ONOFF_H_ + +#include + +#include +#include + +class OnOff : public Actuator { + public: + + /** + * Constructor. + * @param pin Digital output pin for controlling this on/off actuator. + * @param actuatorid ID of this actuator + */ + OnOff(uint8_t pin, actuatorid_t actuatorid); + private: + errorlevel_t initialize(void) override; + errorlevel_t set(float target) override; + + // Digital output pin. + uint8_t pin; +}; + +#endif diff --git a/software/microcontroller/src/actuators/pwm.cc b/software/microcontroller/src/actuators/pwm.cc new file mode 100644 index 00000000..72482b3c --- /dev/null +++ b/software/microcontroller/src/actuators/pwm.cc @@ -0,0 +1,22 @@ +#include + +#include + +#include +#include + +PWM::PWM(uint8_t pin, actuatorid_t actuatorid) : Actuator(actuatorid, 0) { + this->pin = pin; +} + +errorlevel_t PWM::initialize(void) { + pinMode(this->pin, OUTPUT); + return ERR_NONE; +} + +errorlevel_t PWM::set(float target) { + // Clamp to 0 < x < 1 + target = min(max(target, 0), 1); + analogWrite(this->pin, target * 255); + return ERR_NONE; +} \ No newline at end of file diff --git a/software/microcontroller/src/actuators/pwm.h b/software/microcontroller/src/actuators/pwm.h new file mode 100644 index 00000000..7fd38f0f --- /dev/null +++ b/software/microcontroller/src/actuators/pwm.h @@ -0,0 +1,26 @@ +#ifndef PEAPOD_ACTUATORS_PWM_H_ +#define PEAPOD_ACTUATORS_PWM_H_ + +#include + +#include +#include + +class PWM : public Actuator { + public: + + /** + * Constructor. + * @param pin Digital output PWM pin for controlling this PWM-capable actuator. + * @param actuatorid ID of this actuator + */ + PWM(uint8_t pin, actuatorid_t actuatorid); + private: + errorlevel_t initialize(void) override; + errorlevel_t set(float target) override; + + // PWM output pin. + uint8_t pin; +}; + +#endif diff --git a/software/microcontroller/src/actuators/solenoid.cc b/software/microcontroller/src/actuators/solenoid.cc new file mode 100644 index 00000000..a194f6db --- /dev/null +++ b/software/microcontroller/src/actuators/solenoid.cc @@ -0,0 +1,9 @@ +#include + +#include + +#include +#include +#include + +Solenoid::Solenoid(uint8_t pin) : OnOff(pin, ACTUATOR_SOLENOID) { } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/solenoid.h b/software/microcontroller/src/actuators/solenoid.h new file mode 100644 index 00000000..4f3a058d --- /dev/null +++ b/software/microcontroller/src/actuators/solenoid.h @@ -0,0 +1,19 @@ +#ifndef PEAPOD_ACTUATORS_SOLENOID_H_ +#define PEAPOD_ACTUATORS_SOLENOID_H_ + +#include + +#include +#include +#include + +class Solenoid : public OnOff { + public: + /** + * Constructor. + * @param pin PWM pin for controlling this LED. + */ + Solenoid(uint8_t pin); +}; + +#endif diff --git a/software/microcontroller/src/actuators/supply.cc b/software/microcontroller/src/actuators/supply.cc new file mode 100644 index 00000000..52ec5678 --- /dev/null +++ b/software/microcontroller/src/actuators/supply.cc @@ -0,0 +1,9 @@ +#include + +#include + +#include +#include +#include + +SupplyPump::SupplyPump(uint8_t pin) : OnOff(pin, ACTUATOR_SUPPLY) { } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/supply.h b/software/microcontroller/src/actuators/supply.h new file mode 100644 index 00000000..66e76629 --- /dev/null +++ b/software/microcontroller/src/actuators/supply.h @@ -0,0 +1,19 @@ +#ifndef PEAPOD_ACTUATORS_SUPPLY_H_ +#define PEAPOD_ACTUATORS_SUPPLY_H_ + +#include + +#include +#include +#include + +class SupplyPump : public OnOff { + public: + /** + * Constructor. + * @param pin PWM pin for controlling this LED. + */ + SupplyPump(uint8_t pin); +}; + +#endif diff --git a/software/microcontroller/src/main.h b/software/microcontroller/src/main.h index 9a50ff50..3a875c26 100644 --- a/software/microcontroller/src/main.h +++ b/software/microcontroller/src/main.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include // Settings @@ -12,7 +14,7 @@ // Constants #define NUM_SENSORS 2 -#define NUM_ACTUATORS 5 +#define NUM_ACTUATORS 7 #define REVISION 0 #define BAUDRATE 115200 @@ -23,6 +25,8 @@ #define PIN_LEDWARM 6 #define PIN_LEDRED 9 #define PIN_LEDFAR 10 +#define PIN_SUPPLY 8 +#define PIN_SOLENOID 7 // Sensors SHT31 sht31 = SHT31(); @@ -39,6 +43,8 @@ LED led_cool = LED(PIN_LEDCOOL, LED::LEDCOLOR_COOL); LED led_warm = LED(PIN_LEDWARM, LED::LEDCOLOR_WARM); LED led_red = LED(PIN_LEDRED, LED::LEDCOLOR_RED); LED led_far = LED(PIN_LEDFAR, LED::LEDCOLOR_FAR); +SupplyPump supply = SupplyPump(PIN_SUPPLY); +Solenoid solenoid = Solenoid(PIN_SOLENOID); Actuator* actuators [NUM_ACTUATORS] = { &led_blue, @@ -46,9 +52,11 @@ Actuator* actuators [NUM_ACTUATORS] = { &led_warm, &led_red, &led_far, + &supply, + &solenoid, }; -const char* instr [NUM_ACTUATORS] = { "led_blue", "led_cool", "led_warm", "led_red", "led_far" }; +const char* instr [NUM_ACTUATORS] = { "led_blue", "led_cool", "led_warm", "led_red", "led_far", "supply", "solenoid" }; static const InstructionActuatorMatrix matrix = { NUM_ACTUATORS, From 95a3248ec2f7899f8ef0ece7b7d4270d7c5b3eba Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sat, 16 Jul 2022 13:13:03 -0400 Subject: [PATCH 25/85] TEC Actuator Class --- .../microcontroller/src/actuators/actuator.h | 1 + software/microcontroller/src/actuators/tec.cc | 28 +++++++++++++++++ software/microcontroller/src/actuators/tec.h | 31 +++++++++++++++++++ software/microcontroller/src/main.cc | 8 ++--- software/microcontroller/src/main.h | 5 +++ 5 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 software/microcontroller/src/actuators/tec.cc create mode 100644 software/microcontroller/src/actuators/tec.h diff --git a/software/microcontroller/src/actuators/actuator.h b/software/microcontroller/src/actuators/actuator.h index e64e70f6..2af87398 100644 --- a/software/microcontroller/src/actuators/actuator.h +++ b/software/microcontroller/src/actuators/actuator.h @@ -9,6 +9,7 @@ typedef enum actuatorid_t { ACTUATOR_LED, ACTUATOR_SUPPLY, ACTUATOR_SOLENOID, + ACTUATOR_TEC } actuatorid_t; // All actuator state info diff --git a/software/microcontroller/src/actuators/tec.cc b/software/microcontroller/src/actuators/tec.cc new file mode 100644 index 00000000..03c998fa --- /dev/null +++ b/software/microcontroller/src/actuators/tec.cc @@ -0,0 +1,28 @@ +#include + +#include + +#include +#include + +TEC::TEC(uint8_t powerpin, uint8_t relayApin, uint8_t relayBpin) : Actuator(ACTUATOR_TEC, 0) { + this->power = powerpin; + this->relayA = relayApin; + this->relayB = relayBpin; +} + +errorlevel_t TEC::initialize(void) { + pinMode(this->power, OUTPUT); + pinMode(this->relayA, OUTPUT); + pinMode(this->relayB, OUTPUT); + return ERR_NONE; +} + +errorlevel_t TEC::set(float target) { + // Clamp to -1 < x < 1 + target = min(max(target, -1), 1); + analogWrite(this->power, abs(target) * 255); + analogWrite(this->relayA, 255 * (target >= TEC_DEADZONE ? 1 : 0)); + analogWrite(this->relayB, 255 * (target <= -TEC_DEADZONE ? 1 : 0)); + return ERR_NONE; +} \ No newline at end of file diff --git a/software/microcontroller/src/actuators/tec.h b/software/microcontroller/src/actuators/tec.h new file mode 100644 index 00000000..1e1061a5 --- /dev/null +++ b/software/microcontroller/src/actuators/tec.h @@ -0,0 +1,31 @@ +#ifndef PEAPOD_ACTUATORS_TEC_H_ +#define PEAPOD_ACTUATORS_TEC_H_ + +#include + +#include +#include + +#define TEC_DEADZONE 0.01 + +class TEC : public Actuator { + public: + + /** + * Constructor. + * @param powerpin Digital PWM output pin for controlling heat pump magnitude + * @param directionpin Digital output pin for controlling heat pump direction + */ + TEC(uint8_t powerpin, uint8_t relayApin, uint8_t relayBpin); + private: + errorlevel_t initialize(void) override; + errorlevel_t set(float target) override; + + // "Magnitude" PWM output pin. + uint8_t power; + + // "Direction"/relay control digital output pin. + uint8_t relayA, relayB; +}; + +#endif diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index 7df9d126..88756963 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -4,10 +4,6 @@ #include -#if ENABLE_WATCHDOG - #include -#endif - #include #include #include @@ -53,7 +49,7 @@ void setup(void) { // Enable the watchdog timer #if ENABLE_WATCHDOG - wdt_enable(WDTO_1S); + wdt_enable(WDTO); #endif digitalWrite(PIN_STATUS, HIGH); } @@ -90,7 +86,7 @@ void loop(void) { } #if ENABLE_WATCHDOG - wdt_enable(WDTO_1S); + wdt_enable(WDTO); #endif } diff --git a/software/microcontroller/src/main.h b/software/microcontroller/src/main.h index 3a875c26..6ced33f5 100644 --- a/software/microcontroller/src/main.h +++ b/software/microcontroller/src/main.h @@ -1,4 +1,5 @@ #include +#include #include #include @@ -17,6 +18,10 @@ #define NUM_ACTUATORS 7 #define REVISION 0 #define BAUDRATE 115200 +#if ENABLE_WATCHDOG + #include + #define WDTO WDTO_1S +#endif // Pins #define PIN_STATUS 13 From 47a6387eb027b58381e8d30b6262e0b2ce1845b1 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sat, 16 Jul 2022 15:33:19 -0400 Subject: [PATCH 26/85] Idle State --- software/index.ts | 9 +- software/package-lock.json | 36 ++++++++ software/package.json | 2 + software/src/controller.ts | 10 +- software/src/peapod.ts | 55 ++++++++++- software/src/publisher.ts | 9 +- software/src/ui.ts | 182 +++++++++++++++++++++++-------------- 7 files changed, 226 insertions(+), 77 deletions(-) diff --git a/software/index.ts b/software/index.ts index 17fec257..4d69c96d 100644 --- a/software/index.ts +++ b/software/index.ts @@ -2,8 +2,8 @@ import chalk from 'chalk'; import * as inquirer from 'inquirer'; -import { checkInternet } from './src/utils'; -import * as Spinner from './src/ui'; +import { checkInternet, sleep } from './src/utils'; +import { Spinner } from './src/ui'; import { PublishingMode } from './src/publisher'; import PeaPod from './src/peapod'; @@ -36,6 +36,11 @@ async function main(): Promise { let peapod: PeaPod = new PeaPod(publishingmode); + let idleInterval = await peapod.idle(); + + // Temporary; replace with main menu + await sleep(10000); + peapod.start(); } diff --git a/software/package-lock.json b/software/package-lock.json index f477c46a..f0709a7c 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -12,6 +12,7 @@ "@mapbox/node-pre-gyp": "^1.0.8", "@peapodtech/firebasedeviceflow": "^0.2.0", "@types/yargs": "^16.0.0", + "blessed": "^0.1.81", "chalk": "^4.1.0", "dotenv": "^10.0.0", "firebase": "^9.3.0", @@ -31,6 +32,7 @@ }, "devDependencies": { "@serialport/parser-readline": "^9.0.1", + "@types/blessed": "^0.1.19", "@types/jest": "^27.5.0", "@types/jsonwebtoken": "^8.5.5", "@types/node": "^17.0.31", @@ -1765,6 +1767,15 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/blessed": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.19.tgz", + "integrity": "sha512-r4qnseYWBsi/kxo5AAlCS22EnTXFbGpnvuXUubJikVeRnYB3e5HwV3NtcwJ0Sk5KOGaLvo9Rtwb8hzxfbqbQPg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -2184,6 +2195,17 @@ "readable-stream": "^3.4.0" } }, + "node_modules/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -7588,6 +7610,15 @@ "@babel/types": "^7.3.0" } }, + "@types/blessed": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.19.tgz", + "integrity": "sha512-r4qnseYWBsi/kxo5AAlCS22EnTXFbGpnvuXUubJikVeRnYB3e5HwV3NtcwJ0Sk5KOGaLvo9Rtwb8hzxfbqbQPg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -7935,6 +7966,11 @@ "readable-stream": "^3.4.0" } }, + "blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", diff --git a/software/package.json b/software/package.json index a066f336..f66944a3 100644 --- a/software/package.json +++ b/software/package.json @@ -33,6 +33,7 @@ "@mapbox/node-pre-gyp": "^1.0.8", "@peapodtech/firebasedeviceflow": "^0.2.0", "@types/yargs": "^16.0.0", + "blessed": "^0.1.81", "chalk": "^4.1.0", "dotenv": "^10.0.0", "firebase": "^9.3.0", @@ -49,6 +50,7 @@ }, "devDependencies": { "@serialport/parser-readline": "^9.0.1", + "@types/blessed": "^0.1.19", "@types/jest": "^27.5.0", "@types/jsonwebtoken": "^8.5.5", "@types/node": "^17.0.31", diff --git a/software/src/controller.ts b/software/src/controller.ts index c721915d..f9f42204 100644 --- a/software/src/controller.ts +++ b/software/src/controller.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import { ReadlineParser, SerialPort } from 'serialport'; import { ControllerInstructionsError } from './errors'; -import * as Spinner from './ui'; +import { Spinner } from './ui'; import { sleep, updateMicrocontroller } from './utils'; import { Gpio } from 'onoff'; @@ -118,6 +118,10 @@ export default class MicroController implements Controller { } start(onMessage: (msg: ControllerMessage) => void): Promise { + this.clearTimeout(); + // Reset listeners + this.parser.removeAllListeners('data'); + // Explicit promise construction so we can resolve only on valid comms AND revision check return new Promise(async (res, rej) => { // Reset the microcontroller (opens the serial port) @@ -143,7 +147,7 @@ export default class MicroController implements Controller { case 'revision': // Software update if(msg.data === CONTROLLER_REVISION) { - Spinner.succeed('Microcontroller software up to date!'); + Spinner.succeed(`Microcontroller software up to date! Got ${msg.data}`); res(); //Successful start sequence } else { Spinner.fail(`Microcontroller software out of date! Got ${msg.data}, expected ${CONTROLLER_REVISION}.`); @@ -191,6 +195,8 @@ export default class MicroController implements Controller { stop(): void { this.clearTimeout(); if (this.serial.isOpen) this.serial.close(); + // Stop listening for data + this.parser.removeAllListeners('data'); } /** diff --git a/software/src/peapod.ts b/software/src/peapod.ts index ba8218b4..291888e2 100644 --- a/software/src/peapod.ts +++ b/software/src/peapod.ts @@ -1,4 +1,4 @@ -import * as Spinner from './ui'; +import { Screen, Spinner } from './ui'; import chalk from 'chalk'; import { initializeApp, getApps } from 'firebase/app'; @@ -6,7 +6,8 @@ import { getAuth } from 'firebase/auth'; import { loadPeaPodEnv, loadAuthEnv, loadFirebaseEnv, loadIoTEnv } from './env'; import MicroController, { ControllerInstructions, Controller } from './controller'; -import OnlinePublisher, { DataBatch, Publisher, OfflinePublisher, PublishingMode } from './publisher'; +import OnlinePublisher, { DataBatch, DataSet, Publisher, OfflinePublisher, PublishingMode } from './publisher'; +import { sleep } from './utils'; // CONSTANTS @@ -15,6 +16,11 @@ import OnlinePublisher, { DataBatch, Publisher, OfflinePublisher, PublishingMode */ const BATCH_PUBLISH_INTERVAL = 5; +/** + * Milliseconds between refresh during idle. + */ +const IDLE_INTERVAL = 100; + // GLOBAL VARIABLES /** @@ -32,6 +38,11 @@ let batchInterval: NodeJS.Timer; */ let instructions: ControllerInstructions = {}; +/** + * + */ +let data: DataSet = {}; + /** * Main driver class. */ @@ -67,6 +78,43 @@ export default class PeaPod { } } + async idle(): Promise { + await this.controller.start(msg => { + switch (msg.type) { + case "data": + data[msg.data.label] = msg.data.value; + Screen.setData(data); + Screen.render(); + break; + case "revision": + this.controller.write(instructions); + break; + default: + // TODO: Console box? + Spinner.log(`[${ chalk.blueBright('CONTROLLER') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`); + } + }); + + // Angle to "wheel" the lights + let angle = 0; + + const idleInterval = setInterval(() => { + instructions = { + "led_blue": Math.sin(angle)/2+0.5, + "led_cool": Math.sin(angle + 2*Math.PI/5)/2+0.5, + "led_warm": Math.sin(angle + 4*Math.PI/5)/2+0.5, + "led_red": Math.sin(angle + 6*Math.PI/5)/2+0.5, + "led_far": Math.sin(angle + 8*Math.PI/5)/2+0.5, + }; + this.controller.write(instructions); + + // One revolution every 5 seconds + angle += 2*Math.PI/(5000/IDLE_INTERVAL); + angle -= angle > 2*Math.PI ? 2*Math.PI : 0; + }, IDLE_INTERVAL); + return idleInterval; + } + async start(): Promise { // INITIALIZE CONTROLLER await this.controller.start(msg => { @@ -90,6 +138,9 @@ export default class PeaPod { } }); + // Wait for all POST messages + await sleep(1000); + // INITIALIZE PUBLISHER let { projectid, projectname, run } = await this.publisher.start(config => { // Hot-swap programs diff --git a/software/src/publisher.ts b/software/src/publisher.ts index cc161355..5b66d643 100644 --- a/software/src/publisher.ts +++ b/software/src/publisher.ts @@ -12,7 +12,7 @@ import { getFirestore, doc, setDoc, collection, getDocs, query, where, DocumentR import { getAuth } from 'firebase/auth'; import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; -import * as Spinner from './ui'; +import { Spinner } from './ui'; import { fetchServerCert } from './utils'; import { ControllerInstructions } from './controller'; @@ -33,6 +33,13 @@ export type DataBatch = { [key: string]: DataPoint[], }; +/** + * Set of datapoints for display + */ + export type DataSet = { + [key: string]: number, +}; + /** * Message TO the publisher. */ diff --git a/software/src/ui.ts b/software/src/ui.ts index 2dea2201..dc3f1aa1 100644 --- a/software/src/ui.ts +++ b/software/src/ui.ts @@ -1,4 +1,6 @@ import ora from 'ora'; +import * as blessed from 'blessed'; +import { DataSet } from './publisher'; const DEFAULT_SPINNER: ora.Spinner = { interval: 50, @@ -25,79 +27,119 @@ const DEFAULT_SPINNER: ora.Spinner = { /** * Global spinner object. */ -export let Spinner: ora.Ora = ora(); +let spinner: ora.Ora = ora(); -/** -* Start the loading spinner. -* @param text Text to display. -* @param spinner Spinner to use. Defaults to the default spinner. -*/ -export function start(text: string = "", spinner: ora.Spinner = DEFAULT_SPINNER) { - // If it's already spinning, just change the text - if (Spinner.isSpinning) { - Spinner.text = text; - Spinner.spinner = spinner; - } else { - // Otherwise, start a new one - Spinner = ora({ text, spinner }).start(); - } -}; +export namespace Spinner { + /** + * Start the loading spinner. + * @param text Text to display. + * @param spinner Spinner to use. Defaults to the default spinner. + */ + export function start(text: string = "") { + // If it's already spinning, just change the text + if (spinner.isSpinning) { + spinner.text = text; + } else { + // Otherwise, start a new one + spinner = ora({ text, spinner: DEFAULT_SPINNER }).start(); + } + }; -/** -* Fail the loading spinner. -* @param text Text to display. -*/ -export function fail(text: string = "") { - // If it's spinning, change the text and fail - if (Spinner.isSpinning) { - Spinner.fail(text); - } else { - // Otherwise, start a new one and fail it - Spinner = ora({ text }).fail(); - } -}; + /** + * Fail the loading spinner. + * @param text Text to display. + */ + export function fail(text: string = "") { + // If it's spinning, change the text and fail + if (spinner.isSpinning) { + spinner.fail(text); + } else { + // Otherwise, start a new one and fail it + spinner = ora({ text }).fail(); + } + }; -/** -* Succeed the loading spinner. -* @param text Text to display. -*/ -export function succeed(text: string = "") { - // If it's spinning, change the text and succeed - if (Spinner.isSpinning) { - Spinner.succeed(text); - } else { - // Otherwise, start a new one and succeed it - Spinner = ora({ text }).succeed(); - } -}; + /** + * Succeed the loading spinner. + * @param text Text to display. + */ + export function succeed(text: string = "") { + // If it's spinning, change the text and succeed + if (spinner.isSpinning) { + spinner.succeed(text); + } else { + // Otherwise, start a new one and succeed it + spinner = ora({ text }).succeed(); + } + }; -/** - * Complete the loading spinner with info (blue `i`) - * @param text Text to display. - */ -export function info(text: string = "") { - if (Spinner.isSpinning) { - // If it's spinning, change the text and set to info - Spinner.info(text); - } else { - // Otherwise, start a new one and info it - Spinner = ora({ text }).info(); - } -}; + /** + * Complete the loading spinner with info (blue `i`) + * @param text Text to display. + */ + export function info(text: string = "") { + if (spinner.isSpinning) { + // If it's spinning, change the text and set to info + spinner.info(text); + } else { + // Otherwise, start a new one and info it + spinner = ora({ text }).info(); + } + }; -/** - * If spinning: stop and clear the current spinner, log some text, then restart the spinner - * Else: Just log - * - * @param text Text to log - */ -export function log(text: string) { - let oldtext = Spinner.text; - if (Spinner.isSpinning) { - Spinner.stop(); - console.log(text); - Spinner.start(oldtext); - } else { - console.log(text); + /** + * If spinning: stop and clear the current spinner, log some text, then restart the spinner + * Else: Just log + * + * @param text Text to log + */ + export function log(text: string) { + let oldtext = spinner.text; + if (spinner.isSpinning) { + spinner.stop(); + console.log(text); + spinner.start(oldtext); + } else { + console.log(text); + } + }; +} + +// Create a screen object. +let screen = blessed.screen({ + smartCSR: true +}); + +let box = blessed.box({ + top: 'top', + left: 'left', + width: '50%', + height: '50%', + border: { + type: 'line' + }, + style: { + fg: 'white', + bg: 'black', + border: { + fg: '#ffffff' + } + }, + align: 'left', +}); + +screen.append(box); + +export namespace Screen { + export function render() { box.focus(); screen.render(); } + export function hide() { box.hide(); } + export function setData(data: DataSet) { + let s = 'PeaPod\n'; + s += Object.entries(data).map((datapoint)=>{ + return datapoint[0].split('-').map(word=> + word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase() + ).join(' ') + ': ' + datapoint[1].toFixed(2); + }).join('\n'); + box.setContent(s); } -}; \ No newline at end of file +} \ No newline at end of file From 0e9ac61b0a2e19d89bc13b0fbf24dbb04e902136 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sun, 17 Jul 2022 22:26:57 -0400 Subject: [PATCH 27/85] Water Temperature, Pressure, Flow Sensor Classes * namespaced instruction handler functions * optimized main loop to speed up instruction handling --- software/index.ts | 1 + software/microcontroller/src/main.cc | 72 ++++++----- software/microcontroller/src/main.h | 11 +- .../microcontroller/src/sensors/ge_2158.cc | 38 ++++++ .../microcontroller/src/sensors/ge_2158.h | 35 ++++++ .../microcontroller/src/sensors/sen0257.cc | 37 ++++++ .../microcontroller/src/sensors/sen0257.h | 32 +++++ software/microcontroller/src/sensors/sensor.h | 3 + software/microcontroller/src/sensors/yf_b1.cc | 47 +++++++ software/microcontroller/src/sensors/yf_b1.h | 29 +++++ .../microcontroller/src/utils/instructions.cc | 117 ++++++++---------- .../microcontroller/src/utils/instructions.h | 39 +++--- .../test_instructions/test_instructions.cc | 14 +-- 13 files changed, 356 insertions(+), 119 deletions(-) create mode 100644 software/microcontroller/src/sensors/ge_2158.cc create mode 100644 software/microcontroller/src/sensors/ge_2158.h create mode 100644 software/microcontroller/src/sensors/sen0257.cc create mode 100644 software/microcontroller/src/sensors/sen0257.h create mode 100644 software/microcontroller/src/sensors/yf_b1.cc create mode 100644 software/microcontroller/src/sensors/yf_b1.h diff --git a/software/index.ts b/software/index.ts index 4d69c96d..defb4648 100644 --- a/software/index.ts +++ b/software/index.ts @@ -41,6 +41,7 @@ async function main(): Promise { // Temporary; replace with main menu await sleep(10000); + clearInterval(idleInterval); peapod.start(); } diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index 88756963..9c87707f 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -9,8 +9,13 @@ #include #include +// DECLARATIONS bool post(void); +// GLOBAL VARIABLES +uint8_t sensor = 0, actuator = 0; + +// FUNCTIONS void setup(void) { // Status LED pinMode(PIN_STATUS, OUTPUT); @@ -25,6 +30,17 @@ void setup(void) { // Send software revision first JSONMessenger::sendRevision(REVISION); + // Await valid initial instruction set + String in; + do { + // Await instructions + while (!Serial.available()); + in = Serial.readStringUntil('\n'); + + // Trim whitespace, newline terminator + in.trim(); + } while (InstructionHandler::handleSet(in, &matrix) != ERR_NONE); + // Serial communications established, initialize sensors and actuators if (!post()) { // Failed POST, blink @@ -36,17 +52,6 @@ void setup(void) { } } - // Await valid initial instruction set - String in; - do { - // Await instructions - while (!Serial.available()); - in = Serial.readStringUntil('\n'); - - // Trim whitespace, newline terminator - in.trim(); - } while (handleInstructions(in, &matrix) != ERR_NONE); - // Enable the watchdog timer #if ENABLE_WATCHDOG wdt_enable(WDTO); @@ -59,32 +64,35 @@ void loop(void) { if (Serial.available()) { String in = Serial.readStringUntil('\n'); in.trim(); - handleInstructions(in, &matrix); + InstructionHandler::handleSet(in, &matrix); } - for (int i = 0; i < NUM_SENSORS; ++i) { - SensorState* state = sensors[i]->update(); - if (state->error == ERR_NONE) { - if (state->debug == DS_SUCCESS) { - for (int j = 0; j < state->numdata; ++j) { - JSONMessenger::sendData(state->data[j].label, state->data[j].value); - } - } // else do nothing - } else if (state->error == ERR_WARNING) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[i]->getID()) + " (non-fatal).")); - } else if (state->error == ERR_FATAL) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[i]->getID()) + " (FATAL, SENSOR DISABLED!).")); - } + // Read sensor + SensorState* sstate = sensors[sensor]->update(); + if (sstate->error == ERR_NONE) { + if (sstate->debug == DS_SUCCESS) { + for (int j = 0; j < sstate->numdata; ++j) { + JSONMessenger::sendData(sstate->data[j].label, sstate->data[j].value); + } + } // else do nothing + } else if (sstate->error == ERR_WARNING) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[sensor]->getID()) + " (non-fatal).")); + } else if (sstate->error == ERR_FATAL) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[sensor]->getID()) + " (FATAL, SENSOR DISABLED!).")); } - for (int i = 0; i < NUM_ACTUATORS; ++i) { - ActuatorState* state = actuators[i]->update(); - if (state->error == ERR_WARNING) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[i]->getID()) + " (non-fatal).")); - } else if (state->error == ERR_FATAL) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[i]->getID()) + " (FATAL, ACTUATOR DISABLED!).")); - } + + // Update actuator + ActuatorState* astate = actuators[actuator]->update(); + if (astate->error == ERR_WARNING) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[actuator]->getID()) + " (non-fatal).")); + } else if (astate->error == ERR_FATAL) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[actuator]->getID()) + " (FATAL, ACTUATOR DISABLED!).")); } + // Increment + sensor = (sensor+1) % NUM_SENSORS; + actuator = (actuator+1) % NUM_ACTUATORS; + #if ENABLE_WATCHDOG wdt_enable(WDTO); #endif diff --git a/software/microcontroller/src/main.h b/software/microcontroller/src/main.h index 6ced33f5..4779f276 100644 --- a/software/microcontroller/src/main.h +++ b/software/microcontroller/src/main.h @@ -1,9 +1,10 @@ #include -#include #include #include #include +#include +#include #include #include #include @@ -14,7 +15,7 @@ #define ENABLE_WATCHDOG false // Constants -#define NUM_SENSORS 2 +#define NUM_SENSORS 4 #define NUM_ACTUATORS 7 #define REVISION 0 #define BAUDRATE 115200 @@ -32,14 +33,20 @@ #define PIN_LEDFAR 10 #define PIN_SUPPLY 8 #define PIN_SOLENOID 7 +#define PIN_YF_B1 2 +#define PIN_SEN0257 A0 // Sensors SHT31 sht31 = SHT31(); K30 k30 = K30(); +YF_B1 yf_b1 = YF_B1(PIN_YF_B1); +SEN0257 sen0257 = SEN0257(PIN_SEN0257); Sensor* sensors[NUM_SENSORS] = { &sht31, &k30, + &yf_b1, + &sen0257, }; // Actuators diff --git a/software/microcontroller/src/sensors/ge_2158.cc b/software/microcontroller/src/sensors/ge_2158.cc new file mode 100644 index 00000000..3426bb97 --- /dev/null +++ b/software/microcontroller/src/sensors/ge_2158.cc @@ -0,0 +1,38 @@ +#include + +#include + +#include + +#include +#include + +// Data setup +static const char* labels[1] = { "water_temperature" }; +static const SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels +}; + +GE_2158::GE_2158(uint8_t pin) : Sensor(SENSOR_GE_2158, &datasetup, GE_2158_DELTA) { + this->pin = pin; +} + +errorlevel_t GE_2158::initialize(void) { + pinMode(this->pin, INPUT); + return ERR_NONE; +} + +errorlevel_t GE_2158::read(float* data, uint8_t numdata) { + uint16_t read = analogRead(this->pin); + float v = read * GE_2158_V / 1023; + float temp = (log((v * GE_2158_R) / (GE_2158_V - v) - GE_2158_C) - GE_2158_B) / GE_2158_A; + + if (temp < 0 || temp > 100) { + return ERR_WARNING; + } + + data[0] = temp; + + return ERR_NONE; +} diff --git a/software/microcontroller/src/sensors/ge_2158.h b/software/microcontroller/src/sensors/ge_2158.h new file mode 100644 index 00000000..0c6516c8 --- /dev/null +++ b/software/microcontroller/src/sensors/ge_2158.h @@ -0,0 +1,35 @@ +#ifndef PEAPOD_SENSORS_GE_2158_H_ +#define PEAPOD_SENSORS_GE_2158_H_ + +#include + +#include +#include + +#define GE_2158_DELTA 100 +#define GE_2158_V 5.0 // Feed voltage +#define GE_2158_R 3000 // Series resistor + +// Equation: T(v) = (ln((v * GE_2158_R) / (GE_2158_V - v) - GE_2158_C) - GE_2158_B) / GE_2158_A +#define GE_2158_A -0.055481 // A parameter +#define GE_2158_B 9.30108 // B parameter +#define GE_2158_C 122.933 // C parameter + +// Interface for the SenseAir K30 Extended Range CO2 sensor. +class GE_2158 : public Sensor { + public: + /** + * Constructor. + * @param pin Analog input pin. + */ + GE_2158(uint8_t pin); + private: + errorlevel_t initialize(void) override; + + errorlevel_t read(float* data, uint8_t numdata) override; + + // Analog input pin. + uint8_t pin; +}; + +#endif diff --git a/software/microcontroller/src/sensors/sen0257.cc b/software/microcontroller/src/sensors/sen0257.cc new file mode 100644 index 00000000..fab28d88 --- /dev/null +++ b/software/microcontroller/src/sensors/sen0257.cc @@ -0,0 +1,37 @@ +#include + +#include + +#include +#include + +// Data setup +static const char* labels[1] = { "water_pressure" }; +static const SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels +}; + +SEN0257::SEN0257(uint8_t pin) : Sensor(SENSOR_SEN0257, &datasetup, SEN0257_DELTA) { + this->pin = pin; +} + +errorlevel_t SEN0257::initialize(void) { + pinMode(this->pin, INPUT); + return ERR_NONE; +} + +errorlevel_t SEN0257::read(float* data, uint8_t numdata) { + uint16_t read = analogRead(this->pin); + float v = read * 5.0 / 1023; + + float pres = (float)((v - SEN0257_VMIN) * (SEN0257_PMAX - SEN0257_PMIN) / (SEN0257_VMAX - SEN0257_VMIN) + SEN0257_PMIN); + + if (pres < SEN0257_PMIN || pres > SEN0257_PMAX) { + return ERR_WARNING; + } + + data[0] = pres; + + return ERR_NONE; +} diff --git a/software/microcontroller/src/sensors/sen0257.h b/software/microcontroller/src/sensors/sen0257.h new file mode 100644 index 00000000..98aac0b9 --- /dev/null +++ b/software/microcontroller/src/sensors/sen0257.h @@ -0,0 +1,32 @@ +#ifndef PEAPOD_SENSORS_SEN0257_H_ +#define PEAPOD_SENSORS_SEN0257_H_ + +#include + +#include +#include + +#define SEN0257_DELTA 100 +#define SEN0257_VMIN 0.5 // Min output voltage +#define SEN0257_VMAX 4.5 // Max output voltage +#define SEN0257_PMIN 0.0 // Min output pressure (PSI) +#define SEN0257_PMAX 261.1 // Max output pressure (PSI) + +// Interface for the SenseAir K30 Extended Range CO2 sensor. +class SEN0257 : public Sensor { + public: + /** + * Constructor. + * @param pin Analog input pin. + */ + SEN0257(uint8_t pin); + private: + errorlevel_t initialize(void) override; + + errorlevel_t read(float* data, uint8_t numdata) override; + + // Analog input pin. + uint8_t pin; +}; + +#endif diff --git a/software/microcontroller/src/sensors/sensor.h b/software/microcontroller/src/sensors/sensor.h index 3293f0c5..005d1f6a 100644 --- a/software/microcontroller/src/sensors/sensor.h +++ b/software/microcontroller/src/sensors/sensor.h @@ -10,6 +10,9 @@ typedef enum sensorid_t { SENSOR_NULL, SENSOR_SHT31, SENSOR_K30, + SENSOR_GE_2158, + SENSOR_YF_B1, + SENSOR_SEN0257, } sensorid_t; // Single datapoint diff --git a/software/microcontroller/src/sensors/yf_b1.cc b/software/microcontroller/src/sensors/yf_b1.cc new file mode 100644 index 00000000..14be025b --- /dev/null +++ b/software/microcontroller/src/sensors/yf_b1.cc @@ -0,0 +1,47 @@ +#include + +#include + +#include + +#include +#include + +// Data setup +static const char* labels[1] = { "water_flow_rate" }; +static const SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels +}; + +void flow(void); + +volatile uint32_t flow_count; +unsigned long last_count; + +YF_B1::YF_B1(uint8_t pin) : Sensor(SENSOR_YF_B1, &datasetup, YF_B1_DELTA) { + this->pin = pin; +} + +errorlevel_t YF_B1::initialize(void) { + pinMode(this->pin, INPUT); + attachInterrupt(digitalPinToInterrupt(this->pin), flow, RISING); + return ERR_NONE; +} + +errorlevel_t YF_B1::read(float* data, uint8_t numdata) { + // Get seconds between last read and now + float diff = (millis() - this->getState()->timestamp)/1000.0; + float freq = flow_count / diff; + float flow_rate = freq / YF_B1_COUNT_PER_LPM; + + data[0] = flow_rate; + + flow_count = 0; + + return ERR_NONE; +} + +void flow(void) { + flow_count++; +} \ No newline at end of file diff --git a/software/microcontroller/src/sensors/yf_b1.h b/software/microcontroller/src/sensors/yf_b1.h new file mode 100644 index 00000000..95be905c --- /dev/null +++ b/software/microcontroller/src/sensors/yf_b1.h @@ -0,0 +1,29 @@ +#ifndef PEAPOD_SENSORS_YF_B1_H_ +#define PEAPOD_SENSORS_YF_B1_H_ + +#include + +#include +#include + +#define YF_B1_DELTA 1000 // Calculate frequency every second +#define YF_B1_COUNT_PER_LPM 7.5 // 7.5 counts is one liter per minute + +// Interface for the SenseAir K30 Extended Range CO2 sensor. +class YF_B1 : public Sensor { + public: + /** + * Constructor. + * @param pin Digital input pin. MUST BE 2 OR 3 + */ + YF_B1(uint8_t pin); + private: + errorlevel_t initialize(void) override; + + errorlevel_t read(float* data, uint8_t numdata) override; + + // Digital input pin. + uint8_t pin; +}; + +#endif diff --git a/software/microcontroller/src/utils/instructions.cc b/software/microcontroller/src/utils/instructions.cc index e275a505..1eb61cb3 100644 --- a/software/microcontroller/src/utils/instructions.cc +++ b/software/microcontroller/src/utils/instructions.cc @@ -6,83 +6,74 @@ #include #include -/** - * Checks a non-empty instruction set for formatting validity - * @param instructionSet JSON-formatted instruction set - * @return Validity - */ -bool isValidSet(String instructionSet) { - return (instructionSet.charAt(0) == '{' && instructionSet.charAt(instructionSet.length()-1) == '}' && instructionSet.indexOf(':') != -1); -} - -/** - * Handles incoming instruction set according to an instruction-actuator matrix - * @param instructionSet JSON-formatted instruction set - * @param matrix Instruction-actuator matrix - * @return Error level - `ERR_FATAL`: invalid instruction set formatting; `ERR_WARNING`: one or more instruction labels were unknown - */ -errorlevel_t handleInstructions(String instructionSet, const InstructionActuatorMatrix* matrix) { - // If empty, by default handled - if(instructionSet.equals("{}")){ - return ERR_NONE; - } - - // If invalid, throw error - if (!isValidSet(instructionSet)) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction set '" + instructionSet + "', bad formatting.")); - return ERR_FATAL; +namespace InstructionHandler { + bool isValidSet(String instructionSet) { + return instructionSet.equals("{}") || (instructionSet.charAt(0) == '{' && instructionSet.charAt(instructionSet.length()-1) == '}' && instructionSet.indexOf(':') != -1); } - // Valid and non-empty, strip curly braces - instructionSet = instructionSet.substring(1, instructionSet.length()-1); + errorlevel_t handleSet(String instructionSet, const InstructionActuatorMatrix* matrix) { + // If empty, by default handled + if(instructionSet.equals("{}")){ + return ERR_NONE; + } - errorlevel_t result = ERR_NONE; - // While there are instructions: - while (instructionSet.indexOf(':') != -1) { - // Find end of leftmost instruction - int stop = instructionSet.indexOf(','); - if (stop == -1) { - stop = instructionSet.length(); + // If invalid, throw error + if (!isValidSet(instructionSet)) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction set '" + instructionSet + "', bad formatting.")); + return ERR_FATAL; } - // Handle leftmost instruction - String instruction = instructionSet.substring(0, stop); - errorlevel_t latest = handleInstruction(instruction, matrix); + // Valid and non-empty, strip curly braces + instructionSet = instructionSet.substring(1, instructionSet.length()-1); + + errorlevel_t result = ERR_NONE; + // While there are instructions: + while (instructionSet.indexOf(':') != -1) { + // Find end of leftmost instruction + int stop = instructionSet.indexOf(','); + if (stop == -1) { + stop = instructionSet.length(); + } + + // Handle leftmost instruction + String instruction = instructionSet.substring(0, stop); + errorlevel_t latest = handleInstruction(instruction, matrix); - // Raise result errorlevel? - result = max(result, latest); + // Raise result errorlevel? + result = max(result, latest); - // Truncate leftmost instruction - instructionSet = instructionSet.substring(stop+1); + // Truncate leftmost instruction + instructionSet = instructionSet.substring(stop+1); + } + return result; } - return result; -} -errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatrix* matrix) { - uint8_t split = instruction.indexOf(":"); + errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatrix* matrix) { + uint8_t split = instruction.indexOf(":"); - // Check formatting - if (split != -1 && instruction.charAt(0) == '"' && instruction.charAt(split-1) == '"') { - // Get label (remove quotations) - String label = instruction.substring(1, split-1); + // Check formatting + if (split != -1 && instruction.charAt(0) == '"' && instruction.charAt(split-1) == '"') { + // Get label (remove quotations) + String label = instruction.substring(1, split-1); - // Get value - float value = instruction.substring(split+1).toFloat(); + // Get value + float value = instruction.substring(split+1).toFloat(); - if(value == 0 && !(instruction.charAt(split+1) == '0' || (instruction.charAt(split+1) == '.' && instruction.charAt(split+2) == '0'))) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid target value '" + instruction.substring(split+1) + "'.")); - return ERR_FATAL; - } + if(value == 0 && !(instruction.charAt(split+1) == '0' || (instruction.charAt(split+1) == '.' && instruction.charAt(split+2) == '0'))) { + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid target value '" + instruction.substring(split+1) + "'.")); + return ERR_FATAL; + } - for (int i = 0; i < matrix->numActuators; ++i) { - if (label.equals(matrix->instructions[i])) { - matrix->actuators[i]->setTarget(value); - return ERR_NONE; + for (int i = 0; i < matrix->numActuators; ++i) { + if (label.equals(matrix->instructions[i])) { + matrix->actuators[i]->setTarget(value); + return ERR_NONE; + } } + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Unknown instruction label '" + label + "'.")); + return ERR_WARNING; } - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Unknown instruction label '" + label + "'.")); - return ERR_WARNING; + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction '" + instruction + String("', bad formatting."))); + return ERR_FATAL; } - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Invalid instruction '" + instruction + String("', bad formatting."))); - return ERR_FATAL; } \ No newline at end of file diff --git a/software/microcontroller/src/utils/instructions.h b/software/microcontroller/src/utils/instructions.h index 183ce88c..126ea54b 100644 --- a/software/microcontroller/src/utils/instructions.h +++ b/software/microcontroller/src/utils/instructions.h @@ -18,20 +18,29 @@ typedef struct InstructionActuatorMatrix { const char** instructions; } InstructionActuatorMatrix; -/** - * Handles incoming instruction set according to an instruction-actuator matrix - * @param instructionSet JSON-formatted instruction set - * @param matrix Instruction-actuator matrix - * @return Error level - `ERR_FATAL`: invalid instruction set formatting; `ERR_WARNING`: one or more instruction labels were unknown - */ -errorlevel_t handleInstructions(String instructionSet, const InstructionActuatorMatrix* matrix); - -/** - * Handles incoming instruction label-target pair. - * @param instruction Label-target pair (i.e. `"\"actuator_label\":value_float"`) - * @param matrix Instruction-actuator matrix - * @return Error level - `ERR_FATAL`: invalid instruction formatting; `ERR_WARNING`: instruction label was unknown - */ -errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatrix* matrix); +namespace InstructionHandler { + /** + * Checks a non-empty instruction set for formatting validity + * @param instructionSet JSON-formatted instruction set + * @return Validity + */ + bool isValidSet(String instructionSet); + + /** + * Handles incoming instruction set according to an instruction-actuator matrix + * @param instructionSet JSON-formatted instruction set + * @param matrix Instruction-actuator matrix + * @return Error level - `ERR_FATAL`: invalid instruction set formatting; `ERR_WARNING`: one or more instruction labels were unknown + */ + errorlevel_t handleSet(String instructionSet, const InstructionActuatorMatrix* matrix); + + /** + * Handles incoming instruction label-target pair. + * @param instruction Label-target pair (i.e. `"\"actuator_label\":value_float"`) + * @param matrix Instruction-actuator matrix + * @return Error level - `ERR_FATAL`: invalid instruction formatting; `ERR_WARNING`: instruction label was unknown + */ + errorlevel_t handleInstruction(String instruction, const InstructionActuatorMatrix* matrix); +} #endif diff --git a/software/microcontroller/test/test_instructions/test_instructions.cc b/software/microcontroller/test/test_instructions/test_instructions.cc index 7c0401ad..149acc37 100644 --- a/software/microcontroller/test/test_instructions/test_instructions.cc +++ b/software/microcontroller/test/test_instructions/test_instructions.cc @@ -36,39 +36,39 @@ static const InstructionActuatorMatrix matrix = { }; void test_non_json(void) { - uint8_t result = handleInstructions("hello world", &matrix); + uint8_t result = InstructionHandler::handleSet("hello world", &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); } void test_incomplete_json(void) { - uint8_t result = handleInstructions("{\"incomplete", &matrix); + uint8_t result = InstructionHandler::handleSet("{\"incomplete", &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); } void test_invalid_target(void) { - uint8_t result = handleInstructions("{\"incomplete\":abc", &matrix); + uint8_t result = InstructionHandler::handleSet("{\"incomplete\":abc", &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); } void test_bad_instruction(void) { - uint8_t result = handleInstructions("{\"C\":0}", &matrix); + uint8_t result = InstructionHandler::handleSet("{\"C\":0}", &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_WARNING, result); } void test_empty_instruction_set(void) { - uint8_t result = handleInstructions("{}"), &matrix); + uint8_t result = InstructionHandler::handleSet("{}"), &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); } void test_one_instruction(void) { - uint8_t result = handleInstructions("{\"A\":1.3}", &matrix); + uint8_t result = InstructionHandler::handleSet("{\"A\":1.3}", &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); float target = actuatorA.update()->lasttarget; TEST_ASSERT_EQUAL_FLOAT(1.3, target); } void test_multiple_instructions(void) { - uint8_t result = handleInstructions("{\"A\":2.4,\"B\":4.3}", &matrix); + uint8_t result = InstructionHandler::handleSet("{\"A\":2.4,\"B\":4.3}", &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); float target = actuatorA.update()->lasttarget; TEST_ASSERT_EQUAL_FLOAT(2.4, target); From 7c06305416a5160adeb29b598cf99a612f5225fc Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 29 Jul 2022 13:11:56 -0400 Subject: [PATCH 28/85] Microcontroller Optimization, UX, Interrupts + Added sensor and actuator name strings (progmem), toString methods * Moved datasetup to progmem * Moved globals to static class properties * Replaced all String with c strings + Added InterruptHandler interface to allow hardware interrupts to modify object instance data using a small hack * Modified variable storage properties where fitting (i.e. const char* const* as opposed to const char**) --- software/microcontroller/platformio.ini | 4 +- .../microcontroller/src/actuators/actuator.cc | 19 +++--- .../microcontroller/src/actuators/actuator.h | 30 +++++---- software/microcontroller/src/actuators/led.cc | 15 ++++- software/microcontroller/src/actuators/led.h | 48 ++++++++++---- .../microcontroller/src/actuators/onoff.cc | 10 ++- .../microcontroller/src/actuators/onoff.h | 6 +- software/microcontroller/src/actuators/pwm.cc | 10 ++- software/microcontroller/src/actuators/pwm.h | 11 +++- .../microcontroller/src/actuators/solenoid.cc | 6 +- .../microcontroller/src/actuators/solenoid.h | 15 ++++- .../microcontroller/src/actuators/supply.cc | 6 +- .../microcontroller/src/actuators/supply.h | 15 ++++- software/microcontroller/src/actuators/tec.cc | 12 ++-- software/microcontroller/src/actuators/tec.h | 25 ++++++-- software/microcontroller/src/main.cc | 16 ++--- software/microcontroller/src/main.h | 10 +-- .../microcontroller/src/sensors/ge_2158.cc | 15 ++--- .../microcontroller/src/sensors/ge_2158.h | 28 ++++++++- software/microcontroller/src/sensors/k30.cc | 16 ++--- software/microcontroller/src/sensors/k30.h | 28 ++++++++- .../microcontroller/src/sensors/sen0257.cc | 15 ++--- .../microcontroller/src/sensors/sen0257.h | 28 ++++++++- .../microcontroller/src/sensors/sensor.cc | 20 ++++-- software/microcontroller/src/sensors/sensor.h | 31 ++++------ software/microcontroller/src/sensors/sht31.cc | 18 ++---- software/microcontroller/src/sensors/sht31.h | 62 +++++++++++++------ software/microcontroller/src/sensors/yf_b1.cc | 27 +++----- software/microcontroller/src/sensors/yf_b1.h | 42 +++++++++++-- .../microcontroller/src/utils/interrupts.cc | 23 +++++++ .../microcontroller/src/utils/interrupts.h | 40 ++++++++++++ .../test_instructions/test_instructions.cc | 2 +- 32 files changed, 462 insertions(+), 191 deletions(-) create mode 100644 software/microcontroller/src/utils/interrupts.cc create mode 100644 software/microcontroller/src/utils/interrupts.h diff --git a/software/microcontroller/platformio.ini b/software/microcontroller/platformio.ini index 34ac7f42..76522fb0 100644 --- a/software/microcontroller/platformio.ini +++ b/software/microcontroller/platformio.ini @@ -9,7 +9,7 @@ upload_protocol = custom upload_command = sudo avrdude -p $BOARD_MCU -C avrdude_gpio.conf -c peapod -v -U flash:w:$SOURCE:i test_port = /dev/ttyS0 test_speed = 115200 -test_build_project_src = true +test_build_src = true monitor_port = /dev/ttyS0 monitor_speed = 115200 @@ -19,6 +19,6 @@ board = nanoatmega328 framework = arduino test_port = /dev/ttyS0 test_speed = 115200 -test_build_project_src = true +test_build_src = true monitor_port = /dev/ttyS0 monitor_speed = 115200 \ No newline at end of file diff --git a/software/microcontroller/src/actuators/actuator.cc b/software/microcontroller/src/actuators/actuator.cc index b3d43007..a6802638 100644 --- a/software/microcontroller/src/actuators/actuator.cc +++ b/software/microcontroller/src/actuators/actuator.cc @@ -1,18 +1,23 @@ +// HEADERS + #include #include #include -Actuator::Actuator(actuatorid_t actuatorid, float failtarget) { - this->actuatorid = actuatorid; +// CONSTRUCTOR + +Actuator::Actuator(const char* const* id, float failtarget) : _id(id), failtarget(failtarget) { state.error = ERR_NONE; state.debug = DS_DISABLED; // Default start target - target = this->failtarget = failtarget; + target = failtarget; } +// PUBLIC METHODS + ActuatorState* Actuator::begin(void) { state.error = initialize(); if (state.error > ERR_NONE) { @@ -66,10 +71,10 @@ ActuatorState* Actuator::getState(void) { return &state; } -actuatorid_t Actuator::getID(void) { - return actuatorid; -} - void Actuator::setTarget(float target) { this->target = target; +} + +String Actuator::toString(void) { + return String((const char*)(this->_id)); } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/actuator.h b/software/microcontroller/src/actuators/actuator.h index 2af87398..798b1f69 100644 --- a/software/microcontroller/src/actuators/actuator.h +++ b/software/microcontroller/src/actuators/actuator.h @@ -1,16 +1,13 @@ #ifndef PEAPOD_ACTUATORS_ACTUATOR_H_ #define PEAPOD_ACTUATORS_ACTUATOR_H_ +// HEADERS + +#include + #include -// Actuator identifiers -typedef enum actuatorid_t { - ACTUATOR_NULL, - ACTUATOR_LED, - ACTUATOR_SUPPLY, - ACTUATOR_SOLENOID, - ACTUATOR_TEC -} actuatorid_t; +// DECLARATIONS // All actuator state info typedef struct ActuatorState { @@ -24,13 +21,14 @@ typedef struct ActuatorState { float lasttarget; } ActuatorState; +// CLASS + class Actuator { public: /** - * @param actuatorid Identifier * @param failtarget Target that the actuator should be set to in case of failure */ - Actuator(actuatorid_t actuatorid, float failtarget); + Actuator(const char* const* id, float failtarget); /** * Wrapper function for `set()`. Checks debug state (initialized). @@ -47,12 +45,12 @@ class Actuator { // @return Pointer to actuator state ActuatorState* getState(void); - // @return Actuator identifier - actuatorid_t getID(void); - // @param target Actuator target void setTarget(float target); + // @return String representation of this actuator + String toString(void); + protected: /** * Initializes actuator. To be implemented by the child class. @@ -68,12 +66,12 @@ class Actuator { virtual errorlevel_t set(float target) = 0; private: + // Actuator ID + const char* const* _id; + // Stores all the latest state data for this actuator. ActuatorState state; - // Actuator identifier - actuatorid_t actuatorid; - // Failsafe target float failtarget; diff --git a/software/microcontroller/src/actuators/led.cc b/software/microcontroller/src/actuators/led.cc index 609e2d36..d9324aa5 100644 --- a/software/microcontroller/src/actuators/led.cc +++ b/software/microcontroller/src/actuators/led.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -6,6 +8,13 @@ #include #include -LED::LED(uint8_t pin, led_color_t color) : PWM(pin, ACTUATOR_LED) { - this->color = color; -} \ No newline at end of file +// CONSTRUCTOR + +LED::LED(const uint8_t pin, ledwavelength_t wavelength) : PWM(&id, pin), color(String(wavelength + "nm")) { } + +LED::LED(const uint8_t pin, ledtemperature_t temperature) : PWM(&id, pin), color(String(temperature + "K")) { } + +String LED::toString(void) { + return Actuator::toString() + " (" + color + ")"; +} + diff --git a/software/microcontroller/src/actuators/led.h b/software/microcontroller/src/actuators/led.h index 4e793dcf..ec69dc0e 100644 --- a/software/microcontroller/src/actuators/led.h +++ b/software/microcontroller/src/actuators/led.h @@ -1,31 +1,57 @@ #ifndef PEAPOD_ACTUATORS_LED_H_ #define PEAPOD_ACTUATORS_LED_H_ +// HEADERS + #include #include #include #include +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors - overallocated for suffix + static const PROGMEM char* const id = "LED"; +#endif + +// CLASS + +// Interface for PWM-dimmable LEDs. class LED : public PWM { public: - typedef enum { - LEDCOLOR_BLUE, - LEDCOLOR_COOL, - LEDCOLOR_WARM, - LEDCOLOR_RED, - LEDCOLOR_FAR - } led_color_t; - + typedef enum ledwavelength_t { + LED_CREE_XPG3_BLUE = 448, + LED_CREE_XPG3_RED = 645, + LED_CREE_XPE2_FARRED = 730, + } ledwavelength_t; + + typedef enum ledtemperature_t { + LED_CREE_XPG3_WARMWHITE = 2700, + LED_CREE_XPG3_COOLWHITE = 5700, + } ledtemperature_t; + /** * Constructor. * @param pin PWM pin for controlling this LED. - * @param color Color of this LED (enum). + * @param wavelength Wavelength of light emitted (nm) */ - LED(uint8_t pin, led_color_t color); + LED(const uint8_t pin, ledwavelength_t wavelength); + + /** + * Constructor. + * @param pin PWM pin for controlling this LED. + * @param temperature Color temperature (K) + */ + LED(const uint8_t pin, ledtemperature_t temperature); + + String toString(void); + private: // LED color. - led_color_t color; + const String color; }; #endif diff --git a/software/microcontroller/src/actuators/onoff.cc b/software/microcontroller/src/actuators/onoff.cc index 141f0d33..d0406634 100644 --- a/software/microcontroller/src/actuators/onoff.cc +++ b/software/microcontroller/src/actuators/onoff.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -5,9 +7,11 @@ #include #include -OnOff::OnOff(uint8_t pin, actuatorid_t actuatorid) : Actuator(actuatorid, 0) { - this->pin = pin; -} +// CONSTRUCTOR + +OnOff::OnOff(const char* const* id, const uint8_t pin) : Actuator(id, 0), pin(pin) { } + +// PUBLIC METHODS errorlevel_t OnOff::initialize(void) { pinMode(this->pin, OUTPUT); diff --git a/software/microcontroller/src/actuators/onoff.h b/software/microcontroller/src/actuators/onoff.h index b82d2e26..fdc6a33e 100644 --- a/software/microcontroller/src/actuators/onoff.h +++ b/software/microcontroller/src/actuators/onoff.h @@ -11,16 +11,16 @@ class OnOff : public Actuator { /** * Constructor. + * @param id Actuator ID * @param pin Digital output pin for controlling this on/off actuator. - * @param actuatorid ID of this actuator */ - OnOff(uint8_t pin, actuatorid_t actuatorid); + OnOff(const char* const* id, const uint8_t pin); private: errorlevel_t initialize(void) override; errorlevel_t set(float target) override; // Digital output pin. - uint8_t pin; + const uint8_t pin; }; #endif diff --git a/software/microcontroller/src/actuators/pwm.cc b/software/microcontroller/src/actuators/pwm.cc index 72482b3c..f061ef47 100644 --- a/software/microcontroller/src/actuators/pwm.cc +++ b/software/microcontroller/src/actuators/pwm.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -5,9 +7,11 @@ #include #include -PWM::PWM(uint8_t pin, actuatorid_t actuatorid) : Actuator(actuatorid, 0) { - this->pin = pin; -} +// CONSTRUCTOR + +PWM::PWM(const char* const* id, const uint8_t pin) : Actuator(id, 0), pin(pin) { } + +// PUBLIC METHODS errorlevel_t PWM::initialize(void) { pinMode(this->pin, OUTPUT); diff --git a/software/microcontroller/src/actuators/pwm.h b/software/microcontroller/src/actuators/pwm.h index 7fd38f0f..5a5a2fa4 100644 --- a/software/microcontroller/src/actuators/pwm.h +++ b/software/microcontroller/src/actuators/pwm.h @@ -1,26 +1,31 @@ #ifndef PEAPOD_ACTUATORS_PWM_H_ #define PEAPOD_ACTUATORS_PWM_H_ +// HEADERS + #include #include #include +// CLASS + +// Abstract interface for any PWM-controllable actuator class PWM : public Actuator { public: /** * Constructor. + * @param id ID of this actuator * @param pin Digital output PWM pin for controlling this PWM-capable actuator. - * @param actuatorid ID of this actuator */ - PWM(uint8_t pin, actuatorid_t actuatorid); + PWM(const char* const* id, const uint8_t pin); private: errorlevel_t initialize(void) override; errorlevel_t set(float target) override; // PWM output pin. - uint8_t pin; + const uint8_t pin; }; #endif diff --git a/software/microcontroller/src/actuators/solenoid.cc b/software/microcontroller/src/actuators/solenoid.cc index a194f6db..927403b1 100644 --- a/software/microcontroller/src/actuators/solenoid.cc +++ b/software/microcontroller/src/actuators/solenoid.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -6,4 +8,6 @@ #include #include -Solenoid::Solenoid(uint8_t pin) : OnOff(pin, ACTUATOR_SOLENOID) { } \ No newline at end of file +// CONSTRUCTOR + +Solenoid::Solenoid(const uint8_t pin) : OnOff(&id, pin) { } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/solenoid.h b/software/microcontroller/src/actuators/solenoid.h index 4f3a058d..aba0990b 100644 --- a/software/microcontroller/src/actuators/solenoid.h +++ b/software/microcontroller/src/actuators/solenoid.h @@ -1,19 +1,32 @@ #ifndef PEAPOD_ACTUATORS_SOLENOID_H_ #define PEAPOD_ACTUATORS_SOLENOID_H_ +// HEADERS + #include #include #include #include +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "Solenoid"; +#endif + +// CLASS + +// Interface for relay-controlled solenoids class Solenoid : public OnOff { public: /** * Constructor. * @param pin PWM pin for controlling this LED. */ - Solenoid(uint8_t pin); + Solenoid(const uint8_t pin); }; #endif diff --git a/software/microcontroller/src/actuators/supply.cc b/software/microcontroller/src/actuators/supply.cc index 52ec5678..c41dd8fb 100644 --- a/software/microcontroller/src/actuators/supply.cc +++ b/software/microcontroller/src/actuators/supply.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -6,4 +8,6 @@ #include #include -SupplyPump::SupplyPump(uint8_t pin) : OnOff(pin, ACTUATOR_SUPPLY) { } \ No newline at end of file +// CONSTRUCTOR + +SupplyPump::SupplyPump(const uint8_t pin) : OnOff(&id, pin) { } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/supply.h b/software/microcontroller/src/actuators/supply.h index 66e76629..de8da177 100644 --- a/software/microcontroller/src/actuators/supply.h +++ b/software/microcontroller/src/actuators/supply.h @@ -1,19 +1,32 @@ #ifndef PEAPOD_ACTUATORS_SUPPLY_H_ #define PEAPOD_ACTUATORS_SUPPLY_H_ +// HEADERS + #include #include #include #include +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "Supply Pump"; +#endif + +// CLASS + +// Interface for the supply pump. class SupplyPump : public OnOff { public: /** * Constructor. * @param pin PWM pin for controlling this LED. */ - SupplyPump(uint8_t pin); + SupplyPump(const uint8_t pin); }; #endif diff --git a/software/microcontroller/src/actuators/tec.cc b/software/microcontroller/src/actuators/tec.cc index 03c998fa..74c84804 100644 --- a/software/microcontroller/src/actuators/tec.cc +++ b/software/microcontroller/src/actuators/tec.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -5,11 +7,11 @@ #include #include -TEC::TEC(uint8_t powerpin, uint8_t relayApin, uint8_t relayBpin) : Actuator(ACTUATOR_TEC, 0) { - this->power = powerpin; - this->relayA = relayApin; - this->relayB = relayBpin; -} +// CONSTRUCTOR + +TEC::TEC(uint8_t powerpin, uint8_t relayApin, uint8_t relayBpin) : Actuator(&id, 0), power(powerpin), relayA(relayApin), relayB(relayBpin) { } + +// PUBLIC METHODS errorlevel_t TEC::initialize(void) { pinMode(this->power, OUTPUT); diff --git a/software/microcontroller/src/actuators/tec.h b/software/microcontroller/src/actuators/tec.h index 1e1061a5..8d8a975c 100644 --- a/software/microcontroller/src/actuators/tec.h +++ b/software/microcontroller/src/actuators/tec.h @@ -1,20 +1,37 @@ #ifndef PEAPOD_ACTUATORS_TEC_H_ #define PEAPOD_ACTUATORS_TEC_H_ +// HEADERS + #include #include #include +// MACROS + +// Settings #define TEC_DEADZONE 0.01 +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "Thermoelectric Controller"; +#endif + +// CLASS + +// Interface for Thermoelectric Controllers. class TEC : public Actuator { public: /** * Constructor. * @param powerpin Digital PWM output pin for controlling heat pump magnitude - * @param directionpin Digital output pin for controlling heat pump direction + * @param relayApin Digital output pin for controlling heat pump direction relay A + * @param relayBpin Digital output pin for controlling heat pump direction relay B */ TEC(uint8_t powerpin, uint8_t relayApin, uint8_t relayBpin); private: @@ -22,10 +39,10 @@ class TEC : public Actuator { errorlevel_t set(float target) override; // "Magnitude" PWM output pin. - uint8_t power; + const uint8_t power; - // "Direction"/relay control digital output pin. - uint8_t relayA, relayB; + // "Direction"/relay control digital output pins. + const uint8_t relayA, relayB; }; #endif diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index 9c87707f..d5f5b94a 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -76,17 +76,17 @@ void loop(void) { } } // else do nothing } else if (sstate->error == ERR_WARNING) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[sensor]->getID()) + " (non-fatal).")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + sensors[sensor]->toString() + " (non-fatal).")); } else if (sstate->error == ERR_FATAL) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + String(sensors[sensor]->getID()) + " (FATAL, SENSOR DISABLED!).")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + sensors[sensor]->toString() + " (FATAL, SENSOR DISABLED!).")); } // Update actuator ActuatorState* astate = actuators[actuator]->update(); if (astate->error == ERR_WARNING) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[actuator]->getID()) + " (non-fatal).")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + actuators[actuator]->toString() + " (non-fatal).")); } else if (astate->error == ERR_FATAL) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + String(actuators[actuator]->getID()) + " (FATAL, ACTUATOR DISABLED!).")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + actuators[actuator]->toString() + " (FATAL, ACTUATOR DISABLED!).")); } // Increment @@ -104,9 +104,9 @@ bool post(void) { SensorState* state = sensors[i]->begin(); bool latest = (state->debug >= DS_INITIALIZED && state->error == ERR_NONE); if (latest) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Sensor " + String(sensors[i]->getID()) + " initialized successfully.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Sensor " + sensors[i]->toString() + " initialized successfully.")); } else { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Failed to initialize sensor " + String(sensors[i]->getID()) + ". Check wiring.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Failed to initialize sensor " + sensors[i]->toString() + ". Check wiring.")); } success &= latest; } @@ -114,9 +114,9 @@ bool post(void) { ActuatorState* state = actuators[i]->begin(); bool latest = (state->debug >= DS_INITIALIZED && state->error == ERR_NONE); if (latest) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Actuator " + String(actuators[i]->getID()) + " initialized successfully.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Actuator " + actuators[i]->toString() + " initialized successfully.")); } else { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to initialize actuator " + String(actuators[i]->getID()) + ". Check wiring.")); + JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to initialize actuator " + actuators[i]->toString() + ". Check wiring.")); } success &= latest; } diff --git a/software/microcontroller/src/main.h b/software/microcontroller/src/main.h index 4779f276..f40441be 100644 --- a/software/microcontroller/src/main.h +++ b/software/microcontroller/src/main.h @@ -50,11 +50,11 @@ Sensor* sensors[NUM_SENSORS] = { }; // Actuators -LED led_blue = LED(PIN_LEDBLUE, LED::LEDCOLOR_BLUE); -LED led_cool = LED(PIN_LEDCOOL, LED::LEDCOLOR_COOL); -LED led_warm = LED(PIN_LEDWARM, LED::LEDCOLOR_WARM); -LED led_red = LED(PIN_LEDRED, LED::LEDCOLOR_RED); -LED led_far = LED(PIN_LEDFAR, LED::LEDCOLOR_FAR); +LED led_blue = LED(PIN_LEDBLUE, LED::LED_CREE_XPG3_BLUE); +LED led_cool = LED(PIN_LEDCOOL, LED::LED_CREE_XPG3_COOLWHITE); +LED led_warm = LED(PIN_LEDWARM, LED::LED_CREE_XPG3_WARMWHITE); +LED led_red = LED(PIN_LEDRED, LED::LED_CREE_XPG3_RED); +LED led_far = LED(PIN_LEDFAR, LED::LED_CREE_XPE2_FARRED); SupplyPump supply = SupplyPump(PIN_SUPPLY); Solenoid solenoid = Solenoid(PIN_SOLENOID); diff --git a/software/microcontroller/src/sensors/ge_2158.cc b/software/microcontroller/src/sensors/ge_2158.cc index 3426bb97..bf76c37b 100644 --- a/software/microcontroller/src/sensors/ge_2158.cc +++ b/software/microcontroller/src/sensors/ge_2158.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -7,16 +9,11 @@ #include #include -// Data setup -static const char* labels[1] = { "water_temperature" }; -static const SensorDataSetup datasetup = { - .numdata = 1, - .labels = labels -}; +// CONSTRUCTOR -GE_2158::GE_2158(uint8_t pin) : Sensor(SENSOR_GE_2158, &datasetup, GE_2158_DELTA) { - this->pin = pin; -} +GE_2158::GE_2158(const uint8_t pin) : Sensor(&id, &datasetup, GE_2158_DELTA), pin(pin) { } + +// PUBLIC METHODS errorlevel_t GE_2158::initialize(void) { pinMode(this->pin, INPUT); diff --git a/software/microcontroller/src/sensors/ge_2158.h b/software/microcontroller/src/sensors/ge_2158.h index 0c6516c8..ce4ddbce 100644 --- a/software/microcontroller/src/sensors/ge_2158.h +++ b/software/microcontroller/src/sensors/ge_2158.h @@ -1,11 +1,16 @@ #ifndef PEAPOD_SENSORS_GE_2158_H_ #define PEAPOD_SENSORS_GE_2158_H_ +// HEADERS + #include #include #include +// MACROS + +// Settings #define GE_2158_DELTA 100 #define GE_2158_V 5.0 // Feed voltage #define GE_2158_R 3000 // Series resistor @@ -15,21 +20,38 @@ #define GE_2158_B 9.30108 // B parameter #define GE_2158_C 122.933 // C parameter -// Interface for the SenseAir K30 Extended Range CO2 sensor. +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "GE-2158"; + + // Data setup + static const PROGMEM char* const labels[1] = { "water_temperature" }; + static const PROGMEM SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels + }; +#endif + +// CLASS + +// Interface for the Amphenol GE-2158 water temperature sensor. class GE_2158 : public Sensor { public: /** * Constructor. * @param pin Analog input pin. */ - GE_2158(uint8_t pin); + GE_2158(const uint8_t pin); private: errorlevel_t initialize(void) override; errorlevel_t read(float* data, uint8_t numdata) override; // Analog input pin. - uint8_t pin; + const uint8_t pin; }; #endif diff --git a/software/microcontroller/src/sensors/k30.cc b/software/microcontroller/src/sensors/k30.cc index a8474b06..8e15c2cc 100644 --- a/software/microcontroller/src/sensors/k30.cc +++ b/software/microcontroller/src/sensors/k30.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -6,17 +8,11 @@ #include #include -// Data setup -static const char* labels[1] = { "co2_ppm" }; -static const SensorDataSetup datasetup = { - .numdata = 1, - .labels = labels -}; +// CONSTRUCTOR -K30::K30(TwoWire* wire, uint8_t address) : Sensor(SENSOR_K30, &datasetup, K30_DELTA) { - this->wire = wire; - this->address = address; -} +K30::K30(TwoWire* wire, uint8_t address) : Sensor(&id, &datasetup, K30_DELTA), address(address), wire(wire) { } + +// PUBLIC METHODS errorlevel_t K30::initialize(void) { wire->begin(); diff --git a/software/microcontroller/src/sensors/k30.h b/software/microcontroller/src/sensors/k30.h index aeae8205..d11512be 100644 --- a/software/microcontroller/src/sensors/k30.h +++ b/software/microcontroller/src/sensors/k30.h @@ -1,18 +1,40 @@ #ifndef PEAPOD_SENSORS_K30_H_ #define PEAPOD_SENSORS_K30_H_ +// HEADERS + #include #include #include #include +// MACROS + +// Settings #define K30_DEFAULT_ADDR 0x68 #define K30_DELTA 2000 -// Import the default `Wire` interface provided by Arduino. +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "K30"; + + // Data setup + static const PROGMEM char* const labels[1] = { "co2_ppm" }; + static const PROGMEM SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels + }; +#endif + +// Import the default I2C interface provided by Arduino. extern TwoWire Wire; +// CLASS + // Interface for the SenseAir K30 Extended Range CO2 sensor. class K30 : public Sensor { public: @@ -21,14 +43,14 @@ class K30 : public Sensor { * @param wire I2C interface. Defaults to the provided `Wire` interface (by reference). * @param address Address of the K30 on the I2C bus. Defaults to 0x68 (hardware default). */ - K30(TwoWire* wire = &Wire, uint8_t address = K30_DEFAULT_ADDR); + K30(TwoWire* wire = &Wire, const uint8_t address = K30_DEFAULT_ADDR); private: errorlevel_t initialize(void) override; errorlevel_t read(float* data, uint8_t numdata) override; // Address of the K30 on the I2C bus. - uint8_t address; + const uint8_t address; // I2C interface. TwoWire* wire; diff --git a/software/microcontroller/src/sensors/sen0257.cc b/software/microcontroller/src/sensors/sen0257.cc index fab28d88..c65ac81e 100644 --- a/software/microcontroller/src/sensors/sen0257.cc +++ b/software/microcontroller/src/sensors/sen0257.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -5,16 +7,11 @@ #include #include -// Data setup -static const char* labels[1] = { "water_pressure" }; -static const SensorDataSetup datasetup = { - .numdata = 1, - .labels = labels -}; +// CONSTRUCTOR -SEN0257::SEN0257(uint8_t pin) : Sensor(SENSOR_SEN0257, &datasetup, SEN0257_DELTA) { - this->pin = pin; -} +SEN0257::SEN0257(const uint8_t pin) : Sensor(&id, &datasetup, SEN0257_DELTA), pin(pin) { } + +// PUBLIC METHODS errorlevel_t SEN0257::initialize(void) { pinMode(this->pin, INPUT); diff --git a/software/microcontroller/src/sensors/sen0257.h b/software/microcontroller/src/sensors/sen0257.h index 98aac0b9..c8162f9a 100644 --- a/software/microcontroller/src/sensors/sen0257.h +++ b/software/microcontroller/src/sensors/sen0257.h @@ -1,32 +1,54 @@ #ifndef PEAPOD_SENSORS_SEN0257_H_ #define PEAPOD_SENSORS_SEN0257_H_ +// HEADERS + #include #include #include +// MACROS + +// Settings #define SEN0257_DELTA 100 #define SEN0257_VMIN 0.5 // Min output voltage #define SEN0257_VMAX 4.5 // Max output voltage #define SEN0257_PMIN 0.0 // Min output pressure (PSI) #define SEN0257_PMAX 261.1 // Max output pressure (PSI) -// Interface for the SenseAir K30 Extended Range CO2 sensor. +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "SEN0257"; + + // Data setup + static const PROGMEM char* const labels[1] = { "water_pressure" }; + static const PROGMEM SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels + }; +#endif + +// CLASS + +// Interface for the DFRobot SEN0257 water pressure sensor. class SEN0257 : public Sensor { public: /** * Constructor. * @param pin Analog input pin. */ - SEN0257(uint8_t pin); + SEN0257(const uint8_t pin); private: errorlevel_t initialize(void) override; errorlevel_t read(float* data, uint8_t numdata) override; // Analog input pin. - uint8_t pin; + const uint8_t pin; }; #endif diff --git a/software/microcontroller/src/sensors/sensor.cc b/software/microcontroller/src/sensors/sensor.cc index a281415a..9475c2f9 100644 --- a/software/microcontroller/src/sensors/sensor.cc +++ b/software/microcontroller/src/sensors/sensor.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -6,10 +8,9 @@ #include -Sensor::Sensor(sensorid_t sensorid, const SensorDataSetup* setup, uint32_t delta) { - this->sensorid = sensorid; - this->delta = delta; +// CONSTRUCTOR +Sensor::Sensor(const char* const* id, const SensorDataSetup* setup, uint32_t delta) : delta(delta), _id(id) { // Initial error and debug states state.error = ERR_NONE; state.debug = DS_DISABLED; @@ -23,6 +24,8 @@ Sensor::Sensor(sensorid_t sensorid, const SensorDataSetup* setup, uint32_t delta } } +// PUBLIC METHODS + SensorState* Sensor::begin(void) { state.error = initialize(); if (state.error > ERR_NONE) { @@ -84,6 +87,13 @@ SensorState* Sensor::getState(void) { return &state; } -sensorid_t Sensor::getID(void) { - return sensorid; +String Sensor::toString(void) { + String s = String((const char*)(this->_id)) + " ("; + for (int i = 0; i < state.numdata; i++) { + s += state.data[i].label; + if (i < state.numdata - 1) { + s += ", "; + } + } + return s + ")"; } \ No newline at end of file diff --git a/software/microcontroller/src/sensors/sensor.h b/software/microcontroller/src/sensors/sensor.h index 005d1f6a..0b95b45b 100644 --- a/software/microcontroller/src/sensors/sensor.h +++ b/software/microcontroller/src/sensors/sensor.h @@ -1,19 +1,13 @@ #ifndef PEAPOD_SENSORS_SENSOR_H_ #define PEAPOD_SENSORS_SENSOR_H_ +// HEADERS + #include #include -// Sensor identifiers -typedef enum sensorid_t { - SENSOR_NULL, - SENSOR_SHT31, - SENSOR_K30, - SENSOR_GE_2158, - SENSOR_YF_B1, - SENSOR_SEN0257, -} sensorid_t; +// DECLARATIONS // Single datapoint typedef struct DataPoint { @@ -47,18 +41,19 @@ typedef struct SensorDataSetup { const uint8_t numdata; // Array of string literals corresponding to the labels for each dataset - const char** labels; + const char* const* labels; } SensorDataSetup; +// CLASS + class Sensor { public: /** * Sensor interface constructor. - * @param sensor Identifier * @param setup Pointer to datasets setup * @param delta Minimum delay (in milliseconds) between sensor read attempts */ - Sensor(sensorid_t sensorid, const SensorDataSetup* setup, uint32_t delta); + Sensor(const char* const* id, const SensorDataSetup* setup, uint32_t delta); /** Wrapper function for `initialize()`. Sets debug state to indicate initialization success or failure. * @return Pointer to sensor state @@ -74,8 +69,8 @@ class Sensor { // @return Pointer to sensor state SensorState* getState(void); - // @return Sensor identifier - sensorid_t getID(void); + // @return String representation + String toString(void); protected: /** @@ -95,15 +90,15 @@ class Sensor { private: // Stores all the latest state data for this sensor. SensorState state; - - // Sensor identifier - sensorid_t sensorid; // Minimum delay (in milliseconds) between sensor read attempts - uint32_t delta; + const uint32_t delta; // Last read attempt (milliseconds since program start) - NOT THE SAME AS `state.timestamp`, an unsuccessful read attempt will still update `lastread`. uint32_t lastread; + + // Sensor ID + const char* const* _id; }; #endif diff --git a/software/microcontroller/src/sensors/sht31.cc b/software/microcontroller/src/sensors/sht31.cc index 2cda093b..2b8dedda 100644 --- a/software/microcontroller/src/sensors/sht31.cc +++ b/software/microcontroller/src/sensors/sht31.cc @@ -1,3 +1,5 @@ +// HEADERS + #include #include @@ -6,21 +8,13 @@ #include #include -static uint8_t crc8(const uint8_t *data, int len); +// DECLARATIONS -// Data setup -static const char* labels[2] = { "air_temperature", "air_humidity" }; -static const SensorDataSetup datasetup = { - .numdata = 2, - .labels = labels -}; +static uint8_t crc8(const uint8_t *data, int len); // CONSTRUCTOR -SHT31::SHT31(TwoWire *wire, uint8_t address) : Sensor(SENSOR_SHT31, &datasetup, SHT31_DELTA) { - this->wire = wire; - this->address = address; -} +SHT31::SHT31(TwoWire *wire, uint8_t address) : Sensor(&id, &datasetup, SHT31_DELTA), address(address), wire(wire) { } // PUBLIC METHODS @@ -70,7 +64,7 @@ errorlevel_t SHT31::read(float* data, uint8_t numdata) { return ERR_NONE; } -// UTILITY FUNCTIONS +// HELPER FUNCTIONS void SHT31::writeCommand(uint16_t cmd) { wire->beginTransmission(address); diff --git a/software/microcontroller/src/sensors/sht31.h b/software/microcontroller/src/sensors/sht31.h index eca62efd..2009f405 100644 --- a/software/microcontroller/src/sensors/sht31.h +++ b/software/microcontroller/src/sensors/sht31.h @@ -1,38 +1,64 @@ #ifndef PEAPOD_SENSORS_SHT31_H_ #define PEAPOD_SENSORS_SHT31_H_ +// HEADERS + #include #include #include #include -#define SHT31_DEFAULT_ADDR 0x44 // SHT31 Default Address -#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Measurement High Repeatability with Clock Stretch Enabled -#define SHT31_MEAS_MEDREP_STRETCH 0x2C0D // Measurement Medium Repeatability with Clock Stretch Enabled -#define SHT31_MEAS_LOWREP_STRETCH 0x2C10 // Measurement Low Repeatability with Clock Stretch Enabled -#define SHT31_MEAS_HIGHREP 0x2400 // Measurement High Repeatability with Clock Stretch Disabled -#define SHT31_MEAS_MEDREP 0x240B // Measurement Medium Repeatability with Clock Stretch Disabled -#define SHT31_MEAS_LOWREP 0x2416 // Measurement Low Repeatability with Clock Stretch Disabled -#define SHT31_READSTATUS 0xF32D // Read Out of Status Register -#define SHT31_CLEARSTATUS 0x3041 // Clear Status -#define SHT31_SOFTRESET 0x30A2 // Soft Reset -#define SHT31_HEATEREN 0x306D // Heater Enable -#define SHT31_HEATERDIS 0x3066 // Heater Disable -#define SHT31_REG_HEATER_BIT 0x0d // Status Register Heater Bit -#define SHT31_DELTA 1000 - -// Import the default `Wire` interface provided by Arduino. +// MACROS + +// Registers +#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Measurement High Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_MEDREP_STRETCH 0x2C0D // Measurement Medium Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_LOWREP_STRETCH 0x2C10 // Measurement Low Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_HIGHREP 0x2400 // Measurement High Repeatability with Clock Stretch Disabled +#define SHT31_MEAS_MEDREP 0x240B // Measurement Medium Repeatability with Clock Stretch Disabled +#define SHT31_MEAS_LOWREP 0x2416 // Measurement Low Repeatability with Clock Stretch Disabled +#define SHT31_READSTATUS 0xF32D // Read Out of Status Register +#define SHT31_CLEARSTATUS 0x3041 // Clear Status +#define SHT31_SOFTRESET 0x30A2 // Soft Reset +#define SHT31_HEATEREN 0x306D // Heater Enable +#define SHT31_HEATERDIS 0x3066 // Heater Disable +#define SHT31_REG_HEATER_BIT 0x000d // Status Register Heater Bit + +// Settings +#define SHT31_ADDR 0x44 // SHT31 Default Address +#define SHT31_DELTA 1000 // read() once every delta milliseconds + + +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "SHT31"; + + // Data setup + static const PROGMEM char* const labels[2] = { "air_temperature", "air_humidity" }; + static const PROGMEM SensorDataSetup datasetup = { + .numdata = 2, + .labels = labels + }; +#endif + +// Import the default I2C interface provided by Arduino. extern TwoWire Wire; +// CLASS + // Interface for the Adafruit SHT31-D Temperature and Humidity breakout board. class SHT31 : public Sensor { public: /** * Constructor. * @param wire I2C interface. Defaults to the provided `Wire` interface (by reference). + * @param address I2C address. Defaults to the default SHT31 address */ - SHT31(TwoWire* wire = &Wire, uint8_t address = SHT31_DEFAULT_ADDR); + SHT31(TwoWire* wire = &Wire, uint8_t address = SHT31_ADDR); /** * UNUSED @@ -69,7 +95,7 @@ class SHT31 : public Sensor { void reset(void); // I2C address - uint8_t address; + const uint8_t address; // Wire object TwoWire* wire; diff --git a/software/microcontroller/src/sensors/yf_b1.cc b/software/microcontroller/src/sensors/yf_b1.cc index 14be025b..f048955a 100644 --- a/software/microcontroller/src/sensors/yf_b1.cc +++ b/software/microcontroller/src/sensors/yf_b1.cc @@ -1,31 +1,22 @@ -#include +// HEADERS -#include +#include #include #include #include -// Data setup -static const char* labels[1] = { "water_flow_rate" }; -static const SensorDataSetup datasetup = { - .numdata = 1, - .labels = labels -}; +// CONSTRUCTOR -void flow(void); +YF_B1::YF_B1(const uint8_t pin) : Sensor(&id, &datasetup, YF_B1_DELTA), InterruptHandler(&(this->pin)), pin(pin) { } -volatile uint32_t flow_count; -unsigned long last_count; - -YF_B1::YF_B1(uint8_t pin) : Sensor(SENSOR_YF_B1, &datasetup, YF_B1_DELTA) { - this->pin = pin; -} +// PUBLIC METHODS errorlevel_t YF_B1::initialize(void) { pinMode(this->pin, INPUT); - attachInterrupt(digitalPinToInterrupt(this->pin), flow, RISING); + // Attach rising-edge interrupt to our digital pin, triggers flow() + attachInstanceInterrupt(RISING); return ERR_NONE; } @@ -42,6 +33,6 @@ errorlevel_t YF_B1::read(float* data, uint8_t numdata) { return ERR_NONE; } -void flow(void) { - flow_count++; +void YF_B1::handleInterrupt(void) { + this->flow_count++; } \ No newline at end of file diff --git a/software/microcontroller/src/sensors/yf_b1.h b/software/microcontroller/src/sensors/yf_b1.h index 95be905c..8deef43a 100644 --- a/software/microcontroller/src/sensors/yf_b1.h +++ b/software/microcontroller/src/sensors/yf_b1.h @@ -1,29 +1,61 @@ #ifndef PEAPOD_SENSORS_YF_B1_H_ #define PEAPOD_SENSORS_YF_B1_H_ +// HEADERS + #include #include #include +#include + +// MACROS +// Settings #define YF_B1_DELTA 1000 // Calculate frequency every second #define YF_B1_COUNT_PER_LPM 7.5 // 7.5 counts is one liter per minute -// Interface for the SenseAir K30 Extended Range CO2 sensor. -class YF_B1 : public Sensor { +// GLOBALS +// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file +#ifndef GLOBALS // Double-declaration blocker + #define GLOBALS + // Descriptors + static const PROGMEM char* const id = "YF-B1"; + + // Data setup + static const PROGMEM char* const labels[1] = { "water_flow_rate" }; + static const PROGMEM SensorDataSetup datasetup = { + .numdata = 1, + .labels = labels + }; +#endif + +// CLASS + +// Interface for the Seeed Studio YF-B1 flow sensor. +class YF_B1 : public Sensor, public InterruptHandler { public: /** * Constructor. - * @param pin Digital input pin. MUST BE 2 OR 3 + * @param pin Digital input pin. MUST be capable of interrupts (pins 2 and 3 on the Arduino Nano) */ - YF_B1(uint8_t pin); + YF_B1(const uint8_t pin); private: errorlevel_t initialize(void) override; errorlevel_t read(float* data, uint8_t numdata) override; // Digital input pin. - uint8_t pin; + const uint8_t pin; + + // Triggered by interrupt. Increments flow counter. + void handleInterrupt(void) override; + + // Flow interrupt counter - volatile for interrupt compatibility + volatile uint32_t flow_count; + + // Timestamp of last flowrate calculation + unsigned long last_count; }; #endif diff --git a/software/microcontroller/src/utils/interrupts.cc b/software/microcontroller/src/utils/interrupts.cc new file mode 100644 index 00000000..40345ca2 --- /dev/null +++ b/software/microcontroller/src/utils/interrupts.cc @@ -0,0 +1,23 @@ +#include + +#include + +InterruptHandler::InterruptHandler(const uint8_t* pin) { + this->pin = pin; +} + +InterruptHandler::~InterruptHandler() { + detachInterrupt(digitalPinToInterrupt(*this->pin)); +} + +void InterruptHandler::attachInstanceInterrupt(int mode) { + // Pointer-to-member-function &InterruptHandler::handleInterrupt points to handleInterrupt on THIS instance + // and is then converted to a normal function pointer to its place in memory + // This causes a `-Wpmf-conversions` warning, but works so long as the object instance outlives the interrupt + // This is ensured by detaching the interrupt on object destruction + attachInterrupt(digitalPinToInterrupt(*this->pin), (interruptHandler)(&InterruptHandler::handlerTrigger), RISING); +} + +void InterruptHandler::handlerTrigger(void) { + this->handleInterrupt(); +} \ No newline at end of file diff --git a/software/microcontroller/src/utils/interrupts.h b/software/microcontroller/src/utils/interrupts.h new file mode 100644 index 00000000..671dc788 --- /dev/null +++ b/software/microcontroller/src/utils/interrupts.h @@ -0,0 +1,40 @@ +#ifndef PEAPOD_UTILS_INTERRUPTS_H_ +#define PEAPOD_UTILS_INTERRUPTS_H_ + +#include + +typedef void (*interruptHandler)(void); + +class InterruptHandler { + public: + /** + * Constructor. + * @param pin Digital pin to attach interrupt to + */ + InterruptHandler(const uint8_t* pin); + + /** + * Destructor. Used to detach the interrupt + */ + ~InterruptHandler(); + + /** + * Attaches the interrupt to trigger `handleInterrupt` on THIS instance + * @param mode Mode (i.e. RISING) + */ + void attachInstanceInterrupt(int mode); + + /** + * Triggered by interrupt. Triggers handlerTrigger + */ + void handlerTrigger(void); + + /** + * Triggered by handlerTrigger. To be implemented by the child class. + */ + virtual void handleInterrupt(void) = 0; + private: + const uint8_t* pin; +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/test/test_instructions/test_instructions.cc b/software/microcontroller/test/test_instructions/test_instructions.cc index 149acc37..7196c6e5 100644 --- a/software/microcontroller/test/test_instructions/test_instructions.cc +++ b/software/microcontroller/test/test_instructions/test_instructions.cc @@ -13,7 +13,7 @@ class TestActuator : public Actuator { errorlevel_t set(float target) override; }; -TestActuator::TestActuator(void) : Actuator(ACTUATOR_NULL, 0) { } +TestActuator::TestActuator(void) : Actuator(0) { } errorlevel_t TestActuator::initialize(void) { return ERR_NONE; From beefa0703275694cf1393409b4d30c129cbf0a5d Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 29 Jul 2022 14:58:05 -0400 Subject: [PATCH 29/85] Microcontroller Modular Unit Tests --- .../test_instructions/test_instructions.cc | 6 ++- .../microcontroller/test/test_k30/test_k30.cc | 37 ++++--------------- .../test/test_sht31/test_sht31.cc | 37 ++++--------------- software/microcontroller/test/tests.cc | 34 +++++++++++++++++ software/microcontroller/test/tests.h | 34 +++++++++++++++++ 5 files changed, 86 insertions(+), 62 deletions(-) create mode 100644 software/microcontroller/test/tests.cc create mode 100644 software/microcontroller/test/tests.h diff --git a/software/microcontroller/test/test_instructions/test_instructions.cc b/software/microcontroller/test/test_instructions/test_instructions.cc index 7196c6e5..6c2de6a9 100644 --- a/software/microcontroller/test/test_instructions/test_instructions.cc +++ b/software/microcontroller/test/test_instructions/test_instructions.cc @@ -13,7 +13,9 @@ class TestActuator : public Actuator { errorlevel_t set(float target) override; }; -TestActuator::TestActuator(void) : Actuator(0) { } +const char* const id = "Test Actuator"; + +TestActuator::TestActuator(void) : Actuator(&id, 0) { } errorlevel_t TestActuator::initialize(void) { return ERR_NONE; @@ -56,7 +58,7 @@ void test_bad_instruction(void) { } void test_empty_instruction_set(void) { - uint8_t result = InstructionHandler::handleSet("{}"), &matrix); + uint8_t result = InstructionHandler::handleSet("{}", &matrix); TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); } diff --git a/software/microcontroller/test/test_k30/test_k30.cc b/software/microcontroller/test/test_k30/test_k30.cc index 1c56a3c7..11037470 100644 --- a/software/microcontroller/test/test_k30/test_k30.cc +++ b/software/microcontroller/test/test_k30/test_k30.cc @@ -4,52 +4,29 @@ #include #include #include +#include "../tests.h" -K30 sensor = K30(); - -void test_begin(void) { - SensorState* state = sensor.begin(); - TEST_ASSERT_EQUAL(DS_INITIALIZED, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} - -void test_read(void) { - SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} - -void test_delta(void) { - SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); - - // No delay - - state = sensor.update(); - TEST_ASSERT_EQUAL(DS_WAITING, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} +Sensor* sensor = new K30(); void setup(void) { delay(2000); UNITY_BEGIN(); - RUN_TEST(test_begin); + RUN_TEST(SensorTests::test_begin); } int i = 0; void loop(void) { if (i < 3) { - delay(3000); + delay(K30_DELTA * 1.5); - RUN_TEST(test_read); + RUN_TEST(SensorTests::test_update); - delay(3000); + delay(K30_DELTA * 1.5); - RUN_TEST(test_delta); + RUN_TEST(SensorTests::test_delta); i++; } else { UNITY_END(); diff --git a/software/microcontroller/test/test_sht31/test_sht31.cc b/software/microcontroller/test/test_sht31/test_sht31.cc index db77acec..a0fda98d 100644 --- a/software/microcontroller/test/test_sht31/test_sht31.cc +++ b/software/microcontroller/test/test_sht31/test_sht31.cc @@ -4,52 +4,29 @@ #include #include #include +#include "../tests.h" -SHT31 sensor = SHT31(); - -void test_begin(void) { - SensorState* state = sensor.begin(); - TEST_ASSERT_EQUAL(DS_INITIALIZED, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} - -void test_read(void) { - SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} - -void test_delta(void) { - SensorState* state = sensor.update(); - TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); - - // No delay - - state = sensor.update(); - TEST_ASSERT_EQUAL(DS_WAITING, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} +Sensor* sensor = new SHT31(); void setup(void) { delay(2000); UNITY_BEGIN(); - RUN_TEST(test_begin); + RUN_TEST(SensorTests::test_begin); } int i = 0; void loop(void) { if (i < 3) { - delay(1500); + delay(SHT31_DELTA * 1.5); - RUN_TEST(test_read); + RUN_TEST(SensorTests::test_update); - delay(1500); + delay(SHT31_DELTA * 1.5); - RUN_TEST(test_delta); + RUN_TEST(SensorTests::test_delta); i++; } else { UNITY_END(); diff --git a/software/microcontroller/test/tests.cc b/software/microcontroller/test/tests.cc new file mode 100644 index 00000000..26b2219d --- /dev/null +++ b/software/microcontroller/test/tests.cc @@ -0,0 +1,34 @@ +// HEADERS +#include "tests.h" + +#include +#include + +#include +#include + +// DECLARATIONS + +void SensorTests::test_begin(void) { + SensorState* state = sensor->begin(); + TEST_ASSERT_EQUAL(DS_INITIALIZED, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); +} + +void SensorTests::test_update(void) { + SensorState* state = sensor->update(); + TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); +} + +void SensorTests::test_delta(void) { + SensorState* state = sensor->update(); + TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); + + // No delay + + state = sensor->update(); + TEST_ASSERT_EQUAL(DS_WAITING, state->debug); + TEST_ASSERT_EQUAL(ERR_NONE, state->error); +} \ No newline at end of file diff --git a/software/microcontroller/test/tests.h b/software/microcontroller/test/tests.h new file mode 100644 index 00000000..90af46dd --- /dev/null +++ b/software/microcontroller/test/tests.h @@ -0,0 +1,34 @@ +#ifndef PEAPOD_TESTS_H_ +#define PEAPOD_TESTS_H_ + +// HEADERS + +#include +#include + +#include +#include + +// Requires `sensor` to be defined in the test source file +extern Sensor* sensor; + +// DECLARATAIONS + +namespace SensorTests { + /** + * Ensures state compliance with `begin()` + */ + void test_begin(void); + + /** + * Ensures state compliance with `update()` + */ + void test_update(void); + + /** + * Ensures state compliance when `delta` is skipped + */ + void test_delta(void); +} + +#endif \ No newline at end of file From f0af5b58a8d34ebce3985351e52bb88be96365a6 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Wed, 3 Aug 2022 21:26:40 -0400 Subject: [PATCH 30/85] Schedule Parsing, Control Systems --- software/assets/program_schema.json | 19 --- software/assets/schedule_schema.json | 43 +++++++ software/src/controller.ts | 2 +- software/src/controlsystem.ts | 61 +++++++++ software/src/peapod.ts | 178 ++++++++++++++++++++++++--- software/src/publisher.ts | 12 +- 6 files changed, 273 insertions(+), 42 deletions(-) delete mode 100644 software/assets/program_schema.json create mode 100644 software/assets/schedule_schema.json create mode 100644 software/src/controlsystem.ts diff --git a/software/assets/program_schema.json b/software/assets/program_schema.json deleted file mode 100644 index 34b1bb4f..00000000 --- a/software/assets/program_schema.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "{projectid}-program-{uuid}", - "description": "A short description of the program, including parameter values, duration, etc.", - "revision": 0, - // Metadata - "parameters": { - "{parametername}": { - // Metadata - "values": [ - { - "timestamp": 102375613, - "value": 23.4 - }, - // More values... - ] - } - // Other parameters... - } -} \ No newline at end of file diff --git a/software/assets/schedule_schema.json b/software/assets/schedule_schema.json new file mode 100644 index 00000000..95dec732 --- /dev/null +++ b/software/assets/schedule_schema.json @@ -0,0 +1,43 @@ +// All timestamps in ms since program start +// All durations in ms +{ + "id": "{projectid}-program-{uuid}", + "name": "A short description of the program, including parameter values, duration, etc.", + "revision": 0, //Software revision + "parameters": { + "{parametername}": [ // Schedule is loaded in phase by phase + { + "type": "piecewise", // Step through values until end + "end": 10000, + "values": [ + { + "value": 23.4, + "timestamp": 0 + }, + { + "value": 24.6, + "timestamp": 123456 + }, + // More values... + ] + }, + { + "type": "periodic", // Step through values until end + "period": 10000, + "end": 1234567, + "values": [ + { + "value": 23.4, + "duration": 1000 + }, + { + "value": 24.6, + "duration": 1000 + }, + // More values... + ] + } + // More phases... + ] + } +} \ No newline at end of file diff --git a/software/src/controller.ts b/software/src/controller.ts index f9f42204..574a4334 100644 --- a/software/src/controller.ts +++ b/software/src/controller.ts @@ -15,7 +15,7 @@ const BAUDRATE = 115200; /** * Microcontroller software revision matching this software. */ -const CONTROLLER_REVISION = 0; +export const CONTROLLER_REVISION = 0; /** * Seconds to wait between messages before timing out. diff --git a/software/src/controlsystem.ts b/software/src/controlsystem.ts new file mode 100644 index 00000000..3f95bac3 --- /dev/null +++ b/software/src/controlsystem.ts @@ -0,0 +1,61 @@ +export abstract class ControlSystem { + protected target?: number; + protected value?: number; + constructor(readonly actuator: string) {} + abstract refresh(): number + setTarget(target: number) { this.target = target; } + setValue(value: number) { this.value = value; } +} + +// MAIN CLASS + +/** + * Interface for publishing data to and receiving instructions from Google Cloud/Firebase via IoT Core PubSub. + */ +export class BangBang extends ControlSystem { + constructor(readonly actuator: string, readonly deadzone: number = 0) { + super(actuator); + } + refresh(): number { + if (this.target === undefined || this.value === undefined) { + return NaN; + } + if (this.value > this.target + this.deadzone) { + return -1; + } + if (this.value < this.target - this.deadzone) { + return 1; + } + return 0; + } +} + +/** + * Interface for publishing data to and receiving instructions from Google Cloud/Firebase via IoT Core PubSub. + */ +export class PID extends ControlSystem { + private valueLastSet: number = NaN; + private lasterr: number = NaN; + private integral: number = 0; + constructor(readonly actuator: string, readonly Kp: number, readonly Ki: number, readonly Kd: number) { + super(actuator); + } + refresh(): number { + if (this.target === undefined || this.value === undefined) { + return NaN; + } + const err = this.target - this.value; + let derivative = 0; + if (this.valueLastSet != NaN || this.lasterr != NaN) { + // Trapezoidal approximation + this.integral += (this.lasterr + err) / 2 * (Date.now() - this.valueLastSet); + derivative = (err - this.lasterr) / (Date.now() - this.valueLastSet); + } + return this.Kp * err + this.Ki * this.integral + this.Kd * derivative; + } + + override setValue(value: number): void { + this.value = value; + this.valueLastSet = Date.now(); + } +} \ No newline at end of file diff --git a/software/src/peapod.ts b/software/src/peapod.ts index 291888e2..0f232d68 100644 --- a/software/src/peapod.ts +++ b/software/src/peapod.ts @@ -5,7 +5,7 @@ import { initializeApp, getApps } from 'firebase/app'; import { getAuth } from 'firebase/auth'; import { loadPeaPodEnv, loadAuthEnv, loadFirebaseEnv, loadIoTEnv } from './env'; -import MicroController, { ControllerInstructions, Controller } from './controller'; +import MicroController, { ControllerInstructions, Controller, CONTROLLER_REVISION } from './controller'; import OnlinePublisher, { DataBatch, DataSet, Publisher, OfflinePublisher, PublishingMode } from './publisher'; import { sleep } from './utils'; @@ -17,9 +17,14 @@ import { sleep } from './utils'; const BATCH_PUBLISH_INTERVAL = 5; /** - * Milliseconds between refresh during idle. + * Milliseconds between target refresh. */ -const IDLE_INTERVAL = 100; +const REFRESH_INTERVAL = 1000; + +/** + * Period of the idle loop. + */ +const IDLE_PERIOD = 5000; // GLOBAL VARIABLES @@ -32,6 +37,16 @@ let batch: DataBatch = { }; * JS interval to trigger batch publishing. */ let batchInterval: NodeJS.Timer; + +/** + * Currently loaded schedule. + */ +let schedule: EnvironmentSchedule; + +/** + * Current environment schedule targets. + */ +let targets: EnvironmentTargets = {}; /** * Latest controller instruction set. @@ -43,6 +58,40 @@ let instructions: ControllerInstructions = {}; */ let data: DataSet = {}; +let controlSystems: {[key: string]: ControlSystem} + +// DECLARATIONS + +export type EnvironmentSchedule = { + id: string, + name: string, + revision: number, + parameters: { + [key: string]: EnvironmentTargetPhase[] + } +}; + +type EnvironmentTargets = { + [key: string]: number +}; + +type EnvironmentTargetPhase = { + type: 'piecewise', + end: number, + values: { + value: number, + timestamp: number, + }[] +} | { + type: 'periodic', + period: number, + end: number, + values: { + value: number, + duration: number, + }[] +}; + /** * Main driver class. */ @@ -50,6 +99,7 @@ export default class PeaPod { controller: Controller; publisher: Publisher; + private startTime: number = 0; constructor(pm: PublishingMode) { const ENV_PEAPOD = loadPeaPodEnv(); @@ -99,19 +149,18 @@ export default class PeaPod { let angle = 0; const idleInterval = setInterval(() => { - instructions = { - "led_blue": Math.sin(angle)/2+0.5, - "led_cool": Math.sin(angle + 2*Math.PI/5)/2+0.5, - "led_warm": Math.sin(angle + 4*Math.PI/5)/2+0.5, - "led_red": Math.sin(angle + 6*Math.PI/5)/2+0.5, - "led_far": Math.sin(angle + 8*Math.PI/5)/2+0.5, + const idleInstructions = { + "led_blue": Math.sin(angle ) / 2 + 0.5, + "led_cool": Math.sin(angle + 2 * Math.PI / 5) / 2 + 0.5, + "led_warm": Math.sin(angle + 4 * Math.PI / 5) / 2 + 0.5, + "led_red": Math.sin(angle + 6 * Math.PI / 5) / 2 + 0.5, + "led_far": Math.sin(angle + 8 * Math.PI / 5) / 2 + 0.5, }; - this.controller.write(instructions); + this.controller.write(idleInstructions); - // One revolution every 5 seconds - angle += 2*Math.PI/(5000/IDLE_INTERVAL); - angle -= angle > 2*Math.PI ? 2*Math.PI : 0; - }, IDLE_INTERVAL); + angle += (2 * Math.PI) / (IDLE_PERIOD / REFRESH_INTERVAL); + angle -= (angle > 2 * Math.PI) ? (2 * Math.PI) : 0; + }, REFRESH_INTERVAL); return idleInterval; } @@ -143,23 +192,45 @@ export default class PeaPod { // INITIALIZE PUBLISHER let { projectid, projectname, run } = await this.publisher.start(config => { - // Hot-swap programs - // TODO: This - Spinner.log(`[${ chalk.yellow('PUBLISHER') } | CONFIG] - ${ config }`); + Spinner.log(`[${ chalk.yellow('PUBLISHER') } | SCHEDULE] - ${ config }`); + switch (config.type) { + case 'schedule': + Spinner.log(`[${ chalk.yellow('PUBLISHER') } | SCHEDULE] - ${ JSON.stringify(config.data.name) }`); + if (config.data.revision != CONTROLLER_REVISION) { + Spinner.fail(`Failed to load schedule '${config.data.name}', software version mismatch (Expected ${CONTROLLER_REVISION}, got ${config.data.revision}).`); + break; + } + + // Save new schedule + schedule = config.data; + + break; + default: + Spinner.log(`[${ chalk.yellow('PUBLISHER') } | CONFIG] - ${ JSON.stringify(config.data) }`); + } }, command => { switch (command.type) { case 'instructions': + Spinner.log(`[${ chalk.yellow('PUBLISHER') } | INSTRUCTIONS] - ${ JSON.stringify(command.data) }`); this.controller.write(command.data); + break; default: Spinner.log(`[${ chalk.yellow('PUBLISHER') } | COMMAND] - ${ JSON.stringify(command) }`); } }); - // Todo: get instructions // START Spinner.log(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); + this.startTime = Date.now(); + + // Start schedule phase 0 + + for (const parameter of Object.keys(schedule.parameters)) { + this.startPhase(schedule, parameter); + } + // Reset batch = { }; @@ -185,5 +256,76 @@ export default class PeaPod { // Reset batch to empty batch = { }; }, BATCH_PUBLISH_INTERVAL*1000); + + refreshInterval = set + } + + /** + * Start the schedule for a single parameter. Runs recursively until all phases are executed. + * @param schedule Environment schedule + * @param parameter Parameter to start + * @param n Phase to start at + */ + private startPhase(schedule: EnvironmentSchedule, parameter: string, n: number = 0): Promise { + return new Promise(res => { + // Break once we've reached the end + if(n >= schedule.parameters[parameter].length) { + res(); + return; + } + + const phase = schedule.parameters[parameter][n]; + + let timeouts: NodeJS.Timeout[] = []; + let intervals: NodeJS.Timer[] = []; + + // Extract all targets + switch (phase.type) { + case 'piecewise': + // Trigger each piecewise expression at its timestamp + for (const value of phase.values) { + let t = setTimeout(() => { + // Assign value + targets[parameter] = value.value; + // Remove this timeout if/when it self-clears + timeouts.splice(timeouts.findIndex(v => v == t), 1); + }, value.timestamp - (Date.now() - this.startTime)); + timeouts.push(t); + } + break; + case 'periodic': + // Retrigger each value at a fixed period, with duration offset + let durationSum = 0; + phase.values.forEach((value, i) => { + // Await duration offset + let t = setTimeout(() => { + // Start the interval + intervals.push(setInterval(() => { + // Assign value + targets[parameter] = value.value; + }, phase.period)); + // Remove this timeout if/when it self-clears + timeouts.splice(timeouts.findIndex(v => v == t), 1); + // First expression? Trigger now. + // Else: Timeout for the sum of all previous expressions' durations, minus the elapsed time in this phase + }, i > 0 ? durationSum - (Date.now() - (n > 0 ? schedule.parameters[parameter][n-1].end : this.startTime)) : 0); + durationSum += value.duration; + timeouts.push(t); + }); + break; + } + + // End this phase once Date.now() - startTime > end, and start the next phase, waiting for its completion + setTimeout(() => { + // Clear all outstanding timeouts and intervals + for (const interval of intervals) { + clearInterval(interval); + } + for (const timeout of timeouts) { + clearTimeout(timeout); + } + this.startPhase(schedule, parameter, n+1).then(() => { res(); }); + }, phase.end - (Date.now() - this.startTime)); + }); } } \ No newline at end of file diff --git a/software/src/publisher.ts b/software/src/publisher.ts index 5b66d643..49c2091c 100644 --- a/software/src/publisher.ts +++ b/software/src/publisher.ts @@ -15,6 +15,7 @@ import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflo import { Spinner } from './ui'; import { fetchServerCert } from './utils'; import { ControllerInstructions } from './controller'; +import { EnvironmentSchedule } from './peapod'; // TYPES @@ -65,9 +66,12 @@ export type PubSubCommand = { }; /** - * Message FROM the publisher (config). WIP. + * Message FROM the publisher (config). */ -export type PubSubConfig = string; +export type PubSubConfig = { + type: 'schedule', + data: EnvironmentSchedule, +}; /** * Types of publishers. @@ -101,7 +105,7 @@ export type IoTConfig = { * Base type for any publisher. */ export type Publisher = { - start(onConfig?: (message: PubSubConfig)=>void, onCommand?: (message: PubSubCommand)=>void) : Promise<{projectid: string, projectname?: string, run: string}>, + start(onConfig: (message: PubSubConfig)=>void, onCommand: (message: PubSubCommand)=>void) : Promise<{projectid: string, projectname?: string, run: string}>, stop(): void, publish(msg: PubSubMessage): void, } @@ -339,7 +343,7 @@ export default class PubSubPublisher implements Publisher { * Publishes data batches to local JSON files. */ export class OfflinePublisher implements Publisher { - async start() { + async start(onConfig: (message: PubSubConfig) => void, onCommand: (message: PubSubCommand) => void) { let config = { projectid: 'testproject', run: 'testrun-'+uuid() }; Spinner.info(`Logging data to ${ chalk.bold('projects/'+config.projectid+'/runs/'+config.run+'/') }`); return config; From 27bad88be89f262f3185ec2ac1a02109a9cf6eb3 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Tue, 23 Aug 2022 18:40:02 -0400 Subject: [PATCH 31/85] Minor Changes --- docs/progressreport/ProgressReport.pdf | Bin 11171629 -> 11170537 bytes docs/progressreport/ProgressReport.tex | 8 +- software/microcontroller/platformio.ini | 18 ++- .../microcontroller/src/actuators/actuator.cc | 6 +- .../microcontroller/src/actuators/actuator.h | 2 + .../microcontroller/src/actuators/onoff.cc | 4 +- software/microcontroller/src/actuators/pwm.cc | 4 +- software/microcontroller/src/actuators/tec.cc | 12 +- .../microcontroller/src/modules/module.cc | 43 ++++++ software/microcontroller/src/modules/module.h | 48 +++++++ .../microcontroller/src/sensors/ge_2158.cc | 4 +- .../microcontroller/src/sensors/ge_2158.h | 1 + .../microcontroller/src/sensors/sen0257.cc | 4 +- .../microcontroller/src/sensors/sensor.cc | 6 +- software/microcontroller/src/sensors/sensor.h | 4 +- software/microcontroller/src/sensors/yf_b1.cc | 4 +- .../microcontroller/src/utils/interrupts.cc | 4 +- software/src/peapod.ts | 131 ++++++++++++------ 18 files changed, 234 insertions(+), 69 deletions(-) create mode 100644 software/microcontroller/src/modules/module.cc create mode 100644 software/microcontroller/src/modules/module.h diff --git a/docs/progressreport/ProgressReport.pdf b/docs/progressreport/ProgressReport.pdf index d21d7d53ee9c67b66a901af93a0f1216ecc6c315..1e1db05f69af46144560b1ccbc8f3fee637d743a 100644 GIT binary patch delta 141595 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%0Asj3o`NRBE(V#|#+=!4`lluxKhN3x2o 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%0D*9_rZ0e&DM=cvSe~( zHb#%7%WojVyM1D?*q|>~P_+}@^HY9F7>)OTHLsk~O99DmeL=HTAHwUv#s zz4FKi@Mo{#pRC-y`hE=wRM!WVgf%%+OAr7q^n=~%k_~2;Y@ReY2GixBtWw)G`n>-1B zbl(|a7d>S?-YAT1-1VLRuW-%ePx+MJRM6dK;XV`azuJNh;gw-;NLElgoLx zZ=dNDeuGl8ECg1{4J-Ll1eWE?G|F6@D?`a+MS24nq-WCzzRR7{E$}4U;G*dw=G}10 zOF{7MN`YYFmi#iUeZJ+&0n8c(_u;gEM(?-j78Q?*tN=+sw!dZ43OPZTDEQMBL1rDS zf*a2cr?)*Gu-ed#M>K3~XRuE$=|-OTMau$YxhK>CGzU;IYt1G@taF+G5j?szH_wNP z>FrQE`ul>h^~snyUL$}aqXOJ9dwz0r#b0ZS^5D>;zFzilj^<$|PyU2QF`liN`A!0R ze~b8ptp(uElwrE5uQwa2x3RKnvJ^V&?aI6mnnkY+t;joNZoY^rL4W27jk7!dxxhne zqv~Z4tMtB`Qt{B;FXeGwrJi2Vx94%G3PXo0DuT;+ zl#VOe&>e`CspPaO3_;UKp)u+PPSZf*_ovd|vkGn^mkxM}^cTjH%)KNp0s73_Al0T= z{bTVQ@$9rF!Ea|K6qSW^b&2V$XVc)HsC-3#50=rO(?XJw4FC^ z29W-9Ms8Rlp}UI+X;0p|44smCtt*E~FNPvLrq_@Cee@gPys)5Cb%OL$D2w=ZTMJyC=-jZ(>vw<@<{kLy| zKy*UmOH6O1-1v3`XB_KHkC-G-b;m+$on46JZYMZcNQF=Gt7J+#5KNFH3PAk*p#()r zm4D@N`dtSWTBNb)AT>v=G-AYbZ3O1@LuAu$-089rg8eVpIT|?fAwkuOcz?>!^&_ z$So^@D<~>VRRz>q?1MCKlkxw#jn2IcgvJQ0^!!V$0!7q)qs>SEEyWajB=~90ixM$G zs3Mc@TmZYdVFV+Pf9I-+&wC#1{QBvJ6jn?&gO1=rwTa#}BL4m|6=Blt5nBA3he|R= z8nzv7+^kG8C&x9*KkmYEu}`+}pVC~w$4tn0E6*vhu z^wV!^4PJndJz%OvT#qC6-x+l8nDNlGG&Y1lq&_psD&CjGa*rzDUnb7- zD3n%?#vW)@SRa4hYt^>)`L0*YxUhI z3;wZRj+p5ab;dFy^&jOQsPaWY>D`Io)|=wW_%)#Hy|p%lW}=`SX*Z(R6idOX$9;tj zsZ~a_e|HdSCV_!aq2dbJVxjrs9nVaMsS)|W-B)zc(e!iw;tdp9zcaeaAH>*E5?Ow? zCwHn+YfS!5)_?szj{T%aOhbn?8AN1IVE9qs07G^5^x`!q3^<< zMfE}92RXrZ*fvgPDVhS>G>p9GTbL>SQ$obse>H_{@(b1ZsOqj05k_QkZLB{B{JgRcReRdXoHmg~cNBSK zf**o~7}BCS*k+>(drPOVe+H50oODbYcFqTR0mcV)gBfwsy_78CD5`-G zRmQyx*%M)VelHCwvmX$QOTMjX#8K;z9Bh7HI_XRnYqT(GMfyl;TRCc`g8&{& zK8yS1UmRgq9z6rqK*p%a+|vldghy0V{^pSVB){;Z4-gawxy-L%0viP*uYqpBe}wrL z9u%@PNY7?n26a!(jHE2*4~j)cg=JB0$~WvS{IP2W3>1n&px6>=FJDB~gv#j?5mXx| zEb4_O<6gPFl#$eP5ZNfIo2Myb)O7*O@vcKSi3b*Z=$Yi3s0iQw+ z{@rWpHJF|s(uGjH-hp8_{C)7kf9ei(i2_ou1UVhLWaUS3>k4MrQK z7PIO|(pZT|PY9=Z2^eN%bOUIhX#w3CQ=3XaEPas(2wmk9bzQz zW$mUsg_aG(=AT4uSc5qBSFfjwmC3#tP=7dZ6&ARBf4V?9oLb9dx-hZ2e^5AAL_0{r zwS;afTU8>(QoCIL+SCbE<#9sCU3fP;!FNd4bNn%xdX7o>PM3g-eXyb`Pgq<%crjBe zg2sVq&}bkTc?;*miCq{<9hezNmnf1k3T6|P5ewEqEn$t9f}}n*F}7(hT_2nRr4k*f z&7jm&xXP5<15RPJb!Qsce-j6tX5Uo3MH|cmZtijTLWA5L`_F%J_}QCm6Ed zVOfIbhfYS~ezug38eOg%+oQ{uJ!vUURd1!$b_5w%?Daye_KHD|9_2pT#g8Q6 z$l)P@)I*TAaj?TaS<3-zdl`^pj@VELA`MKnsg|DCyORkwZ~0(Rf7!r3ijv|e>s?fC zC}gaGyWkn8vV?DCxQ4Ultt^)ql0|j)vc4A0WoU#gdLd!>t8x*+a!E%QkiljGbHZE# zHLJ!j{L8O6)+p{Tf4b5&Y-GS9N)dSJSxIbSJzut$C#2bqWN<6=0DWM6XY-txmJfwz zos}33^){<48}=n|KM&M6s)x-;>fy0U?hlD4Bv(;&%WqwRy1-TQZ{>ttsM>b2SkC5vlweh< zS5mlM6xSC3z8JQqR4aHb19JtNJ6ZR?79_-2&OU0*da6D0DE-DZ4I>=($*x}GVEpYH zJ6#833$*{%M##7#vPJG3-cGHp|IOH2)u%eo;)R*rehBm*o3g+IyA7IftIb|Y`^iGEMYhzqC2yl{40P&y?jkMOU5 zheV!pPgc_r1syJlpC7#HIP*(w4m8Mb9jif5A<0G6;~-9wb}&~hHowoNV;i zNtdKpf9; z^C%XGQw9->Hn=-%mw>e}i`@t@E=t+(k~6yR8AHSy5F3d~J3dP-a*~rJn|ZF|k>Km0 zdTi@^rY(rZA#8KaX5bv?sJecBW8ve9@JN*cfAF2Z2f2gzeGe&{uDHciGa2HTSQ+9L zp4^~ZJuWvYHn0tmk^*d5j{H~sDwByNYN?tMlJ~?Qhy3%Fh?K`v@@Z=YtW(1R3xjIg z6**ldbx#5JhQQiHwNNcPmxEqoOO2cn@z#GWIbT6Y*1r7nIE0~g=wY~K9Xr;ReP}$q zfB!ox3x|lX{&O*PGt4!erL8BvDo;1EaUsB*)J6D!}nG5 zDSm?XE??;lh$8+@Oql4HALD#33&H=+lPEf((^_%kN@WC8;{wWJGe)h1_yzKM!u8|{ z&;yu|+g-)B+=)jEKRCjgv;DUy%Cv$Af7}q6F=|o(wfR zYlbbYVhXCDaLYwx*2`GoUK~Uo)K`ukR%0 z;iKBpi?fF_7%pd`EjqnF)f&WKpGk4QFwmi!ZM#-sU}DN0#EY1a{EdYg4j`u@e>+O? z6(qRdo$S_sVt7E7y^|bq@QhmP0#003;_a1}g7-$S(OOTIX=0|2UT3QpS_kY~74uIp z$5%CK27%GnU2K=WdYYH`i;8+k_PPwN$C%r^IAp?>&C zDbC|bL?*fSz+eJDDJsSv?9j+x@G!dunRV{B8Z;E!C5@lR>Iuzr0p$qPMm6+=y}{k2!5u6QUPTzzT!O>FdhmQm27;EZ(ywcI9x zI|v*7`k!fa`l{w7WpPv%7a7*r$2MkaT}N124E+4b^6Tc0_k!zPe~LFcz2J|%?Th{; z;FH@K7$<~NaA|jDe!>-XF80*^Co@yR{gp`!E-yGM?*}iHTnKi&#;}tuqeaHnCeI+7 zy(JTM4zpBryQ4t3$9draC^gA4g(# zxt>?f!p0dOI0Q&%E&Yb|H_&Q7!mPDB2KcV~?R?F>Xl}A5e}d|gLy1ejgy_c`>ZzxF zr-B8kcV=28hT*FG&fQjLi&Z80k2KqLMZe<6%QfzcVx$mhI_`&XPy)mErhQvv4}-}x zw7)cL^VM*s`f+v8Gm`?}s*kIzI*3BasVOFJE~N-z;)cUNZ-nSve?%DpK9PMtXqZKw zgEYiAuznhEe@mBvN>$R+4Pbe|qWSe}2JOu81%2z_a%am>_F(^Y8W=BF=0`kR+~rxz zGJ*`qawO2<5eIUAb0qv}@F^y?*IZ?-$C`CK;!a!?t`Yk41)`7%=?fUrMG{^q%x_eJ zy?wyAZF+n1%$+T`Zp2NXHDAGs<6k-J9FF#Jh7mj%f1HzdGd9)*AASGmZabmdBU@Pg zGH^UsFB2)!WO1+zZKfNh;|GxA`-vu5q^Ebm72D5wuPr?_#=BPn_Z~1M14rRM4x{0{ z1!j;iaFf}ZlIMM4%~&Rj0%ZHO3C6_il@a6BmAK314F8kq{+Mg@4vlUFovH%h{CQAg zh>1#Ue|5?paS|Z43^@qo1Rhxh`6D>XaxO3M{wF`wRof#{n0P67-e#bj8A8zhDppfg zO+7KYmD9a26K_V3EJxM1IZGdbHOyxjxNd<+m>O04u`D``XHcp8VlET^94 zWPSZg5cCc|TDUW-D&k4?G9@Yvq=!RR7BYisK8GSvSa>G8i3}fY` ze`|6Cv|6i+%n@ptv#qU{GFM(Ls%X>ZunE#D zni_eikdKMOM@$X`)gF94h25G_53I~ zMX~AU?O2wUEF|B~kTt9kwcXUnw?w@>nY|32SNK|iOPvS=w5pocBf$EU74&1s>tuvh z^(IqdH-E{5$dagq?nYL|bn|xgUx|=@6PVf@S#VV_K!q#jOB;1~_a98VD`8uneocNK z_?mv`Fkc|Rqxu9x*4-zd%djWMV9s}HDD}P<MV9HH0w+t40lRvyo74a=BscZ@IYOFdhqus5#S~Q`q z4tgnT{&P@01e-38@<;iT;`dEJxLv?Ba%6-dA!K6)06JZJSnHu15ChR8X z1-wz1mAq*1KJVV}e{k3xO2iU26yObnoKYL5^4Svb_#0&B7 zqkaLHr|RM&P~H@wguD=P>5|T$4|V%(EIFWuTJ72eUK5FYRo)`jR+a{tbGX!48kqDBH(~V$}e+@5SVQaxSBR-zo49oSs za^~Gzvtv%@z^Pj}b+DLMBh@cXtj+8Fo+R{(fPPGD$MYxYNSlGg4;Bd}eaYXB#cD4;8`M^sz{j8pM}0dkN4&-- z)J%6t#{^Rve;W`$bcPjNCqc&~5Riz%C-8h$8Iga<_?t*X>Zsd{=F~!wDd?{d^>LL8 z-{{gpb1F-mG&CEf!ng$+Ae}v9Yl1RmHa=K{RH`^_)-x^r-veq( zkQDO4GxLb4zJ8jrkRg?NV}Sl06pBGN^7rXIWT_D7f9^BH3-Y|%-mKhRbvQ<(JR$5T z+y;f-U`K!J$r@EINk|)9j`{{oDK|2gS2;~f(<4!z7vqXSqY@ljNc(pnQT2Q?8`Cy! zcKcLolG-fb)F-FxHJ!1=1Eekg6VDki=2X0;+7}uJvXXeGM!_f2_56lq(VXuTspX_K z0>L(0f2+wTigCjGMNMw;R;L(e=%JEv7@r*_&0t+BeUTX775uFRD0%YdTs|j341tuZ zd1NiVqr)bE{UO`_5A8z_ovhlWpN4Rh#c0mmwk^tX*{ z7{%`dPiDC^Zm+l*A3XGQL5DkS0re_3LjxB6 z4fbnAe~J&?`b&RX9w{|GIs?-4z_W@waTeaMcfW+Crsr7euCOzj+O`|1FcYV+Kz85laL3Wk2@Js0~8M zMwzO7)D1`7EvjOZ2e7#+rnFq!R@yt=%$2YCYyfBrDN|+C3UPTan7F|2f5U~n@sJmp zRRbX)y@C)zpxpG~ykJt4jVxnW*Bk~cSk4~mH#We?P;amPTX$$yM7TTY)zzt6CM+c9 zT&IZNrJ`BZJXGte1%8Y0J>oPBm@N=%$P(i$ye|6E@S6_|h4RYv{c>mSD%Sxj>o9(D{%|=}1tSbX+qbaPG zFY&NR_Dt`ij;uteh>rNml?KKM0ZElI2L;8Kc|B(6lqAJGEs80`s5^#%fgH7UxKxLb zQp%P4``Q@XpQ8RYrO6XOGON@INOpsp00^ZkKw-wUy50x9B;*__e`F&E2@2XsoC&_b zP_T}aZn$J`Dxq53EN(8w>?$SeFEqwyBBf@NL%YVhCUKGz_Cz7Pa)c_++zJf;DL#Ld zBRS><7*s5Lh&|67)E9n)FOFv}fR&f_ZXiMqZqO70#(!bpHF$Qg9zGyYBl|{l4 zef$Q4!%5oTK#P`ke;>oIvHnnFf4{Y=>PIIy9ZZ%u(%Q$5Ue z)EHP5B(!5GKfR)RV>b>dtDNo;=(a`m@qjkK45wz_L+uF+HL)}gzR8nBdXCGLe}EVL z6FU*d>9qM7f7E_Zz3QpZtM4Xgt)%mho{J@B;^|!b9=ije-`{^%TUn9R0bfa~5a*b-+ zxB1tTv@QNx@kc>-Zota8mV2g~IaX{~BbRQ3eS7^6f4N)%`pSCru$&}h$!pvjRJhd=X+(ZLUepH4-lEOFekWvO$JW8IX#MQVU^ zCmsvt@pN87^odKEmZ9k2cP?Z|Oy$0fVL$H#7EumCUNPIfgL%S`h-wPh0zDwRw-^rI zeSLnvf9tjvhTU#qh?wI8T#U=V2r8tMEA+6e6mc9MG17=fQzxr6q34h@r6v^2fZTF5 z?fb8q^1f^4ch}i%vkM6DT$y5PuPx8N5dGKc-d%k-K}|TLvyp?laQW1TJjlA$J9Fj2 z*gn6K#>N=?>|);|c{g%&!oGo}6z;Mq`Nb_ze?3!FO|_81#T^!{2HxS_ivq>xqh-ej=a39as+O&A!f+r+ayy-tt}H{D^1+w$u_P- z{(5fX4F7W?ASdG8PMP3AU^9go-A2TOU(B3L=X&<1^ti9;1@@Cg-O2Zl~G zFv7?$45$DL%|M--lUvy?&+$zv`m5`nLu?w57ItPJOj?yE0DYoySM4rUJX_Rr7sBe% z$b6BKFPGlYH#ZQn4>ZLp@BKL8d}>=Ze<8GBBP=AT=itCyR6eQvjR=J7-@|IrmXN?c z#UI4W5#~CDw>NQl3z`5JJLq;h-J6~`w#Ax1V?kgkfDZqJ02-UV558mQ$~eaVoANog z*-OJKVpTPTL%E)pl-IjPTymOF>Vw0VZ@6}h2vSf(2G&1VUE5UBo&b{)d|+{;fBMHQ zLF_&}5P026Yttvv>RLoKeEWfVa@X*sghT+Wz__HI9y~2t*_kiAPl5hxi~hl&&xI2ZS1Bf9su6IviCdnaK(^P~JJIaN_9L_8n^$NrASfCeS+% zG#z5*9jHx^7`Rzb;8og=9*?sp+yF>uSQiT@0#K1OD ztEq46n=8vQQ~VT0sNU}_Wrgz`(wYKubnw*gu#Ff<(|Fa>0(!nbYJU$rf8+sfq*wHP zPiD6Xta9Iuy>PnSOu@bt>-yT4wuB4_#6I(TU7|Y(U+skWo4G7#jGp=Mv8H zCM@nM4%C&-C{yu|?H=KffC#eW19eU-e6;9pk(<|7f7)B=Oc3Hfx41(l)t9~zo*$g8 zysT#J0;vxLm1mAs6`i8ve}eZQs$H@_P0@pOUjDq%Ev`76(0iceaJ5z+JSCz2gR-wt zC+rLz=XC?mm6Ig;m7uuF%uGz&^|I#9UashsZ{bri_Fo-I{0VLV>pZxc(~}+7q|7S1 zn=u{$Vj_Q5t9F&VvnkHtxHj)%D!55n(e3gYUgZLb+H4EoSz4jTm?)fJ((#4 zVEDTsMN2RN8||f_7r061EqPTH-J9n~a15OdoIvHS zF3rJ6wR}9mVStl#*38$QhC%c$Ca$R{?p`(B?^Q| z-IFgq5_x0qBh;7vBxj@1(p;IWzCKd&ErW(su93JZ6rafCe?`;RH-ml%I5yOYR~@g!Hj0x(lF1p)cUXFn)Zs+{LifUgcu)*;%@q14ckri)EG^82SL5I zm_KsI-v@e|e@aLB?e?YvVei(VOSu-$}f4G*~l`|nC0y31)3Tqu`?scy; z9pSH?D$eBuwfQrJuuz;zc`)vBQWYlephYu<2=Q_v=w>>Dhvnvc&)b=S->fQ371b$e znrr!}%fG-J^^VJuWhN+-p6W|t`1aZsGMw#F!wPvbii@p3N3eI33b6YBQKhhw(^Yb_ zNW;x|fB30Cj$AJz=nh)q{M-hMgWTC~T;P|n1Lh?C;A7}db38l{_$5LYhHPj%xj~X6 zl}(ycgol0>|A?781u=iRE!AgU(HCKoj@nIIe>9cVJVNQ8?y~(UR2;>WtmZal(`>MUZFdzAblxB+rHS?B5#&tS|Kfn0e^w$Za#S*&hu9s)ne=Wd;ChFv)gDvLiiFtSE#Bo zf5Za7H{fsq%k5dGL6p9R9```xy?N09^`?3TWM723Q}FB>nco@x1mSIs{5}?t7!3L9 zz$f*cYVfG{sLf-&&f(z1SWo_bu8<3Q6qNPA*4je#b!hRg_t#yWGqs|AT4ZWGR0h|6 z37WbuSf4R*R9HdTyy-_MUtr#|)L588e|@~@0MV6)Ny1iuZ8fo|iTRUPaZdnYn7{Gf z?46Yzr$ZaTqeo%;?ga)ko-p0KrsLQ{JvC}1e~#T)ybpF~BI2yw=BWeu{uL1>w}rps zgl#^Vp3Yp=iL?|O!qFiqUP($64C-gS^w}_N-)ih4Y3(O4Dwn7KL+*mC31}oAf1FLX zO4jvxHgnJ8xF0GEBN}`Poe0a`0^5;5W0M%aqlCu*nz@#aJr(5j`EGdj)<8ujSD*rm z=KJOCZ%d{teVXJ3h`Sp2?$+p~BT(QxKwgqXP&oF*gL5R*dug-rtV8^A76>u%+Oye} z!tKwo4u+4m?2N1esl_F?c^PcvfBI||h=@TiJwIssY#byHs>iT<4vOSRLwt;YqZ$oX zk5V1tF){V{J4tuO+#wlUBIq}yVQ>XNBf5BPvWi>NILIwx&h##>nCJ8SG&gfCR(+qG zk@=d^k+*PQsuzF{F-HGMBJZSkD#56Ui;@HAi*mL0F9D2`pZNV|hSCu4fAD&CJUb+{ zD3!E~?5!0TyizkpIk5(}hkjeQ880EhujUAi<>OdfiWHIOPo_+s2hajH!jwY3A6)w~ zqtxS={v$Ss+`!C6iy0I7G(!$OaIadG$C#OA zBdH2oY3we{lsXs^HsL<2e_kOy0C?}**3phxIux(k_H6eBlztmRNR|R%vVt}x8}G~I zwsiU&jTsH*7B^a_5L{z$_6oHSeN76xPr|Q?{ zQ2yQ}U?U_&pVWQVeeTdYLdtN zwN@DF3jKi6e#1tkBm@|nLRdGO*UyL{*Iub~?fbLH)8 z;24b6jo1eq)}c0jP^%f#@mAxYH0AJ`Q6lD8cGxEA*q%Bvf2OW>FI5nee|&cyreL&q zg|m<9`Ia_^tva6Z%}FGvSOqUK-YeowFUOti4(#A>?Z!UW&z^R@fe6mhduJJ1-YDK08BEm*g;79T-E zfqyiAUgjBge(7J1=;7oh!%!98D39vw|ceFsg2+ePC&X=j~f)_2o1%0Y|L zlS!LBf2R?xppP=Qq;>S)i$uL&#zp*}0G*<($N4=XC@{Lqhx=fDt#$gotvc{=bSLd) zUXU-aOw9C%na@LO*2X18UQ%Zqu?9>A zIQsYE^$S&-B1xjt-OX~k>}O2T4VIlUIALiLbFd~SGnp%ipdCP}!kC3jOuwO$+TV@L z*f^AxQ@ax}8$}VrpDNP8u+15lmEyppl#vP-nmJ?kd8uixz8CSrdKnPMv4YB^jfk!Q ze_Z)e`7~xSrR|+MU2}t=XW+M)fLMe5bf=fqv4Jm**Oub3$JUzfDJgj1aIFI=Jg_I8+?QP`c=_15M4tZ~hf2*>5mzDtmb$oMxnbnGa6085lXH-B&?YPE*0j-G_ zGI*t(t4C;P`%E7uC~L?qd9^oXYvL4eNb}(P(UE7vo_s%-Cma1ilb53-JJAoahRbU4 zNWz1JV#>M-&Q<9V?QIe#>`Qf_Mp8&HuDgeRY$nb8N?~CRWpt1rmmktkb0rE12^M)~iwsikGCDu4RbMTPjiJ9vmP z$EY`$`gEv3sODZkTp(Yjj#t3Nf}bZQAV{wfkq1Q#;cxCnR29@5E(Q(Wf;bBg8Eu<( zNBBPlOzntl1JXJhS<8p1e@?qexw76QI1VOgE;5`ESVo`JFT}34?&IFCh^h!)+bGP- zhER#sFZ_5jo4%KtU1%pkm8-S5amSn-z|xj%YvduODo-#2YWm#$xxxh;H_}}K1+aEi z9mZjU&^1WrxXGrZ`GhO@Yu@{ROPzZ%Rr(_V*gE9p`s?I$J30NTf5*WBXVes?ChkH& z$;_tmwNg9*73l6)uX@uM-H~H83tgT9P#wp6BaP$&?keV&`k_*7?c#Ok&1<};1Tn?S z7$Eupp4XtkZ}_ziCX{CVBaUsGGc*AXUSS9}|5Xm<7y86H4$sEYkGcFMwXZhsWQhO+ zCXsR);{2>9pQCzQf5AzgjD01fnPJZ9EnQ-(f(w-7`jBU&j9OO$Hfx)A&b+!8Apu;b ze^~uGLd7r`?>hRC3=oyFDFqFRsf8E-xfZ}0E^Ju{QM;}bcXpoLbc|^^I0SWeF#18y zhs@)^4NcTEhl1sxmIV5TzED>b>gUV1p>;xCA?tCEjm}Tie=sJ%STy=$-7Bxjgg>vc z(c`zVx-3Q7q;pK_X=aH~$gDAJS>>tFqRkl~1$eMtHvB?IxK8|Y%A!KlT@y;btuqVR zBU0@^x9a5$97|0cxcZBuU=lb_8;9T5YO@9Dc0>SN@@G>KY0wkf>YH?0VI zfP70I<(py8KIY_qasV7q;D-j}c7;MvJV4ZJ0Ap+>dSlq{7Q#7 zVH25VMYV|3KO(-*?tgwgxj4l4*yYf$Y?)~m6igzCfb%$fNa->eW2p_Oztu6&n^zb1 zhrI9+pD_*fgzN?#K1&0begi%^XpP^neTs=K2WVSLm+)()6t_A>rq90tJGTl2rvd_h zhD1gW9*o43;+Q%siysQ{`lp{U9}y`XII!Hz=Wj-T{p-izApJ5xS@*(ez=0F{;H=T5 zGuP&Ox>|O{g9n9pN`?NpZ>w27d9?biIOY;rEs}nNWhM>Yg~DVs zc1Uqsq(Bxfl+>U95h+gIBqiR86c1IrPDWjCg|? z&VLp&#It6#S;Nt|{_^2+xx&oHIfM|kYZ&C`Bk^d}%&I0&E#HW5#B=c&7ohHc4Uw6G zil9m<5wy@7I4}g1Qo$veXiKLJz+P@nI!q~E)XQJR!v_!Y0^;Gct5-|$QvCSCbGAR7 zcip=Eix6jZcfL6(r>liHswPLP*{f=LzFf^$XLZ#Mw?x-em1w)kS-m_H?dr6<0JUF% zfGt_VPv1)BJ8+-y6xZPBDdLBJJjHc5A4@v)JDAPu?@xXqj|upE!NB4Mox&AE`W~eM z81(4zKIQT^orbsfQLZg!cy~J??7)3O2-n~c$IE07!CVA&Zw|uFU~jN9=mW7cnA@>) z2ksN>yatDCsFVj`ClaL~uXHsrH#!J&qrSo1C=bNksBXvH9k@?0_Zr-P!E}||hma1z z&LD5-Ds#clV(0rn60;x4c|3cj@qHhM`*FBV!*$;?gU{DW4%ce9R>QR(dNcHP=-mK! z`P>hE817%f59_93U1f*$FV`LBk?LN7&{#x5L&bNch7)jBmI*k4{t~;Q&yR!`7Z+tS zY1b!J)4W^PIV7%|)oe3=?OJj5V7#u!zgNv_tj8B(D+GQf_K0RS(h15kLKU?_-Wo}a zEwQis!jab@mc|(Fs*!vY&M4#_RM9d9`}w%H-Kwd^n|4x8n%SmauFC23J@Ui7@p7_U z-5q5Et+3ov!h2Zm?k}|kxNGSS%<$=CQ8knC0+;h@{8k@8QKZR#p+ZCJmEo?^m)hzt z7jNTYiA^pyI;9Ezis6niFR8mBzMfCou0EZd?4mu}G^=(!S&q-v?YLgHoB1S9vU9(2 z<$1T5-&+#S`x36v=v4Ad#zk2w9shvz7#pGbAjrajdi`EPkX#CP@Ig(zhAFR4-gW2j zG4uH@QEF_>c=h{#c=m2tRg1|O57(=zS}gtYU?U2ty}zZ4~Jsl@|`QJgaJweO?74D!Md`gQ+ITci|GPBvT8Dp zh~lbKQr#ymB_;yU%K)=7?#iqT1P){mfTUJY{XLoc$9#iG6QwJZH6@%GP!34DzAO|q z%mvB!rMWPFDGxy(&|H);_C7HV=^nk1M9`L7($=!Piw|uU-Fm)hnXzJP*H+86n$%5s zGG8%-H&uC2FK4TZwp>=7n60L*SZ_||a9qu#6GtzPl#&N=Q{=bx=whj`-8T~U@4_FL z9t}A;xLe4_A*<5py}M(U*iP$eImLdu!4g@ShCA4QOLX;gUWs*GimJk#$hPD6&JiSECF^y{Oq^wf?ek zh6eM0v#!DJQ>Fqt&(g?n#|TtSmKW80&W%1X->m1W$xHxE@#4vo@i+bNxLsG%Yq&ot zj`{uiHm8NVa2o$ZsogE$kh3^%_QH9u%BbCbI4Guaem@+nO(GrH2j_e!lMegg4DeI? z>9yE~^W<(XoJ%>d?}gI_0V(c>lfZ8;lmWGWQSRpoH{otS5jp|6TL?oVo3Z<4s}OvR zxdoi_FnD#Z&zYb8f_bcl!&LFK?%w?WF1|HtOCk^ zE#&6#&~l@PMge+_y=sWLI*4T%Y>45xCDk{+v(v2JZTecf4##!38bU4|8rF8X-I#A^ zz!WCNQWa{7QieD5)_h;uAp5mb729923uLm<0YXKM0KM~xnvqM0 zbgvMs@$$L$sL0hzn4RByay=5MQ`9HyYFXz5d*$ZQ!FE{|q+zK}<`Zx{m$_!hYTv&{ zL4res=QEcNK`7u}5wo7!FjPyKeqPM!qRsPMr`)K%X%K(a8dVD+Bj76jXod|#Qq;hu$y z-f29Er_;z~Nc_jDut@GEu0XDb9&?O+{`^NBBJbH)envclrDm#J6~wr=J*H#hQ4c@3 zSQdpbWWY13VMUiKcc`V*SbbS!N0&m)#oM{%FS*3dVqFUwE$C%q&QG0x%X*Avf*Z!F zAH%Y)oELP8`ozW$&i!Ub<(h`erh%k(oCgj1Y=KeVQf`hP1z6d~`8XzL{NN;cYBV&? zO}jLdxila57}CI^sX^Ke+Qf)K&RUsE<7kOC5_{aE1vy~NLOS1Dn}^05pZZv0``SCg zIqQ3Cq(34Bo@J4=qIl(hx^HWZTFcxr#%2g1$qdwoY=NFdCcZBMq=m37ge3zyQp=k+hu#NaMK*Ghwm^SisPl>#_5q*Ova~h!tQ@uA=xthtSLD)=G|)fN z8|ZW^30u~--c!QH(8sFWX9;DA#ScqZ*rc+a#6aHq-<6Yw;v^owIyz)MthTduE|bbmm8Q+Q zV-|*M*%QVJXnJU~Md}Uzz+HAO52E)rxu(PA3W~n&Li2e4nEv>(F$?Nj%$YPxlD8r! zqA)d&exJ4&;Xkq^3ZDDyxB z(mbGce%BH{lzGtCA2#!#FEVzJORlR2O-Aut0DhO{*z^REf0!_MDBnj+r)+LnE;?nu zN8ituJruJT{?P*bX71JdclI?#Wu5uKXsdE>!SfWUK{6LO_C@nHm#_CV->fm`w+J+r z##65F8j&)8VX;2{B0<^QlP2i^C3~eF=droas}J9DIE) ztEb!PgEuSZ(|ZQej9^ea&8IufgfI;cy-+P(z3Wp9$D)3^IHpsM!q;!M4#T(=e^Ry6 zre1ferr(->H(B7x>u2A8`|RsKzo<{DrmL5m_GQ(VbrPp6K%1&QJMZZ8M}o`p*;pGj z63@HIyq-Q>p3SR5j>OBZTKtoLG%^yehnFCF%yB+xcn;`HCOIIZ7S;&ioX#F;o3=kM0% z)l$^rS1}h0u@oz@T2^8$FkiiziAJ=dt=BqKiyT3N6)v9NyI@SSz$un{=FB7PkvV*az`L` zS0H1hFGUjkmPB;Ay+r;o$mGY5N@dRS4oHQ9INfFcFE<6in!tH=+V!I*OLAK(pDBxQ zDU1IA0|WQgm+)()6o=vyr-$MbsE6Vcskh=2s?bFNHkX0I0VsdHTHA8mHWGc;SMV{~ zEgQrgs7j?uz9t)md}UA^OtUra?(Q4h7g*fg7q{RVG)RIju7Ti-1qlhxvPjUdNCaI1 z!4?P)5?q55^zyv#S6|irb!)16&h(tB>G?U+-AC5I$q%-vlMc-Niu7<3Q)S#EqgGt87X>=Q!n=**z6wMMJz1 z>GqAUm|aytF1>6m+Cog7<_SiB(s+_>35#C)vv&$MRI$z=b3bG-U&wLl2o z{ure$Vtr7SAS9JdsEZq!vBkE%uI=m`O;WV%LR`NK_o)tpZ@(`osa?+nR>1T*qczACur5-xSBHV6oelZ*T zdceE!uHLP=PIzL@qMo3ssd#S-m*A;>%4Eq7KP)t&7u5?ZS{D!b&eA+|fFC|u+PrVl)GN+BFr!ZhKR7vb&p1Md?h=0)2627d8fkiFy%|DgcyL)p*z1~S+^ zHau&x+U3oIKu*8WzDliSohW@gofg*C>s=Ebp`BJ%cN9p)ZPRlh;)hgxQjE_#5h50- zJYa8Q!Q)j>=%!gZ^gTn|{0?a@dYdLm=0qnTrH}VflFodVSdL19>%p(rzKM~c-BSM7 zJj5(wC5%;ZJ(-d0rv}FQD$H2qba^1~E+v~!@|mQNq@mLp5Lovn{f$0Td64%y$kmxL ziJ!H&6{RL9<25BG$pMixF3hkFVuJnlXHMGJYz}2APS#T-Oiz)~pYeQez{x6Pt{?G+ zP2cnp6x0e>Xol(TQd=w6KCJb86H-uYuGrk1MxAn3>(vwqsNf2 zQKYeRU@1&kax5%TeR>uO1$spqAEw#o4QKP}fQ!Wlea*66cV^qhHV@wc3IB03TeM*> zq@ikw#HrTT>A_f1TZF~YUH>t(lu5qtBpAdX(iLrZ3kNU8S5%MU&)%rYS1fA_@Fa-W z@U)>j_Dzz+LA*w#%@P55F}kBi^9|qg*|~x?PKlSm_*@h`UnD&*KQp5LV$$CFmRX9} zD}QM4__26Zo$gyEL;?`u#WBH8!~i_Ydn6qbEe-}$@k?rjwz(ftaBlii{l2BT;?kdf zE;2Tg6XiALyAP-seRxS$v`JKXlL7pRPy`1|z<@-u!D88|em4P|kyqS>_F^jrZV zBNrzBy3I<^5QmXyF{aq)(?V-lho8}UtwsqPLYsD(K)5JcT%h=9K|w_R-B0+?Ta-zfllR%XM}o-3knZnW+S{Zq8IaZtEJ@JyRKHNQcE2# z!m^THABV`b{Hb|XJ`tfkMUbi?c?H!oL zxs2a?Y!vM;%rp~aCv0r}y5Qrgbz5Wl(@3scqi^#&7RNo>izV51+IQyHrM6+&&YW5g zN3sXEsTO(Q9t}>_i6Qu>+C>+ZUywjtIi7D?FE=uGCX@K+{S0!Dyed^K9+ri9P8AjK z5KQk$k}LWyAIm4DAU+U#m5db(zw?w97n(@WKUx`Q*Jsrd9C&XQyJC!$?X7E2Kl4?I zktZ^EslkJ`-LJCO1F8_q+?RtV9H22v+Ta4Zen&MnHYB~$#`=|EQ7i=PHo<0<|L~6ERbVsujJy^BW ziCLxe%KiD+RUG;nVtZ*jXlX`I$`QIh60$Wsoa?1H(6*lvvYuR&yQK}3ADPpyOX1`O zwaX7((hV)M2r*=nAAu3~#@#kPh8@V=qnV_x@%Wgk>ZJlsPLu9KtmP+|94a9r5kjGF z*fFs?SI8jFQXAg=sfdYwTYy{R^4BisFJ5S!ac!A3Xv*-A=v|qb0(5V|ToYx^yA&4d zGuzsj0cCTpV}N8)&Wx3YS+Q7gTK#d=RjZm=ib&~<5SlU(ez_Ow)ldL8JohFHaB&%J zFL1Cq_YRb5O04!atXPv@vi(ocC<8BtiFO@vr~Ffsu%5s_dV|OFkGXr6sn0n68j)1r zv*LsV%v61ABS_huOGSMO6r0M_x*@ z5%Pizd)@I!wGS%iQpfsi)vr{EXO_b`Elx12`P;ypgBKhi)(%3YO4rj4+!d4zp^08q ztUzO)xDfpt+W3#@GI>IR4ESfu;}iqGlA;39 z2J(NP@xk-fOdQ6xIpCY(!vx+?R@XW_EYXXnaQI#8*Z@y=ppRzoc9q+qkP&YOCh;7r zdlyFRzQ8wDAln-HsQA6wFvl0_?^?%-ZVt_KtrJFvL;GFpIMFSk8~;vlD4ts#2cabI zj;QB3TNhpq3@YJPCjk-E{=hn75|$b1(%$!FFn{6FfKdw^)|S9UjHjP7R`{;us6Mxp zAH3-NTIVggJTHOCd))O*^+p}AnJ8j zX+)Q|YL|dp8gN@oTL8o@as_s@x7*6O$J%MmU3$m}!&>yIldt>bltFxTDP*25`HgJSZR$3DXd-hm0+S^;X<1E*nCyPOv@y5oF7&4dT3<4eMKQ z{4H-c9^-@xjqkWeEFOdf+eH$U<wc7}|< z_~mDtgAT~{tnZbhP25A!*N5(bd^M{)yb~#yR530zZ5ClAu)7bUCi8XQBGR%?=Nn}99oAN zks#KtCB>QfY@`<>^j4Wk?_~LnIeNS#uH&t1t9`x-4e0fdpRiRavOoI@6ip_4N?xl3 z%RoeZT~fZOXL!Y2QmeoCJ;4)zN& zl2L_C%J9GAXQjQiL{v$TbU-E-t3RKyvbh6Gl`@ za@A2r5G4Gc7jcLwb(2H~l>F+w@XJ0yb|}cJE?WR3Dk&x|E-5Z8C<5db5#i<~5Z3o| zQF9D(W#dtk5fc#;6Z!v?j!Un)-nP0De?cl;sH<_Z!;_Up{rM6NrP6os^L1k^1rOZNM2cc zRGh2uUbRzscWwjX z;mEjyQ;;TQmK zuywo@A!X{~-?*fJq3bWq6t`zCsdmy(4I?v`^gBs_?p^SAzk~*9jJRHv{xTRehnJQ_ zEs+dL!r2M!IJ*cOWl>tbxLsn75L})tM!>v&C%*Ty>Xqhq{)DKa1O@ha^-kiTZ1I=x z;%}mNZ<=AqRNtfYY7$VY6Is(UZOOO8jhq%7GdSm& z(}-Pn&oh$DeYTD)BSvA)52Mil?dLa47)f1*gzXH#97Zfv2fC;FLI)})2&VHY2!w7B zYqAOiVNS#bh@d|kkQTw7h$CF(D8Lb4r6|A-TonU3X&(s}oqYxn+fU6>Si(QFzyCvE z2@KyR9^^5i&ZU++P7-N|;C1F+M zH1e8>5G{?9pFTi}M!9|gVo&NpRr2OQ&t;4woPJ?r_(-!Y`I>^9*U9oLJUNHFiGxPd zJM}ed01ud#JdooP^$7r+POi&w!5F~Va*$=0%s8%4G8qrT=EzqQNU@{{;AFwUT*XsL zOQs>G=MYnKNggK4V&_m*OH3yEUk;Zm&K}lAXmgMT=yeua$jbkt(+(Uh);g7uV>kh@X#Ag`bgEMTzV7`bZY5Aly zG%quW!7%bmJm)L71!v%xPxWqAiIO(YJ@J^ZvZDezzOev4ktapl`p4dLc<+k z_opL@=5!z)Z#Srh+DwsvwnJhh3=+55COW5pGf+I?S3i{sZey9oCw#K4|$05zqI;sx=KaJ zm{5OufoT3!zD^uioH`uKUr@w!HEUHkhMvFAwvlNNJDw1ac27y(0}YS%ytBULC*k8P zWYxaEFB*dWi1C1ZM|e&9NBF;v826SkoSQs#{>X5l<_Qbm7e-H?(1km%9$;|los$K( zcddWFcBeK3PPF==&#T}n4Z`sqEKwMf0_9dptE7ewCdnmTRz{hG5*KS3`|6i0g; z1SyS+;Ae`gnt@z5Y0JELN{Dvo+KjJh-Owm-W(aF&jfBgemm>qS-6lp~vAm$tBhsSF z6#2DE|%biZ|`U@ zjqUQ-Y3|DC%ui*5NaW@*QlqI~odaORHG-9Z0I$!I=$~xb;(QrV9-H%iyJNF~%(&eB z+Ar;*pN$1$TJz82$~4~nfju-nHOVsYbudn9boHVke?=*cZG@qxXUitDjn4!kI#r_~ zPA`Kre9_pTzeQd#ZILuFrS04_-u?b@zNtAN+P}6{(Cd6@&^kX{JwWeI^Ck{o1b;#l z6DHU3+V|wwx}nYAZC01L$n3*Fz*SUY*0V@}U8}6$W?7^?J77a7){`G1m$?j;$7mG0 zep0_p3LXUzx6kTH*OX#3Flgfi6_v4a0A9$I# z(pOFTXSC^4#mcft4X#EjG76Q0I;eK1?71k`yg@@oOt~rEw5sGq7B7eO0h)Ut$WQc= z5en{iXNMsj7u5uz%6|aiBDc*dCFh{s+1>h27Q#(icFQs^LoarQH%~_EsV$Dv!3fsX zO!u67w{W<>=9kJ`OR0F?!HU8_phG= zO4uAtYv=6wBvBhm%YpU5ew26prnq7fIbiYH38!8uFmLTYpLw9By=a@+U?JYk>^)_} zt`9RRI)YDTRHuqcTjy!%PzP1nFw9ciBrn)Y$mR~H!**^2H|F`|Yx95jHr4H+-Nrzr z){WJ2*2b6P_BSnw0!Fl{%$x delta 142553 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®Z7hs@K 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 AyZ`_I diff --git a/docs/progressreport/ProgressReport.tex b/docs/progressreport/ProgressReport.tex index 571049d9..760b4e69 100644 --- a/docs/progressreport/ProgressReport.tex +++ b/docs/progressreport/ProgressReport.tex @@ -17,7 +17,7 @@ % Headers \pagestyle{fancy} -\lhead{PeaPod - Progress Report} +\lhead{PeaPod - Solution Overview \& Progress Report} \rhead{PeaPod Technologies Inc.} \begin{document} @@ -26,7 +26,7 @@ \begin{center} \vspace*{1.2cm} - \textbf{\large{PeaPod - Progress Report}} + \textbf{\large{PeaPod - Solution Overview \& Progress Report}} \vspace{0.5cm} @@ -36,9 +36,9 @@ \input{../tex/Namecard.tex} \vspace{.75cm} - Revision 1.0\\ + Revision 1.1\\ PeaPod Technologies Inc.\\ - May 31st, 2022 + August 16th, 2022 \end{center} \end{titlepage} diff --git a/software/microcontroller/platformio.ini b/software/microcontroller/platformio.ini index 76522fb0..463c1f67 100644 --- a/software/microcontroller/platformio.ini +++ b/software/microcontroller/platformio.ini @@ -1,3 +1,13 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + [platformio] default_envs = peapod @@ -12,6 +22,9 @@ test_speed = 115200 test_build_src = true monitor_port = /dev/ttyS0 monitor_speed = 115200 +lib_deps = + adafruit/Adafruit BusIO@^1.13.2 + bblanchon/ArduinoJson@^6.19.4 [env:direct] platform = atmelavr @@ -21,4 +34,7 @@ test_port = /dev/ttyS0 test_speed = 115200 test_build_src = true monitor_port = /dev/ttyS0 -monitor_speed = 115200 \ No newline at end of file +monitor_speed = 115200 +lib_deps = + adafruit/Adafruit BusIO@^1.13.2 + bblanchon/ArduinoJson@^6.19.4 diff --git a/software/microcontroller/src/actuators/actuator.cc b/software/microcontroller/src/actuators/actuator.cc index a6802638..74d36381 100644 --- a/software/microcontroller/src/actuators/actuator.cc +++ b/software/microcontroller/src/actuators/actuator.cc @@ -76,5 +76,9 @@ void Actuator::setTarget(float target) { } String Actuator::toString(void) { - return String((const char*)(this->_id)); + return String((const char*)_id); +} + +String Actuator::getID(void) { + return String((const char*)_id); } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/actuator.h b/software/microcontroller/src/actuators/actuator.h index 798b1f69..e6678f01 100644 --- a/software/microcontroller/src/actuators/actuator.h +++ b/software/microcontroller/src/actuators/actuator.h @@ -51,6 +51,8 @@ class Actuator { // @return String representation of this actuator String toString(void); + String getID(void); + protected: /** * Initializes actuator. To be implemented by the child class. diff --git a/software/microcontroller/src/actuators/onoff.cc b/software/microcontroller/src/actuators/onoff.cc index d0406634..9bc52b81 100644 --- a/software/microcontroller/src/actuators/onoff.cc +++ b/software/microcontroller/src/actuators/onoff.cc @@ -14,12 +14,12 @@ OnOff::OnOff(const char* const* id, const uint8_t pin) : Actuator(id, 0), pin(pi // PUBLIC METHODS errorlevel_t OnOff::initialize(void) { - pinMode(this->pin, OUTPUT); + pinMode(pin, OUTPUT); return ERR_NONE; } errorlevel_t OnOff::set(float target) { // Round to 0 or 1 - digitalWrite(this->pin, target >= 0.5 ? 1 : 0); + digitalWrite(pin, target >= 0.5 ? 1 : 0); return ERR_NONE; } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/pwm.cc b/software/microcontroller/src/actuators/pwm.cc index f061ef47..38751a03 100644 --- a/software/microcontroller/src/actuators/pwm.cc +++ b/software/microcontroller/src/actuators/pwm.cc @@ -14,13 +14,13 @@ PWM::PWM(const char* const* id, const uint8_t pin) : Actuator(id, 0), pin(pin) { // PUBLIC METHODS errorlevel_t PWM::initialize(void) { - pinMode(this->pin, OUTPUT); + pinMode(pin, OUTPUT); return ERR_NONE; } errorlevel_t PWM::set(float target) { // Clamp to 0 < x < 1 target = min(max(target, 0), 1); - analogWrite(this->pin, target * 255); + analogWrite(pin, target * 255); return ERR_NONE; } \ No newline at end of file diff --git a/software/microcontroller/src/actuators/tec.cc b/software/microcontroller/src/actuators/tec.cc index 74c84804..930434e6 100644 --- a/software/microcontroller/src/actuators/tec.cc +++ b/software/microcontroller/src/actuators/tec.cc @@ -14,17 +14,17 @@ TEC::TEC(uint8_t powerpin, uint8_t relayApin, uint8_t relayBpin) : Actuator(&id, // PUBLIC METHODS errorlevel_t TEC::initialize(void) { - pinMode(this->power, OUTPUT); - pinMode(this->relayA, OUTPUT); - pinMode(this->relayB, OUTPUT); + pinMode(power, OUTPUT); + pinMode(relayA, OUTPUT); + pinMode(relayB, OUTPUT); return ERR_NONE; } errorlevel_t TEC::set(float target) { // Clamp to -1 < x < 1 target = min(max(target, -1), 1); - analogWrite(this->power, abs(target) * 255); - analogWrite(this->relayA, 255 * (target >= TEC_DEADZONE ? 1 : 0)); - analogWrite(this->relayB, 255 * (target <= -TEC_DEADZONE ? 1 : 0)); + analogWrite(power, abs(target) * 255); + analogWrite(relayA, 255 * (target >= TEC_DEADZONE ? 1 : 0)); + analogWrite(relayB, 255 * (target <= -TEC_DEADZONE ? 1 : 0)); return ERR_NONE; } \ No newline at end of file diff --git a/software/microcontroller/src/modules/module.cc b/software/microcontroller/src/modules/module.cc new file mode 100644 index 00000000..01306258 --- /dev/null +++ b/software/microcontroller/src/modules/module.cc @@ -0,0 +1,43 @@ +// HEADERS + +#include + +#include + +#include +#include +#include + +// CONSTRUCTOR + +Module::Module(const char* const* id, uint8_t numsensors, Sensor* sensors, uint8_t numactuators, Actuator* actuators) : _id(id) { + state.numsensors = numsensors; + state.sensors = sensors; + state.numactuators = numactuators; + state.actuators = actuators; +} + +ModuleState* Module::getState(void) { + return &state; +} + +String Module::toString(void) { + String s = String((const char*)(_id)) + " ("; + for (int i = 0; i < state.numsensors; i++) { + s += state.sensors[i].getID(); + if (i < state.numsensors - 1 || state.numactuators > 0) { + s += ", "; + } + } + for (int i = 0; i < state.numactuators; i++) { + s += state.actuators[i].getID(); + if (i < state.numactuators - 1) { + s += ", "; + } + } + return s + ")"; +} + +String Module::getID(void) { + return String((const char*)_id); +} \ No newline at end of file diff --git a/software/microcontroller/src/modules/module.h b/software/microcontroller/src/modules/module.h new file mode 100644 index 00000000..ea5ee206 --- /dev/null +++ b/software/microcontroller/src/modules/module.h @@ -0,0 +1,48 @@ +#ifndef PEAPOD_MODULES_MODULE_H_ +#define PEAPOD_MODULES_MODULE_H_ + +// HEADERS + +#include + +#include +#include +#include + +// DECLARATIONS + +// All module state info +typedef struct ModuleState { + // Error level + errorlevel_t error; + + // Other state information + debuglevel_t debug; + + uint8_t numsensors; + Sensor* sensors; + + uint8_t numactuators; + Actuator* actuators; +} ModuleState; + +// CLASS +// A "Module" is a single control system group, a collection of sensors and actuators +class Module { + public: + Module(const char* const* id, uint8_t numsensors, Sensor* sensors, uint8_t numactuators, Actuator* actuators); + + // @return Pointer to sensor state + ModuleState* getState(void); + + // @return String representation + String toString(void); + + String getID(void); + private: + ModuleState state; + const char* const* _id; +}; + + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/sensors/ge_2158.cc b/software/microcontroller/src/sensors/ge_2158.cc index bf76c37b..2b660b4c 100644 --- a/software/microcontroller/src/sensors/ge_2158.cc +++ b/software/microcontroller/src/sensors/ge_2158.cc @@ -16,12 +16,12 @@ GE_2158::GE_2158(const uint8_t pin) : Sensor(&id, &datasetup, GE_2158_DELTA), pi // PUBLIC METHODS errorlevel_t GE_2158::initialize(void) { - pinMode(this->pin, INPUT); + pinMode(pin, INPUT); return ERR_NONE; } errorlevel_t GE_2158::read(float* data, uint8_t numdata) { - uint16_t read = analogRead(this->pin); + uint16_t read = analogRead(pin); float v = read * GE_2158_V / 1023; float temp = (log((v * GE_2158_R) / (GE_2158_V - v) - GE_2158_C) - GE_2158_B) / GE_2158_A; diff --git a/software/microcontroller/src/sensors/ge_2158.h b/software/microcontroller/src/sensors/ge_2158.h index ce4ddbce..0d32f2bd 100644 --- a/software/microcontroller/src/sensors/ge_2158.h +++ b/software/microcontroller/src/sensors/ge_2158.h @@ -16,6 +16,7 @@ #define GE_2158_R 3000 // Series resistor // Equation: T(v) = (ln((v * GE_2158_R) / (GE_2158_V - v) - GE_2158_C) - GE_2158_B) / GE_2158_A +// Fit to data tables found in manufacturer datasheet #define GE_2158_A -0.055481 // A parameter #define GE_2158_B 9.30108 // B parameter #define GE_2158_C 122.933 // C parameter diff --git a/software/microcontroller/src/sensors/sen0257.cc b/software/microcontroller/src/sensors/sen0257.cc index c65ac81e..fb9a5f21 100644 --- a/software/microcontroller/src/sensors/sen0257.cc +++ b/software/microcontroller/src/sensors/sen0257.cc @@ -14,12 +14,12 @@ SEN0257::SEN0257(const uint8_t pin) : Sensor(&id, &datasetup, SEN0257_DELTA), pi // PUBLIC METHODS errorlevel_t SEN0257::initialize(void) { - pinMode(this->pin, INPUT); + pinMode(pin, INPUT); return ERR_NONE; } errorlevel_t SEN0257::read(float* data, uint8_t numdata) { - uint16_t read = analogRead(this->pin); + uint16_t read = analogRead(pin); float v = read * 5.0 / 1023; float pres = (float)((v - SEN0257_VMIN) * (SEN0257_PMAX - SEN0257_PMIN) / (SEN0257_VMAX - SEN0257_VMIN) + SEN0257_PMIN); diff --git a/software/microcontroller/src/sensors/sensor.cc b/software/microcontroller/src/sensors/sensor.cc index 9475c2f9..be9e4368 100644 --- a/software/microcontroller/src/sensors/sensor.cc +++ b/software/microcontroller/src/sensors/sensor.cc @@ -88,7 +88,7 @@ SensorState* Sensor::getState(void) { } String Sensor::toString(void) { - String s = String((const char*)(this->_id)) + " ("; + String s = String((const char*)(_id)) + " ("; for (int i = 0; i < state.numdata; i++) { s += state.data[i].label; if (i < state.numdata - 1) { @@ -96,4 +96,8 @@ String Sensor::toString(void) { } } return s + ")"; +} + +String Sensor::getID(void) { + return String((const char*)_id); } \ No newline at end of file diff --git a/software/microcontroller/src/sensors/sensor.h b/software/microcontroller/src/sensors/sensor.h index 0b95b45b..e6553ecc 100644 --- a/software/microcontroller/src/sensors/sensor.h +++ b/software/microcontroller/src/sensors/sensor.h @@ -45,7 +45,7 @@ typedef struct SensorDataSetup { } SensorDataSetup; // CLASS - +// Abstract base class for all sensor interfaces class Sensor { public: /** @@ -72,6 +72,8 @@ class Sensor { // @return String representation String toString(void); + String getID(void); + protected: /** * Initializes sensor. To be implemented by the child class. diff --git a/software/microcontroller/src/sensors/yf_b1.cc b/software/microcontroller/src/sensors/yf_b1.cc index f048955a..b2c44010 100644 --- a/software/microcontroller/src/sensors/yf_b1.cc +++ b/software/microcontroller/src/sensors/yf_b1.cc @@ -14,7 +14,7 @@ YF_B1::YF_B1(const uint8_t pin) : Sensor(&id, &datasetup, YF_B1_DELTA), Interrup // PUBLIC METHODS errorlevel_t YF_B1::initialize(void) { - pinMode(this->pin, INPUT); + pinMode(pin, INPUT); // Attach rising-edge interrupt to our digital pin, triggers flow() attachInstanceInterrupt(RISING); return ERR_NONE; @@ -34,5 +34,5 @@ errorlevel_t YF_B1::read(float* data, uint8_t numdata) { } void YF_B1::handleInterrupt(void) { - this->flow_count++; + flow_count++; } \ No newline at end of file diff --git a/software/microcontroller/src/utils/interrupts.cc b/software/microcontroller/src/utils/interrupts.cc index 40345ca2..158dab61 100644 --- a/software/microcontroller/src/utils/interrupts.cc +++ b/software/microcontroller/src/utils/interrupts.cc @@ -7,7 +7,7 @@ InterruptHandler::InterruptHandler(const uint8_t* pin) { } InterruptHandler::~InterruptHandler() { - detachInterrupt(digitalPinToInterrupt(*this->pin)); + detachInterrupt(digitalPinToInterrupt(*pin)); } void InterruptHandler::attachInstanceInterrupt(int mode) { @@ -15,7 +15,7 @@ void InterruptHandler::attachInstanceInterrupt(int mode) { // and is then converted to a normal function pointer to its place in memory // This causes a `-Wpmf-conversions` warning, but works so long as the object instance outlives the interrupt // This is ensured by detaching the interrupt on object destruction - attachInterrupt(digitalPinToInterrupt(*this->pin), (interruptHandler)(&InterruptHandler::handlerTrigger), RISING); + attachInterrupt(digitalPinToInterrupt(*pin), (interruptHandler)(&InterruptHandler::handlerTrigger), RISING); } void InterruptHandler::handlerTrigger(void) { diff --git a/software/src/peapod.ts b/software/src/peapod.ts index 0f232d68..5e6a3dbc 100644 --- a/software/src/peapod.ts +++ b/software/src/peapod.ts @@ -8,6 +8,7 @@ import { loadPeaPodEnv, loadAuthEnv, loadFirebaseEnv, loadIoTEnv } from './env'; import MicroController, { ControllerInstructions, Controller, CONTROLLER_REVISION } from './controller'; import OnlinePublisher, { DataBatch, DataSet, Publisher, OfflinePublisher, PublishingMode } from './publisher'; import { sleep } from './utils'; +import { ControlSystem } from './controlsystem'; // CONSTANTS @@ -26,40 +27,6 @@ const REFRESH_INTERVAL = 1000; */ const IDLE_PERIOD = 5000; -// GLOBAL VARIABLES - -/** - * Batch of data to be published. - */ -let batch: DataBatch = { }; - -/** - * JS interval to trigger batch publishing. - */ -let batchInterval: NodeJS.Timer; - -/** - * Currently loaded schedule. - */ -let schedule: EnvironmentSchedule; - -/** - * Current environment schedule targets. - */ -let targets: EnvironmentTargets = {}; - -/** - * Latest controller instruction set. - */ -let instructions: ControllerInstructions = {}; - -/** - * - */ -let data: DataSet = {}; - -let controlSystems: {[key: string]: ControlSystem} - // DECLARATIONS export type EnvironmentSchedule = { @@ -94,13 +61,66 @@ type EnvironmentTargetPhase = { /** * Main driver class. + * + * LINGO LESSON: + * > Derived from the Solution Overview/Progress Report + * + * VARIABLE = A **MEASURABLE** environment state variable, both feedback-driven and not. E.g. "air-temperature", "air-humidity", "co2-ppm", + * PARAMETER = A **CONTROLLABLE** environment state variable, both feedback-driven and not. E.g. "air-temperature", "lighting_750nm", "dosage_pump1" + * + * INSTRUCTION + * SCHEDULE = . See `../assets/schedule_schema.json` + * TARGETS = Desired state for each parameter. PeaPod scheduler state variables. Modified + * RUN */ export default class PeaPod { - + // INTERFACES controller: Controller; publisher: Publisher; + + // SCHEDULER FIELDS + + /** + * Currently loaded schedule. + */ + private schedule?: EnvironmentSchedule; + + /** + * Start time of the last run. + */ private startTime: number = 0; + /** + * Current environment schedule targets. + */ + private targets: EnvironmentTargets = { }; + + /** + * Latest controller data for each variable + */ + private data: DataSet = { }; + + // CONTROLLER FIELDS + + private controlSystems?: {[key: string]: ControlSystem}; + + /** + * Latest controller instruction set. + */ + private instructions: ControllerInstructions = { }; + + // PUBLISHER FIELDS + + /** + * Batch of data to be published. + */ + private batch: DataBatch = { }; + + /** + * JS interval to trigger batch publishing. + */ + private batchInterval?: NodeJS.Timer; + constructor(pm: PublishingMode) { const ENV_PEAPOD = loadPeaPodEnv(); @@ -178,6 +198,10 @@ export default class PeaPod { timestamp: Date.now(), value: msg.data.value, }); + // Set actuator values + if (controlSystems[msg.data.label] !== undefined) { + controlSystems[msg.data.label].setValue(msg.data.value); + } break; case "revision": this.controller.write(instructions); @@ -198,12 +222,14 @@ export default class PeaPod { Spinner.log(`[${ chalk.yellow('PUBLISHER') } | SCHEDULE] - ${ JSON.stringify(config.data.name) }`); if (config.data.revision != CONTROLLER_REVISION) { - Spinner.fail(`Failed to load schedule '${config.data.name}', software version mismatch (Expected ${CONTROLLER_REVISION}, got ${config.data.revision}).`); + Spinner.fail(`Failed to load schedule '${config.data.name}' (${config.data.id}), software version mismatch (Expected ${CONTROLLER_REVISION}, got ${config.data.revision}).`); break; } + Spinner.succeed(`Successfully loaded schedule '${config.data.name}' (${config.data.id}).`); + // Save new schedule - schedule = config.data; + this.schedule = config.data; break; default: @@ -226,9 +252,9 @@ export default class PeaPod { this.startTime = Date.now(); // Start schedule phase 0 - - for (const parameter of Object.keys(schedule.parameters)) { - this.startPhase(schedule, parameter); + let phasePromises = []; + for (const parameter of Object.keys(this.schedule.parameters)) { + phasePromises.push(this.startPhase(schedule, parameter)); } // Reset @@ -251,17 +277,36 @@ export default class PeaPod { return; } - Spinner.log(`[${ chalk.magenta('PUBLISH') }] - Batch of ${ Object.values(batch).reduce((sum, entry) => { return sum+entry.length }, 0) } datapoints published.`); + Spinner.log(`[${ chalk.magenta('PUBLISH') }] - Batch of ${ Object.values(batch).reduce((sum, entry) => (sum+entry.length), 0) } datapoints published.`); // Reset batch to empty batch = { }; }, BATCH_PUBLISH_INTERVAL*1000); - refreshInterval = set + // Refresh control system values and targets, update actuator instructions, send new instructions + const refreshInterval = setInterval(()=>{ + let instruction: ControllerInstructions = {}; + for (const parameter of Object.keys(targets)) { + const cs = controlSystems[parameter]; + cs.setTarget(targets[parameter]); + instruction[cs.actuator] = cs.refresh(); + } + this.controller.write(instruction); + }, REFRESH_INTERVAL); + + // When all phases for all parameters are complete, clear refresh interval, send "off" instruction, resolve this promise + return Promise.all(phasePromises).then(()=>{ + clearInterval(refreshInterval); + let off: ControllerInstructions = {}; + for (const parameter of Object.keys(targets)) { + off[controlSystems[parameter].actuator] = 0; + } + this.controller.write(off); + }) } /** - * Start the schedule for a single parameter. Runs recursively until all phases are executed. + * Start the schedule for a single parameter. Runs recursively until all phases are executed. Sets targets. * @param schedule Environment schedule * @param parameter Parameter to start * @param n Phase to start at From ccc39fabf0a02373c401b58bc1e719ed0fd4687f Mon Sep 17 00:00:00 2001 From: Liam Major Date: Mon, 29 Aug 2022 17:12:14 -0400 Subject: [PATCH 32/85] added linting and extensions for vscode --- peapod.code-workspace | 33 +++++++++++++++++++++++++++++++++ software/.prettierrc | 10 ++++++++++ 2 files changed, 43 insertions(+) create mode 100644 peapod.code-workspace create mode 100644 software/.prettierrc diff --git a/peapod.code-workspace b/peapod.code-workspace new file mode 100644 index 00000000..b4ef32a6 --- /dev/null +++ b/peapod.code-workspace @@ -0,0 +1,33 @@ +{ + "folders": [ + { + "name": "Peapod Development Folder", + "path": "software/" + } + ], + "extensions": { + "recommendations": [ + "davidanson.vscode-markdownlint", + "christian-kohler.npm-intellisense", + "2gua.rainbow-brackets", + "vscode-icons-team.vscode-icons", + "eamodio.gitlens", + "eg2.vscode-npm-script", + "esbenp.prettier-vscode" + ] + }, + "settings": { + "diffEditor.renderSideBySide": true, + "breadcrumbs.enabled": true, + "editor.fontSize": 14, + "editor.autoIndent": "full", + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.formatOnSave": false, + "[typescript]": { + "editor.insertSpaces": false, + "editor.tabSize": 2, + "editor.defaultFormatter": "esbenp.prettier-vscode" + } + } +} \ No newline at end of file diff --git a/software/.prettierrc b/software/.prettierrc new file mode 100644 index 00000000..94ae9a39 --- /dev/null +++ b/software/.prettierrc @@ -0,0 +1,10 @@ +{ + "trailingComma": "none", + "tabWidth": 2, + "useTabs": true, + "semi": true, + "singleQuote": true, + "arrowParens": "avoid", + "bracketSpacing": true, + "printWidth": 100 +} \ No newline at end of file From cb627310f6f386638e78be384ad3c698723732df Mon Sep 17 00:00:00 2001 From: Liam Major Date: Mon, 29 Aug 2022 17:12:20 -0400 Subject: [PATCH 33/85] pretty printed files --- software/index.ts | 78 ++-- software/src/controller.ts | 432 ++++++++++---------- software/src/controlsystem.ts | 103 ++--- software/src/env.ts | 172 ++++---- software/src/errors.ts | 20 +- software/src/peapod.ts | 718 ++++++++++++++++++---------------- software/src/publisher.ts | 624 +++++++++++++++-------------- software/src/ui.ts | 244 ++++++------ software/src/utils.ts | 134 ++++--- 9 files changed, 1352 insertions(+), 1173 deletions(-) diff --git a/software/index.ts b/software/index.ts index defb4648..b2b1ef49 100644 --- a/software/index.ts +++ b/software/index.ts @@ -10,43 +10,45 @@ import PeaPod from './src/peapod'; // MAIN async function main(): Promise { - Spinner.start(`Checking for ${ chalk.blue('Internet') } connection...'`); - const internet = await checkInternet(); - if (internet) { - Spinner.succeed(`Connected to the ${ chalk.blue('Internet') }!`); - } else { - Spinner.fail(`Failed to connect to the ${ chalk.blue('Internet') }.`); - } - - // Get desired publishing mode - let publishingmode = (await inquirer.prompt<{pm: PublishingMode}>([ - { - type: 'list', - name: 'pm', - message: 'Select publishing mode:', - choices: Object.entries(PublishingMode).map(pm => { - return { - // Object.entries casts `PublishingMode` values to `string`. This recasts them to PublishingMode enum values (TypeScript magic) - value: PublishingMode[pm[0] as keyof typeof PublishingMode], - name: pm[1] - }; - }) - } - ])).pm; - - let peapod: PeaPod = new PeaPod(publishingmode); - - let idleInterval = await peapod.idle(); - - // Temporary; replace with main menu - await sleep(10000); - - clearInterval(idleInterval); - peapod.start(); + Spinner.start(`Checking for ${chalk.blue('Internet')} connection...'`); + const internet = await checkInternet(); + if (internet) { + Spinner.succeed(`Connected to the ${chalk.blue('Internet')}!`); + } else { + Spinner.fail(`Failed to connect to the ${chalk.blue('Internet')}.`); + } + + // Get desired publishing mode + let publishingmode = ( + await inquirer.prompt<{ pm: PublishingMode }>([ + { + type: 'list', + name: 'pm', + message: 'Select publishing mode:', + choices: Object.entries(PublishingMode).map(pm => { + return { + // Object.entries casts `PublishingMode` values to `string`. This recasts them to PublishingMode enum values (TypeScript magic) + value: PublishingMode[pm[0] as keyof typeof PublishingMode], + name: pm[1] + }; + }) + } + ]) + ).pm; + + let peapod: PeaPod = new PeaPod(publishingmode); + + let idleInterval = await peapod.idle(); + + // Temporary; replace with main menu + await sleep(10000); + + clearInterval(idleInterval); + peapod.start(); } -main().catch((err)=>{ - // Last resort catch - Spinner.fail(JSON.stringify(err)); - process.exit(1); -}); \ No newline at end of file +main().catch(err => { + // Last resort catch + Spinner.fail(JSON.stringify(err)); + process.exit(1); +}); diff --git a/software/src/controller.ts b/software/src/controller.ts index 574a4334..1a8ae4b0 100644 --- a/software/src/controller.ts +++ b/software/src/controller.ts @@ -7,7 +7,7 @@ import { Gpio } from 'onoff'; // CONSTANTS -/** +/** * Baud rate for serial communication. Must match that found in microcontroller code. */ const BAUDRATE = 115200; @@ -22,10 +22,10 @@ export const CONTROLLER_REVISION = 0; */ const SERIAL_TIMEOUT_SECONDS = 10; -/** +/** * GPIO pin attached to the reset grounding circuit */ - const RESET_PIN = 26; +const RESET_PIN = 26; // TYPES @@ -33,58 +33,61 @@ const SERIAL_TIMEOUT_SECONDS = 10; * Base type for any controller. */ export type Controller = { - /** - * Establish communications with the Controller. - * @param onMessage Pipe all received messages. - * @throws If received message is invalid (JSON parsing fails). - */ - start(onMessage: (msg : ControllerMessage) => void): Promise, - - /** - * Write instructions to the Controller. - * @param instructions Instruction set. - */ - write(instructions: ControllerInstructions): void, - - /** - * Halt communications with the Controller. - */ - stop(): void, + /** + * Establish communications with the Controller. + * @param onMessage Pipe all received messages. + * @throws If received message is invalid (JSON parsing fails). + */ + start(onMessage: (msg: ControllerMessage) => void): Promise; + + /** + * Write instructions to the Controller. + * @param instructions Instruction set. + */ + write(instructions: ControllerInstructions): void; + + /** + * Halt communications with the Controller. + */ + stop(): void; }; /** * Messages FROM the controller */ -export type ControllerMessage = { - type: 'info' | 'debug' | 'error', - data: string, -} | { - type: 'data', - data: { - label: string, - value: number, - }, -} | { - type: 'revision', - data: number, -}; +export type ControllerMessage = + | { + type: 'info' | 'debug' | 'error'; + data: string; + } + | { + type: 'data'; + data: { + label: string; + value: number; + }; + } + | { + type: 'revision'; + data: number; + }; /** * Messages TO the controller */ export type ControllerInstructions = { - [key: string]: number, + [key: string]: number; }; /** * Simulated controller parameters */ export type SimulatorConfig = { - [key: string]: { - min: number, - max: number, - interval: number, - }, + [key: string]: { + min: number; + max: number; + interval: number; + }; }; // CLASSES @@ -93,178 +96,185 @@ export type SimulatorConfig = { * Interface between this computer and the microcontroller. */ export default class MicroController implements Controller { - - serial: SerialPort; - parser: ReadlineParser; - private timeout?: NodeJS.Timeout; - private resetpin: Gpio; - - constructor(readonly serialport: string) { - // Reset pin GPIO interface - this.resetpin = new Gpio(RESET_PIN, 'out'); - - // Create the serial port interface - this.serial = new SerialPort({ - path: serialport, - baudRate: BAUDRATE, - autoOpen: false - }); - - // Create the newline parser - this.parser = this.serial.pipe(new ReadlineParser({ - delimiter: '\n', - includeDelimiter: false - })); - } - - start(onMessage: (msg: ControllerMessage) => void): Promise { - this.clearTimeout(); - // Reset listeners - this.parser.removeAllListeners('data'); - - // Explicit promise construction so we can resolve only on valid comms AND revision check - return new Promise(async (res, rej) => { - // Reset the microcontroller (opens the serial port) - await this.reset(); - - Spinner.start('Awaiting microcontroller software revision number...'); - this.resetTimeout(); - - // Set up the listener - this.parser.on('data', async msgtxt => { - - // Attempt to parse the raw text as a valid JSON object - let msg: ControllerMessage; - try { - msg = JSON.parse(msgtxt); - } catch (err) { - rej(err); - return; - } - - // Microcontroller-specific pre-handling - switch (msg.type) { - case 'revision': - // Software update - if(msg.data === CONTROLLER_REVISION) { - Spinner.succeed(`Microcontroller software up to date! Got ${msg.data}`); - res(); //Successful start sequence - } else { - Spinner.fail(`Microcontroller software out of date! Got ${msg.data}, expected ${CONTROLLER_REVISION}.`); - // Attempt to update the microcontroller, and then restart - this.stop(); - Spinner.start('Compiling latest microcontroller software and flashing...'); - await updateMicrocontroller(); - Spinner.succeed('Updated microcontroller software successfully!'); - } - // Do not break - default: - onMessage(msg); - break; - } - this.resetTimeout(); - }); - }); - } - - /** - * Clear the serial timeout. - */ - private clearTimeout(): void { - if(this.timeout) clearTimeout(this.timeout); - } - - /** - * Refresh (or start) the serial timeout. - */ - private resetTimeout(timeoutSeconds: number = SERIAL_TIMEOUT_SECONDS): void { - this.clearTimeout(); - this.timeout = setTimeout(() => { - Spinner.fail(`Microcontroller serial communications timed out after ${ timeoutSeconds } seconds.`); - this.reset(); - }, timeoutSeconds*1000); - } - - write(msg: ControllerInstructions): void { - Spinner.info(`[${ chalk.yellow('WRITE') }] - ${ JSON.stringify(msg) }`); - this.serial.write(JSON.stringify(msg)+'\n', undefined, err => { - if (err) throw new ControllerInstructionsError(JSON.stringify(msg)); - }); - } - - stop(): void { - this.clearTimeout(); - if (this.serial.isOpen) this.serial.close(); - // Stop listening for data - this.parser.removeAllListeners('data'); - } - - /** - * Resets the microcontroller by closing and re-opening serial. - */ - private async reset(): Promise { - // Stop and reset - this.stop(); - this.resetpin.writeSync(1); - - // Wait, then stop resetting - await sleep(1000); - this.resetpin.writeSync(0); - - // (Re-)open serial - await new Promise((reso, reje) => { - Spinner.start('Establishing serial communications with the microcontroller...'); - this.serial.open(err => { - if (err) { - reje(err); - } else { - Spinner.succeed('Microcontroller serial communications established!'); - reso(); - } - }); - }); - - // Restart timeout - this.resetTimeout(); - } + serial: SerialPort; + parser: ReadlineParser; + private timeout?: NodeJS.Timeout; + private resetpin: Gpio; + + constructor(readonly serialport: string) { + // Reset pin GPIO interface + this.resetpin = new Gpio(RESET_PIN, 'out'); + + // Create the serial port interface + this.serial = new SerialPort({ + path: serialport, + baudRate: BAUDRATE, + autoOpen: false + }); + + // Create the newline parser + this.parser = this.serial.pipe( + new ReadlineParser({ + delimiter: '\n', + includeDelimiter: false + }) + ); + } + + start(onMessage: (msg: ControllerMessage) => void): Promise { + this.clearTimeout(); + // Reset listeners + this.parser.removeAllListeners('data'); + + // Explicit promise construction so we can resolve only on valid comms AND revision check + return new Promise(async (res, rej) => { + // Reset the microcontroller (opens the serial port) + await this.reset(); + + Spinner.start('Awaiting microcontroller software revision number...'); + this.resetTimeout(); + + // Set up the listener + this.parser.on('data', async msgtxt => { + // Attempt to parse the raw text as a valid JSON object + let msg: ControllerMessage; + try { + msg = JSON.parse(msgtxt); + } catch (err) { + rej(err); + return; + } + + // Microcontroller-specific pre-handling + switch (msg.type) { + case 'revision': + // Software update + if (msg.data === CONTROLLER_REVISION) { + Spinner.succeed(`Microcontroller software up to date! Got ${msg.data}`); + res(); //Successful start sequence + } else { + Spinner.fail( + `Microcontroller software out of date! Got ${msg.data}, expected ${CONTROLLER_REVISION}.` + ); + // Attempt to update the microcontroller, and then restart + this.stop(); + Spinner.start('Compiling latest microcontroller software and flashing...'); + await updateMicrocontroller(); + Spinner.succeed('Updated microcontroller software successfully!'); + } + // Do not break + default: + onMessage(msg); + break; + } + this.resetTimeout(); + }); + }); + } + + /** + * Clear the serial timeout. + */ + private clearTimeout(): void { + if (this.timeout) clearTimeout(this.timeout); + } + + /** + * Refresh (or start) the serial timeout. + */ + private resetTimeout(timeoutSeconds: number = SERIAL_TIMEOUT_SECONDS): void { + this.clearTimeout(); + this.timeout = setTimeout(() => { + Spinner.fail( + `Microcontroller serial communications timed out after ${timeoutSeconds} seconds.` + ); + this.reset(); + }, timeoutSeconds * 1000); + } + + write(msg: ControllerInstructions): void { + Spinner.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); + this.serial.write(JSON.stringify(msg) + '\n', undefined, err => { + if (err) throw new ControllerInstructionsError(JSON.stringify(msg)); + }); + } + + stop(): void { + this.clearTimeout(); + if (this.serial.isOpen) this.serial.close(); + // Stop listening for data + this.parser.removeAllListeners('data'); + } + + /** + * Resets the microcontroller by closing and re-opening serial. + */ + private async reset(): Promise { + // Stop and reset + this.stop(); + this.resetpin.writeSync(1); + + // Wait, then stop resetting + await sleep(1000); + this.resetpin.writeSync(0); + + // (Re-)open serial + await new Promise((reso, reje) => { + Spinner.start('Establishing serial communications with the microcontroller...'); + this.serial.open(err => { + if (err) { + reje(err); + } else { + Spinner.succeed('Microcontroller serial communications established!'); + reso(); + } + }); + }); + + // Restart timeout + this.resetTimeout(); + } } -/** -* A simulated controller for generating random data. -*/ +/** + * A simulated controller for generating random data. + */ export class SimulatedController implements Controller { - - private intervals: NodeJS.Timeout[] = []; - - constructor(readonly parameters: SimulatorConfig) { } - - async start(onMessage: (msg: ControllerMessage) => any): Promise { - for(const label of Object.keys(this.parameters)) { - this.intervals.push(setInterval(() => { - onMessage(this.generateData(label, this.parameters[label].min, this.parameters[label].max)); - }, this.parameters[label].interval)); - } - } - write() {} - async stop(): Promise { - for (const interval of this.intervals) { - clearInterval(interval); - } - } - - /** - * Generate a single data point - * @param label Dataset label - * @param min Minimum value - * @param max Maximum value - */ - private generateData(label: string, min: number, max: number): ControllerMessage { - return { - type: 'data', - data: { - label, - value: Math.random()*(max-min)+min - } - }; - } -} \ No newline at end of file + private intervals: NodeJS.Timeout[] = []; + + constructor(readonly parameters: SimulatorConfig) {} + + async start(onMessage: (msg: ControllerMessage) => any): Promise { + for (const label of Object.keys(this.parameters)) { + this.intervals.push( + setInterval(() => { + onMessage( + this.generateData(label, this.parameters[label].min, this.parameters[label].max) + ); + }, this.parameters[label].interval) + ); + } + } + write() {} + async stop(): Promise { + for (const interval of this.intervals) { + clearInterval(interval); + } + } + + /** + * Generate a single data point + * @param label Dataset label + * @param min Minimum value + * @param max Maximum value + */ + private generateData(label: string, min: number, max: number): ControllerMessage { + return { + type: 'data', + data: { + label, + value: Math.random() * (max - min) + min + } + }; + } +} diff --git a/software/src/controlsystem.ts b/software/src/controlsystem.ts index 3f95bac3..3809f508 100644 --- a/software/src/controlsystem.ts +++ b/software/src/controlsystem.ts @@ -1,61 +1,70 @@ export abstract class ControlSystem { - protected target?: number; - protected value?: number; - constructor(readonly actuator: string) {} - abstract refresh(): number - setTarget(target: number) { this.target = target; } - setValue(value: number) { this.value = value; } + protected target?: number; + protected value?: number; + constructor(readonly actuator: string) {} + abstract refresh(): number; + setTarget(target: number) { + this.target = target; + } + setValue(value: number) { + this.value = value; + } } // MAIN CLASS -/** +/** * Interface for publishing data to and receiving instructions from Google Cloud/Firebase via IoT Core PubSub. */ export class BangBang extends ControlSystem { - constructor(readonly actuator: string, readonly deadzone: number = 0) { - super(actuator); - } - refresh(): number { - if (this.target === undefined || this.value === undefined) { - return NaN; - } - if (this.value > this.target + this.deadzone) { - return -1; - } - if (this.value < this.target - this.deadzone) { - return 1; - } - return 0; - } + constructor(readonly actuator: string, readonly deadzone: number = 0) { + super(actuator); + } + refresh(): number { + if (this.target === undefined || this.value === undefined) { + return NaN; + } + if (this.value > this.target + this.deadzone) { + return -1; + } + if (this.value < this.target - this.deadzone) { + return 1; + } + return 0; + } } -/** +/** * Interface for publishing data to and receiving instructions from Google Cloud/Firebase via IoT Core PubSub. */ export class PID extends ControlSystem { - private valueLastSet: number = NaN; - private lasterr: number = NaN; - private integral: number = 0; - constructor(readonly actuator: string, readonly Kp: number, readonly Ki: number, readonly Kd: number) { - super(actuator); - } - refresh(): number { - if (this.target === undefined || this.value === undefined) { - return NaN; - } - const err = this.target - this.value; - let derivative = 0; - if (this.valueLastSet != NaN || this.lasterr != NaN) { - // Trapezoidal approximation - this.integral += (this.lasterr + err) / 2 * (Date.now() - this.valueLastSet); - derivative = (err - this.lasterr) / (Date.now() - this.valueLastSet); - } - return this.Kp * err + this.Ki * this.integral + this.Kd * derivative; - } + private valueLastSet: number = NaN; + private lasterr: number = NaN; + private integral: number = 0; + constructor( + readonly actuator: string, + readonly Kp: number, + readonly Ki: number, + readonly Kd: number + ) { + super(actuator); + } + refresh(): number { + if (this.target === undefined || this.value === undefined) { + return NaN; + } + const err = this.target - this.value; + let derivative = 0; + if (this.valueLastSet != NaN || this.lasterr != NaN) { + // Trapezoidal approximation + this.integral += ((this.lasterr + err) / 2) * (Date.now() - this.valueLastSet); + derivative = (err - this.lasterr) / (Date.now() - this.valueLastSet); + } + return this.Kp * err + this.Ki * this.integral + this.Kd * derivative; + } - override setValue(value: number): void { - this.value = value; - this.valueLastSet = Date.now(); - } -} \ No newline at end of file + override setValue(value: number): void { + this.value = value; + this.valueLastSet = Date.now(); + } +} diff --git a/software/src/env.ts b/software/src/env.ts index 8ebfff0f..d43c6131 100644 --- a/software/src/env.ts +++ b/software/src/env.ts @@ -8,42 +8,62 @@ import { DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; /** * Env fields required for ALL modes. */ -const ENV_FIELDS_PEAPOD = ["SERIALPORT"]; +const ENV_FIELDS_PEAPOD = ['SERIALPORT']; /** * Env fields required for Firebase functionality. */ -const ENV_FIELDS_FIREBASE = ["FIREBASE_APIKEY", "FIREBASE_AUTHDOMAIN", "FIREBASE_PROJECTID", "FIREBASE_STORAGEBUCKET", "FIREBASE_MESSAGINGSENDERID", "FIREBASE_APPID", "FIREBASE_MEASUREMENTID"]; +const ENV_FIELDS_FIREBASE = [ + 'FIREBASE_APIKEY', + 'FIREBASE_AUTHDOMAIN', + 'FIREBASE_PROJECTID', + 'FIREBASE_STORAGEBUCKET', + 'FIREBASE_MESSAGINGSENDERID', + 'FIREBASE_APPID', + 'FIREBASE_MEASUREMENTID' +]; /** * Env fields required for GCP IoT Core functionality. */ -const ENV_FIELDS_IOT = ["IOT_CLOUDREGION", "FIREBASE_PROJECTID", "IOT_REGISTRY", "IOT_JWT_EXPIRYMINS"]; +const ENV_FIELDS_IOT = [ + 'IOT_CLOUDREGION', + 'FIREBASE_PROJECTID', + 'IOT_REGISTRY', + 'IOT_JWT_EXPIRYMINS' +]; /** * Env fields required for FirebaseDeviceFlow Auth functionality. */ -const ENV_FIELDS_AUTH = ["GOOGLE_SCOPES", "GOOGLE_CLIENTID", "GOOGLE_CLIENTSECRET", "GITHUB_SCOPES", "GITHUB_CLIENTID", "GITHUB_CLIENTSECRET"]; +const ENV_FIELDS_AUTH = [ + 'GOOGLE_SCOPES', + 'GOOGLE_CLIENTID', + 'GOOGLE_CLIENTSECRET', + 'GITHUB_SCOPES', + 'GITHUB_CLIENTID', + 'GITHUB_CLIENTSECRET' +]; export type PeaPodEnv = { - serialport: string, + serialport: string; }; /** -* Load a `.env`-style file to `process.env`. -* -* Throws an error if the file does not exist. -*/ + * Load a `.env`-style file to `process.env`. + * + * Throws an error if the file does not exist. + */ function loadDotEnv(path: string = '.env'): void { - // Check for file - if (existsSync(path)) { - const config = dotenv.config({path}); - if (config.error) { - throw config.error - } - } else { - throw new Error('Environment variable file not found.'); - } + // Check for file + if (existsSync(path)) { + const config = dotenv.config({ path }); + if (config.error) { + throw config.error; + } + } else { + throw new Error('Environment variable file not found.'); + } } /** @@ -51,8 +71,8 @@ function loadDotEnv(path: string = '.env'): void { * @param fields List of fields to check * @returns List of missing fields */ -function getMissingEnvFields(fields: string[]) : string[] { - return fields.filter(field => !Object.keys(process.env).includes(field)); +function getMissingEnvFields(fields: string[]): string[] { + return fields.filter(field => !Object.keys(process.env).includes(field)); } /** @@ -60,75 +80,75 @@ function getMissingEnvFields(fields: string[]) : string[] { * @returns Firebase app config object */ export function loadFirebaseEnv(): FirebaseOptions { - const missingFields = getMissingEnvFields(ENV_FIELDS_FIREBASE); - if (missingFields.length) { - throw new EnvFieldError("Firebase", missingFields); - } else { - return { - apiKey: process.env.FIREBASE_APIKEY, - authDomain: process.env.FIREBASE_AUTHDOMAIN, - databaseURL: process.env.FIREBASE_DATABASEURL, - projectId: process.env.FIREBASE_PROJECTID, - storageBucket: process.env.FIREBASE_STORAGEBUCKET, - messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, - appId: process.env.FIREBASE_APPID, - measurementId: process.env.FIREBASE_MEASUREMENTID - }; - } -}; + const missingFields = getMissingEnvFields(ENV_FIELDS_FIREBASE); + if (missingFields.length) { + throw new EnvFieldError('Firebase', missingFields); + } else { + return { + apiKey: process.env.FIREBASE_APIKEY, + authDomain: process.env.FIREBASE_AUTHDOMAIN, + databaseURL: process.env.FIREBASE_DATABASEURL, + projectId: process.env.FIREBASE_PROJECTID, + storageBucket: process.env.FIREBASE_STORAGEBUCKET, + messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, + appId: process.env.FIREBASE_APPID, + measurementId: process.env.FIREBASE_MEASUREMENTID + }; + } +} /** * Load config for GCP IoT Core and check for all fields. * @returns GCP IoT Core config object */ export function loadIoTEnv(): IoTConfig { - const missingFields = getMissingEnvFields(ENV_FIELDS_IOT); - if (missingFields.length) { - throw new EnvFieldError("GCP IoT Core", missingFields); - } else { - return { - cloudregion: process.env.IOT_CLOUDREGION!, - projectid: process.env.FIREBASE_PROJECTID!, - registryid: process.env.IOT_REGISTRY!, - jwtexpiryminutes: Number(process.env.IOT_JWT_EXPIRYMINS ?? "1440"), - }; - } -}; + const missingFields = getMissingEnvFields(ENV_FIELDS_IOT); + if (missingFields.length) { + throw new EnvFieldError('GCP IoT Core', missingFields); + } else { + return { + cloudregion: process.env.IOT_CLOUDREGION!, + projectid: process.env.FIREBASE_PROJECTID!, + registryid: process.env.IOT_REGISTRY!, + jwtexpiryminutes: Number(process.env.IOT_JWT_EXPIRYMINS ?? '1440') + }; + } +} /** * Load config for the Firebase app and check for all fields. * @returns Firebase app config object */ export function loadAuthEnv(): DeviceFlowUIOptions { - const missingFields = getMissingEnvFields(ENV_FIELDS_AUTH); - if (missingFields.length) { - throw new EnvFieldError("Firebase Device Flow Auth", missingFields); - } else { - return { - Google : { - scopes : process.env.GOOGLE_SCOPES?.split(' '), - clientid : process.env.GOOGLE_CLIENTID, - clientsecret : process.env.GOOGLE_CLIENTSECRET - }, - GitHub : { - scopes : process.env.GITHUB_SCOPES?.split(' '), - clientid : process.env.GITHUB_CLIENTID, - clientsecret : process.env.GITHUB_CLIENTSECRET - } - }; - } -}; + const missingFields = getMissingEnvFields(ENV_FIELDS_AUTH); + if (missingFields.length) { + throw new EnvFieldError('Firebase Device Flow Auth', missingFields); + } else { + return { + Google: { + scopes: process.env.GOOGLE_SCOPES?.split(' '), + clientid: process.env.GOOGLE_CLIENTID, + clientsecret: process.env.GOOGLE_CLIENTSECRET + }, + GitHub: { + scopes: process.env.GITHUB_SCOPES?.split(' '), + clientid: process.env.GITHUB_CLIENTID, + clientsecret: process.env.GITHUB_CLIENTSECRET + } + }; + } +} export function loadPeaPodEnv(): PeaPodEnv { - const missingFields = getMissingEnvFields(ENV_FIELDS_PEAPOD); - if (missingFields.length) { - throw new EnvFieldError("PeaPod", missingFields); - } else { - return { - serialport: process.env.SERIALPORT! - }; - } -}; + const missingFields = getMissingEnvFields(ENV_FIELDS_PEAPOD); + if (missingFields.length) { + throw new EnvFieldError('PeaPod', missingFields); + } else { + return { + serialport: process.env.SERIALPORT! + }; + } +} // MAIN -loadDotEnv(); \ No newline at end of file +loadDotEnv(); diff --git a/software/src/errors.ts b/software/src/errors.ts index 3a78e441..74cde432 100644 --- a/software/src/errors.ts +++ b/software/src/errors.ts @@ -1,8 +1,8 @@ export class ControllerInstructionsError extends Error { - constructor(instructions: any) { - super("Failed to send instructions to Arduino: '"+JSON.stringify(instructions)+"'"); - } -}; + constructor(instructions: any) { + super("Failed to send instructions to Arduino: '" + JSON.stringify(instructions) + "'"); + } +} // UNUSED // export class SerialTimeoutError extends Error { @@ -12,7 +12,11 @@ export class ControllerInstructionsError extends Error { // }; export class EnvFieldError extends Error { - constructor(mode: string, missingFields: string[]) { - super(`.env file is missing the following fields necessary for ${ mode } functionality: ${ missingFields.join(", ") }`); - } -}; \ No newline at end of file + constructor(mode: string, missingFields: string[]) { + super( + `.env file is missing the following fields necessary for ${mode} functionality: ${missingFields.join( + ', ' + )}` + ); + } +} diff --git a/software/src/peapod.ts b/software/src/peapod.ts index 5e6a3dbc..3ef2b21a 100644 --- a/software/src/peapod.ts +++ b/software/src/peapod.ts @@ -5,8 +5,18 @@ import { initializeApp, getApps } from 'firebase/app'; import { getAuth } from 'firebase/auth'; import { loadPeaPodEnv, loadAuthEnv, loadFirebaseEnv, loadIoTEnv } from './env'; -import MicroController, { ControllerInstructions, Controller, CONTROLLER_REVISION } from './controller'; -import OnlinePublisher, { DataBatch, DataSet, Publisher, OfflinePublisher, PublishingMode } from './publisher'; +import MicroController, { + ControllerInstructions, + Controller, + CONTROLLER_REVISION +} from './controller'; +import OnlinePublisher, { + DataBatch, + DataSet, + Publisher, + OfflinePublisher, + PublishingMode +} from './publisher'; import { sleep } from './utils'; import { ControlSystem } from './controlsystem'; @@ -30,347 +40,397 @@ const IDLE_PERIOD = 5000; // DECLARATIONS export type EnvironmentSchedule = { - id: string, - name: string, - revision: number, - parameters: { - [key: string]: EnvironmentTargetPhase[] - } + id: string; + name: string; + revision: number; + parameters: { + [key: string]: EnvironmentTargetPhase[]; + }; }; type EnvironmentTargets = { - [key: string]: number + [key: string]: number; }; -type EnvironmentTargetPhase = { - type: 'piecewise', - end: number, - values: { - value: number, - timestamp: number, - }[] -} | { - type: 'periodic', - period: number, - end: number, - values: { - value: number, - duration: number, - }[] -}; +type EnvironmentTargetPhase = + | { + type: 'piecewise'; + end: number; + values: { + value: number; + timestamp: number; + }[]; + } + | { + type: 'periodic'; + period: number; + end: number; + values: { + value: number; + duration: number; + }[]; + }; /** * Main driver class. - * + * * LINGO LESSON: * > Derived from the Solution Overview/Progress Report - * - * VARIABLE = A **MEASURABLE** environment state variable, both feedback-driven and not. E.g. "air-temperature", "air-humidity", "co2-ppm", + * + * VARIABLE = A **MEASURABLE** environment state variable, both feedback-driven and not. E.g. "air-temperature", "air-humidity", "co2-ppm", * PARAMETER = A **CONTROLLABLE** environment state variable, both feedback-driven and not. E.g. "air-temperature", "lighting_750nm", "dosage_pump1" - * + * * INSTRUCTION * SCHEDULE = . See `../assets/schedule_schema.json` * TARGETS = Desired state for each parameter. PeaPod scheduler state variables. Modified * RUN */ export default class PeaPod { - // INTERFACES - controller: Controller; - publisher: Publisher; - - // SCHEDULER FIELDS - - /** - * Currently loaded schedule. - */ - private schedule?: EnvironmentSchedule; - - /** - * Start time of the last run. - */ - private startTime: number = 0; - - /** - * Current environment schedule targets. - */ - private targets: EnvironmentTargets = { }; - - /** - * Latest controller data for each variable - */ - private data: DataSet = { }; - - // CONTROLLER FIELDS - - private controlSystems?: {[key: string]: ControlSystem}; - - /** - * Latest controller instruction set. - */ - private instructions: ControllerInstructions = { }; - - // PUBLISHER FIELDS - - /** - * Batch of data to be published. - */ - private batch: DataBatch = { }; - - /** - * JS interval to trigger batch publishing. - */ - private batchInterval?: NodeJS.Timer; - - constructor(pm: PublishingMode) { - const ENV_PEAPOD = loadPeaPodEnv(); - - // CREATE CONTROLLER - this.controller = new MicroController(ENV_PEAPOD.serialport); - - // CREATE PUBLISHER - Spinner.info(`Running ${ chalk.green('PeaPod') } in ${ pm } mode.`); - switch (pm) { - - case PublishingMode.ONLINE: - initializeApp(loadFirebaseEnv()); - if (getApps().length === 0) { - throw new Error("Failed to initialize Firebase app! Check .env fields."); - } else { - Spinner.succeed(`Initialized Firebase app!`); - } - this.publisher = new OnlinePublisher(loadIoTEnv(), loadAuthEnv()); - break; - - case PublishingMode.OFFLINE: - default: - this.publisher = new OfflinePublisher(); - break; - } - } - - async idle(): Promise { - await this.controller.start(msg => { - switch (msg.type) { - case "data": - data[msg.data.label] = msg.data.value; - Screen.setData(data); - Screen.render(); - break; - case "revision": - this.controller.write(instructions); - break; - default: - // TODO: Console box? - Spinner.log(`[${ chalk.blueBright('CONTROLLER') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`); - } - }); - - // Angle to "wheel" the lights - let angle = 0; - - const idleInterval = setInterval(() => { - const idleInstructions = { - "led_blue": Math.sin(angle ) / 2 + 0.5, - "led_cool": Math.sin(angle + 2 * Math.PI / 5) / 2 + 0.5, - "led_warm": Math.sin(angle + 4 * Math.PI / 5) / 2 + 0.5, - "led_red": Math.sin(angle + 6 * Math.PI / 5) / 2 + 0.5, - "led_far": Math.sin(angle + 8 * Math.PI / 5) / 2 + 0.5, - }; - this.controller.write(idleInstructions); - - angle += (2 * Math.PI) / (IDLE_PERIOD / REFRESH_INTERVAL); - angle -= (angle > 2 * Math.PI) ? (2 * Math.PI) : 0; - }, REFRESH_INTERVAL); - return idleInterval; - } - - async start(): Promise { - // INITIALIZE CONTROLLER - await this.controller.start(msg => { - // Handle different message types - // TODO: Handle error, etc. types - switch (msg.type) { - case "data": - // Initialize batch array - if (batch[msg.data.label] === undefined) batch[msg.data.label] = []; - // Accumulate data into batches - batch[msg.data.label].push({ - timestamp: Date.now(), - value: msg.data.value, - }); - // Set actuator values - if (controlSystems[msg.data.label] !== undefined) { - controlSystems[msg.data.label].setValue(msg.data.value); - } - break; - case "revision": - this.controller.write(instructions); - break; - default: - Spinner.log(`[${ chalk.blueBright('CONTROLLER') } | ${ msg.type.toUpperCase() }] - ${ JSON.stringify(msg.data) }`); - } - }); - - // Wait for all POST messages - await sleep(1000); - - // INITIALIZE PUBLISHER - let { projectid, projectname, run } = await this.publisher.start(config => { - Spinner.log(`[${ chalk.yellow('PUBLISHER') } | SCHEDULE] - ${ config }`); - switch (config.type) { - case 'schedule': - Spinner.log(`[${ chalk.yellow('PUBLISHER') } | SCHEDULE] - ${ JSON.stringify(config.data.name) }`); - - if (config.data.revision != CONTROLLER_REVISION) { - Spinner.fail(`Failed to load schedule '${config.data.name}' (${config.data.id}), software version mismatch (Expected ${CONTROLLER_REVISION}, got ${config.data.revision}).`); - break; - } - - Spinner.succeed(`Successfully loaded schedule '${config.data.name}' (${config.data.id}).`); - - // Save new schedule - this.schedule = config.data; - - break; - default: - Spinner.log(`[${ chalk.yellow('PUBLISHER') } | CONFIG] - ${ JSON.stringify(config.data) }`); - } - }, command => { - switch (command.type) { - case 'instructions': - Spinner.log(`[${ chalk.yellow('PUBLISHER') } | INSTRUCTIONS] - ${ JSON.stringify(command.data) }`); - this.controller.write(command.data); - break; - default: - Spinner.log(`[${ chalk.yellow('PUBLISHER') } | COMMAND] - ${ JSON.stringify(command) }`); - } - }); - - // START - Spinner.log(`${ chalk.green('PeaPod') } start - Project ${ chalk.bold(projectname ?? projectid) }, Run ${ chalk.bold(run) }`); - - this.startTime = Date.now(); - - // Start schedule phase 0 - let phasePromises = []; - for (const parameter of Object.keys(this.schedule.parameters)) { - phasePromises.push(this.startPhase(schedule, parameter)); - } - - // Reset - batch = { }; - - batchInterval = setInterval(() => { - // Publish entire batch - try { - this.publisher.publish({ - type: 'data', - metadata: { - owner: (getApps().length ? (getAuth().currentUser?.uid ?? 'user') : 'user'), - project: projectid, - run - }, - data: batch - }); - } catch { - Spinner.fail('Batch publish failed, will retry...'); - return; - } - - Spinner.log(`[${ chalk.magenta('PUBLISH') }] - Batch of ${ Object.values(batch).reduce((sum, entry) => (sum+entry.length), 0) } datapoints published.`); - - // Reset batch to empty - batch = { }; - }, BATCH_PUBLISH_INTERVAL*1000); - - // Refresh control system values and targets, update actuator instructions, send new instructions - const refreshInterval = setInterval(()=>{ - let instruction: ControllerInstructions = {}; - for (const parameter of Object.keys(targets)) { - const cs = controlSystems[parameter]; - cs.setTarget(targets[parameter]); - instruction[cs.actuator] = cs.refresh(); - } - this.controller.write(instruction); - }, REFRESH_INTERVAL); - - // When all phases for all parameters are complete, clear refresh interval, send "off" instruction, resolve this promise - return Promise.all(phasePromises).then(()=>{ - clearInterval(refreshInterval); - let off: ControllerInstructions = {}; - for (const parameter of Object.keys(targets)) { - off[controlSystems[parameter].actuator] = 0; - } - this.controller.write(off); - }) - } - - /** - * Start the schedule for a single parameter. Runs recursively until all phases are executed. Sets targets. - * @param schedule Environment schedule - * @param parameter Parameter to start - * @param n Phase to start at - */ - private startPhase(schedule: EnvironmentSchedule, parameter: string, n: number = 0): Promise { - return new Promise(res => { - // Break once we've reached the end - if(n >= schedule.parameters[parameter].length) { - res(); - return; - } - - const phase = schedule.parameters[parameter][n]; - - let timeouts: NodeJS.Timeout[] = []; - let intervals: NodeJS.Timer[] = []; - - // Extract all targets - switch (phase.type) { - case 'piecewise': - // Trigger each piecewise expression at its timestamp - for (const value of phase.values) { - let t = setTimeout(() => { - // Assign value - targets[parameter] = value.value; - // Remove this timeout if/when it self-clears - timeouts.splice(timeouts.findIndex(v => v == t), 1); - }, value.timestamp - (Date.now() - this.startTime)); - timeouts.push(t); - } - break; - case 'periodic': - // Retrigger each value at a fixed period, with duration offset - let durationSum = 0; - phase.values.forEach((value, i) => { - // Await duration offset - let t = setTimeout(() => { - // Start the interval - intervals.push(setInterval(() => { - // Assign value - targets[parameter] = value.value; - }, phase.period)); - // Remove this timeout if/when it self-clears - timeouts.splice(timeouts.findIndex(v => v == t), 1); - // First expression? Trigger now. - // Else: Timeout for the sum of all previous expressions' durations, minus the elapsed time in this phase - }, i > 0 ? durationSum - (Date.now() - (n > 0 ? schedule.parameters[parameter][n-1].end : this.startTime)) : 0); - durationSum += value.duration; - timeouts.push(t); - }); - break; - } - - // End this phase once Date.now() - startTime > end, and start the next phase, waiting for its completion - setTimeout(() => { - // Clear all outstanding timeouts and intervals - for (const interval of intervals) { - clearInterval(interval); - } - for (const timeout of timeouts) { - clearTimeout(timeout); - } - this.startPhase(schedule, parameter, n+1).then(() => { res(); }); - }, phase.end - (Date.now() - this.startTime)); - }); - } -} \ No newline at end of file + // INTERFACES + controller: Controller; + publisher: Publisher; + + // SCHEDULER FIELDS + + /** + * Currently loaded schedule. + */ + private schedule?: EnvironmentSchedule; + + /** + * Start time of the last run. + */ + private startTime: number = 0; + + /** + * Current environment schedule targets. + */ + private targets: EnvironmentTargets = {}; + + /** + * Latest controller data for each variable + */ + private data: DataSet = {}; + + // CONTROLLER FIELDS + + private controlSystems?: { [key: string]: ControlSystem }; + + /** + * Latest controller instruction set. + */ + private instructions: ControllerInstructions = {}; + + // PUBLISHER FIELDS + + /** + * Batch of data to be published. + */ + private batch: DataBatch = {}; + + /** + * JS interval to trigger batch publishing. + */ + private batchInterval?: NodeJS.Timer; + + constructor(pm: PublishingMode) { + const ENV_PEAPOD = loadPeaPodEnv(); + + // CREATE CONTROLLER + this.controller = new MicroController(ENV_PEAPOD.serialport); + + // CREATE PUBLISHER + Spinner.info(`Running ${chalk.green('PeaPod')} in ${pm} mode.`); + switch (pm) { + case PublishingMode.ONLINE: + initializeApp(loadFirebaseEnv()); + if (getApps().length === 0) { + throw new Error('Failed to initialize Firebase app! Check .env fields.'); + } else { + Spinner.succeed(`Initialized Firebase app!`); + } + this.publisher = new OnlinePublisher(loadIoTEnv(), loadAuthEnv()); + break; + + case PublishingMode.OFFLINE: + default: + this.publisher = new OfflinePublisher(); + break; + } + } + + async idle(): Promise { + await this.controller.start(msg => { + switch (msg.type) { + case 'data': + this.data[msg.data.label] = msg.data.value; + Screen.setData(this.data); + Screen.render(); + break; + case 'revision': + this.controller.write(instructions); + break; + default: + // TODO: Console box? + Spinner.log( + `[${chalk.blueBright('CONTROLLER')} | ${msg.type.toUpperCase()}] - ${JSON.stringify( + msg.data + )}` + ); + } + }); + + // Angle to "wheel" the lights + let angle = 0; + + const idleInterval = setInterval(() => { + const idleInstructions = { + led_blue: Math.sin(angle) / 2 + 0.5, + led_cool: Math.sin(angle + (2 * Math.PI) / 5) / 2 + 0.5, + led_warm: Math.sin(angle + (4 * Math.PI) / 5) / 2 + 0.5, + led_red: Math.sin(angle + (6 * Math.PI) / 5) / 2 + 0.5, + led_far: Math.sin(angle + (8 * Math.PI) / 5) / 2 + 0.5 + }; + this.controller.write(idleInstructions); + + angle += (2 * Math.PI) / (IDLE_PERIOD / REFRESH_INTERVAL); + angle -= angle > 2 * Math.PI ? 2 * Math.PI : 0; + }, REFRESH_INTERVAL); + return idleInterval; + } + + async start(): Promise { + // INITIALIZE CONTROLLER + await this.controller.start(msg => { + // Handle different message types + // TODO: Handle error, etc. types + switch (msg.type) { + case 'data': + // Initialize batch array + if (batch[msg.data.label] === undefined) batch[msg.data.label] = []; + // Accumulate data into batches + batch[msg.data.label].push({ + timestamp: Date.now(), + value: msg.data.value + }); + // Set actuator values + if (controlSystems[msg.data.label] !== undefined) { + controlSystems[msg.data.label].setValue(msg.data.value); + } + break; + case 'revision': + this.controller.write(instructions); + break; + default: + Spinner.log( + `[${chalk.blueBright('CONTROLLER')} | ${msg.type.toUpperCase()}] - ${JSON.stringify( + msg.data + )}` + ); + } + }); + + // Wait for all POST messages + await sleep(1000); + + // INITIALIZE PUBLISHER + let { projectid, projectname, run } = await this.publisher.start( + config => { + Spinner.log(`[${chalk.yellow('PUBLISHER')} | SCHEDULE] - ${config}`); + switch (config.type) { + case 'schedule': + Spinner.log( + `[${chalk.yellow('PUBLISHER')} | SCHEDULE] - ${JSON.stringify(config.data.name)}` + ); + + if (config.data.revision != CONTROLLER_REVISION) { + Spinner.fail( + `Failed to load schedule '${config.data.name}' (${config.data.id}), software version mismatch (Expected ${CONTROLLER_REVISION}, got ${config.data.revision}).` + ); + break; + } + + Spinner.succeed( + `Successfully loaded schedule '${config.data.name}' (${config.data.id}).` + ); + + // Save new schedule + this.schedule = config.data; + + break; + default: + Spinner.log(`[${chalk.yellow('PUBLISHER')} | CONFIG] - ${JSON.stringify(config.data)}`); + } + }, + command => { + switch (command.type) { + case 'instructions': + Spinner.log( + `[${chalk.yellow('PUBLISHER')} | INSTRUCTIONS] - ${JSON.stringify(command.data)}` + ); + this.controller.write(command.data); + break; + default: + Spinner.log(`[${chalk.yellow('PUBLISHER')} | COMMAND] - ${JSON.stringify(command)}`); + } + } + ); + + // START + Spinner.log( + `${chalk.green('PeaPod')} start - Project ${chalk.bold( + projectname ?? projectid + )}, Run ${chalk.bold(run)}` + ); + + this.startTime = Date.now(); + + // Start schedule phase 0 + let phasePromises = []; + for (const parameter of Object.keys(this.schedule.parameters)) { + phasePromises.push(this.startPhase(schedule, parameter)); + } + + // Reset + batch = {}; + + batchInterval = setInterval(() => { + // Publish entire batch + try { + this.publisher.publish({ + type: 'data', + metadata: { + owner: getApps().length ? getAuth().currentUser?.uid ?? 'user' : 'user', + project: projectid, + run + }, + data: batch + }); + } catch { + Spinner.fail('Batch publish failed, will retry...'); + return; + } + + Spinner.log( + `[${chalk.magenta('PUBLISH')}] - Batch of ${Object.values(batch).reduce( + (sum, entry) => sum + entry.length, + 0 + )} datapoints published.` + ); + + // Reset batch to empty + batch = {}; + }, BATCH_PUBLISH_INTERVAL * 1000); + + // Refresh control system values and targets, update actuator instructions, send new instructions + const refreshInterval = setInterval(() => { + let instruction: ControllerInstructions = {}; + for (const parameter of Object.keys(targets)) { + const cs = controlSystems[parameter]; + cs.setTarget(targets[parameter]); + instruction[cs.actuator] = cs.refresh(); + } + this.controller.write(instruction); + }, REFRESH_INTERVAL); + + // When all phases for all parameters are complete, clear refresh interval, send "off" instruction, resolve this promise + return Promise.all(phasePromises).then(() => { + clearInterval(refreshInterval); + let off: ControllerInstructions = {}; + for (const parameter of Object.keys(targets)) { + off[controlSystems[parameter].actuator] = 0; + } + this.controller.write(off); + }); + } + + /** + * Start the schedule for a single parameter. Runs recursively until all phases are executed. Sets targets. + * @param schedule Environment schedule + * @param parameter Parameter to start + * @param n Phase to start at + */ + private startPhase( + schedule: EnvironmentSchedule, + parameter: string, + n: number = 0 + ): Promise { + return new Promise(res => { + // Break once we've reached the end + if (n >= schedule.parameters[parameter].length) { + res(); + return; + } + + const phase = schedule.parameters[parameter][n]; + + let timeouts: NodeJS.Timeout[] = []; + let intervals: NodeJS.Timer[] = []; + + // Extract all targets + switch (phase.type) { + case 'piecewise': + // Trigger each piecewise expression at its timestamp + for (const value of phase.values) { + let t = setTimeout(() => { + // Assign value + targets[parameter] = value.value; + // Remove this timeout if/when it self-clears + timeouts.splice( + timeouts.findIndex(v => v == t), + 1 + ); + }, value.timestamp - (Date.now() - this.startTime)); + timeouts.push(t); + } + break; + case 'periodic': + // Retrigger each value at a fixed period, with duration offset + let durationSum = 0; + phase.values.forEach((value, i) => { + // Await duration offset + let t = setTimeout( + () => { + // Start the interval + intervals.push( + setInterval(() => { + // Assign value + targets[parameter] = value.value; + }, phase.period) + ); + // Remove this timeout if/when it self-clears + timeouts.splice( + timeouts.findIndex(v => v == t), + 1 + ); + // First expression? Trigger now. + // Else: Timeout for the sum of all previous expressions' durations, minus the elapsed time in this phase + }, + i > 0 + ? durationSum - + (Date.now() - + (n > 0 ? schedule.parameters[parameter][n - 1].end : this.startTime)) + : 0 + ); + durationSum += value.duration; + timeouts.push(t); + }); + break; + } + + // End this phase once Date.now() - startTime > end, and start the next phase, waiting for its completion + setTimeout(() => { + // Clear all outstanding timeouts and intervals + for (const interval of intervals) { + clearInterval(interval); + } + for (const timeout of timeouts) { + clearTimeout(timeout); + } + this.startPhase(schedule, parameter, n + 1).then(() => { + res(); + }); + }, phase.end - (Date.now() - this.startTime)); + }); + } +} diff --git a/software/src/publisher.ts b/software/src/publisher.ts index 49c2091c..7076146c 100644 --- a/software/src/publisher.ts +++ b/software/src/publisher.ts @@ -1,5 +1,5 @@ import * as fs from 'fs'; -import {v4 as uuid} from 'uuid'; +import { v4 as uuid } from 'uuid'; import * as jwt from 'jsonwebtoken'; import * as mqtt from 'mqtt'; import * as inquirer from 'inquirer'; @@ -8,7 +8,16 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { getApp } from 'firebase/app'; import { getFunctions, httpsCallable } from 'firebase/functions'; -import { getFirestore, doc, setDoc, collection, getDocs, query, where, DocumentReference } from 'firebase/firestore'; +import { + getFirestore, + doc, + setDoc, + collection, + getDocs, + query, + where, + DocumentReference +} from 'firebase/firestore'; import { getAuth } from 'firebase/auth'; import { DeviceFlowUI, DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; @@ -23,349 +32,390 @@ import { EnvironmentSchedule } from './peapod'; * A single datapoint. */ type DataPoint = { - timestamp: number, - value: number + timestamp: number; + value: number; }; /** * Batch of datapoints to be published, organized by dataset label */ export type DataBatch = { - [key: string]: DataPoint[], + [key: string]: DataPoint[]; }; /** * Set of datapoints for display */ - export type DataSet = { - [key: string]: number, +export type DataSet = { + [key: string]: number; }; /** * Message TO the publisher. */ -export type PubSubMessage = { - type: 'info' | 'debug' | 'error', - data: any, -} | { - type: 'data', - metadata: { - owner: string, - project: string, - run: string, - } - data: DataBatch, -}; +export type PubSubMessage = + | { + type: 'info' | 'debug' | 'error'; + data: any; + } + | { + type: 'data'; + metadata: { + owner: string; + project: string; + run: string; + }; + data: DataBatch; + }; /** * Message FROM the publisher (command). */ export type PubSubCommand = { - type: 'instructions', - data: ControllerInstructions, + type: 'instructions'; + data: ControllerInstructions; }; /** * Message FROM the publisher (config). */ export type PubSubConfig = { - type: 'schedule', - data: EnvironmentSchedule, + type: 'schedule'; + data: EnvironmentSchedule; }; /** * Types of publishers. */ export enum PublishingMode { - ONLINE = "Online", - OFFLINE = "Offline", -}; + ONLINE = 'Online', + OFFLINE = 'Offline' +} /** * Response from the device registration cloud function. */ type RegisterResponse = { - id: string, - name: string, - privateKey: string, -} + id: string; + name: string; + privateKey: string; +}; /** * GCP IoT Core configuration parameters. */ export type IoTConfig = { - deviceid?: string, - projectid: string, - cloudregion: string, - registryid: string, - jwtexpiryminutes: number, -} + deviceid?: string; + projectid: string; + cloudregion: string; + registryid: string; + jwtexpiryminutes: number; +}; /** * Base type for any publisher. */ export type Publisher = { - start(onConfig: (message: PubSubConfig)=>void, onCommand: (message: PubSubCommand)=>void) : Promise<{projectid: string, projectname?: string, run: string}>, - stop(): void, - publish(msg: PubSubMessage): void, -} + start( + onConfig: (message: PubSubConfig) => void, + onCommand: (message: PubSubCommand) => void + ): Promise<{ projectid: string; projectname?: string; run: string }>; + stop(): void; + publish(msg: PubSubMessage): void; +}; // MAIN CLASS -/** +/** * Interface for publishing data to and receiving instructions from Google Cloud/Firebase via IoT Core PubSub. */ export default class PubSubPublisher implements Publisher { + private tokenRefreshInterval?: NodeJS.Timer; + private mqttclient?: mqtt.MqttClient; + private deviceid!: string; + + constructor(readonly iotConfig: IoTConfig, readonly authConfig: DeviceFlowUIOptions) {} + + publish(msg: PubSubMessage): void { + if (!this.mqttclient || !this.mqttclient.connected) { + throw new Error('MQTT client not connected!'); + } + + // Build topic path + const topic = + `/devices/${this.deviceid}/` + + (msg.type === 'data' ? 'events/data' : msg.type === 'info' ? 'events' : 'state'); + + // Strip type from published object + this.mqttclient.publish(topic, JSON.stringify({ ...msg, type: undefined }), { qos: 1 }); + } + + async start( + onConfig: (message: PubSubConfig) => void, + onCommand: (message: PubSubCommand) => void + ) { + // AUTHENTICATION - FIREBASE DEVICE FLOW + const auth = new DeviceFlowUI(getApp(), this.authConfig); + const user = await auth.signIn(); + if (user.displayName) { + Spinner.info(`Welcome, ${chalk.bold(user.displayName)}!`); + } else { + Spinner.info('Welcome!'); + } + + // GET DEVICE INFO OR REGISTER + const { privatekey, deviceid } = await this.getDeviceInfoOrRegister(user.uid); + this.deviceid = deviceid; + + // CHOOSE PROJECT, RUN, PROGRAM + // TODO: choose program + const [project, projectname] = await this.selectProject(); + const run = await this.createRun(project); + + // SETUP MQTT + Spinner.start('Fetching Google root CA certificates...'); + const servercert = await fetchServerCert(); + Spinner.succeed('Certificates fetched!'); + + Spinner.start('Connecting to MQTT broker...'); + await this.connect(servercert, this.refreshToken(privatekey)); + Spinner.succeed('Device connected!'); - private tokenRefreshInterval?: NodeJS.Timer; - private mqttclient?: mqtt.MqttClient; - private deviceid!: string; - - constructor(readonly iotConfig: IoTConfig, readonly authConfig: DeviceFlowUIOptions){ } - - publish(msg: PubSubMessage): void { - if (!this.mqttclient || !this.mqttclient.connected) { - throw new Error('MQTT client not connected!'); - } - - // Build topic path - const topic = `/devices/${ this.deviceid }/` + (msg.type === 'data' ? 'events/data' : msg.type === 'info' ? 'events' : 'state'); - - // Strip type from published object - this.mqttclient.publish(topic, JSON.stringify({ ...msg, type: undefined }), { qos: 1 }); - } - - async start(onConfig: (message: PubSubConfig) => void, onCommand: (message: PubSubCommand) => void) { - - // AUTHENTICATION - FIREBASE DEVICE FLOW - const auth = new DeviceFlowUI(getApp(), this.authConfig); - const user = await auth.signIn(); - if (user.displayName) { - Spinner.info(`Welcome, ${ chalk.bold(user.displayName) }!`); - } else { - Spinner.info('Welcome!'); - } - - // GET DEVICE INFO OR REGISTER - const {privatekey, deviceid} = await this.getDeviceInfoOrRegister(user.uid); - this.deviceid = deviceid; - - // CHOOSE PROJECT, RUN, PROGRAM - // TODO: choose program - const [project, projectname] = await this.selectProject(); - const run = await this.createRun(project); - - // SETUP MQTT - Spinner.start('Fetching Google root CA certificates...'); - const servercert = await fetchServerCert(); - Spinner.succeed('Certificates fetched!'); - - Spinner.start('Connecting to MQTT broker...'); - await this.connect(servercert, this.refreshToken(privatekey)); - Spinner.succeed('Device connected!'); - - // Token Refresh - this.tokenRefreshInterval = setInterval(async () => { - Spinner.start('Refreshing token...'); - await this.connect(servercert, this.refreshToken(privatekey)); - Spinner.succeed('Token refreshed. Reconnected.'); - }, this.iotConfig.jwtexpiryminutes*60*1000); - - // Message listeners - this.mqttclient?.subscribe(`/devices/${ this.deviceid }/config`, { qos: 1 }); - this.mqttclient?.subscribe(`/devices/${ this.deviceid }/commands/#`, { qos: 0 }); - - this.mqttclient?.on('error', err => { throw err }); - this.mqttclient?.on('message', (topic, message) => { - if (topic === `/devices/${this.deviceid}/config`) { - try { - const config: PubSubConfig = JSON.parse(message.toString()); - onConfig(config); - } catch (e) { - if (e instanceof SyntaxError) { - // TODO: handle invalid config - } - } - } else if (topic.startsWith(`/devices/${this.deviceid}/commands`)) { - try { - const command: PubSubCommand = JSON.parse(message.toString()); - onCommand(command); - } catch (e) { - if (e instanceof SyntaxError) { - // TODO: handle invalid command - } - } - } - }); - - return { projectid: project.id, projectname, run: run.id }; - } - - /** - * Select a project owned by the current user - * @returns A tuple containing both the document reference and the project name (if any) - */ - private async selectProject(): Promise<[DocumentReference, string]> { - const myProjects = query(collection(getFirestore(), 'projects'), where('owners', 'array-contains', getAuth().currentUser?.uid)); - const projects = (await getDocs(myProjects)).docs; - if (projects.length < 1) { - throw new Error("No projects found! Create one first."); - } - const ref = (await inquirer.prompt<{ref: [DocumentReference, string]}>([ - { - type: 'list', - name: 'ref', - message: 'Select a project:', - choices: projects.map(project => ({ - name: project.get('name')+' - '+project.id, - value: [project.ref, project.get('name')] - })) - } - ])).ref; - return ref; - } - - /** - * Publish a new project. - * @param project Document reference to the project - * @returns Documetn reference to the new run - */ - private async createRun(project: DocumentReference): Promise { - const runid = project.id+'-'+uuid(); - const rundoc = doc(getFirestore(), project.path+'/runs/'+runid); - setDoc(rundoc, { - owner: getAuth().currentUser?.uid, - deviceId: this.deviceid, - }); - return rundoc; - } - - /** - * Look for the private key and device info in local files. If not found, register the device and store the new key and info. - * @param user The UID of the user to whom this device is to be registered. - * @returns The private key and device ID. - */ - private async getDeviceInfoOrRegister(user: string): Promise<{privatekey: string, deviceid: string}> { - if (fs.existsSync('./rsa_private.pem') && fs.existsSync('./deviceInfo.json')) { - Spinner.succeed('Private key and device info found!'); - const privatekey = fs.readFileSync('./rsa_private.pem').toString(); - - let deviceinfo: {id: string, owner: string} = JSON.parse(fs.readFileSync('./deviceInfo.json').toString()); - - if (deviceinfo['owner'] != user) { - throw new Error('This PeaPod is not owned by this user!'); - } - return { privatekey, deviceid: deviceinfo.id }; - } else { - Spinner.info('Private key and/or device info not found!'); - - Spinner.start('Registering device...'); - const registerDevice = httpsCallable(getFunctions(), 'registerDevice'); - let result = (await registerDevice()).data; - Spinner.succeed('Device '+(result.id) + ' registered!'); - - fs.writeFileSync('./rsa_private.pem', result.privateKey); - fs.writeFileSync('./deviceInfo.json', JSON.stringify({name: result.name, id: result.id, owner: getAuth().currentUser?.uid}, null, 2)); - - return { privatekey: result.privateKey, deviceid: result.id }; - } - } - - /** - * Sign a new JWT. - * @returns JSON Web Token string payload. - */ - private refreshToken(privatekey: string) : string { - const now = Date.now() / 1000; - const token = { - iat: now, - exp: now + this.iotConfig.jwtexpiryminutes * 60, - aud: this.iotConfig.projectid, - }; - return jwt.sign(token, privatekey, {algorithm: 'RS256'}); - } - - /** - * Connect to the MQTT broker. - * @param servercert Root CA certificate. - * @param password JWT - */ - private async connect(servercert: string, password: string): Promise { - // Disconnect existing client - this.disconnect(); - - let client = mqtt.connect({ - host: 'mqtt.googleapis.com', - port: 8883, - clientId: `projects/${ this.iotConfig.projectid }/locations/${ this.iotConfig.cloudregion }/registries/${ this.iotConfig.registryid }/devices/${ this.deviceid }`, - username: 'unused', - password, - protocol: 'mqtts', - secureProtocol: 'TLSv1_2_method', - ca: [servercert], - }); - - return new Promise(res => { - client.on('connect', packet => { - if (!packet) { - throw new Error('Could not connect to MQTT broker!'); - } - this.mqttclient = client; - res(); - }); - }); - } - - /** - * If the MQTT client is connected, disconnect it. - */ - private async disconnect(): Promise{ - if (this.mqttclient && this.mqttclient.connected) { - await new Promise(res => { - this.mqttclient?.end(true, undefined, err => { - if (err) { - throw err; - } else { - res(); - } - }); - }); - } - } - - stop() { - if(this.tokenRefreshInterval) clearInterval(this.tokenRefreshInterval); - if(this.mqttclient) this.disconnect(); - } + // Token Refresh + this.tokenRefreshInterval = setInterval(async () => { + Spinner.start('Refreshing token...'); + await this.connect(servercert, this.refreshToken(privatekey)); + Spinner.succeed('Token refreshed. Reconnected.'); + }, this.iotConfig.jwtexpiryminutes * 60 * 1000); + + // Message listeners + this.mqttclient?.subscribe(`/devices/${this.deviceid}/config`, { qos: 1 }); + this.mqttclient?.subscribe(`/devices/${this.deviceid}/commands/#`, { qos: 0 }); + + this.mqttclient?.on('error', err => { + throw err; + }); + this.mqttclient?.on('message', (topic, message) => { + if (topic === `/devices/${this.deviceid}/config`) { + try { + const config: PubSubConfig = JSON.parse(message.toString()); + onConfig(config); + } catch (e) { + if (e instanceof SyntaxError) { + // TODO: handle invalid config + } + } + } else if (topic.startsWith(`/devices/${this.deviceid}/commands`)) { + try { + const command: PubSubCommand = JSON.parse(message.toString()); + onCommand(command); + } catch (e) { + if (e instanceof SyntaxError) { + // TODO: handle invalid command + } + } + } + }); + + return { projectid: project.id, projectname, run: run.id }; + } + + /** + * Select a project owned by the current user + * @returns A tuple containing both the document reference and the project name (if any) + */ + private async selectProject(): Promise<[DocumentReference, string]> { + const myProjects = query( + collection(getFirestore(), 'projects'), + where('owners', 'array-contains', getAuth().currentUser?.uid) + ); + const projects = (await getDocs(myProjects)).docs; + if (projects.length < 1) { + throw new Error('No projects found! Create one first.'); + } + const ref = ( + await inquirer.prompt<{ ref: [DocumentReference, string] }>([ + { + type: 'list', + name: 'ref', + message: 'Select a project:', + choices: projects.map(project => ({ + name: project.get('name') + ' - ' + project.id, + value: [project.ref, project.get('name')] + })) + } + ]) + ).ref; + return ref; + } + + /** + * Publish a new project. + * @param project Document reference to the project + * @returns Documetn reference to the new run + */ + private async createRun(project: DocumentReference): Promise { + const runid = project.id + '-' + uuid(); + const rundoc = doc(getFirestore(), project.path + '/runs/' + runid); + setDoc(rundoc, { + owner: getAuth().currentUser?.uid, + deviceId: this.deviceid + }); + return rundoc; + } + + /** + * Look for the private key and device info in local files. If not found, register the device and store the new key and info. + * @param user The UID of the user to whom this device is to be registered. + * @returns The private key and device ID. + */ + private async getDeviceInfoOrRegister( + user: string + ): Promise<{ privatekey: string; deviceid: string }> { + if (fs.existsSync('./rsa_private.pem') && fs.existsSync('./deviceInfo.json')) { + Spinner.succeed('Private key and device info found!'); + const privatekey = fs.readFileSync('./rsa_private.pem').toString(); + + let deviceinfo: { id: string; owner: string } = JSON.parse( + fs.readFileSync('./deviceInfo.json').toString() + ); + + if (deviceinfo['owner'] != user) { + throw new Error('This PeaPod is not owned by this user!'); + } + return { privatekey, deviceid: deviceinfo.id }; + } else { + Spinner.info('Private key and/or device info not found!'); + + Spinner.start('Registering device...'); + const registerDevice = httpsCallable( + getFunctions(), + 'registerDevice' + ); + let result = (await registerDevice()).data; + Spinner.succeed('Device ' + result.id + ' registered!'); + + fs.writeFileSync('./rsa_private.pem', result.privateKey); + fs.writeFileSync( + './deviceInfo.json', + JSON.stringify( + { name: result.name, id: result.id, owner: getAuth().currentUser?.uid }, + null, + 2 + ) + ); + + return { privatekey: result.privateKey, deviceid: result.id }; + } + } + + /** + * Sign a new JWT. + * @returns JSON Web Token string payload. + */ + private refreshToken(privatekey: string): string { + const now = Date.now() / 1000; + const token = { + iat: now, + exp: now + this.iotConfig.jwtexpiryminutes * 60, + aud: this.iotConfig.projectid + }; + return jwt.sign(token, privatekey, { algorithm: 'RS256' }); + } + + /** + * Connect to the MQTT broker. + * @param servercert Root CA certificate. + * @param password JWT + */ + private async connect(servercert: string, password: string): Promise { + // Disconnect existing client + this.disconnect(); + + let client = mqtt.connect({ + host: 'mqtt.googleapis.com', + port: 8883, + clientId: `projects/${this.iotConfig.projectid}/locations/${this.iotConfig.cloudregion}/registries/${this.iotConfig.registryid}/devices/${this.deviceid}`, + username: 'unused', + password, + protocol: 'mqtts', + secureProtocol: 'TLSv1_2_method', + ca: [servercert] + }); + + return new Promise(res => { + client.on('connect', packet => { + if (!packet) { + throw new Error('Could not connect to MQTT broker!'); + } + this.mqttclient = client; + res(); + }); + }); + } + + /** + * If the MQTT client is connected, disconnect it. + */ + private async disconnect(): Promise { + if (this.mqttclient && this.mqttclient.connected) { + await new Promise(res => { + this.mqttclient?.end(true, undefined, err => { + if (err) { + throw err; + } else { + res(); + } + }); + }); + } + } + + stop() { + if (this.tokenRefreshInterval) clearInterval(this.tokenRefreshInterval); + if (this.mqttclient) this.disconnect(); + } } /** * Publishes data batches to local JSON files. */ export class OfflinePublisher implements Publisher { - async start(onConfig: (message: PubSubConfig) => void, onCommand: (message: PubSubCommand) => void) { - let config = { projectid: 'testproject', run: 'testrun-'+uuid() }; - Spinner.info(`Logging data to ${ chalk.bold('projects/'+config.projectid+'/runs/'+config.run+'/') }`); - return config; - } - stop() { }; - publish(msg: PubSubMessage): void { - switch(msg.type) { - case 'data': - for (const label of Object.keys(msg.data)) { - for (const datum of msg.data[label]) { - console.log(`[${ chalk.magenta(msg.type.toUpperCase()) }] - [${ (new Date(datum.timestamp)).toLocaleTimeString() }] - ${ label }: ${ datum.value }`); - } - const dir = `./projects/${ msg.metadata.project }/runs/${ msg.metadata.run }/${ label }/`; - if (!existsSync(dir)) { - mkdirSync(dir, { recursive: true }); - } - writeFileSync(`${ dir }${ label+'-'+uuid()+'.json' }`, JSON.stringify(msg.data[label], null, 2)) - } - break; - default: - Spinner.log(`[${ chalk.yellow(msg.type.toUpperCase()) }] - ${ JSON.stringify(msg.data) }`); - break; - } - } -} \ No newline at end of file + async start( + onConfig: (message: PubSubConfig) => void, + onCommand: (message: PubSubCommand) => void + ) { + let config = { projectid: 'testproject', run: 'testrun-' + uuid() }; + Spinner.info( + `Logging data to ${chalk.bold('projects/' + config.projectid + '/runs/' + config.run + '/')}` + ); + return config; + } + stop() {} + publish(msg: PubSubMessage): void { + switch (msg.type) { + case 'data': + for (const label of Object.keys(msg.data)) { + for (const datum of msg.data[label]) { + console.log( + `[${chalk.magenta(msg.type.toUpperCase())}] - [${new Date( + datum.timestamp + ).toLocaleTimeString()}] - ${label}: ${datum.value}` + ); + } + const dir = `./projects/${msg.metadata.project}/runs/${msg.metadata.run}/${label}/`; + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true }); + } + writeFileSync( + `${dir}${label + '-' + uuid() + '.json'}`, + JSON.stringify(msg.data[label], null, 2) + ); + } + break; + default: + Spinner.log(`[${chalk.yellow(msg.type.toUpperCase())}] - ${JSON.stringify(msg.data)}`); + break; + } + } +} diff --git a/software/src/ui.ts b/software/src/ui.ts index dc3f1aa1..5a8d3e7d 100644 --- a/software/src/ui.ts +++ b/software/src/ui.ts @@ -3,25 +3,25 @@ import * as blessed from 'blessed'; import { DataSet } from './publisher'; const DEFAULT_SPINNER: ora.Spinner = { - interval: 50, - frames: [ - "▁▁▁▁▁▁▁▁▁▁▁", - "█▁▁▁▁▁▁▁▁▁▁", - "██▁▁▁▁▁▁▁▁▁", - "███▁▁▁▁▁▁▁▁", - "████▁▁▁▁▁▁▁", - "█████▁▁▁▁▁▁", - "▁█████▁▁▁▁▁", - "▁▁█████▁▁▁▁", - "▁▁▁█████▁▁▁", - "▁▁▁▁█████▁▁", - "▁▁▁▁▁█████▁", - "▁▁▁▁▁▁█████", - "▁▁▁▁▁▁▁████", - "▁▁▁▁▁▁▁▁███", - "▁▁▁▁▁▁▁▁▁██", - "▁▁▁▁▁▁▁▁▁▁█", - ] + interval: 50, + frames: [ + '▁▁▁▁▁▁▁▁▁▁▁', + '█▁▁▁▁▁▁▁▁▁▁', + '██▁▁▁▁▁▁▁▁▁', + '███▁▁▁▁▁▁▁▁', + '████▁▁▁▁▁▁▁', + '█████▁▁▁▁▁▁', + '▁█████▁▁▁▁▁', + '▁▁█████▁▁▁▁', + '▁▁▁█████▁▁▁', + '▁▁▁▁█████▁▁', + '▁▁▁▁▁█████▁', + '▁▁▁▁▁▁█████', + '▁▁▁▁▁▁▁████', + '▁▁▁▁▁▁▁▁███', + '▁▁▁▁▁▁▁▁▁██', + '▁▁▁▁▁▁▁▁▁▁█' + ] }; /** @@ -30,116 +30,128 @@ const DEFAULT_SPINNER: ora.Spinner = { let spinner: ora.Ora = ora(); export namespace Spinner { - /** - * Start the loading spinner. - * @param text Text to display. - * @param spinner Spinner to use. Defaults to the default spinner. - */ - export function start(text: string = "") { - // If it's already spinning, just change the text - if (spinner.isSpinning) { - spinner.text = text; - } else { - // Otherwise, start a new one - spinner = ora({ text, spinner: DEFAULT_SPINNER }).start(); - } - }; + /** + * Start the loading spinner. + * @param text Text to display. + * @param spinner Spinner to use. Defaults to the default spinner. + */ + export function start(text: string = '') { + // If it's already spinning, just change the text + if (spinner.isSpinning) { + spinner.text = text; + } else { + // Otherwise, start a new one + spinner = ora({ text, spinner: DEFAULT_SPINNER }).start(); + } + } - /** - * Fail the loading spinner. - * @param text Text to display. - */ - export function fail(text: string = "") { - // If it's spinning, change the text and fail - if (spinner.isSpinning) { - spinner.fail(text); - } else { - // Otherwise, start a new one and fail it - spinner = ora({ text }).fail(); - } - }; + /** + * Fail the loading spinner. + * @param text Text to display. + */ + export function fail(text: string = '') { + // If it's spinning, change the text and fail + if (spinner.isSpinning) { + spinner.fail(text); + } else { + // Otherwise, start a new one and fail it + spinner = ora({ text }).fail(); + } + } - /** - * Succeed the loading spinner. - * @param text Text to display. - */ - export function succeed(text: string = "") { - // If it's spinning, change the text and succeed - if (spinner.isSpinning) { - spinner.succeed(text); - } else { - // Otherwise, start a new one and succeed it - spinner = ora({ text }).succeed(); - } - }; + /** + * Succeed the loading spinner. + * @param text Text to display. + */ + export function succeed(text: string = '') { + // If it's spinning, change the text and succeed + if (spinner.isSpinning) { + spinner.succeed(text); + } else { + // Otherwise, start a new one and succeed it + spinner = ora({ text }).succeed(); + } + } - /** - * Complete the loading spinner with info (blue `i`) - * @param text Text to display. - */ - export function info(text: string = "") { - if (spinner.isSpinning) { - // If it's spinning, change the text and set to info - spinner.info(text); - } else { - // Otherwise, start a new one and info it - spinner = ora({ text }).info(); - } - }; + /** + * Complete the loading spinner with info (blue `i`) + * @param text Text to display. + */ + export function info(text: string = '') { + if (spinner.isSpinning) { + // If it's spinning, change the text and set to info + spinner.info(text); + } else { + // Otherwise, start a new one and info it + spinner = ora({ text }).info(); + } + } - /** - * If spinning: stop and clear the current spinner, log some text, then restart the spinner - * Else: Just log - * - * @param text Text to log - */ - export function log(text: string) { - let oldtext = spinner.text; - if (spinner.isSpinning) { - spinner.stop(); - console.log(text); - spinner.start(oldtext); - } else { - console.log(text); - } - }; + /** + * If spinning: stop and clear the current spinner, log some text, then restart the spinner + * Else: Just log + * + * @param text Text to log + */ + export function log(text: string) { + let oldtext = spinner.text; + if (spinner.isSpinning) { + spinner.stop(); + console.log(text); + spinner.start(oldtext); + } else { + console.log(text); + } + } } // Create a screen object. let screen = blessed.screen({ - smartCSR: true + smartCSR: true }); let box = blessed.box({ - top: 'top', - left: 'left', - width: '50%', - height: '50%', - border: { - type: 'line' - }, - style: { - fg: 'white', - bg: 'black', - border: { - fg: '#ffffff' - } - }, - align: 'left', + top: 'top', + left: 'left', + width: '50%', + height: '50%', + border: { + type: 'line' + }, + style: { + fg: 'white', + bg: 'black', + border: { + fg: '#ffffff' + } + }, + align: 'left' }); screen.append(box); export namespace Screen { - export function render() { box.focus(); screen.render(); } - export function hide() { box.hide(); } - export function setData(data: DataSet) { - let s = 'PeaPod\n'; - s += Object.entries(data).map((datapoint)=>{ - return datapoint[0].split('-').map(word=> - word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase() - ).join(' ') + ': ' + datapoint[1].toFixed(2); - }).join('\n'); - box.setContent(s); - } -} \ No newline at end of file + export function render() { + box.focus(); + screen.render(); + } + export function hide() { + box.hide(); + } + export function setData(data: DataSet) { + let s = 'PeaPod\n'; + s += Object.entries(data) + .map(datapoint => { + return ( + datapoint[0] + .split('-') + .map(word => word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase()) + .join(' ') + + ': ' + + datapoint[1].toFixed(2) + ); + }) + .join('\n'); + box.setContent(s); + } +} diff --git a/software/src/utils.ts b/software/src/utils.ts index d7a0fa7c..8efcebf0 100644 --- a/software/src/utils.ts +++ b/software/src/utils.ts @@ -8,88 +8,100 @@ import { spawn } from 'child_process'; * @param timeout Timeout in milliseconds. Default: 5000 * @param url The URL to ping. Default: 'www.google.com' */ -export function checkInternet(timeout: number = 5000, url: string = 'www.google.com'): Promise { - const resolver = new dns.Resolver({ timeout }); - return new Promise(ret => { - resolver.resolve(url, err => { - if (err) { - ret(false); - } else { - ret(true); - } - }); - }); -}; +export function checkInternet( + timeout: number = 5000, + url: string = 'www.google.com' +): Promise { + const resolver = new dns.Resolver({ timeout }); + return new Promise(ret => { + resolver.resolve(url, err => { + if (err) { + ret(false); + } else { + ret(true); + } + }); + }); +} /** - * Sleep a given number of milliseconds. - * + * Sleep a given number of milliseconds. + * * Usage: `await sleep(x)` * @param millis Number of milliseconds to sleep. - * @returns + * @returns */ export function sleep(millis: number): Promise { - return new Promise(resolve => { - setTimeout(resolve, millis); - }); -}; + return new Promise(resolve => { + setTimeout(resolve, millis); + }); +} /** * Fetch the Google Root CA certificate. * @returns The very same certificate. */ export async function fetchServerCert(): Promise { - return String((await axios.get('https://pki.goog/roots.pem')).data); + return String((await axios.get('https://pki.goog/roots.pem')).data); } /** * Compiles the microcontroller software, and flashes the binary to the chip. */ export function updateMicrocontroller(): Promise { - return new Promise((res, rej) => { - // Create log folder - if (!existsSync('logs/')) { - mkdirSync('logs/', { recursive: true }); - } - execute(`${ process.env.HOME }/.platformio/penv/bin/platformio run -d microcontroller/ -e peapod -t upload`, [1]).catch(err => { - writeFileSync('logs/updateMicrocontroller.log', err); - rej(new Error(`Failed to update the microcontroller software. See logs/updateMicrocontroller.log`)); - }).then(log1 => { - if (log1) writeFileSync('logs/updateMicrocontroller.log', log1); - res(); - }); - }); + return new Promise((res, rej) => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute( + `${process.env.HOME}/.platformio/penv/bin/platformio run -d microcontroller/ -e peapod -t upload`, + [1] + ) + .catch(err => { + writeFileSync('logs/updateMicrocontroller.log', err); + rej( + new Error( + `Failed to update the microcontroller software. See logs/updateMicrocontroller.log` + ) + ); + }) + .then(log1 => { + if (log1) writeFileSync('logs/updateMicrocontroller.log', log1); + res(); + }); + }); } /** * General purpose command execution and logging. No `sudo` support. */ export function execute(command: string, failureCodes: number[] = []): Promise { - return new Promise((res, rej) => { - const args = command.split(' '); - const eprocess = spawn(args[0], args.slice(1)); - let log = "> "+command+"\n"; - eprocess.stdout?.on('data', out => { - log += out; - }); - eprocess.stderr?.on('data', out => { - log += out; - }); - eprocess.on('error', error => { - log += error.message; - rej(log); - eprocess.kill(); - }); - eprocess.on('close', code => { - if (code) { - log += "> Process exited with code "+code; - if (failureCodes.includes(code)) { - rej(log); - } - return; - } - // If no options, no codes, OR non-failure: - res(log); - }); - }); -} \ No newline at end of file + return new Promise((res, rej) => { + const args = command.split(' '); + const eprocess = spawn(args[0], args.slice(1)); + let log = '> ' + command + '\n'; + eprocess.stdout?.on('data', out => { + log += out; + }); + eprocess.stderr?.on('data', out => { + log += out; + }); + eprocess.on('error', error => { + log += error.message; + rej(log); + eprocess.kill(); + }); + eprocess.on('close', code => { + if (code) { + log += '> Process exited with code ' + code; + if (failureCodes.includes(code)) { + rej(log); + } + return; + } + // If no options, no codes, OR non-failure: + res(log); + }); + }); +} From 1e364b5e760849ecdbb869ac1d9cd1db02d61b2e Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 29 Aug 2025 15:33:47 -0400 Subject: [PATCH 34/85] Docs WIP --- builds/README.md | 11 + builds/microgreens/README.md | 37 +++ builds/microgreens/docs/.gitignore | 284 ++++++++++++++++++ .../docs/productsheet/ProductSheet.tex | 62 ++++ .../docs/requirements/Requirements.pdf | Bin 0 -> 363769 bytes .../docs/requirements/Requirements.tex | 210 +++++++++++++ builds/microgreens/hardware/README.md | 56 ++++ .../boms/microgreens_bom_components.csv | 12 + .../boms/microgreens_bom_purchase.csv | 12 + builds/template/README.md | 3 + builds/template/docs/.gitignore | 284 ++++++++++++++++++ .../docs/productsheet/ProductSheet.tex | 62 ++++ .../docs/requirements/Requirements.pdf | Bin 0 -> 363699 bytes .../docs/requirements/Requirements.tex | 210 +++++++++++++ builds/template/hardware/README.md | 47 +++ .../hardware/boms/template_bom_components.csv | 2 + .../hardware/boms/template_bom_purchase.csv | 2 + software/README.md | 11 +- subsystems/README.md | 4 +- 19 files changed, 1302 insertions(+), 7 deletions(-) create mode 100644 builds/README.md create mode 100644 builds/microgreens/README.md create mode 100644 builds/microgreens/docs/.gitignore create mode 100644 builds/microgreens/docs/productsheet/ProductSheet.tex create mode 100644 builds/microgreens/docs/requirements/Requirements.pdf create mode 100644 builds/microgreens/docs/requirements/Requirements.tex create mode 100644 builds/microgreens/hardware/README.md create mode 100644 builds/microgreens/hardware/boms/microgreens_bom_components.csv create mode 100644 builds/microgreens/hardware/boms/microgreens_bom_purchase.csv create mode 100644 builds/template/README.md create mode 100644 builds/template/docs/.gitignore create mode 100644 builds/template/docs/productsheet/ProductSheet.tex create mode 100644 builds/template/docs/requirements/Requirements.pdf create mode 100644 builds/template/docs/requirements/Requirements.tex create mode 100644 builds/template/hardware/README.md create mode 100644 builds/template/hardware/boms/template_bom_components.csv create mode 100644 builds/template/hardware/boms/template_bom_purchase.csv diff --git a/builds/README.md b/builds/README.md new file mode 100644 index 00000000..0e93be5f --- /dev/null +++ b/builds/README.md @@ -0,0 +1,11 @@ +# PeaPod Builds + +All builds, each representing an implementation-specific formulation of the PeaPod framework, per-unit. + +## Folder Structure + +All builds will follow the same folder structure: + +- `assets/` - various graphical/etc. assets (diagrams, etc. excluding pictures of schematics/PCB layouts) +- `docs/` - build-specific documentation (user manuals, build guides, quick-start, product briefs, etc.) +- `hardware/` - compiled BoMs, including fabrication (3D printing, PCB w/ gerber, etc.) and purchase (DigiKey, McMaster-Carr, etc.) diff --git a/builds/microgreens/README.md b/builds/microgreens/README.md new file mode 100644 index 00000000..359523cf --- /dev/null +++ b/builds/microgreens/README.md @@ -0,0 +1,37 @@ +# PeaPod for Microgreens + +PeaPod for Microgreens is an implementation-specific formulation of the PeaPod framework, designed to repeatably grow high-quality microgreens at minimal operating cost. This repository contains all the necessary information to build and operate the PeaPod for Microgreens. + +# Cost Breakdown + +> Production & Revenue Estimate (Per Unit, 3 Trays, 1 Week Cycle): 2 lbs. (>$40) + +## Materials + +> Aeroponic Supply System can provide water for up to 4 trays + + - Microgreen Tray: $400 ea. + - Aeroponic Supply System: $400 + - Lighting System: $100 ea. + - Power & Control: $100 + - Housing: $200 + +Total: $1200 + +Break-Even: 30 Weeks + +## Labor + + - Design & Fabrication Fees + - Manufacturing Labor + - Assembly Labor + - Delivery & Installation Labor + - Setup & Training Fees + +## Operating Costs + + - Electricity + - Water + - Fertilizer (optional) + - Seeds + - Operating Labor \ No newline at end of file diff --git a/builds/microgreens/docs/.gitignore b/builds/microgreens/docs/.gitignore new file mode 100644 index 00000000..d2972f0e --- /dev/null +++ b/builds/microgreens/docs/.gitignore @@ -0,0 +1,284 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib \ No newline at end of file diff --git a/builds/microgreens/docs/productsheet/ProductSheet.tex b/builds/microgreens/docs/productsheet/ProductSheet.tex new file mode 100644 index 00000000..35aec316 --- /dev/null +++ b/builds/microgreens/docs/productsheet/ProductSheet.tex @@ -0,0 +1,62 @@ +\documentclass{../../../docs/tex/report} +\usepackage{setspace} % Setting line spacing +\usepackage{ulem} % Underline +\usepackage{caption} % Captioning figures +\usepackage{subcaption} % Subfigures +\usepackage{geometry} % Page layout +\usepackage{multicol} % Columned pages +\usepackage{array,etoolbox} +\usepackage{fancyhdr} +\usepackage{enumitem} +\usepackage[toc,page]{appendix} +\setlist{noitemsep} + +% Page layout (margins, size, line spacing) +\geometry{letterpaper, left=1in, right=1in, bottom=1in, top=1in} +\setstretch{1} + +% Headers +\pagestyle{fancy} +\lhead{PeaPod for Microgreens} +\rhead{PeaPod Technologies Inc.} + +\begin{document} + +\begin{titlepage} + \begin{center} + \vspace*{1.2cm} + + \textbf{\large{PeaPod for Microgreens - Product Sheet}} + + \vspace{0.5cm} + + Low-Cost Modular High-Quality Microgreens Production System + + \vfill + \input{../../../docs/tex/documentation/Namecard.tex} + \vspace{1.25cm} + + Revision 0.1\\ + PeaPod Technologies Inc.\\ + March 31st, 2024 + + \end{center} +\end{titlepage} + +\thispagestyle{plain} + +\tableofcontents +\clearpage + +\section{Product Overview} + +\subsection{Introduction} + +PeaPod for Microgreens is a custom aeroponic plant growth unit optimized for growing high-quality microgreens at minimal operating cost. The system is designed to be turn-key and easy to use, from germination to harvest. Full-stack automation and control systems ensure repeatability and reliability, enabling business owners to make surefire return on investment, making this product ideal for in-house restaurant production and small-scale commercial growing. + +\clearpage + +% References +\bibliographystyle{IEEEtran} +\bibliography{references} +\end{document} \ No newline at end of file diff --git a/builds/microgreens/docs/requirements/Requirements.pdf b/builds/microgreens/docs/requirements/Requirements.pdf new file mode 100644 index 0000000000000000000000000000000000000000..81ad9f7cef029d11032cbea7d8ed80bad5dc52fa GIT binary patch literal 363769 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

    NH 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 Entries marked with a (*) are manufactured in-house from raw materials +> +> Entries marked with a (**) are fabricated in-house using 3D printing + +### Trays + +> Industry-Standard 1020 Tray Size on Drawer-Slide Tray Frames (Note: Dimensions do not include tray framing, drawers, mounting hardware, etc.) + +Tray System Dimensions: 20" wide x 30" deep (3x1020 per tray) + +- Tray Frame (T-Slot, Gusset Brackets**, Fasteners, Handle**) +- Drawer System (Slides, Mounts**, Fasteners) +- Mesh Trays* (Mesh, Velcro, Staples) +- Root-Zone Container* (Waterproof Composite, Adhesive, Drawstring w/ Locking Wheel) + +### Watering + +> Aeroponic Watering System + +- Reservoir (Bucket w/ Lid, Heater w/ Thermostat, Water Supply Line w/ Valve) +- Supply System w/ CAM Lock Fittings (Diaphragm Pump [12V 10A] w/ Pressure Switch, Bladder Accumulator, Solenoid Valve [12V 1A]) +- Watering System (Manifold, Loc-Line Modular Adjustable Tubing, Spray Nozzles) +- Root-Zone Drainage System (Auto Sump Pump [12V 1A]) +- Supply & Drainage Harness (Tubing, Push-to-Connect Harness Fittings, Quick-Disconnect Tray Fittings) + +### Lighting + +> LED Grow Light System + +- Adjustable Light Fixture (DIN Rail, Mounting Plate, Drawstring) +- LED Strips* (LEDs w/ Onboard Ballast, PCB, Wiring Harness) + +### Power & Control + +- Power Supplies (AC/DC Adapters at 5V, 12V, 24V) +- Relay Board w/ Transistor Control +- Microcontroller (Arduino Nano) + +### Housing + +> T-Slot Frame System + +Outer Dimensions (Per Unit): 24" wide x 34" deep x 36" tall \ No newline at end of file diff --git a/builds/microgreens/hardware/boms/microgreens_bom_components.csv b/builds/microgreens/hardware/boms/microgreens_bom_components.csv new file mode 100644 index 00000000..c4fe1162 --- /dev/null +++ b/builds/microgreens/hardware/boms/microgreens_bom_components.csv @@ -0,0 +1,12 @@ +Index ,Manufacturer Part Number ,Manufacturer Name ,Description ,Quantity ,Digi-Key Part Number ,Unit Price ,Extended Price ,Datasheet ,Reference Designator + 1 ,A000005 ,Arduino ,ARDUINO NANO ATMEGA328 EVAL BRD , 1 ,1050-1001-ND , 28.63000 ,$28.63 ,https://media.digikey.com/pdf/Data%20Sheets/Arduino%20PDFs/A000005.pdf ,U2 + 2 ,S404GSEJ6-U3000-3 ,"Delkin Devices, Inc." ,4GB MLC MICROSD CARD (-25C - +85 , 1 ,3247-S404GSEJ6-U3000-3-ND , 19.33000 ,$19.33 ,https://www.delkin.com/wp-content/uploads/2021/01/microSD-Industrial-Spec-Sheet-Jan-2021.pdf , + 3 ,61304021121 ,Würth Elektronik ,CONN HEADER VERT 40POS 2.54MM , 1 ,732-5310-ND , 2.70000 ,$2.70 ,https://www.we-online.com/katalog/datasheet/6130xx21121.pdf ,"U1, U2" + 4 ,SC0510 ,Raspberry Pi ,ZERO 2 W , 1 , , , ,https://datasheets.raspberrypi.com/rpizero2/raspberry-pi-zero-2-w-product-brief.pdf ,U1 + 5 ,DMN2005K-7 ,Diodes Incorporated ,MOSFET N-CH 20V 300MA SOT23-3 , 2 ,DMN2005K-7DICT-ND , 0.58000 ,$1.16 ,https://www.diodes.com/assets/Datasheets/ds30734.pdf ,"Q1, Q2" + 6 ,RC0603FR-0710KL ,YAGEO ,RES 10K OHM 1% 1/10W 0603 , 5 ,311-10.0KHRCT-ND , 0.15000 ,$0.75 ,https://www.yageo.com/upload/media/product/productsearch/datasheet/rchip/PYu-RC_Group_51_RoHS_L_11.pdf ,R1-5 + 7 ,4484 ,Adafruit Industries LLC ,MINI PITFT 1.3 FOR RASPBERRY PI , 1 ,1528-4484-ND , 22.02000 ,$22.02 ,https://media.digikey.com/pdf/Data%20Sheets/Adafruit%20PDFs/4484_Web.pdf , + 8 ,5055670271 ,Molex ,CONN HEADER SMD R/A 2POS 1.25MM , 2 ,WM17038CT-ND , 1.19000 ,$2.38 ,https://www.molex.com/pdm_docs/ps/5055650000-000.pdf ,"J3, J8" + 9 ,5055670471 ,Molex ,CONN HEADER SMD R/A 4POS 1.25MM , 5 ,WM17040CT-ND , 1.30000 ,$6.50 ,http://www.literature.molex.com/SQLImages/kelmscott/Molex/PDF_Images/987651-4888.pdf ,"J1, J2, J6, J9, J10" + 10 ,5055670871 ,Molex ,CONN HEADER SMD R/A 8POS 1.25MM , 3 ,WM17115CT-ND , 1.77000 ,$5.31 ,https://www.molex.com/pdm_docs/pk/5055679200-200.pdf ,"J4, J5, J7" + 11 ,5055670681 ,Molex ,CONN HEADER SMD R/A 6POS 1.25MM , 3 ,WM22471CT-ND , 1.42000 ,$4.26 ,https://www.molex.com/pdm_docs/ps/5055650002-000.pdf ,J11-13 \ No newline at end of file diff --git a/builds/microgreens/hardware/boms/microgreens_bom_purchase.csv b/builds/microgreens/hardware/boms/microgreens_bom_purchase.csv new file mode 100644 index 00000000..512556ac --- /dev/null +++ b/builds/microgreens/hardware/boms/microgreens_bom_purchase.csv @@ -0,0 +1,12 @@ +Part ,Description ,Quantity ,Supplier ,Supplier Part Number ,Unit Cost (CAD) ,Reference +Tray Frame X +Tray Frame Y +Tray Frame Z +Tray Drawer Slide ,24in.; Pair , 1 ,McMaster-Carr ,11435A28 , 42.92 ,. +Tray Mesh Medium ,2ft. Wide , 1 ,McMaster-Carr ,9218T13 , 119.04 ,. +Tray Mesh Velcro ,1n. Wide; 30ft. Roll , 1 ,McMaster-Carr ,95005K817 , 55.27 ,. +Root-Zone Container Composite ,1.43oz Dyneema; 0.5yd sections; Cut & Folded , 2 ,Ripstop by the Roll , , 34.09 ,. +Root-Zone Container Adhesive ,Pressure-Sensitive Adhesive Tape for Dyneema; 1/2in. Wide; 5yd. Roll , 3 ,Ripstop by the Roll , , 6.41 ,https://ripstopbytheroll.com/products/dyneema-composite-fabric-double-sided-adhesive-tape +Root-Zone Container Drawstring ,Nylon Cord; 5/32in. dia.; 50ft. Length , 1 ,McMaster-Carr ,3696T38 , 2.82 ,https://www.mcmaster.com/3696T38/ +Root-Zone Container Lock ,Slide-to-Release Wheel; Pack of 25 , 1 ,McMaster-Carr ,3734T4 , 29.23 ,https://www.mcmaster.com/3734T4 +Aeroponic Misting Nozzle ,80deg Full-Cone; Brass; 5gal. per hr. @100PSI; 1/8 MPT , 4 ,McMaster-Carr ,3178K75 , 22.66 ,https://www.mcmaster.com/3178K75/ diff --git a/builds/template/README.md b/builds/template/README.md new file mode 100644 index 00000000..97253281 --- /dev/null +++ b/builds/template/README.md @@ -0,0 +1,3 @@ +# PeaPod for XYZ + +PeaPod for XYZ is an implementation-specific formulation of the PeaPod framework, designed to lorem ipsum dolor sit amet, consectetur adipiscing elit. This repository contains all the necessary information to build and operate the PeaPod for XYZ. \ No newline at end of file diff --git a/builds/template/docs/.gitignore b/builds/template/docs/.gitignore new file mode 100644 index 00000000..d2972f0e --- /dev/null +++ b/builds/template/docs/.gitignore @@ -0,0 +1,284 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib \ No newline at end of file diff --git a/builds/template/docs/productsheet/ProductSheet.tex b/builds/template/docs/productsheet/ProductSheet.tex new file mode 100644 index 00000000..9bbab28d --- /dev/null +++ b/builds/template/docs/productsheet/ProductSheet.tex @@ -0,0 +1,62 @@ +\documentclass{../../../../docs/tex/report} +\usepackage{setspace} % Setting line spacing +\usepackage{ulem} % Underline +\usepackage{caption} % Captioning figures +\usepackage{subcaption} % Subfigures +\usepackage{geometry} % Page layout +\usepackage{multicol} % Columned pages +\usepackage{array,etoolbox} +\usepackage{fancyhdr} +\usepackage{enumitem} +\usepackage[toc,page]{appendix} +\setlist{noitemsep} + +% Page layout (margins, size, line spacing) +\geometry{letterpaper, left=1in, right=1in, bottom=1in, top=1in} +\setstretch{1} + +% Headers +\pagestyle{fancy} +\lhead{PeaPod XYZ} +\rhead{PeaPod Technologies Inc.} + +\begin{document} + +\begin{titlepage} + \begin{center} + \vspace*{1.2cm} + + \textbf{\large{PeaPod XYZ - Product Sheet}} + + \vspace{0.5cm} + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + + \vfill + \input{../../../docs/tex/documentation/Namecard.tex} + \vspace{1.25cm} + + Revision 0.1\\ + PeaPod Technologies Inc.\\ + Month XX, 2024 + + \end{center} +\end{titlepage} + +\thispagestyle{plain} + +\tableofcontents +\clearpage + +\section{Product Overview} + +\subsection{Introduction} + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed auctor, nunc nec ultricies ultricies, nunc nunc ultricies nunc, nec ultricies nunc nunc nec. Sed auctor, nunc nec ultricies ultricies, nunc nunc ultricies nunc, nec ultricies nunc nunc nec. Sed auctor, nunc nec ultricies ultricies, nunc nunc ultricies nunc, nec ultricies nunc nunc nec. Sed auctor, nunc nec ultricies ultricies, nunc nunc ultricies nunc, nec ultricies nunc nunc nec. + +\clearpage + +% References +\bibliographystyle{IEEEtran} +\bibliography{references} +\end{document} \ No newline at end of file diff --git a/builds/template/docs/requirements/Requirements.pdf b/builds/template/docs/requirements/Requirements.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e47ac10a972cdfaa3ca18aa2321374ae1a995088 GIT binary patch literal 363699 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

    an)a0a4hohq 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 literal 0 HcmV?d00001 diff --git a/builds/template/docs/requirements/Requirements.tex b/builds/template/docs/requirements/Requirements.tex new file mode 100644 index 00000000..54bec7a0 --- /dev/null +++ b/builds/template/docs/requirements/Requirements.tex @@ -0,0 +1,210 @@ +\documentclass{../../../../docs/tex/report} +\usepackage{setspace} % Setting line spacing +\usepackage{ulem} % Underline +\usepackage{caption} % Captioning figures +\usepackage{subcaption} % Subfigures +\usepackage{geometry} % Page layout +\usepackage{multicol} % Columned pages +\usepackage{array,etoolbox} +\usepackage{fancyhdr} +\usepackage{enumitem} +\usepackage[toc,page]{appendix} + +% Page layout (margins, size, line spacing) +\geometry{letterpaper, left=1in, right=1in, bottom=1in, top=1in} +\setstretch{1.5} + +% Headers +\pagestyle{fancy} +\lhead{PeaPod for XYZ - Requirements} +\rhead{PeaPod Technologies Inc.} + +% Metric counter, referencing commands +\newcounter{metricnumber} +\setcounter{metricnumber}{1} +\newcommand{\metricrow}{M\arabic{metricnumber}} +\newcommand{\mlabel}[1]{\addtocounter{metricnumber}{-1}\refstepcounter{metricnumber}\label{#1}\addtocounter{metricnumber}{1}} +\newcommand{\mref}[1]{\hyperref[#1]{M\ref{#1}}} + +\begin{document} + +\begin{titlepage} + \begin{center} + \vspace*{1.2cm} + + \textbf{\large{PeaPod for XYZ - Requirements}} + + \vspace{0.5cm} + + Outlining the Implementation-Specific Requirements for a PeaPod XYZ\\ + + \textit{Extends: \textbf{PeaPod - Requirements}} + + \vfill + \input{../../../../docs/tex/documentation/Namecard.tex} + \vspace{1.25cm} + + Revision 0.1\\ + PeaPod Technologies Inc.\\ + March 31st, 2024 + + \end{center} +\end{titlepage} + +\thispagestyle{plain} + +\tableofcontents +\newpage + +\section{Introduction} +\label{sec:intro} + +\subsection{Purpose} +\label{sec:purpose} + +The purpose of this document is to outline both the category requirements (Section \ref{sec:requirements}) for an implementation of the PeaPod framework (See \textit{PeaPod - Requirements}) that XYZ and the scoped requirements (Section \ref{sec:scope}) for the design being proposed by PeaPod Technologies Inc.: \textbf{PeaPod for XYZ}. + +\subsection{Design Paradigm} +\label{sec:structure} + +\input{../../../../docs/tex/documentation/DesignParadigm.tex} + +\clearpage + + +\subsection{Scope and Justification} +\label{sec:scope} + +\begin{enumerate}[label=SC\arabic*., ref=SC\arabic*] + \item \label{sc:1} Lorem ipsum dolor sit amet, consectetur adipiscing elit: + \begin{enumerate}[label=SC3\alph*., ref=SC3\alph*] + \item \label{sc:1a} Sed auctor, nunc nec ultricies ultricies, nunc nunc ultricies nunc, nec ultricies nunc nunc nec. + \end{enumerate} +\end{enumerate} + +\subsection{Definitions} +\label{sec:definitions} + +A number of useful definitions have emerged from the above scoping: +\begin{enumerate} + \item \textbf{ABC} - Lorem ipsum dolor sit amet, consectetur adipiscing elit. +\end{enumerate} + +\clearpage + + +\section{Framing} +\label{sec:framing} + +\subsection{Problem Statement} +\label{sec:opportunity} + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed auctor, nunc nec ultricies ultricies, nunc nunc ultricies nunc, nec ultricies nunc nunc nec. + +\subsection{Solution Requirements} +\label{sec:requirements} + +The following are the overall challenge requirements compiled from A, B, and an excerpt from C: +\begin{enumerate}[label=R\arabic*., ref=R\arabic*] + \item \label{r:1} \textbf{Must} lorem ipsum dolor sit amet, consectetur adipiscing elit: + \begin{enumerate}[ref=R1\alph*] + \item \label{r:1a} \textbf{Should} lorem ipsum dolor sit amet, consectetur adipiscing elit; + \end{enumerate} +\end{enumerate} + +% Change line spacing for the more list-heavy sections +\setstretch{1} +\subsection{Stakeholders and Values} +\label{sec:stakeholders} + +\begin{enumerate}[label=S\arabic*., ref=S\arabic*] + \item \label{s:1} A - Values, etc. + \item \label{s:2} B - DfX, etc. +\end{enumerate} + +\clearpage + + +\subsection{Problem-Solving Goals} +\label{sec:goals} + +% High-Level +\begin{multicols}{2}[] + \begin{enumerate}[label=HL\arabic*., ref=HL\arabic*] + \item \label{hl:output} Goal ABC \hfill (\ref{s:1}, \ref{r:1}, \ref{r:1a}) + \end{enumerate} +\end{multicols} + +\subsection{Solution Objectives} +\label{sec:objectives} + +% Low-Level +\begin{multicols}{2}[] + \begin{enumerate}[label=LL\arabic*., ref=LL\arabic*] + \item \label{ll:output_variety} Objective ABC \hfill (\ref{hl:output}) + \end{enumerate} +\end{multicols} + +\clearpage + + +\subsection{Metrics} +\label{sec:metrics} + +\begin{tabular}{| @{\makebox[2.4em][c]{\metricrow}} | p{8.7cm} | p{5.9cm} |} + \hline + \multicolumn{1}{| @{\makebox[2.4em][c]{\textbf{\#}}} | l |}{\textbf{Metric}} & \textbf{Units}\\ + \hline + Metric ABC \mlabel{m:constraint} \hfill (\ref{ll:output_variety}) & Yes/No \\ + Metric XYZ \mlabel{m:criteria} \hfill (\ref{ll:output_variety}) & 0 - 100\% \\ + \hline +\end{tabular} + +\clearpage + + +\subsection{Constraints} +\label{sec:constraints} + +\begin{tabular}{|l|p{14.35cm}|} + \hline + \textbf{Metric} & \textbf{Constraint \hfill Justification} \\ + \hline + \mref{m:constraint} & Yes \hfill (\ref{s:1})\\ + \hline +\end{tabular} + +\subsection{Criteria} +\label{sec:criteria} + +\begin{tabular}{|l|p{14.35cm}|} + \hline + \textbf{Metric} & \textbf{Criteria \hfill Justification} \\ + \hline + \mref{m:criteria} & Should Maximize \hfill (\ref{r:1a}) \\ + \hline +\end{tabular} + +% Refer to Appendix \ref{sec:assessment} for prototype verification Assessment Criteria (categories, weights, etc.). + +\newpage + +\section{Reference Designs} + +% -------- TEMPLATE -------- +% Introduction - Project goal, scope, differences from this project +% Graphics - Design drawings/photos, etc. +% Analysis - Rank the design across each of our metrics + % TODO: Metrics might be too much, maybe just qualitative analysis based on LLOs? + +\subsection{Reference Design XYZ} + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed auctor, nunc nec ultricies ultricies, nunc nunc ultricies nunc, nec ultricies nunc nunc nec. + +% \newpage + +% % References +% \bibliographystyle{IEEEtran} +% \bibliography{references} + +\end{document} \ No newline at end of file diff --git a/builds/template/hardware/README.md b/builds/template/hardware/README.md new file mode 100644 index 00000000..ca86875e --- /dev/null +++ b/builds/template/hardware/README.md @@ -0,0 +1,47 @@ +# PeaPod for Microgreens - Hardware + +## Scope and Use-Case + +- Follows PeaPod framework "housing unit" system +- Indoor microgreens cultivation (i.e. no temperature/humidity control required) +- Minimal scale (i.e. 1-2 trays, "desktop" size) +- 1020 tray system +- ZERO consumables (no soil/media, optional fertilizers, ) + +## Specifications + +### Trays + +> Industry-Standard 1020 Tray Size on Drawer-Slide Tray Frames (Note: Dimensions do not include tray framing) + +Tray System Dimensions: 20" wide x 30" deep (3 trays per unit) + +### Lighting + +### Watering + +### Housing + +Outer Dimensions (Per Unit): 24" wide x 34" deep x 36" tall + +# Cost Breakdown + +> Production & Revenue Estimate (Per Unit, 3 Trays, 1 Week Cycle): X lbs. (Y$) + +## Materials + +## Labor + + - Design & Fabrication Fees + - Manufacturing Labor + - Assembly Labor + - Delivery & Installation Labor + - Setup & Training Fees + +## Operating Costs + + - Electricity + - Water + - Fertilizer (optional) + - Seeds + - Operating Labor \ No newline at end of file diff --git a/builds/template/hardware/boms/template_bom_components.csv b/builds/template/hardware/boms/template_bom_components.csv new file mode 100644 index 00000000..4432b6c4 --- /dev/null +++ b/builds/template/hardware/boms/template_bom_components.csv @@ -0,0 +1,2 @@ +Index ,Manufacturer Part Number ,Manufacturer Name ,Description ,Quantity ,Digi-Key Part Number ,Unit Price ,Extended Price ,Datasheet ,Reference Designator + 1 ,A000005 ,Arduino ,ARDUINO NANO ATMEGA328 EVAL BRD , 1 ,1050-1001-ND , 28.63000 ,$28.63 ,https://media.digikey.com/pdf/Data%20Sheets/Arduino%20PDFs/A000005.pdf ,U2 \ No newline at end of file diff --git a/builds/template/hardware/boms/template_bom_purchase.csv b/builds/template/hardware/boms/template_bom_purchase.csv new file mode 100644 index 00000000..1c22ff1e --- /dev/null +++ b/builds/template/hardware/boms/template_bom_purchase.csv @@ -0,0 +1,2 @@ +Part ,Description ,Quantity ,Supplier ,Supplier Part Number ,Unit Cost (CAD) ,Reference +Automation Motherboard PCB ,"2 Layers, 1 oz. Copper, 1.6mm Thickness, Suggested: HASL Finish, White PCB, Black Silkscreen" ,1 (MOQ 5) ,JLCPCB ,N/A , 0.51 ,../gerber/ \ No newline at end of file diff --git a/software/README.md b/software/README.md index 0cce9216..9dd3b2cf 100644 --- a/software/README.md +++ b/software/README.md @@ -32,7 +32,7 @@ Designed as both a hassle-free food production system and a research tool for pr The following are performed on a computer: -1. Format a microSD card (>=4GB) with a single FAT partition. +1. Format a microSD card (>=32GB) with a single FAT partition. 2. Download the Raspberry Pi Imager [(Download)](https://www.raspberrypi.com/software/). 3. Flash the SD card with a *Raspberry Pi OS Lite (64-bit)* image. @@ -46,7 +46,7 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: 6. Execute `sudo raspi-config` and perform these steps to setup the Pi: 1. *System Options > Wireless Lan* - Setup WiFi and connect to the Internet 2. *System Options > Hostname* - Set a unique hostname (`e.g. peapod`) - 3. *System Options > Boot/Auto Login* - Select `Console Autologin` + 3. *System Options > Boot/Auto Login* - Select `Console Autologin` (B2) 4. *Interface Options > SSH* - `Enable` SSH Server 5. *Interface Options > Serial Port* - `Disable` serial login shell, but `Enable` the serial port hardware 6. *Performance Options > GPU Memory* - 256 (MB) @@ -60,11 +60,12 @@ The following are performed on the Raspberry Pi, with a keyboard and monitor: > Note: In Future, steps 7-11 will be performed at runtime. 7. Update Packages: - 1. Update package listings, upgrade existing packages: `sudo apt-get update && sudo apt-get upgrade -y` - 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt-get install -y nodejs npm avrdude python3-venv` (could take a while) + 1. Update package listings, upgrade existing packages: `sudo apt update && sudo apt full-upgrade -y` + 2. Install Node.JS, the Node package manager, and *avrdude*: `sudo apt install -y nodejs npm avrdude python3-venv python3-dev` (could take a while) 3. Install main software package: `sudo npm i -g @peapodtech/peapodos --save` + 4. If using a Raspberry Pi Camera, install the camera package: `sudo apt install -y libcamera-apps` -8. Install PlatformIO Core with `python3 -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)"` +8. Install [PlatformIO Core](https://docs.platformio.org/en/latest/core/installation/methods/installer-script.html#super-quick-macos-linux) 9. Create a custom configuration file for the AVR flash utility *avrdude* to be able to program the Arduino Nano via ICSP over the Raspberry Pi's GPIO pins: 1. Create a local copy of the *avrdude* configuration file with `cp /etc/avrdude.conf ~/avrdude_gpio.conf`, then modify your copy with `nano ~/avrdude_gpio.conf`. Copy the following to the end of the file: diff --git a/subsystems/README.md b/subsystems/README.md index 4f8f4162..fdb3e66f 100644 --- a/subsystems/README.md +++ b/subsystems/README.md @@ -1,6 +1,6 @@ # PeaPod Subsystems -All subsystems will follow the same folder structure: +All subsystems will follow the same folder structure (where applicable): - `assets/` - various graphical/etc. assets (diagrams, etc. excluding pictures of schematics/PCB layouts) - `hardware/` @@ -14,7 +14,7 @@ In addition, each subsystem should have its own `README.MD` with the following i ## Hardware -Includes all bills of materials (BOM), 3D printing files, PCB files, etc. +Includes all **reference** bills of materials (BOM), 3D printing files, PCB files, etc. Subdirectories: From 001711d8e068c4583cec901cd90afd4914f253d0 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 29 Aug 2025 15:36:57 -0400 Subject: [PATCH 35/85] Archived WIP --- .../microcontroller/ControlSystemsOS.cc | 754 ++ .../microcontroller/ControlSystemsOS.h | 91 + .../microcontroller}/actuators/actuator.cc | 0 .../microcontroller}/actuators/actuator.h | 0 .../microcontroller}/actuators/led.cc | 0 .../microcontroller}/actuators/led.h | 0 .../microcontroller}/actuators/onoff.cc | 0 .../microcontroller}/actuators/onoff.h | 0 .../microcontroller}/actuators/pwm.cc | 0 .../microcontroller}/actuators/pwm.h | 0 .../microcontroller}/actuators/solenoid.cc | 0 .../microcontroller}/actuators/solenoid.h | 0 .../microcontroller}/actuators/supply.cc | 0 .../microcontroller}/actuators/supply.h | 0 .../microcontroller}/actuators/tec.cc | 0 .../microcontroller}/actuators/tec.h | 0 software/archive/microcontroller/debug.h | 38 + .../archive/microcontroller/interfaces/adc.cc | 107 + .../archive/microcontroller/interfaces/adc.h | 142 + .../microcontroller/interfaces/eeprom.cc | 46 + .../microcontroller/interfaces/eeprom.h | 29 + .../microcontroller/interfaces/gpio.cc | 69 + .../archive/microcontroller/interfaces/gpio.h | 110 + .../microcontroller/interfaces/interface.cc | 109 + .../microcontroller/interfaces/interface.h | 112 + .../microcontroller/interfaces/linker.cc | 35 + .../microcontroller/interfaces/linker.h | 61 + .../microcontroller/interfaces/mcp23017.cc | 2 + .../microcontroller/interfaces/mcp23017.h | 46 + .../archive/microcontroller/interfaces/mux.cc | 16 + .../archive/microcontroller/interfaces/mux.h | 45 + .../archive/microcontroller/interfaces/pwm.cc | 40 + .../archive/microcontroller/interfaces/pwm.h | 84 + software/archive/microcontroller/main.cc | 199 + .../src => archive/microcontroller}/main.h | 0 .../microcontroller}/modules/module.cc | 0 .../microcontroller}/modules/module.h | 0 software/archive/microcontroller/peapod.cc | 166 + software/archive/microcontroller/peapod.h | 37 + .../microcontroller}/sensors/ge_2158.cc | 0 .../microcontroller}/sensors/ge_2158.h | 0 .../microcontroller}/sensors/k30.cc | 0 .../microcontroller}/sensors/k30.h | 0 .../microcontroller}/sensors/sen0257.cc | 0 .../microcontroller}/sensors/sen0257.h | 0 .../archive/microcontroller/sensors/sensor.cc | 205 + .../archive/microcontroller/sensors/sensor.h | 83 + .../archive/microcontroller/sensors/sht31.cc | 94 + .../archive/microcontroller/sensors/sht31.h | 63 + .../microcontroller}/sensors/yf_b1.cc | 0 .../microcontroller}/sensors/yf_b1.h | 0 software/archive/microcontroller/simple.cc | 114 + software/archive/microcontroller/simple.h | 44 + software/archive/microcontroller/types.h | 66 + .../microcontroller}/utils/base.h | 0 .../microcontroller}/utils/instructions.cc | 0 .../microcontroller}/utils/instructions.h | 0 .../microcontroller}/utils/interrupts.cc | 0 .../microcontroller}/utils/interrupts.h | 0 .../microcontroller}/utils/jsonmessenger.cc | 0 .../microcontroller}/utils/jsonmessenger.h | 0 software/archive/microcontroller/utils/map.h | 71 + software/index.ts | 49 +- .../lib/FiniteStateMachine/chrono.cc | 256 + .../lib/FiniteStateMachine/chrono.h | 93 + .../lib/FiniteStateMachine/comparators.cc | 283 + .../lib/FiniteStateMachine/comparators.h | 176 + .../lib/FiniteStateMachine/comparators.tpp | 170 + .../lib/FiniteStateMachine/debug.h | 33 + .../lib/FiniteStateMachine/library.properties | 10 + .../lib/FiniteStateMachine/number.cc | 135 + .../lib/FiniteStateMachine/number.h | 74 + .../lib/FiniteStateMachine/state.cc | 89 + .../lib/FiniteStateMachine/state.h | 112 + .../lib/FiniteStateMachine/state.tpp | 105 + .../lib/FiniteStateMachine/statelinker.cc | 148 + .../lib/FiniteStateMachine/statelinker.h | 70 + .../lib/FiniteStateMachine/statelinker.tpp | 40 + software/microcontroller/lib/I2CIP/I2CIP.cc | 560 ++ software/microcontroller/lib/I2CIP/I2CIP.h | 110 + software/microcontroller/lib/I2CIP/bst.h | 71 + software/microcontroller/lib/I2CIP/bst.tpp | 136 + software/microcontroller/lib/I2CIP/debug.h | 28 + software/microcontroller/lib/I2CIP/device.cc | 632 ++ software/microcontroller/lib/I2CIP/device.h | 398 + software/microcontroller/lib/I2CIP/device.tpp | 147 + software/microcontroller/lib/I2CIP/eeprom.cc | 368 + software/microcontroller/lib/I2CIP/eeprom.h | 93 + software/microcontroller/lib/I2CIP/fqa.cc | 32 + software/microcontroller/lib/I2CIP/fqa.h | 84 + .../microcontroller/lib/I2CIP/hashtable.h | 66 + .../microcontroller/lib/I2CIP/hashtable.tpp | 115 + .../lib/I2CIP/library.properties | 10 + software/microcontroller/lib/I2CIP/mux.cc | 105 + software/microcontroller/lib/I2CIP/mux.h | 67 + software/microcontroller/library.properties | 11 + software/microcontroller/platformio.ini | 16 +- software/microcontroller/src/main.cc | 126 - .../microcontroller/src/sensors/sensor.cc | 103 - software/microcontroller/src/sensors/sensor.h | 106 - software/microcontroller/src/sensors/sht31.cc | 141 - software/microcontroller/src/sensors/sht31.h | 104 - software/microcontroller/test/README.md | 9 - software/microcontroller/test/config.h | 8 + .../test_0_blink.cc} | 0 .../test/test_1_eeprom/test_1_eeprom.cc | 61 + .../test/test_2_json/test_2_json.cc | 110 + .../test/test_3_csos/test_3_csos.cc | 60 + .../test_instructions/test_instructions.cc | 97 - .../microcontroller/test/test_k30/test_k30.cc | 34 - .../test/test_messenger/test_messenger.cc | 40 - .../test/test_sht31/test_sht31.cc | 34 - software/microcontroller/test/tests.cc | 34 - software/microcontroller/test/tests.h | 34 - software/package-lock.json | 7616 ++++------------- software/src/peapod.ts | 74 +- software/src/publisher.ts | 2 +- software/src/utils.ts | 24 + 118 files changed, 9770 insertions(+), 7017 deletions(-) create mode 100644 software/archive/microcontroller/ControlSystemsOS.cc create mode 100644 software/archive/microcontroller/ControlSystemsOS.h rename software/{microcontroller/src => archive/microcontroller}/actuators/actuator.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/actuator.h (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/led.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/led.h (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/onoff.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/onoff.h (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/pwm.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/pwm.h (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/solenoid.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/solenoid.h (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/supply.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/supply.h (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/tec.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/actuators/tec.h (100%) create mode 100644 software/archive/microcontroller/debug.h create mode 100644 software/archive/microcontroller/interfaces/adc.cc create mode 100644 software/archive/microcontroller/interfaces/adc.h create mode 100644 software/archive/microcontroller/interfaces/eeprom.cc create mode 100644 software/archive/microcontroller/interfaces/eeprom.h create mode 100644 software/archive/microcontroller/interfaces/gpio.cc create mode 100644 software/archive/microcontroller/interfaces/gpio.h create mode 100644 software/archive/microcontroller/interfaces/interface.cc create mode 100644 software/archive/microcontroller/interfaces/interface.h create mode 100644 software/archive/microcontroller/interfaces/linker.cc create mode 100644 software/archive/microcontroller/interfaces/linker.h create mode 100644 software/archive/microcontroller/interfaces/mcp23017.cc create mode 100644 software/archive/microcontroller/interfaces/mcp23017.h create mode 100644 software/archive/microcontroller/interfaces/mux.cc create mode 100644 software/archive/microcontroller/interfaces/mux.h create mode 100644 software/archive/microcontroller/interfaces/pwm.cc create mode 100644 software/archive/microcontroller/interfaces/pwm.h create mode 100644 software/archive/microcontroller/main.cc rename software/{microcontroller/src => archive/microcontroller}/main.h (100%) rename software/{microcontroller/src => archive/microcontroller}/modules/module.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/modules/module.h (100%) create mode 100644 software/archive/microcontroller/peapod.cc create mode 100644 software/archive/microcontroller/peapod.h rename software/{microcontroller/src => archive/microcontroller}/sensors/ge_2158.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/sensors/ge_2158.h (100%) rename software/{microcontroller/src => archive/microcontroller}/sensors/k30.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/sensors/k30.h (100%) rename software/{microcontroller/src => archive/microcontroller}/sensors/sen0257.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/sensors/sen0257.h (100%) create mode 100644 software/archive/microcontroller/sensors/sensor.cc create mode 100644 software/archive/microcontroller/sensors/sensor.h create mode 100644 software/archive/microcontroller/sensors/sht31.cc create mode 100644 software/archive/microcontroller/sensors/sht31.h rename software/{microcontroller/src => archive/microcontroller}/sensors/yf_b1.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/sensors/yf_b1.h (100%) create mode 100644 software/archive/microcontroller/simple.cc create mode 100644 software/archive/microcontroller/simple.h create mode 100644 software/archive/microcontroller/types.h rename software/{microcontroller/src => archive/microcontroller}/utils/base.h (100%) rename software/{microcontroller/src => archive/microcontroller}/utils/instructions.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/utils/instructions.h (100%) rename software/{microcontroller/src => archive/microcontroller}/utils/interrupts.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/utils/interrupts.h (100%) rename software/{microcontroller/src => archive/microcontroller}/utils/jsonmessenger.cc (100%) rename software/{microcontroller/src => archive/microcontroller}/utils/jsonmessenger.h (100%) create mode 100644 software/archive/microcontroller/utils/map.h create mode 100644 software/microcontroller/lib/FiniteStateMachine/chrono.cc create mode 100644 software/microcontroller/lib/FiniteStateMachine/chrono.h create mode 100644 software/microcontroller/lib/FiniteStateMachine/comparators.cc create mode 100644 software/microcontroller/lib/FiniteStateMachine/comparators.h create mode 100644 software/microcontroller/lib/FiniteStateMachine/comparators.tpp create mode 100644 software/microcontroller/lib/FiniteStateMachine/debug.h create mode 100644 software/microcontroller/lib/FiniteStateMachine/library.properties create mode 100644 software/microcontroller/lib/FiniteStateMachine/number.cc create mode 100644 software/microcontroller/lib/FiniteStateMachine/number.h create mode 100644 software/microcontroller/lib/FiniteStateMachine/state.cc create mode 100644 software/microcontroller/lib/FiniteStateMachine/state.h create mode 100644 software/microcontroller/lib/FiniteStateMachine/state.tpp create mode 100644 software/microcontroller/lib/FiniteStateMachine/statelinker.cc create mode 100644 software/microcontroller/lib/FiniteStateMachine/statelinker.h create mode 100644 software/microcontroller/lib/FiniteStateMachine/statelinker.tpp create mode 100644 software/microcontroller/lib/I2CIP/I2CIP.cc create mode 100644 software/microcontroller/lib/I2CIP/I2CIP.h create mode 100644 software/microcontroller/lib/I2CIP/bst.h create mode 100644 software/microcontroller/lib/I2CIP/bst.tpp create mode 100644 software/microcontroller/lib/I2CIP/debug.h create mode 100644 software/microcontroller/lib/I2CIP/device.cc create mode 100644 software/microcontroller/lib/I2CIP/device.h create mode 100644 software/microcontroller/lib/I2CIP/device.tpp create mode 100644 software/microcontroller/lib/I2CIP/eeprom.cc create mode 100644 software/microcontroller/lib/I2CIP/eeprom.h create mode 100644 software/microcontroller/lib/I2CIP/fqa.cc create mode 100644 software/microcontroller/lib/I2CIP/fqa.h create mode 100644 software/microcontroller/lib/I2CIP/hashtable.h create mode 100644 software/microcontroller/lib/I2CIP/hashtable.tpp create mode 100644 software/microcontroller/lib/I2CIP/library.properties create mode 100644 software/microcontroller/lib/I2CIP/mux.cc create mode 100644 software/microcontroller/lib/I2CIP/mux.h create mode 100644 software/microcontroller/library.properties delete mode 100644 software/microcontroller/src/main.cc delete mode 100644 software/microcontroller/src/sensors/sensor.cc delete mode 100644 software/microcontroller/src/sensors/sensor.h delete mode 100644 software/microcontroller/src/sensors/sht31.cc delete mode 100644 software/microcontroller/src/sensors/sht31.h delete mode 100644 software/microcontroller/test/README.md create mode 100644 software/microcontroller/test/config.h rename software/microcontroller/test/{test_blink/test_blink.cc => test_0_blink/test_0_blink.cc} (100%) create mode 100644 software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc create mode 100644 software/microcontroller/test/test_2_json/test_2_json.cc create mode 100644 software/microcontroller/test/test_3_csos/test_3_csos.cc delete mode 100644 software/microcontroller/test/test_instructions/test_instructions.cc delete mode 100644 software/microcontroller/test/test_k30/test_k30.cc delete mode 100644 software/microcontroller/test/test_messenger/test_messenger.cc delete mode 100644 software/microcontroller/test/test_sht31/test_sht31.cc delete mode 100644 software/microcontroller/test/tests.cc delete mode 100644 software/microcontroller/test/tests.h diff --git a/software/archive/microcontroller/ControlSystemsOS.cc b/software/archive/microcontroller/ControlSystemsOS.cc new file mode 100644 index 00000000..7ed9e23e --- /dev/null +++ b/software/archive/microcontroller/ControlSystemsOS.cc @@ -0,0 +1,754 @@ + +#include + +#include + +#include +#include +#include + +// #define DEBUG 1 + +#include +#include +#include + +// GROUP BY MODULE + +ControlSystemsOS::CSOSModule* ControlSystemsOS::csos_modules[I2CIP_NUM_WIRES][I2CIP_MUX_COUNT] = { { nullptr } }; + +// GLOBAL CONSTANT MAPS + +// Internal Use +char ControlSystemsOS::device_id_map[MAP_INDEX_COUNT][I2CIP_ID_SIZE] = { + {'\0'}, + {'\0'} +}; + +bool ControlSystemsOS::device_id_loaded[MAP_INDEX_COUNT] = { + false, + false +}; + +int ControlSystemsOS::getMapIndex(const i2cip_id_t& id) { + for(unsigned char i = 0; i < MAP_INDEX_COUNT; i++) { + // Compare strings ignoring case + if(strcasecmp_P(id, ControlSystemsOS::device_id_progmem[i]) == 0){ + #ifdef CSOS_DEBUG_SERIAL + const char* s = getDeviceID(i); + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Map Lookup ")); + CSOS_DEBUG_SERIAL.print(i); + CSOS_DEBUG_SERIAL.print(_F(" [ID '")); + CSOS_DEBUG_SERIAL.print(s); + CSOS_DEBUG_SERIAL.print(_F("' @0x")); + CSOS_DEBUG_SERIAL.print((uint16_t)s, HEX); + CSOS_DEBUG_SERIAL.print(_F(" | Factory @0x")); + CSOS_DEBUG_SERIAL.print((uint16_t)device_factory[i], HEX); + CSOS_DEBUG_SERIAL.print("]\n"); + DEBUG_DELAY(); + #endif + return i; + } + } + return -1; +} + +const char* ControlSystemsOS::getDeviceID(uint8_t index) { + // #ifdef CSOS_DEBUG_SERIAL + // DEBUG_DELAY(); + // CSOS_DEBUG_SERIAL.print(_F("-> Map ID ")); + // CSOS_DEBUG_SERIAL.print(index); + // CSOS_DEBUG_SERIAL.print(_F(": ")); + // #endif + if(index >= MAP_INDEX_COUNT) { + // #ifdef CSOS_DEBUG_SERIAL + // CSOS_DEBUG_SERIAL.print(_F("Out of Range!")); + // DEBUG_DELAY(); + // #endif + return nullptr; + } + if(!device_id_loaded[index]) { + // #ifdef CSOS_DEBUG_SERIAL + // CSOS_DEBUG_SERIAL.print(_F("(Loading... ")); + // #endif + if(index == MAP_INDEX_EEPROM) { + return EEPROM::getStaticIDBuffer(); + } else { + // char* s1 = (char*)malloc(I2CIP_ID_SIZE * sizeof(char)); + // char* s1 = new char[I2CIP_ID_SIZE]; + // if(s1 == nullptr) return nullptr; + strcpy_P(device_id_map[index], ControlSystemsOS::device_id_progmem[index]); + // device_id_map[index] = s2; + device_id_loaded[index] = true; + } + // device_id_loaded[index] = (device_id_map[index] != nullptr); + // #ifdef CSOS_DEBUG_SERIAL + // CSOS_DEBUG_SERIAL.print(device_id_loaded[index] ? _F("Success) ") : _F("Fail!)\n")); + // #endif + } + // #ifdef CSOS_DEBUG_SERIAL + // if(device_id_map != nullptr) { + // CSOS_DEBUG_SERIAL.print(_F("ID '")); + // CSOS_DEBUG_SERIAL.print(device_id_map[index]); + // CSOS_DEBUG_SERIAL.print(_F("' @0x")); + // CSOS_DEBUG_SERIAL.print((uint16_t)device_id_map[index], HEX); + // CSOS_DEBUG_SERIAL.print("]\n"); + // } + // DEBUG_DELAY(); + // #endif + return &(device_id_map[index][0]); +} + +using namespace ControlSystemsOS; +using namespace I2CIP; + +CSOSModule::CSOSModule(const uint8_t& wire, const uint8_t& module) : Module(wire, module) { + // Build Maps + +} + +DeviceGroup* CSOSModule::deviceGroupFactory(i2cip_id_t lookup) { + if(lookup == nullptr) { Serial.print("wtf?"); return nullptr; } + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> DeviceGroup Factory (ID '")); + CSOS_DEBUG_SERIAL.print(lookup); + CSOS_DEBUG_SERIAL.print(_F("')\n")); + DEBUG_DELAY(); + #endif + int index = ControlSystemsOS::getMapIndex(lookup); + if(index < 0) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Map Index '")); + CSOS_DEBUG_SERIAL.print(lookup); + CSOS_DEBUG_SERIAL.print(_F("' DNE! Check Libraries.\n")); + DEBUG_DELAY(); + #endif + return nullptr; + } + + const char* id = getDeviceID(index); + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Creating DeviceGroup '")); + CSOS_DEBUG_SERIAL.print(id); + CSOS_DEBUG_SERIAL.print(_F("' (Map Index ")); + CSOS_DEBUG_SERIAL.print(index); + CSOS_DEBUG_SERIAL.print(_F(", Factory @0x")); + CSOS_DEBUG_SERIAL.print((uint16_t)device_factory[index], HEX); + CSOS_DEBUG_SERIAL.print("): "); + DEBUG_DELAY(); + #endif + + // Serial.print(_F("-> Creating DeviceGroup '")); + // Serial.print(id); + // Serial.print(_F("' (Map Index ")); + // Serial.print(index); + // Serial.print(_F(", Factory @0x")); + // Serial.print((uint16_t)device_factory[index], HEX); + // Serial.print("): "); + + DeviceGroup* dg = new DeviceGroup(id, device_factory[index]); + + // if(dg == nullptr) { + // Serial.print(_F("Fail!\n")); + // } else { + // Serial.print(_F("Success!\n")); + // } + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + if(dg == nullptr) { + CSOS_DEBUG_SERIAL.print(_F("Fail!\n")); + } else { + CSOS_DEBUG_SERIAL.print(_F("Success!\n")); + } + DEBUG_DELAY(); + #endif + + return dg; +} + +bool CSOSModule::parseEEPROMContents(const char* buffer) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Deserializing Module EEPROM to JSON: ")); + DEBUG_DELAY(); + #endif + + // 0. Buffer size + size_t buflen = strlen(buffer); + + // 1. EEPROM -> JSON Deserialization + // TODO: Buflen + 1 ? + DeserializationError jsonerr = deserializeJson(this->eeprom_json, buffer, buflen); + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print("Code 0x"); + CSOS_DEBUG_SERIAL.print(jsonerr.code(), 16); + CSOS_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + + if(jsonerr.code() != DeserializationError::Code::Ok) return false; + + // 2. Schema Validation and Loading + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Verifying JSON:\n")); + #endif + + // 2a. Base Array of Busses + if(!this->eeprom_json.is() || !this->eeprom_json[0].is()) { + #ifdef CSOS_DEBUG_SERIAL + CSOS_DEBUG_SERIAL.print(_F("Bad JSON: Invalid Structure!\n")); + DEBUG_DELAY(); + #endif + } + + JsonArray busses = this->eeprom_json.as(); + + int busnum = -1; + for (JsonVariant bus : busses) { + busnum++; + + #ifdef CSOS_DEBUG_SERIAL + CSOS_DEBUG_SERIAL.print("[BUS "); + CSOS_DEBUG_SERIAL.print(busnum+1, HEX); + CSOS_DEBUG_SERIAL.print("]\n"); + #endif + + // 2b. Bus Root Object + if(!bus.is()) { + #ifdef CSOS_DEBUG_SERIAL + CSOS_DEBUG_SERIAL.print(_F("Bad JSON: Invalid Bus Structure!\n")); + DEBUG_DELAY(); + #endif + continue; + } + JsonObject root = bus.as(); + + for (JsonPair kv : root) { + // 2c. Array of I2C Addresses + if(!kv.value().is() || kv.value().isNull()) { + #ifdef CSOS_DEBUG_SERIAL + CSOS_DEBUG_SERIAL.print(_F("Bad JSON: Invalid Entry Value!\n")); + DEBUG_DELAY(); + #endif + continue; + } + + const char* key = kv.key().c_str(); + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print("[ ID '"); + CSOS_DEBUG_SERIAL.print(key); + CSOS_DEBUG_SERIAL.print("']\n"); + #endif + + // Get DeviceGroup + DeviceGroup* dg = this->operator[](key); + if(dg == nullptr) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Group DNE! Check Libraries.\n")); + DEBUG_DELAY(); + #endif + break; + } + + uint8_t numfqas = kv.value().size(); + if(numfqas == 0) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Empty! (Skipping)\n")); + DEBUG_DELAY(); + #endif + continue; + } + i2cip_fqa_t fqas[numfqas]; + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print("-> ["); + DEBUG_DELAY(); + #endif + + uint8_t i = 0; + for (JsonVariant addr : kv.value().as()) { + if(!addr.is()) { continue; } + uint8_t address = addr.as(); + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(" 0x"); + CSOS_DEBUG_SERIAL.print(address, HEX); + DEBUG_DELAY(); + #endif + fqas[i] = createFQA(this->getWireNum(), this->getModuleNum(), (uint8_t)busnum, address); + } + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(" ]\n"); + DEBUG_DELAY(); + #endif + + for (i = 0; i < numfqas; i++) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Adding Device ")); + CSOS_DEBUG_SERIAL.print(i+1); + CSOS_DEBUG_SERIAL.print(" / "); + CSOS_DEBUG_SERIAL.print(numfqas); + CSOS_DEBUG_SERIAL.print(_F(" (Factory @0x")); + CSOS_DEBUG_SERIAL.print((uint16_t)dg->factory, HEX); + CSOS_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + + // Invoke DeviceGroup Call Operator - Returns Matching, or Calls Factory and Adds + i2cip_fqa_t fqa = fqas[i]; + Device* d = (*dg)(fqa); + if(d == nullptr) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Factory Failed! (Skipping)\n")); + DEBUG_DELAY(); + #endif + continue; + } else { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Factory Success! (Adding)\n")); + DEBUG_DELAY(); + #endif + Device* dd = this->add(*d); + if(dd == nullptr) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Couldn't Add Device!\n")); + DEBUG_DELAY(); + #endif + return false; + } + } + } + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Group Complete!\n")); + DEBUG_DELAY(); + #endif + } + } + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> EEPROM Parsed Successfully!\n")); + DEBUG_DELAY(); + #endif + return true; +} + +// Subnetwork state change update + rebuild from discovery +i2cip_errorlevel_t ControlSystemsOS::update(const uint8_t& wire, const uint8_t& mod, bool build) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("= [ NETWORK UPDATE: MODULE ")); + CSOS_DEBUG_SERIAL.print(mod, HEX); + CSOS_DEBUG_SERIAL.print(" ] =\n"); + DEBUG_DELAY(); + #endif + + i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; + CSOSModule* m = csos_modules[wire][mod]; + + if(m == nullptr) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Module DNE; Pinging.\n")); + DEBUG_DELAY(); + #endif + + bool b = MUX::pingMUX(wire, mod); + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + if(b) { CSOS_DEBUG_SERIAL.print(_F("-> Module Found! Creating.\n")); } + else { CSOS_DEBUG_SERIAL.print(_F("-> Module Not Found.\n")); } + DEBUG_DELAY(); + #endif + + if(b) { + // New module found! + m = new CSOSModule(wire, mod); + if(m == nullptr) { + Serial.println("CSOSModule ENOMEM"); + return I2CIP_ERR_SOFT; + } + csos_modules[wire][mod] = m; + + if (build) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Discovering\n")); + DEBUG_DELAY(); + #endif + + bool r = m->discover(); + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Discovery")); + CSOS_DEBUG_SERIAL.print(r ? _F(" Success!\n") : _F(" Failure, Deleting Module.\n")); + DEBUG_DELAY(); + #endif + + if (!r) { + delete m; + csos_modules[wire][mod] = nullptr; + return I2CIP_ERR_SOFT; + } + } + } else { + // No net change + return I2CIP_ERR_NONE; + } + } else { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Updating Module.\n")); + DEBUG_DELAY(); + #endif + } + + // csos_modules_lastChecked[wire][module] = millis(); + + errlev = m->operator()(); + + if(errlev > I2CIP_ERR_NONE) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Fail. Deleting Module.\n")); + DEBUG_DELAY(); + #endif + delete m; + csos_modules[wire][mod] = nullptr; + return errlev; + } + #ifdef CSOS_DEBUG_SERIAL + else { + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Pass! Updating Devices.\n")); + DEBUG_DELAY(); + } + #endif + + for(uint8_t i = 0; i < MAP_INDEX_COUNT; i++) { + const char* id = getDeviceID(i); + if(id == nullptr || id[0] == '\0') continue; + DeviceGroup* dg = (*m)[id]; + if(dg == nullptr) continue; + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> [ DEVICE GROUP ")); + CSOS_DEBUG_SERIAL.print(i+1, HEX); + CSOS_DEBUG_SERIAL.print(" / "); + CSOS_DEBUG_SERIAL.print(MAP_INDEX_COUNT, HEX); + CSOS_DEBUG_SERIAL.print(" ]\n"); + DEBUG_DELAY(); + #endif + + for(uint8_t j = 0; j < dg->numdevices; j++) { + Device* device = dg->devices[j]; + if(device == nullptr) continue; + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("[ DEVICE ")); + CSOS_DEBUG_SERIAL.print(j+1, HEX); + CSOS_DEBUG_SERIAL.print(" / "); + CSOS_DEBUG_SERIAL.print(dg->numdevices, HEX); + CSOS_DEBUG_SERIAL.print(_F(" ]\n-> FQA: ")); + CSOS_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(device->getFQA()), HEX); + CSOS_DEBUG_SERIAL.print(":"); + CSOS_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(device->getFQA()), HEX); + CSOS_DEBUG_SERIAL.print(":"); + CSOS_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(device->getFQA()), HEX); + CSOS_DEBUG_SERIAL.print(":"); + CSOS_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(device->getFQA()), HEX); + CSOS_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + + errlev = (*m)(*device, false); + + #ifdef CSOS_DEBUG_SERIAL + switch(errlev) { + case I2CIP_ERR_NONE: + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> 0x0 Pass!\n")); + DEBUG_DELAY(); + break; + case I2CIP_ERR_SOFT: + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> 0x1 Communication Error!\n")); + DEBUG_DELAY(); + break; + case I2CIP_ERR_HARD: + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> 0x2 Hardware Lost, ABORT!\n")); + DEBUG_DELAY(); + break; + } + #endif + + if(errlev == I2CIP_ERR_HARD) return errlev; + } + } + + return errlev; +} + +// Whole-network state change update +i2cip_errorlevel_t ControlSystemsOS::update(bool build) { + i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("=== [ NETWORK UPDATE ] ===\n")); + DEBUG_DELAY(); + #endif + // 1. Scan for Modules + for(uint8_t wirenum = 0; wirenum < I2CIP_NUM_WIRES; wirenum++) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("== [ WIRE ")); + CSOS_DEBUG_SERIAL.print(wirenum+1, HEX); + CSOS_DEBUG_SERIAL.print(" / "); + CSOS_DEBUG_SERIAL.print(I2CIP_NUM_WIRES, HEX); + CSOS_DEBUG_SERIAL.print(_F("] ==\n")); + DEBUG_DELAY(); + #endif + + for(uint8_t modnum = 0; modnum < I2CIP_MUX_COUNT; modnum++) { + errlev = update(wirenum, modnum, build || csos_modules[wirenum][modnum] == nullptr); + I2CIP_ERR_BREAK(errlev); + } + } + + return errlev; +} + +// Control systems update +i2cip_errorlevel_t ControlSystemsOS::fixedUpdate(unsigned long timestamp, CSOSModule& m) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("= [ CONTROLSYSTEMS UPDATE: MODULE ")); + CSOS_DEBUG_SERIAL.print(m.getModuleNum(), HEX); + CSOS_DEBUG_SERIAL.print(_F(" ] =\n")); + #endif + + i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; + + // 1. FSM Timer Functionality + Chronos.set(timestamp); + + // 2. Control Systems Fixed Update per-ID + for(uint8_t i = 0; i < MAP_INDEX_COUNT; i++) { + const char* id = getDeviceID(i); + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> DeviceGroup '")); + CSOS_DEBUG_SERIAL.print(id); + CSOS_DEBUG_SERIAL.print(_F("'\n")); + DEBUG_DELAY(); + #endif + + DeviceGroup* dg = m[id]; + if(dg == nullptr) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Group '")); + CSOS_DEBUG_SERIAL.print(id); + CSOS_DEBUG_SERIAL.print(_F("' DNE! Check Libraries.\n")); + DEBUG_DELAY(); + #endif + continue; + } + + for(uint8_t j = 0; j < dg->numdevices; j++) { + Device* device = dg->devices[j]; + if(device == nullptr) break; + + if(device->getFQA() == ((const EEPROM&)m).getFQA()) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> SPRT EEPROM! Skipping...\n")); + DEBUG_DELAY(); + #endif + continue; + } + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Updating Device ")); + CSOS_DEBUG_SERIAL.print(j+1); + CSOS_DEBUG_SERIAL.print(" / "); + CSOS_DEBUG_SERIAL.print(dg->numdevices); + CSOS_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + + if(m[*device] == nullptr) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> Device Not In BST! Deleting...\n")); + DEBUG_DELAY(); + #endif + m.remove(device); + continue; + } + + errlev = m(*device, true); + + #ifdef CSOS_DEBUG_SERIAL + switch(errlev) { + case I2CIP_ERR_NONE: + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> 0x0 Pass!\n")); + DEBUG_DELAY(); + break; + case I2CIP_ERR_SOFT: + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> 0x1 Communication Error!\n")); + DEBUG_DELAY(); + break; + case I2CIP_ERR_HARD: + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> 0x2 Hardware Lost, ABORT!\n")); + DEBUG_DELAY(); + break; + } + #endif + + if(errlev == I2CIP_ERR_HARD) return errlev; + + // Sensor JSON Output + if(device->getInput() != nullptr && device->getInput()->getSensor() != nullptr) { + StaticJsonDocument<100> data; + data["id"] = device->getID(); + data["timestamp"] = timestamp; + data.createNestedObject("data"); + + Datum* datum = device->getInput()->getSensor()->datumFactory(); + do { + if(datum == nullptr) break; + + JsonObject d = data["data"].as(); + + datum->addToJSON(d); + + if(datum->next != nullptr) { + Serial.print(','); + } + datum = datum->next; + } while(datum != nullptr); + size_t l = serializeJson(data, Serial); + Serial.print(l); + Serial.println(F(" Bytes")); + // data.remove("id"); + // data.remove("timestamp"); + // data.remove("data"); + data.garbageCollect(); + delay(10); + } + #ifdef CSOS_DEBUG_SERIAL + else { + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("-> No Sensor, Skipping!\n")); + DEBUG_DELAY(); + } + #endif + } + } + + return errlev; + + // RPi: "air-temperature" -> Variable-Sensor(s) Linker -> Sensors (i.e. SHT31_Temperature) + // uC: "sht31" -> ID-Interface Linker -> FQA[], SHT31 -> SHT31_Temperature::read(SHT31(fqa), nullptr, dest); +} + +i2cip_errorlevel_t ControlSystemsOS::fixedUpdate(unsigned long timestamp) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("=== [ CONTROLSYSTEMS UPDATE T + ")); + // Chronograph::debugTimestamp(timestamp); + CSOS_DEBUG_SERIAL.print(_F(" ] ===\n")); + DEBUG_DELAY(); + #endif + + i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; + + // 1. Scan for Modules + for(uint8_t wirenum = 0; wirenum < I2CIP_NUM_WIRES; wirenum++) { + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(_F("== [ WIRE ")); + CSOS_DEBUG_SERIAL.print(wirenum+1, HEX); + CSOS_DEBUG_SERIAL.print(" / "); + CSOS_DEBUG_SERIAL.print(I2CIP_NUM_WIRES, HEX); + CSOS_DEBUG_SERIAL.print(_F("] ==\n")); + DEBUG_DELAY(); + #endif + + for(uint8_t modnum = 0; modnum < I2CIP_MUX_COUNT; modnum++) { + CSOSModule* m = csos_modules[wirenum][modnum]; + if(m == nullptr) continue; + errlev = fixedUpdate(timestamp, *m); + + if(errlev == I2CIP_ERR_HARD) return errlev; + } + } + + return errlev; + +} + +// void ControlSystemsOS::deleteDevice(Device* device) { +// if(device == nullptr) return; + +// // Pick module from FQA +// uint8_t wirenum = I2CIP_FQA_SEG_I2CBUS(device->getFQA()); +// uint8_t modnum = I2CIP_FQA_SEG_MODULE(device->getFQA()); +// CSOSModule* m = csos_modules[wirenum][modnum]; +// if(m == nullptr) return; + +// m->remove(device); +// } + +// template i2cip_errorlevel_t ControlSystemsOS::handleInputDevice(Device* device, const A& args) { +// InputGetter* input = device->getInput(); +// if(input == nullptr) return I2CIP_ERR_SOFT; + +// InputInterface* interface = (InputInterface*) input; +// G value = interface->getCache(); +// i2cip_errorlevel_t errlev = interface->get(value, args); +// Serial.print("Value: "); +// Serial.print(value); +// return errlev; +// } + +// template i2cip_errorlevel_t ControlSystemsOS::handleOutputDevice(Device* device, const S& value, const B& args) { +// OutputSetter* output = device->getOutput(); +// if(output == nullptr) return I2CIP_ERR_SOFT; + +// OutputInterface* interface = (OutputInterface*) output; +// i2cip_errorlevel_t errlev = interface->set(value, args); +// return errlev; +// } \ No newline at end of file diff --git a/software/archive/microcontroller/ControlSystemsOS.h b/software/archive/microcontroller/ControlSystemsOS.h new file mode 100644 index 00000000..729a04b7 --- /dev/null +++ b/software/archive/microcontroller/ControlSystemsOS.h @@ -0,0 +1,91 @@ +#ifndef CSOS_CSOS_H_ +#define CSOS_CSOS_H_ + +#include +#include +#include + +#include + +#include + +#ifndef MAP_INDEX_COUNT +#define MAP_INDEX_COUNT 1 +#endif + +#include +// #include + +#define CSOS_MODULE_JSON_DOCSIZE (size_t)(I2CIP_EEPROM_SIZE * 3 / 2) + +using namespace I2CIP; + +typedef i2cip_errorlevel_t (* interfaceHandler_t)(Device*); + +namespace ControlSystemsOS { + + // MODULE DEFINITION + + class CSOSModule : public Module { + private: + StaticJsonDocument eeprom_json; + + protected: + DeviceGroup* deviceGroupFactory(i2cip_id_t id) override; + + bool parseEEPROMContents(const char* eeprom_contents) override; + + public: + CSOSModule(const uint8_t& wire, const uint8_t& module); + + ~CSOSModule() { } + }; + + + i2cip_errorlevel_t update(bool build = false); + i2cip_errorlevel_t update(const uint8_t& wire, const uint8_t& mod, bool build = true); + i2cip_errorlevel_t fixedUpdate(unsigned long timestamp); + i2cip_errorlevel_t fixedUpdate(unsigned long timestamp, CSOSModule& m); + + extern CSOSModule* csos_modules[I2CIP_NUM_WIRES][I2CIP_MUX_COUNT]; + extern char device_id_map[MAP_INDEX_COUNT][I2CIP_ID_SIZE]; + extern bool device_id_loaded[MAP_INDEX_COUNT]; + + // static Linker linker; + + // extern fsm_timestamp_t csos_modules_lastChecked[I2CIP_NUM_WIRES][I2CIP_MUX_COUNT]; + + // extern const csos_types_t csos_map_interface_state[]; + // extern const csos_types_t csos_map_interface_args[]; + + // extern bool stateChange; + + // new SHT31(fqa) + + // void initialize(void); + + + // i2cip_errorlevel_t handleDevice(Device* device); + // template i2cip_errorlevel_t handleInputDevice(Device* device, const A& args); + // template i2cip_errorlevel_t handleOutputDevice(Device* device, const S& value, const B& args); + + /** + * Find a device group by ID. + * @param id + * @returns Pointer to the device group (`nullptr` if none) + */ + // DeviceGroup* operator[](const char* id); + + /** + * Find a device ID by FQA. + * @param fqa + * @returns Pointer to the device ID (`nullptr` if none) + */ + // const char* operator[](const i2cip_fqa_t& fqa); + + // const i2cip_devicetree_t& getDevices(void); + + // const i2cip_devicetable_t& getDeviceGroups(void); +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/actuators/actuator.cc b/software/archive/microcontroller/actuators/actuator.cc similarity index 100% rename from software/microcontroller/src/actuators/actuator.cc rename to software/archive/microcontroller/actuators/actuator.cc diff --git a/software/microcontroller/src/actuators/actuator.h b/software/archive/microcontroller/actuators/actuator.h similarity index 100% rename from software/microcontroller/src/actuators/actuator.h rename to software/archive/microcontroller/actuators/actuator.h diff --git a/software/microcontroller/src/actuators/led.cc b/software/archive/microcontroller/actuators/led.cc similarity index 100% rename from software/microcontroller/src/actuators/led.cc rename to software/archive/microcontroller/actuators/led.cc diff --git a/software/microcontroller/src/actuators/led.h b/software/archive/microcontroller/actuators/led.h similarity index 100% rename from software/microcontroller/src/actuators/led.h rename to software/archive/microcontroller/actuators/led.h diff --git a/software/microcontroller/src/actuators/onoff.cc b/software/archive/microcontroller/actuators/onoff.cc similarity index 100% rename from software/microcontroller/src/actuators/onoff.cc rename to software/archive/microcontroller/actuators/onoff.cc diff --git a/software/microcontroller/src/actuators/onoff.h b/software/archive/microcontroller/actuators/onoff.h similarity index 100% rename from software/microcontroller/src/actuators/onoff.h rename to software/archive/microcontroller/actuators/onoff.h diff --git a/software/microcontroller/src/actuators/pwm.cc b/software/archive/microcontroller/actuators/pwm.cc similarity index 100% rename from software/microcontroller/src/actuators/pwm.cc rename to software/archive/microcontroller/actuators/pwm.cc diff --git a/software/microcontroller/src/actuators/pwm.h b/software/archive/microcontroller/actuators/pwm.h similarity index 100% rename from software/microcontroller/src/actuators/pwm.h rename to software/archive/microcontroller/actuators/pwm.h diff --git a/software/microcontroller/src/actuators/solenoid.cc b/software/archive/microcontroller/actuators/solenoid.cc similarity index 100% rename from software/microcontroller/src/actuators/solenoid.cc rename to software/archive/microcontroller/actuators/solenoid.cc diff --git a/software/microcontroller/src/actuators/solenoid.h b/software/archive/microcontroller/actuators/solenoid.h similarity index 100% rename from software/microcontroller/src/actuators/solenoid.h rename to software/archive/microcontroller/actuators/solenoid.h diff --git a/software/microcontroller/src/actuators/supply.cc b/software/archive/microcontroller/actuators/supply.cc similarity index 100% rename from software/microcontroller/src/actuators/supply.cc rename to software/archive/microcontroller/actuators/supply.cc diff --git a/software/microcontroller/src/actuators/supply.h b/software/archive/microcontroller/actuators/supply.h similarity index 100% rename from software/microcontroller/src/actuators/supply.h rename to software/archive/microcontroller/actuators/supply.h diff --git a/software/microcontroller/src/actuators/tec.cc b/software/archive/microcontroller/actuators/tec.cc similarity index 100% rename from software/microcontroller/src/actuators/tec.cc rename to software/archive/microcontroller/actuators/tec.cc diff --git a/software/microcontroller/src/actuators/tec.h b/software/archive/microcontroller/actuators/tec.h similarity index 100% rename from software/microcontroller/src/actuators/tec.h rename to software/archive/microcontroller/actuators/tec.h diff --git a/software/archive/microcontroller/debug.h b/software/archive/microcontroller/debug.h new file mode 100644 index 00000000..74df0c70 --- /dev/null +++ b/software/archive/microcontroller/debug.h @@ -0,0 +1,38 @@ +#ifndef CSOS_DEBUG_SERIAL + +#include + +// #define DEBUG 1 // Uncomment to enable debug + +// BASIC DEBUG MACRO +#ifdef DEBUG +#if DEBUG == 1 +#ifndef DEBUG_SERIAL +#define DEBUG_SERIAL Serial +#endif +#endif +#endif + +// CROSS-LIBRARY DEBUG COMPATIBILITY +#ifdef DEBUG_SERIAL +#define CSOS_DEBUG_SERIAL DEBUG_SERIAL +#endif + +// DEBUG DELAY MACRO FOR SERIAL OUTPUT STABILITY (OPTIONAL) +#ifdef CSOS_DEBUG_SERIAL +#ifndef DEBUG_DELAY +#define DEBUG_DELAY() {delay(10);} +#endif +#endif + +// #define DEBUG_DISABLE_FSTRINGS 1 +#ifdef DEBUG_DISABLE_FSTRINGS +#define _F(x) x +#else +#define _F(x) F(x) +#endif + +#endif + +// #include <../I2CIP/debug.h> +// #include <../FiniteStateMachine/debug.h> \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/adc.cc b/software/archive/microcontroller/interfaces/adc.cc new file mode 100644 index 00000000..0ca6840c --- /dev/null +++ b/software/archive/microcontroller/interfaces/adc.cc @@ -0,0 +1,107 @@ +#include + +#include +#include + +using namespace ControlSystemsOS; + +ControlSystemsOS::ADC::ADC(const i2cip_fqa_t& fqa, const i2cip_id_t& id) : Device(fqa, id), InputInterface((Device*)this) { } + +i2cip_errorlevel_t ControlSystemsOS::ADC::get(float& dest, const args_adc_t& args) { + // Set config register values + uint16_t config = + ADC_REG_CONFIG_CQUE_1CONV | // Set CQUE to any value other than none so we can use it in RDY mode + ADC_REG_CONFIG_CLAT_NONLAT | // Non-latching (default val) + ADC_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val) + ADC_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val) + ADC_REG_CONFIG_MODE_SINGLE | // Single-ended discrete reading + ADC_GAIN | // Gain setting + ADC_SPS | // Sampling rate setting + ADC_CHANNEL_TO_MUX(args) | // Set MUX from channel # + ADC_REG_CONFIG_OS_SINGLE; // Set 'start single-conversion' bit + + // Overwrite config register + i2cip_errorlevel_t errlev = this->writeRegister(ADC_REG_POINTER_CONFIG, config); + I2CIP_ERR_BREAK(errlev); + + // Write threshold registers + uint16_t instr = 0x8000; + errlev = this->writeRegister(ADC_REG_POINTER_HITHRESH, instr); + I2CIP_ERR_BREAK(errlev); + + instr = 0x0; + errlev = this->writeRegister(ADC_REG_POINTER_LOWTHRESH, instr); + I2CIP_ERR_BREAK(errlev); + + // Wait for the conversion to complete + uint8_t timeout = 0; + uint16_t ready = 0; + do { + if(timeout == ADC_TIMEOUT) { + errlev = I2CIP_ERR_SOFT; + break; + } + errlev = this->readRegisterWord(ADC_REG_POINTER_CONFIG, ready); + timeout++; + } while(((ready & 0x8000) == 0) && (errlev == I2CIP_ERR_NONE)); + I2CIP_ERR_BREAK(errlev); + + // Read the conversion results + uint16_t result; + errlev = this->readRegisterWord(ADC_REG_POINTER_CONVERT, result); + I2CIP_ERR_BREAK(errlev); + uint8_t buf [2] = { 0 }; + size_t readlen = 2; + result &= this->read(buf, readlen); + + // Shift 12-bit results right 4 bits for the ADS1015, making sure we keep the sign bit intact + uint16_t res = (((uint16_t)buf[0] << 8) | buf[1]) >> ADC_SHIFT; + if (res > 0x07FF) { + // negative number - extend the sign to 16th bit + res |= 0xF000; + } + dest = ControlSystemsOS::ADC::computeVolts((int16_t)res); + return errlev; +} + +const args_adc_t& ControlSystemsOS::ADC::getDefaultA(void) const { + return ControlSystemsOS::ADC::default_a; +} + +void ControlSystemsOS::ADC::clearCache(void) { + this->setCache(ControlSystemsOS::ADC::default_cache); + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(F("ADC Cache Cleared\n")); + DEBUG_DELAY(); + #endif +} + +float ControlSystemsOS::ADC::computeVolts(int16_t counts) { + // see data sheet Table 3 + float fsRange; + switch (ADC_GAIN) { + case GAIN_TWOTHIRDS: + fsRange = 6.144f; + break; + case GAIN_ONE: + fsRange = 4.096f; + break; + case GAIN_TWO: + fsRange = 2.048f; + break; + case GAIN_FOUR: + fsRange = 1.024f; + break; + case GAIN_EIGHT: + fsRange = 0.512f; + break; + case GAIN_SIXTEEN: + fsRange = 0.256f; + break; + default: + fsRange = 0.0f; + } + return counts * (fsRange / (32768 >> ADC_SHIFT)); +} \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/adc.h b/software/archive/microcontroller/interfaces/adc.h new file mode 100644 index 00000000..0a3a4bb1 --- /dev/null +++ b/software/archive/microcontroller/interfaces/adc.h @@ -0,0 +1,142 @@ +#ifndef CSOS_INTERFACES_ADC_H_ +#define CSOS_INTERFACES_ADC_H_ + +#include +#include + +// Not sure if this will break something. Let's try it and find out +#ifdef ADC +#undef ADC +#endif + +// REGISTERS + +// Pointer register +#define ADC_REG_POINTER_MASK (uint8_t)0x03 // Point mask +#define ADC_REG_POINTER_CONVERT (uint8_t)0x00 // Conversion +#define ADC_REG_POINTER_CONFIG (uint8_t)0x01 // Configuration +#define ADC_REG_POINTER_LOWTHRESH (uint8_t)0x02 // Low threshold +#define ADC_REG_POINTER_HITHRESH (uint8_t)0x03 // High threshold + +// Config register: OS bit +#define ADC_REG_CONFIG_OS_MASK (uint16_t)0x8000 // OS Mask +#define ADC_REG_CONFIG_OS_SINGLE (uint16_t)0x8000 // Write: Set to start a single-conversion +#define ADC_REG_CONFIG_OS_BUSY (uint16_t)0x0000 // Read: Bit = 0 when conversion is in progress +#define ADC_REG_CONFIG_OS_NOTBUSY (uint16_t)0x8000 // Read: Bit = 1 when device is not performing a conversion + +// Config register: MUX bits +#define ADC_REG_CONFIG_MUX_MASK (uint16_t)0x7000 // Mux Mask +#define ADC_REG_CONFIG_MUX_DIFF_0_1 (uint16_t)0x0000 // Differential P = AIN0, N = AIN1 (default) +#define ADC_REG_CONFIG_MUX_DIFF_0_3 (uint16_t)0x1000 // Differential P = AIN0, N = AIN3 +#define ADC_REG_CONFIG_MUX_DIFF_1_3 (uint16_t)0x2000 // Differential P = AIN1, N = AIN3 +#define ADC_REG_CONFIG_MUX_DIFF_2_3 (uint16_t)0x3000 // Differential P = AIN2, N = AIN3 +#define ADC_REG_CONFIG_MUX_SINGLE_0 (uint16_t)0x4000 // Single-ended AIN0 +#define ADC_REG_CONFIG_MUX_SINGLE_1 (uint16_t)0x5000 // Single-ended AIN1 +#define ADC_REG_CONFIG_MUX_SINGLE_2 (uint16_t)0x6000 // Single-ended AIN2 +#define ADC_REG_CONFIG_MUX_SINGLE_3 (uint16_t)0x7000 // Single-ended AIN3 + +// Config register: Gain bits +#define ADC_REG_CONFIG_PGA_MASK (uint16_t)0x0E00 // PGA Mask +#define ADC_REG_CONFIG_PGA_6_144V (uint16_t)0x0000 // +/-6.144V range = Gain 2/3 +#define ADC_REG_CONFIG_PGA_4_096V (uint16_t)0x0200 // +/-4.096V range = Gain 1 +#define ADC_REG_CONFIG_PGA_2_048V (uint16_t)0x0400 // +/-2.048V range = Gain 2 (default) +#define ADC_REG_CONFIG_PGA_1_024V (uint16_t)0x0600 // +/-1.024V range = Gain 4 +#define ADC_REG_CONFIG_PGA_0_512V (uint16_t)0x0800 // +/-0.512V range = Gain 8 +#define ADC_REG_CONFIG_PGA_0_256V (uint16_t)0x0A00 // +/-0.256V range = Gain 16 + +// Config register: ADC mode bit +#define ADC_REG_CONFIG_MODE_MASK (uint16_t)0x0100 // Mode Mask +#define ADC_REG_CONFIG_MODE_CONTIN (uint16_t)0x0000 // Continuous conversion mode +#define ADC_REG_CONFIG_MODE_SINGLE (uint16_t)0x0100 // Power-down single-shot mode (default) + +// Config register: sample rate bits +#define ADC_REG_CONFIG_RATE_MASK (uint16_t)0x00E0 // Data Rate Mask +#define ADC_RATE_128SPS (uint16_t)0x0000 // 128 samples per second +#define ADC_RATE_250SPS (uint16_t)0x0020 // 250 samples per second +#define ADC_RATE_490SPS (uint16_t)0x0040 // 490 samples per second +#define ADC_RATE_920SPS (uint16_t)0x0060 // 920 samples per second +#define ADC_RATE_1600SPS (uint16_t)0x0080 // 1600 samples per second (default) +#define ADC_RATE_2400SPS (uint16_t)0x00A0 // 2400 samples per second +#define ADC_RATE_3300SPS (uint16_t)0x00C0 // 3300 samples per second + +// Config register: Comparator mode bit +#define ADC_REG_CONFIG_CMODE_MASK (uint16_t)0x0010 // CMode Mask +#define ADC_REG_CONFIG_CMODE_TRAD (uint16_t)0x0000 // Traditional comparator with hysteresis (default) +#define ADC_REG_CONFIG_CMODE_WINDOW (uint16_t)0x0010 // Window comparator + +// Config register: Comparator polarity bit +#define ADC_REG_CONFIG_CPOL_MASK (uint16_t)0x0008 // CPol Mask +#define ADC_REG_CONFIG_CPOL_ACTVLOW (uint16_t)0x0000 // ALERT/RDY pin is low when active (default) +#define ADC_REG_CONFIG_CPOL_ACTVHI (uint16_t)0x0008 // ALERT/RDY pin is high when active + +// Config register: Comparator latching bit +#define ADC_REG_CONFIG_CLAT_MASK (uint16_t)0x0004 // Determines if ALERT/RDY pin latches once asserted +#define ADC_REG_CONFIG_CLAT_NONLAT (uint16_t)0x0000 // Non-latching comparator (default) +#define ADC_REG_CONFIG_CLAT_LATCH (uint16_t)0x0004 // Latching comparator + +// Config register: Comparator queue bits +#define ADC_REG_CONFIG_CQUE_MASK (uint16_t)0x0003 // CQue Mask +#define ADC_REG_CONFIG_CQUE_1CONV (uint16_t)0x0000 // Assert ALERT/RDY after one conversions +#define ADC_REG_CONFIG_CQUE_2CONV (uint16_t)0x0001 // Assert ALERT/RDY after two conversions +#define ADC_REG_CONFIG_CQUE_4CONV (uint16_t)0x0002 // Assert ALERT/RDY after four conversions +#define ADC_REG_CONFIG_CQUE_NONE (uint16_t)0x0003 // Disable the comparator and put ALERT/RDY in high state (default) + +// HELPERS +#define ADC_CHANNEL_TO_MUX(channel) (0x4000 + (channel) * 0x1000) + +// SETTINGS +#define ADC_ADDR 0x48 +#define ADC_SHIFT 4 +#define ADC_GAIN ADC::GAIN_TWOTHIRDS // +/-6.144V for 0-5V single-ended analogRead +#define ADC_SPS ADC_RATE_1600SPS +#define ADC_TIMEOUT 100 + +using namespace I2CIP; + +typedef enum { + ADC_CHANNEL_NULL = 0xFF, + ADC_CHANNEL_0 = 0x0, + ADC_CHANNEL_1, + ADC_CHANNEL_2, + ADC_CHANNEL_3 +} args_adc_t; + +namespace ControlSystemsOS { + + // Interface class for the ADS1015 12-bit ADC IC. Reads analog voltage (range: +/-6.144V) + class ADC : public Device, public InputInterface { + friend Device* ControlSystemsOS::adcFactory(const i2cip_fqa_t& fqa); + + private: + const float default_cache = 0.0f; + const args_adc_t default_a = ADC_CHANNEL_NULL; + + // Gain settings + typedef enum { + GAIN_TWOTHIRDS = ADC_REG_CONFIG_PGA_6_144V, + GAIN_ONE = ADC_REG_CONFIG_PGA_4_096V, + GAIN_TWO = ADC_REG_CONFIG_PGA_2_048V, + GAIN_FOUR = ADC_REG_CONFIG_PGA_1_024V, + GAIN_EIGHT = ADC_REG_CONFIG_PGA_0_512V, + GAIN_SIXTEEN = ADC_REG_CONFIG_PGA_0_256V + } adc_gain_t; + + static float computeVolts(int16_t counts); + + ADC(const i2cip_fqa_t& fqa, const i2cip_id_t& id); + + public: + /** + * Read an ADC channel. + * @param fqa + * @param dest Pin state + * @param args Pin number + **/ + i2cip_errorlevel_t get(float& dest, const args_adc_t& args) override; + + const args_adc_t& getDefaultA(void) const override; + void clearCache(void) override; + }; +}; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/eeprom.cc b/software/archive/microcontroller/interfaces/eeprom.cc new file mode 100644 index 00000000..fd1107cf --- /dev/null +++ b/software/archive/microcontroller/interfaces/eeprom.cc @@ -0,0 +1,46 @@ +#include + +#include + +#include +#include +#include + +EEPROM::EEPROM(MUX* mux, uint8_t addr, uint8_t bus) : Interface(addr, mux, bus), size(EEPROM_SIZE) { } + +EEPROM::EEPROM(MUX* mux, size_t size, uint8_t addr, uint8_t bus) : Interface(addr, mux, bus), size(size) { } + +/** + * Reads until the first null ('/-') character, or the end of the EEPROM, whichever comes first. + * @param buff Pointer to the buffer to copy the contents into. + * @param len Pointer to the size_t into which to copy the number of bytes read. + * @return Error Level + */ +errorlevel_t EEPROM::read(char* buff, size_t* len) { + // Multiple operations being performed, reset the MUX manually + mux. + + // Byte currently being read + uint16_t bytenum = 0; + for (uint16_t x = 0; x < size; x++) { + // Request data from the EEPROM chip at byte `bytenum` + errorlevel_t result = thisinterface.write(bytenum); + // Store the requested data (copy as a byte pointer) + result &= thisinterface.read((uint8_t*)buff, 1, false); + if (result != ERR_NONE) { + // Failed, stop + buff[x] = '\0'; + *len = x+1; + return ERR_SOFT; + } + if (buff[0] == '\0') { + // END + *len = x+1; + break; + } + buff[x] = buff[0]; + + bytenum++; + } + return ERR_NONE; +} \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/eeprom.h b/software/archive/microcontroller/interfaces/eeprom.h new file mode 100644 index 00000000..cede5342 --- /dev/null +++ b/software/archive/microcontroller/interfaces/eeprom.h @@ -0,0 +1,29 @@ +#ifndef CSOS_INTERFACES_EEPROM_H_ +#define CSOS_INTERFACES_EEPROM_H_ + +#define EEPROM_ADDR 0x50 // Default device address +#define EEPROM_SIZE 200 // Bytes + +#include + +#include +#include +#include + +// Interface class for EEPROM +class EEPROM : public Interface { + private: + const size_t size; + public: + EEPROM(MUX* mux, uint8_t addr = EEPROM_ADDR, uint8_t bus = MUX_BUS_DEFAULT); + EEPROM(MUX* mux, size_t size, uint8_t addr = EEPROM_ADDR, uint8_t bus = MUX_BUS_DEFAULT); + + /** + * Read from the EEPROM into a buffer, until '\0' + * @param buffer The buffer to read into + * @param len Pointer to a variable for how many bytes were read + */ + errorlevel_t read(char* buffer, size_t* len); +}; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/gpio.cc b/software/archive/microcontroller/interfaces/gpio.cc new file mode 100644 index 00000000..8583e4b8 --- /dev/null +++ b/software/archive/microcontroller/interfaces/gpio.cc @@ -0,0 +1,69 @@ +#include + +#include +#include + +using namespace ControlSystemsOS; + +GPIO::GPIO(const i2cip_fqa_t& fqa, const i2cip_id_t& id) : Device(fqa, id), IOInterface((Device*)this) { } + +i2cip_errorlevel_t GPIO::get(state_gpio_t& dest, const args_gpio_t& args) { + // Set pin mode + i2cip_errorlevel_t errlev = this->pinMode(args, GPIO_PINMODE_INPUT); + I2CIP_ERR_BREAK(errlev); + + // Read in register + uint8_t value; + errlev = this->readRegisterByte(GPIO_PIN_REG(GPIO_GPIO, args), value); + dest = (errlev > I2CIP_ERR_NONE ? GPIO_PIN_UNDEF : (state_gpio_t)READ_BITS(value, GPIO_PIN_SHIFT(args), 1)); + return errlev; +} + +i2cip_errorlevel_t GPIO::set(const state_gpio_t& value, const args_gpio_t& args) { + if(value == GPIO_PIN_UNDEF) { + return I2CIP_ERR_SOFT; + } + + // Set pin mode + i2cip_errorlevel_t errlev = this->pinMode(args, GPIO_PINMODE_OUTPUT); + I2CIP_ERR_BREAK(errlev); + + // Read register + uint8_t existing; + errlev = this->readRegisterByte(GPIO_PIN_REG(GPIO_GPIO, args), existing); + I2CIP_ERR_BREAK(errlev); + + // Write register + return this->writeRegister(GPIO_PIN_REG(GPIO_GPIO, args), OVERWRITE_BITS(existing, value, (args % 8), 1)); +} + +i2cip_errorlevel_t GPIO::pinMode(const args_gpio_t& pin, const gpio_pinmode_t& mode) { + // Read in register + uint8_t existing; + i2cip_errorlevel_t errlev = this->readRegisterByte(GPIO_PIN_REG(GPIO_IODIR, pin), existing); + I2CIP_ERR_BREAK(errlev); + + // Write to register + return this->writeRegister(GPIO_PIN_REG(GPIO_IODIR, pin), OVERWRITE_BITS(existing, mode, (pin % 8), 1)); +} + +// args_gpio_t getDefaultA(void) const override; +// state_gpio_t getDefaultCache(void) const override; +// args_gpio_t getDefaultB(void) const override; +// state_gpio_t getFailsafe(void) const override; + +const args_gpio_t& GPIO::getDefaultA(void) const { + return default_a; +} + +void GPIO::clearCache(void) { + this->setCache(default_cache); +} + +const args_gpio_t& GPIO::getDefaultB(void) const { + return default_b; +} + +void GPIO::resetFailsafe(void) { + this->setValue(default_failsafe); +} \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/gpio.h b/software/archive/microcontroller/interfaces/gpio.h new file mode 100644 index 00000000..9b03eac5 --- /dev/null +++ b/software/archive/microcontroller/interfaces/gpio.h @@ -0,0 +1,110 @@ +#ifndef CSOS_INTERFACES_GPIO_H_ +#define CSOS_INTERFACES_GPIO_H_ + +#include +#include + +// MACROS +// Registers +#define GPIO_IODIR 0x00 // I/O direction register +#define GPIO_IPOL 0x02 // Input polarity register +#define GPIO_GPINTEN 0x04 // Interrupt-on-change control register +#define GPIO_DEFVAL 0x06 // Default compare register for interrupt-on-change +#define GPIO_INTCON 0x08 // Interrupt control register +#define GPIO_IOCON 0x0A // Configuration register +#define GPIO_GPPU 0x0C // Pull-up resistor configuration register +#define GPIO_INTF 0x0E // Interrupt flag register +#define GPIO_INTCAP 0x10 // Interrupt capture register +#define GPIO_GPIO 0x12 // Port register +#define GPIO_OLAT 0x14 // Output latch register + +// Constants +#define GPIO_NUMPINS 16 + +// Settings +#define GPIO_ADDR 0x20 // Default address + +/** + * Determine shifted register address from pin number. + **/ +#define GPIO_PIN_REG(reg, pin) (uint8_t)((pin < 8) ? reg : reg+1) + +#define GPIO_PIN_SHIFT(pin) (pin % 8) + +typedef enum { + GPIO_PIN_NULL = 0xFF, + GPIO_PIN_A0 = 0, + GPIO_PIN_A1, + GPIO_PIN_A2, + GPIO_PIN_A3, + GPIO_PIN_A4, + GPIO_PIN_A5, + GPIO_PIN_A6, + GPIO_PIN_A7, + GPIO_PIN_B0, + GPIO_PIN_B1, + GPIO_PIN_B2, + GPIO_PIN_B3, + GPIO_PIN_B4, + GPIO_PIN_B5, + GPIO_PIN_B6, + GPIO_PIN_B7, +} args_gpio_t; + +typedef enum { + GPIO_PIN_UNDEF = 0xFF, + GPIO_PIN_LOW = LOW, + GPIO_PIN_HIGH = HIGH +} state_gpio_t; + +typedef enum { + // GPIO_PINMODE_UNDEF = 0xFF, + GPIO_PINMODE_OUTPUT = 0, + GPIO_PINMODE_INPUT = 1 +} gpio_pinmode_t; + +using namespace I2CIP; + +namespace ControlSystemsOS { + + // Interface class for the MCP23017 16-pin GPIO IC + class GPIO : public Device, public IOInterface { + friend Device* gpioFactory(const i2cip_fqa_t& fqa); + + private: + const state_gpio_t default_cache = GPIO_PIN_UNDEF; + const args_gpio_t default_a = GPIO_PIN_NULL; + const state_gpio_t default_failsafe = GPIO_PIN_LOW; + const args_gpio_t default_b = GPIO_PIN_NULL; + /** + * @param fqa + * @param pin Pin number + * @param mode Pin mode + **/ + i2cip_errorlevel_t pinMode(const args_gpio_t& pin, const gpio_pinmode_t& mode); + + GPIO(const i2cip_fqa_t& fqa, const i2cip_id_t& id); + + public: + /** + * Read a GPIO pin. + * @param dest Pin state + * @param args Pin number + **/ + i2cip_errorlevel_t get(state_gpio_t& dest, const args_gpio_t& args) override; + + /** + * Write to a GPIO pin. + * @param dest Pin state + * @param args Pin number + **/ + i2cip_errorlevel_t set(const state_gpio_t& value, const args_gpio_t& args) override; + + const args_gpio_t& getDefaultA(void) const override; + void clearCache(void) override; + const args_gpio_t& getDefaultB(void) const override; + void resetFailsafe(void) override; + }; +}; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/interface.cc b/software/archive/microcontroller/interfaces/interface.cc new file mode 100644 index 00000000..9ec27af7 --- /dev/null +++ b/software/archive/microcontroller/interfaces/interface.cc @@ -0,0 +1,109 @@ +#include + +#include +#include +#include +#include + +#include +#include +#include + +Interface::Interface(uint8_t addr, MUX* mux, uint8_t bus, TwoWire& wire = Wire) : Adafruit_I2CDevice(addr, &wire), mux(mux), bus(bus) { } + +Interface::Interface(uint8_t addr, TwoWire& wire = Wire) : Adafruit_I2CDevice(addr, &wire), mux(nullptr), bus(0x0) {} + +errorlevel_t Interface::begin(void) { + return ping() ? ERR_NONE : ERR_HARD; +} + +bool Interface::ping(void) { + // `ping()` just looks better okayyy + return thisdevice.begin(); +} + +errorlevel_t Interface::write(const uint8_t* buffer, size_t len, bool reset) { + errorlevel_t status = ERR_NONE; + + // If this interface is on a module, switch the MUX to the correct bus + if (mux != nullptr) { + status &= mux->setBus(bus); + } + + // Device alive? + if(!ping()){ + // Device lost! + return ERR_HARD; + } + + if(!thisdevice.write(buffer, len)) { + status = ERR_SOFT; + } + + // If this interface is on a module, switch the module's MUX to the "inactive" bus (to avoid unintentional messaging) + if (mux != nullptr) { + status &= mux->resetBus(); + } + + return status; +} + +errorlevel_t Interface::write(const uint8_t b, bool reset) { + return write(&b, 1, reset); +} + +errorlevel_t Interface::write(const uint16_t b, bool reset) { + return writeRegister((uint8_t)(b >> 8), (uint8_t)(b & 0xFF)); +} + +errorlevel_t Interface::writeRegister(uint8_t reg, uint8_t value) { + uint8_t buf[2] = { reg, value }; + return write(buf, 2); +} + +errorlevel_t Interface::writeRegister(uint8_t reg, uint16_t value) { + uint8_t buf[3] = { reg, value >> 8, value & 0xFF }; + return write(buf, 3); +} + +errorlevel_t Interface::read(uint8_t* buffer, size_t len, bool reset) { + errorlevel_t status = ERR_NONE; + + // If this interface is on a module, switch the MUX to the correct bus + if (mux != nullptr) { + status &= mux->setBus(bus); + } + + // Device alive? + if(!ping()){ + // Device lost! + return ERR_HARD; + } + + // Attempt to read from the device + if(!thisdevice.read(buffer, len)) { + status &= ERR_SOFT; + } + + // If this interface is on a module, switch the module's MUX to the "inactive" bus (to avoid unintentional messaging) + if (mux != nullptr) { + mux->resetBus(); + } + + return status; +} + +template static S* I2CSensorFactory(Interface* i, JsonObject* args) { + // I2C device; `i` is ignored + // Check for all keys + if(args->containsKey("addr") && args->containsKey("bus") && args->containsKey("moduleaddr")) { + // Parse + uint8_t addr = args->getMember("addr").as(); + uint8_t bus = args->getMember("bus").as(); + uint8_t moduleaddr = args->getMember("moduleaddr").as(); + // Construct + return new S(addr, bus, moduleaddr); + } else { + return nullptr; + } +} \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/interface.h b/software/archive/microcontroller/interfaces/interface.h new file mode 100644 index 00000000..a3294244 --- /dev/null +++ b/software/archive/microcontroller/interfaces/interface.h @@ -0,0 +1,112 @@ +#ifndef CSOS_INTERFACES_INTERFACE_H_ +#define CSOS_INTERFACES_INTERFACE_H_ + +// Forward declaration to fix circular dependancy +class Interface; + +// HEADERS + +#include +#include +#include + +#include +#include + +extern TwoWire Wire; + +// CLASS +// An Interface is a single I2C peripheeral +// It is used as a generic I2C communications base class for all device classes +class Interface { + protected: + // Kept protected in case inherited classes need direct MUX access. + MUX* mux; + uint8_t bus; + // Reusable reference reference to this object as an Adafruit_I2CDevice. All methods/fields inaccessible. + Adafruit_I2CDevice& thisdevice = (Adafruit_I2CDevice&)(*this); + + // Reusable reference reference to this object as an Adafruit_I2CDevice. All methods/fields inaccessible. + Interface& thisinterface = (*this); + public: + /** + * Constructor. + * @param addr Device address + * @param bus Module MUX's bus that this device is on + * @param mux Module's MUX + * @param wire I2C bus + */ + Interface(uint8_t addr, MUX* mux, uint8_t bus, TwoWire& wire = Wire); + + /** + * Constructor. Used when the device is not on a module/behind a MUX. + * @param addr Device address + * @param wire I2C bus + */ + Interface(uint8_t addr, TwoWire& wire = Wire); + + /** + * Attempt to communicate with the device. + */ + errorlevel_t begin(void); + + /** + * Attempt to communicate with the device. Effectively the same as begin, just looks better sometimes :) + */ + bool ping(void); + + /** + * Write data to the device. + * @param buffer Bytes to send + * @param len Number of bytes + * @param reset Should the MUX be reset? (Default: `true`) + * @return ERR_FAIL: Device lost. ERR_WARN: Detected, failed to write. + */ + errorlevel_t write(const uint8_t* buffer, size_t len, bool reset = true); + + /** + * Write one byte of data to the device. + * @param b Byte to send + * @param reset Should the MUX be reset? (Default: `true`) + * @return ERR_FAIL: Device lost. ERR_WARN: Detected, failed to write. + */ + errorlevel_t write(const uint8_t b, bool reset = true); + + /** + * Write two bytes of data to the device. + * Effectively equivalent to write({b >> 8, b && 0xFF}, 2), but makes sensor interfacing a little easier :) + * @param b 2 bytes to send + * @param reset Should the MUX be reset? (Default: `true`) + * @return ERR_FAIL: Device lost. ERR_WARN: Detected, failed to write. + */ + errorlevel_t write(const uint16_t b, bool reset = true); + + + /** + * Write one byte of data to a specific register. + * Effectively equivalent to write({reg, value}, 2), but makes sensor interfacing a little easier :) + * @param reg Register to write to + * @param value Byte to write + */ + errorlevel_t writeRegister(uint8_t reg, uint8_t value); + + /** + * Write two bytes of data to a specific register. + * Effectively equivalent to write({reg, value >> 8, value & 0xFF }, 3), but makes sensor interfacing a little easier :) + * @param reg Register to write to + * @param value Two bytes to write + */ + errorlevel_t writeRegister(uint8_t reg, uint16_t value); + + /** + * Read data from the device. + * @param buffer Bytes to read to + * @param len Number of bytes to read (Default: `1`) + * @param reset Should the MUX be reset? (Default: `true`) + */ + errorlevel_t read(uint8_t* buffer, size_t len = 1, bool reset = true); +}; + +template static S* I2CSensorFactory(Interface* i, JsonObject* args); + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/linker.cc b/software/archive/microcontroller/interfaces/linker.cc new file mode 100644 index 00000000..7b15639d --- /dev/null +++ b/software/archive/microcontroller/interfaces/linker.cc @@ -0,0 +1,35 @@ +#include + +// 0. EEPROM +#include "../I2CIP/eeprom.h" +I2CIP::Device* ControlSystemsOS::eepromFactory(const i2cip_fqa_t& fqa) { + if(getDeviceID(MAP_INDEX_EEPROM) == nullptr) { + // First time + return I2CIP::EEPROM::eepromFactory(fqa); + } + return I2CIP::EEPROM::eepromFactory(fqa, getDeviceID(MAP_INDEX_EEPROM)); +} + +// 1. ADC +// #include +// I2CIP::Device* ControlSystemsOS::adcFactory(const i2cip_fqa_t& fqa) { +// return new ControlSystemsOS::ADC(fqa, getDeviceID(MAP_INDEX_ADC)); +// } + +// 2. GPIO +// #include +// I2CIP::Device* ControlSystemsOS::gpioFactory(const i2cip_fqa_t& fqa) { +// return new ControlSystemsOS::GPIO(fqa, getDeviceID(MAP_INDEX_GPIO)); +// } + +// 3. PWM +// #include +// I2CIP::Device* ControlSystemsOS::pwmFactory(const i2cip_fqa_t& fqa) { +// return new ControlSystemsOS::PWM(fqa, getDeviceID(MAP_INDEX_PWM)); +// } + +// 4. SHT31 +#include +I2CIP::Device* ControlSystemsOS::sht31Factory(const i2cip_fqa_t& fqa) { + return new ControlSystemsOS::SHT31(fqa, getDeviceID(MAP_INDEX_SHT31)); +} diff --git a/software/archive/microcontroller/interfaces/linker.h b/software/archive/microcontroller/interfaces/linker.h new file mode 100644 index 00000000..c64d0ead --- /dev/null +++ b/software/archive/microcontroller/interfaces/linker.h @@ -0,0 +1,61 @@ +#ifndef CSOS_INTERFACES_H_ +#define CSOS_INTERFACES_H_ + +#include + +#include +#include + +// #include +// #include +// #include +#include + +// MAPPING +#define MAP_INDEX_COUNT 2 +#define MAP_INDEX_EEPROM 0 +// #define MAP_INDEX_ADC 1 +// #define MAP_INDEX_GPIO 2 +// #define MAP_INDEX_PWM 3 +#define MAP_INDEX_SHT31 1 + +namespace ControlSystemsOS { + + // MAP LINKING + + I2CIP::Device* eepromFactory(const i2cip_fqa_t& fqa); + // I2CIP::Device* adcFactory(const i2cip_fqa_t& fqa); + // I2CIP::Device* gpioFactory(const i2cip_fqa_t& fqa); + // I2CIP::Device* pwmFactory(const i2cip_fqa_t& fqa); + I2CIP::Device* sht31Factory(const i2cip_fqa_t& fqa); + + // const char* const adc_id_progmem PROGMEM = {"ADS1015"}; + // const char* const gpio_id_progmem PROGMEM = {"MCP23017"}; + // const char* const pwm_id_progmem PROGMEM = {"PCA9685"}; + + const char PROGMEM device_id_progmem[MAP_INDEX_COUNT][I2CIP_ID_SIZE] = { + {I2CIP_EEPROM_ID}, + // {"ADS1015\0"}, + // {"MCP23017\0"}, + // {"PCA9685\0"}, + {"SHT31"}, + }; + + const I2CIP::factory_device_t device_factory[MAP_INDEX_COUNT] = { + ControlSystemsOS::eepromFactory, + // ControlSystemsOS::adcFactory, + // ControlSystemsOS::gpioFactory, + // ControlSystemsOS::pwmFactory, + ControlSystemsOS::sht31Factory, + }; + + int getMapIndex(const i2cip_id_t& id); + + const char* getDeviceID(uint8_t index); + + // STATE LINKING + + +}; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/mcp23017.cc b/software/archive/microcontroller/interfaces/mcp23017.cc new file mode 100644 index 00000000..227463a8 --- /dev/null +++ b/software/archive/microcontroller/interfaces/mcp23017.cc @@ -0,0 +1,2 @@ +#include + diff --git a/software/archive/microcontroller/interfaces/mcp23017.h b/software/archive/microcontroller/interfaces/mcp23017.h new file mode 100644 index 00000000..b7ecdcd1 --- /dev/null +++ b/software/archive/microcontroller/interfaces/mcp23017.h @@ -0,0 +1,46 @@ +#ifndef PEAPOD_MODULES_MCP23017_H_ +#define PEAPOD_MODULES_MCP23017_H_ + +// HEADERS + +#include + +#include +#include + +// MACROS + +// Registers +#define MCP23XXX_IODIR 0x00 //!< I/O direction register +#define MCP23XXX_IPOL 0x01 //!< Input polarity register +#define MCP23XXX_GPINTEN 0x02 //!< Interrupt-on-change control register +#define MCP23XXX_DEFVAL \ + 0x03 //!< Default compare register for interrupt-on-change +#define MCP23XXX_INTCON 0x04 //!< Interrupt control register +#define MCP23XXX_IOCON 0x05 //!< Configuration register +#define MCP23XXX_GPPU 0x06 //!< Pull-up resistor configuration register +#define MCP23XXX_INTF 0x07 //!< Interrupt flag register +#define MCP23XXX_INTCAP 0x08 //!< Interrupt capture register +#define MCP23XXX_GPIO 0x09 //!< Port register +#define MCP23XXX_OLAT 0x0A //!< Output latch register + +// Constants +#define MCP23XXX_ADDR 0x20 //!< Default I2C Address + +#define MCP_PORT(pin) ((pin < 8) ? 0 : 1) //!< Determine port from pin number + +class MCP23017 { + private: + int x; + +}; + +uint8_t _width; +uint8_t _addrwidth; +uint16_t _address; +uint8_t _byteorder; +uint8_t *_buffer; + + + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/mux.cc b/software/archive/microcontroller/interfaces/mux.cc new file mode 100644 index 00000000..0d047597 --- /dev/null +++ b/software/archive/microcontroller/interfaces/mux.cc @@ -0,0 +1,16 @@ +#include + +#include + +MUX::MUX(uint8_t addr, TwoWire& wire) : Interface(addr, wire) {} + +errorlevel_t MUX::setBus(uint8_t bus) { + if(bus > 0x07) { + return ERR_SOFT; + } + return write((uint8_t)(0x1 << bus)); +} + +errorlevel_t MUX::resetBus(void) { + return setBus(MUX_BUS_INACTIVE); +} \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/mux.h b/software/archive/microcontroller/interfaces/mux.h new file mode 100644 index 00000000..54afb836 --- /dev/null +++ b/software/archive/microcontroller/interfaces/mux.h @@ -0,0 +1,45 @@ +#ifndef CSOS_INTERFACES_MUX_H_ +#define CSOS_INTERFACES_MUX_H_ + +#include +#include + +#include +#include + +#define MUX_ADDR_MIN 0x70 // The lowest device address +#define MUX_ADDR_MAX 0x77 // The highest device address +#define MUX_BUS_MIN 0x00 // The lowest bus +#define MUX_BUS_MAX 0x07 // The highest bus +#define MUX_BUS_DEFAULT 0x00 // The default bus for "base" interfaces. +#define MUX_BUS_INACTIVE 0x07 // Bus to switch to when "inactive". This bus should be left vacant. + +extern TwoWire Wire; + +// Interface class for the TCA9548A I2C multiplexer IC +class MUX : private Interface { + private: + const uint8_t addr; + public: + /** + * Constructor + * @param addr MUX device address + * @param wire I2C bus + */ + MUX(uint8_t addr, TwoWire& wire = Wire); + + /** + * Set the MUX bus + * @param bus 0-7 + * @return Success? + */ + errorlevel_t setBus(uint8_t bus); + + /** + * Reset the MUX bus to inactive + * @return Success? + */ + errorlevel_t resetBus(void); +}; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/pwm.cc b/software/archive/microcontroller/interfaces/pwm.cc new file mode 100644 index 00000000..ce2e9ff1 --- /dev/null +++ b/software/archive/microcontroller/interfaces/pwm.cc @@ -0,0 +1,40 @@ +#include + +#include + +using namespace ControlSystemsOS; + +PWM::PWM(const i2cip_fqa_t& fqa, const i2cip_id_t& id) : Device(fqa, id), OutputInterface((Device*)this) { } + +i2cip_errorlevel_t PWM::set(const uint16_t& value, const args_pwm_t& args) { + // Encode LED on/off values + uint16_t on = 0, off = 0; + if (value == 0x0000) { + // OFF + off = 0x1000; + } else if (value > 0xFFF) { + // ON + on = 0x1000; + } else { + // Truncate + off = value & 0xFFF; + } + + // Write registers + i2cip_errorlevel_t errlev = this->writeRegister(PWM_CHANNEL_TO_LEDREG(args), (uint8_t)(on & 0xFF)); + I2CIP_ERR_BREAK(errlev); + errlev = this->writeRegister((uint8_t)(PWM_CHANNEL_TO_LEDREG(args) + 0x1), (uint8_t)(on >> 8)); + I2CIP_ERR_BREAK(errlev); + errlev = this->writeRegister((uint8_t)(PWM_CHANNEL_TO_LEDREG(args) + 0x2), (uint8_t)(off & 0xFF)); + I2CIP_ERR_BREAK(errlev); + errlev = this->writeRegister((uint8_t)(PWM_CHANNEL_TO_LEDREG(args) + 0x3), (uint8_t)(off >> 8)); + return errlev; +} + +const args_pwm_t& PWM::getDefaultB(void) const { + return default_b; +} + +void PWM::resetFailsafe(void) { + this->setValue(default_failsafe); +} \ No newline at end of file diff --git a/software/archive/microcontroller/interfaces/pwm.h b/software/archive/microcontroller/interfaces/pwm.h new file mode 100644 index 00000000..b0fdb684 --- /dev/null +++ b/software/archive/microcontroller/interfaces/pwm.h @@ -0,0 +1,84 @@ +#ifndef CSOS_INTERFACES_PWM_H_ +#define CSOS_INTERFACES_PWM_H_ + +#include +#include + +// MACROS +// Registers +#define PCA9685_MODE1 0x00 // Mode Register 1 +#define PCA9685_MODE2 0x01 // Mode Register 2 +#define PCA9685_SUBADR1 0x02 // I2C-bus subaddress 1 +#define PCA9685_SUBADR2 0x03 // I2C-bus subaddress 2 +#define PCA9685_SUBADR3 0x04 // I2C-bus subaddress 3 +#define PCA9685_ALLCALLADR 0x05 // LED All Call I2C-bus address +#define PCA9685_PRESCALE 0xFE // Prescaler for PWM output frequency +#define PCA9685_TESTMODE 0xFF // defines the test mode to be entered + +// Bits - Mode 1 Register +#define MODE1_ALLCAL 0x01 // respond to LED All Call I2C-bus address +#define MODE1_SUB3 0x02 // respond to I2C-bus subaddress 3 +#define MODE1_SUB2 0x04 // respond to I2C-bus subaddress 2 +#define MODE1_SUB1 0x08 // respond to I2C-bus subaddress 1 +#define MODE1_SLEEP 0x10 // Low power mode. Oscillator off +#define MODE1_AI 0x20 // Auto-Increment enabled +#define MODE1_EXTCLK 0x40 // Use EXTCLK pin clock +#define MODE1_RESTART 0x80 // Restart enabled + +// Bits - Mode 2 Register +#define MODE2_OUTNE_0 0x01 // Active LOW output enable input +#define MODE2_OUTNE_1 0x02 // Active LOW output enable input - high impedence +#define MODE2_OUTDRV 0x04 // totem pole structure vs open-drain +#define MODE2_OCH 0x08 // Outputs change on ACK vs STOP +#define MODE2_INVRT 0x10 // Output logic state inverted + +// Settings +#define PWM_ADDR 0x40 +#define PWM_FREQ 490 + +#define PWM_CHANNEL_TO_LEDREG(channel) (uint8_t)(0x06 + (4 * channel)) + +using namespace I2CIP; + +typedef enum { + PWM_CHANNEL_NULL = 0xFF, + PWM_CHANNEL_0 = 0x00, + PWM_CHANNEL_1, + PWM_CHANNEL_2, + PWM_CHANNEL_3, + PWM_CHANNEL_4, + PWM_CHANNEL_5, + PWM_CHANNEL_6, + PWM_CHANNEL_7, + PWM_CHANNEL_8, + PWM_CHANNEL_9, + PWM_CHANNEL_10, + PWM_CHANNEL_11, + PWM_CHANNEL_12, + PWM_CHANNEL_13, + PWM_CHANNEL_14, + PWM_CHANNEL_15, +} args_pwm_t; + +namespace ControlSystemsOS { + + // Interface class for the PCA9685 16-channel 12-bit PWM IC + class PWM : public Device, public OutputInterface { + friend Device* pwmFactory(const i2cip_fqa_t& fqa); + + // Note: unsigned 16-bit args are TRUNCATED to 12-bit PWM control + private: + const uint16_t default_failsafe = 0x0000; + const args_pwm_t default_b = PWM_CHANNEL_NULL; + + PWM(const i2cip_fqa_t& fqa, const i2cip_id_t& id); + + public: + i2cip_errorlevel_t set(const uint16_t& value, const args_pwm_t& args) override; + + const args_pwm_t& getDefaultB(void) const override; + void resetFailsafe(void) override; + }; +}; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/main.cc b/software/archive/microcontroller/main.cc new file mode 100644 index 00000000..db7ee670 --- /dev/null +++ b/software/archive/microcontroller/main.cc @@ -0,0 +1,199 @@ +#ifndef UNIT_TEST +#ifndef MAIN +#define MAIN true + +#include + +#include + +#include +#include + +#include + +#define TIMESTAMP_LOG_DELTA_MS 5000 // Default + +#define FIXED_UPDATE_DELTA 1000 + +#include "simple.h" + +fsm_timestamp_t start = 0; +fsm_timestamp_t ms_last = 0; + +bool rebuild = false; + +void setup(void) { + // Serial + Serial.begin(115200); + while(!Serial); + + // Pin Modes + pinMode(PIN_LIGHTING_LO, OUTPUT); + pinMode(PIN_LIGHTING_HI, OUTPUT); + pinMode(PIN_WATERING, OUTPUT); + pinMode(PIN_DISCO_BUTTON, INPUT); + + lighting.addLoggerCallback(&logLights); + watering.addLoggerCallback(&logWatering); + + lighting.addLatchingConditional(true, false, &controlLights); + watering.addLatchingConditional(true, false, &controlWatering); + disco.addLatchingConditional(true, false, &controlDisco); + + // Timestamp output + Chronos.addInterval(TIMESTAMP_LOG_DELTA_MS, &logTimestamp); + + // Timer Flag Event - Lighting ON (No-Invert) + Chronos.addEventFlag(DELAY_START, &lighting); + + // Timer Flag Event - Lighting OFF (Inverted) + Chronos.addEventFlag(DELAY_START + DURATION_LIGHTING, &lighting, true); + + // Timer Flag Interval - Watering ON (No-Invert) + Chronos.addIntervalFlag(PERIOD_WATERING, &watering); + + // Timer Flag Interval - Watering OFF (Invert) + Chronos.addIntervalFlag(PERIOD_WATERING, DURATION_WATERING, &watering, true); + + // Disco Scroller Callback - passes interval timestamp + // discoScroller = + Chronos.addInterval(DELTA_DISCO, &scrollDisco); + + // Disco Controller - Dis-/En-ables Disco Scroller Callback + + start = millis(); + + // Serial.println(F("==== [ CYCLE -1 (SIZEOF) ] ====")); + // Serial.print(F("Module: ")); + // Serial.print(sizeof(ControlSystemsOS::CSOSModule)); + // Serial.print(F("\nHashTable: ")); + // Serial.print(sizeof(HashTable)); + // Serial.print(F("\nHashTableEntry: ")); + // Serial.print(sizeof(HashTableEntry)); + // Serial.print(F("\nBST: ")); + // Serial.print(sizeof(BST)); + // Serial.print(F("\nBSTNode: ")); + // Serial.print(sizeof(BSTNode)); + // Serial.print(F("\nDeviceGroup: ")); + // Serial.print(sizeof(DeviceGroup)); + // Serial.print(F("\nEEPROM: ")); + // Serial.print(sizeof(EEPROM)); + // Serial.println(); + Serial.println(F("==== [ CYCLE 0 (BUILD) ] ====")); + + delay(1000); + + i2cip_errorlevel_t errlev = ControlSystemsOS::update(true); + if(errlev > I2CIP_ERR_NONE) { + Serial.println(F("==== [ BUILD FAILED, FREEZING ] ====")); + while(true) { // Blink + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); + } + } + + #ifdef _AVR_WDT_H_ + wdt_enable(WDTO_4S); + #endif +} + +unsigned long cycle = 0; + +void loop(void) { + #ifdef _AVR_WDT_H_ + wdt_reset(); + #endif + + cycle++; + Serial.print(F("\n\n==== [ CYCLE ")); + Serial.print(cycle); + Serial.println(F(" ] ====")); + + fsm_timestamp_t ms_start = millis(); + // Watchdog Timer 24Hr Kickout + if(ms_start > TWENTYFOURHRS_MILLIS) { while(true) { delay(1); } } + + // 0. Module State-Change Forward-Propagation + // TODO: Move to `Module`? + + // 0a. Delete Devices Associated with Lost Modules + // if(ControlSystemsOS::stateChange) { + + // for(uint8_t i = 0; i < I2CIP_NUM_WIRES; i++) { + // for(uint8_t x = 0; x < I2CIP_MUX_COUNT; x++) { + + // // Check if module exists + // Module* m = ControlSystemsOS::csos_modules[i][x]; + // if(m == nullptr) { + // for(uint8_t j = 0; j < NUM_DEVICE_TYPES; j++) { + // const char* key = ControlSystemsOS::csos_map_device_id[j]; + // I2CIP::DeviceGroup * group = (*m)[key]; + // if(group == nullptr) continue; + + // for(uint8_t k = 0; k < group->numdevices; k++) { + // Device* device = group->devices[k]; + // if(device != nullptr) { + // // Delete Device + // m->remove(device); + // } + // } + // } + // } + + + // } + // } + + // ControlSystemsOS::stateChange = false; + // } + + // 1. Refresh Update - Network Status Handling & Rebuild + + // 1a. Module "Roll-Call" Check + i2cip_errorlevel_t errlev = ControlSystemsOS::update(rebuild); + // If we lose hardware, return and do FixedUpdate ASAP + rebuild = (errlev == I2CIP_ERR_HARD); + if(errlev > I2CIP_ERR_NONE) return; + + // 1b. Device Checking Per-Group + fsm_timestamp_t ms_now = millis(); + if((ms_now - ms_last) > FIXED_UPDATE_DELTA) { + errlev = ControlSystemsOS::fixedUpdate(ms_now); + ms_last = millis(); + } + + // 2. Fixed Update - Instruction and Control Handling + + Chronos.set(ms_start); + + // Other Stuff + + int discoPin = digitalRead(PIN_DISCO_BUTTON); + disco.set(discoPin == HIGH ? true : false); + + delay(100); + +} + +// Overview: +// +// 1. Module State-Change Forward-Propagation +// 2. Refresh Update - Network Status Handling & Rebuild +// 3. Fixed Update - Instruction and Control Handling +// +// 1. Module State-Change Forward-Propagation +// +// 1a. Delete Devices Associated with Lost Modules +// 1b. Delete Modules +// +// 2. Refresh Update - Network Status Handling & Rebuild +// +// 2a. Module "Roll-Call" Check +// 2b. Device Checking Per-Group +// +// 3. Fixed Update - Instruction and Control Handling + +#endif +#endif \ No newline at end of file diff --git a/software/microcontroller/src/main.h b/software/archive/microcontroller/main.h similarity index 100% rename from software/microcontroller/src/main.h rename to software/archive/microcontroller/main.h diff --git a/software/microcontroller/src/modules/module.cc b/software/archive/microcontroller/modules/module.cc similarity index 100% rename from software/microcontroller/src/modules/module.cc rename to software/archive/microcontroller/modules/module.cc diff --git a/software/microcontroller/src/modules/module.h b/software/archive/microcontroller/modules/module.h similarity index 100% rename from software/microcontroller/src/modules/module.h rename to software/archive/microcontroller/modules/module.h diff --git a/software/archive/microcontroller/peapod.cc b/software/archive/microcontroller/peapod.cc new file mode 100644 index 00000000..2ec5abaa --- /dev/null +++ b/software/archive/microcontroller/peapod.cc @@ -0,0 +1,166 @@ +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +PeaPod::PeaPod(TwoWire* wire) : wire(wire) { + +} + +void PeaPod::scan(void) { + // Module Ping + uint8_t nmodules = 0; + bool moduleFound [MODULE_COUNT] = { false }; + for (uint8_t i = 0; i < MODULE_COUNT; i++) { + if (modules[i] != nullptr) { + moduleFound[i] = true; + } + + // Acknowledge? + uint8_t addr = MODULE_ADDR_MIN + i; + Wire.beginTransmission(addr); + uint8_t error = Wire.endTransmission(); + if (error == 0) { + // Module found! Mark 'true' + // sendMessage(MESSAGE_DEBUG, "Module found at 0x" + String(addr < 16 ? "0" : "") + String(addr, HEX)); + moduleFound[i] = true; + nmodules++; + } else if (error == 4) { + // Wierd error; retry + // sendMessage(MESSAGE_ERROR, "Error at 0x" + String(addr < 16 ? "0" : "") + String(addr, HEX)); + i--; + continue; + } else if (moduleFound[i] == true) { + // Module not found; previously found module has been lost; deallocate + delete(modules[i]); + modules[i] = nullptr; + } + } + + // Read moduleinfo EEPROM + StaticJsonDocument json; + for (uint8_t i = 0; i < MODULE_COUNT; i++) { + if (!moduleFound[i]) break; + uint8_t modaddr = MODULE_ADDR_MIN + i; + + // PING EEPROM + EEPROM eeprom(modaddr); + // Attempt; uncache if failed + if (eeprom.begin() != ERR_NONE) { + // sendMessage(MESSAGE_ERROR, "Error pinging EEPROM at Module 0x" + String(modaddr < 16 ? "0" : "") + String(modaddr, HEX)); + moduleFound[i] = false; + nmodules--; + continue; + } + + // READ EEPROM + char buffer[EEPROM_SIZE] = { '\0' }; + size_t len = 0; + eeprom.read(buffer, &len); + + // SWITCH MUX TO INACTIVE BUS + Wire.beginTransmission(modaddr); + Wire.write(1 << MODULE_BUS_INACTIVE); + Wire.endTransmission(); + + // EEPROM dump + // sendMessage(MESSAGE_DEBUG, "Read from EEPROM: " + String(buffer)); + + // Attempt to parse JSON + DeserializationError error = deserializeJson(json, buffer); + if (error) { + // sendMessage(MESSAGE_ERROR, "Error parsing EEPROM to JSON at Module 0x" + String(modaddr < 16 ? "0" : "") + String(modaddr, HEX)); + moduleFound[i] = false; + nmodules--; + continue; + } + + // Initialize Module + modules[i] = new Module(json["id"], json["numsensors"], json["numactuators"]); + + // Set up interfaces + // GPIO, ADC, etc. + + // ALLOCATE SENSORS, ACTUATORS TO HEAP + for (uint8_t i = 0; i < MODULE_COUNT; i++) { + ModuleState* state = modules[i]->getState(); + uint8_t j = 0; + for (JsonPair sensor : json["sensors"].as()) { + + // Check interface hashtable + Factory* sf = sensorFactories[(const char*)(sensor.key().c_str())]; + + if (sf == nullptr) { + // ERROR: Unrecognized sensor. Flash module EEPROM. + break; + } + + Sensor* s = sf->operator()(&(sensor.value().to())); + + if (s == nullptr) { + // ERROR: constructor arguments invalid + break; + } + + state->sensors[j] = s; + j++; + } + state->numsensors = j; + j = 0; + for (JsonPair actuator : json["actuators"].as()) { + // Check interface hashtable + Factory* af = actuatorFactories[(const char*)(actuator.key().c_str())]; + + if (af == nullptr) { + // ERROR: Unrecognized sensor. Flash module EEPROM. + break; + } + + Actuator* a = af->operator()(&(actuator.value().to())); + + if (a == nullptr) { + // ERROR: constructor arguments invalid + break; + } + + state->actuators[j] = a; + j++; + } + state->numactuators = j; + } + } +} + +errorlevel_t PeaPod::post(void) { + errorlevel_t success = ERR_NONE; + for (int i = 0; i < NUM_SENSORS; ++i) { + SensorState* state = sensors[i]->begin(); + errorlevel_t latest = (state->error == ERR_HARD ? ERR_HARD : (state->debug == ISTATE_OFF || state->error == ERR_SOFT ? ERR_SOFT : ERR_NONE)); + if (latest) { + // sendMessage(MESSAGE_DEBUG, String("Sensor " + sensors[i]->toString() + " initialized successfully.")); + } else { + // sendMessage(MESSAGE_ERROR, String("Failed to initialize sensor " + sensors[i]->toString() + ". Check wiring.")); + } + success &= latest; + } + for (int i = 0; i < NUM_ACTUATORS; ++i) { + ActuatorState* state = actuators[i]->begin(); + errorlevel_t latest = (state->debug >= ISTATE_ON && state->error == ERR_NONE); + if (latest) { + // sendMessage(MESSAGE_DEBUG, String("Actuator " + actuators[i]->toString() + " initialized successfully.")); + } else { + // sendMessage(MESSAGE_DEBUG, String("Failed to initialize actuator " + actuators[i]->toString() + ". Check wiring.")); + } + success &= latest; + } + return success; +} \ No newline at end of file diff --git a/software/archive/microcontroller/peapod.h b/software/archive/microcontroller/peapod.h new file mode 100644 index 00000000..afefe816 --- /dev/null +++ b/software/archive/microcontroller/peapod.h @@ -0,0 +1,37 @@ +#ifndef CSOS_CSOS_H_ +#define CSOS_CSOS_H_ + +// HEADERS + +#include +#include + +#include +#include +#include +#include + +#include + +extern TwoWire wire; + +// CLASS +// An Interface is a single I2C device, wrapping an Adafruit_I2CDevice. +class PeaPod { + private: + const TwoWire* wire; + + HashTable interfaces = HashTable(100); + + Module* modules[MODULE_COUNT] = { nullptr }; + public: + /** + * Constructor. + */ + PeaPod(TwoWire* wire = &Wire); + + errorlevel_t post(void); + void scan(void); +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/sensors/ge_2158.cc b/software/archive/microcontroller/sensors/ge_2158.cc similarity index 100% rename from software/microcontroller/src/sensors/ge_2158.cc rename to software/archive/microcontroller/sensors/ge_2158.cc diff --git a/software/microcontroller/src/sensors/ge_2158.h b/software/archive/microcontroller/sensors/ge_2158.h similarity index 100% rename from software/microcontroller/src/sensors/ge_2158.h rename to software/archive/microcontroller/sensors/ge_2158.h diff --git a/software/microcontroller/src/sensors/k30.cc b/software/archive/microcontroller/sensors/k30.cc similarity index 100% rename from software/microcontroller/src/sensors/k30.cc rename to software/archive/microcontroller/sensors/k30.cc diff --git a/software/microcontroller/src/sensors/k30.h b/software/archive/microcontroller/sensors/k30.h similarity index 100% rename from software/microcontroller/src/sensors/k30.h rename to software/archive/microcontroller/sensors/k30.h diff --git a/software/microcontroller/src/sensors/sen0257.cc b/software/archive/microcontroller/sensors/sen0257.cc similarity index 100% rename from software/microcontroller/src/sensors/sen0257.cc rename to software/archive/microcontroller/sensors/sen0257.cc diff --git a/software/microcontroller/src/sensors/sen0257.h b/software/archive/microcontroller/sensors/sen0257.h similarity index 100% rename from software/microcontroller/src/sensors/sen0257.h rename to software/archive/microcontroller/sensors/sen0257.h diff --git a/software/archive/microcontroller/sensors/sensor.cc b/software/archive/microcontroller/sensors/sensor.cc new file mode 100644 index 00000000..ec5ee40e --- /dev/null +++ b/software/archive/microcontroller/sensors/sensor.cc @@ -0,0 +1,205 @@ +#include +#include + +// CSOS_BOOL: bool, Number != 0 +template <> bool ControlSystemsOS::Datum::applyState(State* state) { + if(state == nullptr) return false; + + double d = 0.0; + switch(this->type) { + case CSOS_BOOL: + state->set(this->value.b); + return true; + case CSOS_UINT8: + d = (double)(this->value.u8); + break; + case CSOS_INT8: + d = (double)(this->value.i8); + break; + case CSOS_UINT16: + d = (double)(this->value.u16); + break; + case CSOS_INT16: + d = (double)(this->value.i16); + break; + case CSOS_UINT32: + d = (double)(this->value.u32); + break; + case CSOS_INT32: + d = (double)(this->value.i32); + break; + case CSOS_FLOAT: + d = (double)(this->value.f); + break; + case CSOS_DOUBLE: + d = this->value.d; + break; + default: + return false; + } + + state->set(d > -NUMBER_FLOP_EPSILON && d < NUMBER_FLOP_EPSILON); + + return true; +} + +// CSOS_(U)INT8 through 32, FLOAT, DOUBLE +template <> bool ControlSystemsOS::Datum::applyState(State* state) { + if(state == nullptr) return false; + + double d = NAN; + switch(this->type) { + case CSOS_BOOL: + d = this->value.b ? 1.0f : 0.0f; + break; + case CSOS_UINT8: + d = (double)(this->value.u8); + break; + case CSOS_INT8: + d = (double)(this->value.i8); + break; + case CSOS_UINT16: + d = (double)(this->value.u16); + break; + case CSOS_INT16: + d = (double)(this->value.i16); + break; + case CSOS_UINT32: + d = (double)(this->value.u32); + break; + case CSOS_INT32: + d = (double)(this->value.i32); + break; + case CSOS_FLOAT: + d = (double)(this->value.f); + break; + case CSOS_DOUBLE: + d = this->value.d; + break; + default: + return false; + } + + state->set(d); + + return true; +} + +char* ControlSystemsOS::Datum::toString() { + char* buffer = (char*)malloc(8 * sizeof(char)); + if(buffer == nullptr) return buffer; + for(uint8_t i = 0; i < 8; i++) buffer[i] = '\0'; + + switch(this->type) { + case CSOS_BOOL: + snprintf(buffer, 8, "%s", (this->value.b) ? "true" : "false"); + break; + case CSOS_UINT8: + snprintf(buffer, 8, "%u", this->value.u8); + break; + case CSOS_INT8: + snprintf(buffer, 8, "%d", this->value.i8); + break; + case CSOS_UINT16: + snprintf(buffer, 8, "%u", this->value.u16); + break; + case CSOS_INT16: + snprintf(buffer, 8, "%d", this->value.i16); + break; + case CSOS_UINT32: + snprintf(buffer, 8, "%lu", this->value.u32); + break; + case CSOS_INT32: + snprintf(buffer, 8, "%ld", this->value.i32); + break; + case CSOS_FLOAT: + snprintf(buffer, 8, "%.2f", (double)this->value.f); + break; + case CSOS_DOUBLE: + snprintf(buffer, 8, "%f", this->value.d); + break; + case CSOS_SCHAR: + snprintf(buffer, 8, "%c", this->value.c); + break; + default: + snprintf(buffer, 8, "null"); + break; + } + return buffer; +} + +void ControlSystemsOS::Datum::addToJSON(JsonObject& json) { + switch(this->type) { + case CSOS_BOOL: + json[this->label] = this->value.b; + break; + case CSOS_UINT8: + json[this->label] = this->value.u8; + break; + case CSOS_INT8: + json[this->label] = this->value.i8; + break; + case CSOS_UINT16: + json[this->label] = this->value.u16; + break; + case CSOS_INT16: + json[this->label] = this->value.i16; + break; + case CSOS_UINT32: + json[this->label] = this->value.u32; + break; + case CSOS_INT32: + json[this->label] = this->value.i32; + break; + case CSOS_FLOAT: + json[this->label] = this->value.f; + break; + case CSOS_DOUBLE: + json[this->label] = this->value.d; + break; + // case CSOS_SCHAR: + // json[this->label] = this->value.c; + // break; + default: + json[this->label] = nullptr; + break; + } +} + +void ControlSystemsOS::Datum::print(Print& p) { + switch(this->type) { + case CSOS_BOOL: + p.print((this->value.b) ? "true" : "false"); + break; + case CSOS_UINT8: + p.print(this->value.u8); + break; + case CSOS_INT8: + p.print(this->value.i8); + break; + case CSOS_UINT16: + p.print(this->value.u16); + break; + case CSOS_INT16: + p.print(this->value.i16); + break; + case CSOS_UINT32: + p.print(this->value.u32); + break; + case CSOS_INT32: + p.print(this->value.i32); + break; + case CSOS_FLOAT: + p.print(this->value.f); + break; + case CSOS_DOUBLE: + p.print(this->value.d); + break; + case CSOS_SCHAR: + p.print(this->value.c); + break; + default: + p.print("null"); + break; + } +} \ No newline at end of file diff --git a/software/archive/microcontroller/sensors/sensor.h b/software/archive/microcontroller/sensors/sensor.h new file mode 100644 index 00000000..98890da0 --- /dev/null +++ b/software/archive/microcontroller/sensors/sensor.h @@ -0,0 +1,83 @@ +#ifndef CSOS_SENSORS_SENSOR_H_ +#define CSOS_SENSORS_SENSOR_H_ + +#include +#include + +#include + +#include +#include + +namespace ControlSystemsOS { + class Datum { + private: + const char* label; + csos_data_t value; + csos_pdata_t ptr; + csos_types_t type; + + public: + ~Datum() { delete next; } + // Datum(const csos_types_t& type, const csos_pdata_t& value) : type(type), next(nullptr) { + // switch(type) { + // case CSOS_BOOL: + // this->value.b = *value.b; + // break; + // case CSOS_UINT8: + // this->value.u8 = *value.u8; + // break; + // case CSOS_INT8: + // this->value.i8 = *value.i8; + // break; + // case CSOS_UINT16: + // this->value.u16 = *value.u16; + // break; + // case CSOS_INT16: + // this->value.i16 = *value.i16; + // break; + // case CSOS_UINT32: + // this->value.u32 = *value.u32; + // break; + // case CSOS_INT32: + // this->value.i32 = *value.i32; + // break; + // case CSOS_FLOAT: + // this->value.f = *value.f; + // break; + // case CSOS_DOUBLE: + // this->value.d = *value.d; + // break; + // case CSOS_SCHAR: + // this->value.c = *value.c; + // break; + // default: + // this->value.null = nullptr; + // break; + // } + // } + + Datum* const next; + + Datum(const csos_types_t& type, csos_data_t value, const char* label, Datum* next = nullptr) : type(type), value(value), label(label), next(next) {} + + template bool applyState(State* state); + + const char* getLabel(void) { return this->label; } + + char* toString(void); + + void addToJSON(JsonObject& json); + + void print(Print& p); + }; + + class Sensor { + public: + Sensor(InputGetter* getter) { getter->setSensor(this); } + virtual Datum* datumFactory(void) = 0; + }; + +}; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/sensors/sht31.cc b/software/archive/microcontroller/sensors/sht31.cc new file mode 100644 index 00000000..7b69c269 --- /dev/null +++ b/software/archive/microcontroller/sensors/sht31.cc @@ -0,0 +1,94 @@ +#include + +static uint8_t crc8(const uint8_t *data, int len) { + /* + * + * CRC-8 formula from page 14 of SHT spec pdf + * + * Test data 0xBE, 0xEF should yield 0x92 + * + * Initialization data 0xFF + * Polynomial 0x31 (x8 + x5 +x4 +1) + * Final XOR 0x00 + */ + + const uint8_t POLYNOMIAL(0x31); + uint8_t crc(0xFF); + + for (int j = len; j; --j) { + crc ^= *data++; + + for (int i = 8; i; --i) { + crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1); + } + } + return crc; +} + +using namespace ControlSystemsOS; + +SHT31::SHT31(const i2cip_fqa_t& fqa, const i2cip_id_t& id) : Device(fqa, id), InputInterface((Device*)this), Sensor((InputGetter*)this) { } + +i2cip_errorlevel_t SHT31::get(state_sht31_t& value, const args_sht31_t& args) { + size_t buflen = 6; + uint8_t buffer[buflen]; + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(F("!!! SHT31 GET !!!")); + DEBUG_DELAY(); + #endif + + // Read registers + i2cip_errorlevel_t errlev = this->readRegister(SHT31_MEAS_HIGHREP_STRETCH, buffer, buflen, false); + I2CIP_ERR_BREAK(errlev); + + if (buflen != sizeof(buffer)) { + return I2CIP_ERR_SOFT; + } + + // Checksum + if (buffer[2] != crc8(buffer, 2) || buffer[5] != crc8(buffer + 3, 2)) { + return I2CIP_ERR_SOFT; + } + + // Temperature conversion + int32_t stemp = (int32_t)(((uint32_t)buffer[0] << 8) | buffer[1]); + stemp = ((4375 * stemp) >> 14) - 4500; + value.temperature = (float)stemp / 100.0f; + + // Humidity conversion + uint32_t shum = ((uint32_t)buffer[3] << 8) | buffer[4]; + shum = (625 * shum) >> 12; + value.humidity = (float)shum / 100.0f; + + #ifdef CSOS_DEBUG_SERIAL + DEBUG_DELAY(); + CSOS_DEBUG_SERIAL.print(F("!!! ")); + CSOS_DEBUG_SERIAL.print(value.temperature); + CSOS_DEBUG_SERIAL.print(F("C, ")); + CSOS_DEBUG_SERIAL.print(value.humidity); + CSOS_DEBUG_SERIAL.print(F("% !!!")); + DEBUG_DELAY(); + #endif + + return I2CIP_ERR_NONE; +} + +const args_sht31_t& SHT31::getDefaultA(void) const { + return this->default_a; +} + +void SHT31::clearCache(void) { + this->setCache(this->default_cache); +} + +Datum* SHT31::datumFactory(void) { + csos_data_t hum = {.f = this->getCache().humidity}; + Datum* humidity = new Datum(CSOS_FLOAT, hum, "air_humidity"); + + csos_data_t temp = {.f = this->getCache().temperature}; + Datum* temperature = new Datum(CSOS_FLOAT, temp, "air_temperature", humidity); + + return temperature; +} \ No newline at end of file diff --git a/software/archive/microcontroller/sensors/sht31.h b/software/archive/microcontroller/sensors/sht31.h new file mode 100644 index 00000000..50079cea --- /dev/null +++ b/software/archive/microcontroller/sensors/sht31.h @@ -0,0 +1,63 @@ +#ifndef CSOS_INTERFACES_SHT31_H_ +#define CSOS_INTERFACES_SHT31_H_ + +#include +#include + +#include + +// MACROS + +// Registers +#define SHT31_MEAS_HIGHREP_STRETCH (uint16_t)0x2C06 // Measurement High Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_MEDREP_STRETCH (uint16_t)0x2C0D // Measurement Medium Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_LOWREP_STRETCH (uint16_t)0x2C10 // Measurement Low Repeatability with Clock Stretch Enabled +#define SHT31_MEAS_HIGHREP (uint16_t)0x2400 // Measurement High Repeatability with Clock Stretch Disabled +#define SHT31_MEAS_MEDREP (uint16_t)0x240B // Measurement Medium Repeatability with Clock Stretch Disabled +#define SHT31_MEAS_LOWREP (uint16_t)0x2416 // Measurement Low Repeatability with Clock Stretch Disabled +#define SHT31_READSTATUS (uint16_t)0xF32D // Read Out of Status Register +#define SHT31_CLEARSTATUS (uint16_t)0x3041 // Clear Status +#define SHT31_SOFTRESET (uint16_t)0x30A2 // Soft Reset +#define SHT31_HEATEREN (uint16_t)0x306D // Heater Enable +#define SHT31_HEATERDIS (uint16_t)0x3066 // Heater Disable +#define SHT31_REG_HEATER_BIT (uint16_t)0x000d // Status Register Heater Bit + +// Settings +#define SHT31_ADDR 0x44 // SHT31 Default Address + +using namespace I2CIP; + +typedef enum { + SHT31_HEATER_DISABLE = 0x00, + SHT31_HEATER_ENABLE = 0x01, +} args_sht31_t; + +typedef struct { + float temperature; + float humidity; +} state_sht31_t; + +namespace ControlSystemsOS { + + // Interface class for the SHT31 air temperature and humidity sensor + class SHT31 : public Device, public InputInterface, public Sensor { + friend Device* ControlSystemsOS::sht31Factory(const i2cip_fqa_t& fqa); + + // Note: unsigned 16-bit args are TRUNCATED to 12-bit PWM control + private: + const state_sht31_t default_cache = { NAN, NAN }; + const args_sht31_t default_a = SHT31_HEATER_DISABLE; + + SHT31(const i2cip_fqa_t& fqa, const i2cip_id_t& id); + + public: + i2cip_errorlevel_t get(state_sht31_t& value, const args_sht31_t& args) override; + + const args_sht31_t& getDefaultA(void) const override; + void clearCache(void) override; + + Datum* datumFactory(void) override; + }; +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/sensors/yf_b1.cc b/software/archive/microcontroller/sensors/yf_b1.cc similarity index 100% rename from software/microcontroller/src/sensors/yf_b1.cc rename to software/archive/microcontroller/sensors/yf_b1.cc diff --git a/software/microcontroller/src/sensors/yf_b1.h b/software/archive/microcontroller/sensors/yf_b1.h similarity index 100% rename from software/microcontroller/src/sensors/yf_b1.h rename to software/archive/microcontroller/sensors/yf_b1.h diff --git a/software/archive/microcontroller/simple.cc b/software/archive/microcontroller/simple.cc new file mode 100644 index 00000000..872d5209 --- /dev/null +++ b/software/archive/microcontroller/simple.cc @@ -0,0 +1,114 @@ +#include "simple.h" + +#include +// #include "../FiniteStateMachine/debug.h" +#ifndef DEBUG_DELAY +#define DEBUG_DELAY() {delay(10);} +#endif + +Flag lighting = Flag(); +Flag watering = Flag(); +Flag disco = Flag(); + +void logTimestamp(bool _, const fsm_timestamp_t& __) { + fsm_timestamp_t timestamp = Chronos.get(); + + DEBUG_DELAY(); + Serial.print(F("==== [ Time Elapsed: ")); + unsigned long seconds = timestamp / 1000; + unsigned long minutes = seconds / 60; + unsigned long hours = minutes / 60; + + seconds -= minutes * 60; + minutes -= hours * 60; + + if(hours < 10) Serial.print("0"); + Serial.print(hours); + Serial.print("h : "); + if(minutes < 10) Serial.print("0"); + Serial.print(minutes - hours * 60); + Serial.print("m : "); + if(seconds < 10) Serial.print("0"); + Serial.print(seconds); + Serial.println(F("s ] ====")); + DEBUG_DELAY(); +} + +void logLights(bool _, const bool& on) { + DEBUG_DELAY(); + Serial.print(F("== [ Lights: ")); + Serial.print(on ? F("ON") : F("OFF")); + Serial.println(F(" ] ==")); + DEBUG_DELAY(); +} + +void logWatering(bool _, const bool& on) { + DEBUG_DELAY(); + Serial.print(F("== [ Watering: ")); + Serial.print(on ? F("ON") : F("OFF")); + Serial.println(F(" ] ==")); + DEBUG_DELAY(); +} + +void logDisco(bool _, const bool& on) { + DEBUG_DELAY(); + Serial.print(F("== [ Disco: ")); + Serial.print(on ? F("ON") : F("OFF")); + Serial.println(F(" ] ==")); + DEBUG_DELAY(); +} + +void timerControlLights(bool on, const fsm_timestamp_t& timestamp) { + if(on) { + analogWrite(PIN_LIGHTING_HI, PWM_LIGHTING_HI); + analogWrite(PIN_LIGHTING_LO, PWM_LIGHTING_LO); + } else { + analogWrite(PIN_LIGHTING_HI, LOW); + analogWrite(PIN_LIGHTING_LO, LOW); + } +} + +void controlLights(bool _, const bool& lights) { + if(lights) { + analogWrite(PIN_LIGHTING_HI, PWM_LIGHTING_HI); + analogWrite(PIN_LIGHTING_LO, PWM_LIGHTING_LO); + } else { + analogWrite(PIN_LIGHTING_HI, LOW); + analogWrite(PIN_LIGHTING_LO, LOW); + } +} + +void controlWatering(bool _, const bool& watering) { + if(watering) { + digitalWrite(PIN_WATERING, HIGH); + } else { + digitalWrite(PIN_WATERING, LOW); + } +} + +void controlDisco(bool _, const bool& disco) { + if(disco) { + // Disable - don't worry about setting, disco scroll will take it from here + lighting.disable(); + } else { + // Enable and retrigger + lighting.resume(); + } +} + +void scrollDisco(bool _, const fsm_timestamp_t& __) { + fsm_timestamp_t timestamp = Chronos.get(); + + uint8_t discoPWM = (uint8_t)((cos(((timestamp % DISCO_CYCLE_MS) / (float)DISCO_CYCLE_MS) * 6.2831853) / 2.25 + 0.5) * 255); + + if(disco.get() && lighting.isDisabled()) { + DEBUG_DELAY(); + Serial.print(F("== [ Disco: ")); + Serial.print(discoPWM); + Serial.print(F(" ] ==\n")); + DEBUG_DELAY(); + + analogWrite(PIN_LIGHTING_HI, discoPWM); + analogWrite(PIN_LIGHTING_LO, 255-discoPWM); + } +} \ No newline at end of file diff --git a/software/archive/microcontroller/simple.h b/software/archive/microcontroller/simple.h new file mode 100644 index 00000000..39f0b845 --- /dev/null +++ b/software/archive/microcontroller/simple.h @@ -0,0 +1,44 @@ +#ifndef CSOS_SIMPLE_H_ +#define CSOS_SIMPLE_H_ + +#include + +// Watering + +#define PIN_WATERING 2 +#define DURATION_WATERING 2000 // 10 seconds +#define PERIOD_WATERING 200000 // 30 minutes + +// Lighting + +#define DELAY_START 1000 + +#define PIN_LIGHTING_LO 5 +#define PIN_LIGHTING_HI 6 +// #define PIN_LIGHTING_PHOTO 5 +#define DURATION_LIGHTING (TWENTYFOURHRS_MILLIS*3/4) +#define PWM_LIGHTING_LO (unsigned char)(255*0.3) +#define PWM_LIGHTING_HI (unsigned char)(255*0.6) + +// Fun Stuff +#define PIN_DISCO_BUTTON 10 +#define DELTA_DISCO 10 +#define DISCO_CYCLE_MS 1000 + +void logTimestamp(bool _, const fsm_timestamp_t& timestamp); +void logLights(bool _, const bool& on); +void logWatering(bool _, const bool& on); +void logDisco(bool _, const bool& on); + +// void timerControlLights(bool on, const fsm_timestamp_t& timestamp); +void controlLights(bool _, const bool& lights); +void controlWatering(bool _, const bool& watering); +void controlDisco(bool _, const bool& __); + +void scrollDisco(bool _, const fsm_timestamp_t& __); + +extern Flag lighting; +extern Flag watering; +extern Flag disco; + +#endif \ No newline at end of file diff --git a/software/archive/microcontroller/types.h b/software/archive/microcontroller/types.h new file mode 100644 index 00000000..0437cb26 --- /dev/null +++ b/software/archive/microcontroller/types.h @@ -0,0 +1,66 @@ +#ifndef CSOS_TYPES_H_ +#define CSOS_TYPES_H_ + +#include + +// Cross-compatible data types +typedef enum { + CSOS_NULL = 0x0, // INVALID - failsafe args + CSOS_BOOL = 0x2, // 0 | !0 + CSOS_UINT8 = 0x4, // 0-255 + CSOS_INT8 = 0x8, // -128-127 + CSOS_UINT16 = 0x10,// etc... + CSOS_INT16 = 0x20, + CSOS_UINT32 = 0x40,// May be a timestamp in ms + CSOS_INT32 = 0x80, + CSOS_FLOAT = 0x200, // 16-bit IEEE 754 floating point + CSOS_DOUBLE = 0x400, // 32-bit IEEE 754 floating point + CSOS_SCHAR = 0x800, // A character from a null-terminated string (quit if '\0') +} csos_types_t; + +// Pointer types (treated like an array, with default a single element) +typedef enum { + CSOS_NULLP = (CSOS_NULL | 0x1), // NOP - void* nullptr, "no args" ergo LAST args used + CSOS_BOOLP = (CSOS_BOOL | 0x1), + CSOS_UINT8P = (CSOS_UINT8 | 0x1), + CSOS_INT8P = (CSOS_INT8 | 0x1), + CSOS_UINT16P = (CSOS_UINT16 | 0x1), + CSOS_INT16P = (CSOS_INT16 | 0x1), + CSOS_UINT32P = (CSOS_UINT32 | 0x1), + CSOS_INT32P = (CSOS_INT32 | 0x1), + CSOS_FLOATP = (CSOS_FLOAT | 0x1), + CSOS_DOUBLEP = (CSOS_DOUBLE | 0x1), + CSOS_STRING = (CSOS_SCHAR | 0x1), // A pointer to a character from a null-terminated string +} csos_ptypes_t; + +// Cross-compatible data union - a single 32-bit value shared between all types +typedef union { + nullptr_t null; + bool b; + uint8_t u8; + int8_t i8; + uint16_t u16; + int16_t i16; + uint32_t u32; + int32_t i32; + float f; + double d; + char c; +} csos_data_t; + +// Cross-compatible pointer union - a single 16-bit pointer shared between all types +typedef union { + void* null; + const bool* b; + const uint8_t* u8; + const int8_t* i8; + const uint16_t* u16; + const int16_t* i16; + const uint32_t* u32; + const int32_t* i32; + const float* f; + const double* d; + const char* c; +} csos_pdata_t; + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/utils/base.h b/software/archive/microcontroller/utils/base.h similarity index 100% rename from software/microcontroller/src/utils/base.h rename to software/archive/microcontroller/utils/base.h diff --git a/software/microcontroller/src/utils/instructions.cc b/software/archive/microcontroller/utils/instructions.cc similarity index 100% rename from software/microcontroller/src/utils/instructions.cc rename to software/archive/microcontroller/utils/instructions.cc diff --git a/software/microcontroller/src/utils/instructions.h b/software/archive/microcontroller/utils/instructions.h similarity index 100% rename from software/microcontroller/src/utils/instructions.h rename to software/archive/microcontroller/utils/instructions.h diff --git a/software/microcontroller/src/utils/interrupts.cc b/software/archive/microcontroller/utils/interrupts.cc similarity index 100% rename from software/microcontroller/src/utils/interrupts.cc rename to software/archive/microcontroller/utils/interrupts.cc diff --git a/software/microcontroller/src/utils/interrupts.h b/software/archive/microcontroller/utils/interrupts.h similarity index 100% rename from software/microcontroller/src/utils/interrupts.h rename to software/archive/microcontroller/utils/interrupts.h diff --git a/software/microcontroller/src/utils/jsonmessenger.cc b/software/archive/microcontroller/utils/jsonmessenger.cc similarity index 100% rename from software/microcontroller/src/utils/jsonmessenger.cc rename to software/archive/microcontroller/utils/jsonmessenger.cc diff --git a/software/microcontroller/src/utils/jsonmessenger.h b/software/archive/microcontroller/utils/jsonmessenger.h similarity index 100% rename from software/microcontroller/src/utils/jsonmessenger.h rename to software/archive/microcontroller/utils/jsonmessenger.h diff --git a/software/archive/microcontroller/utils/map.h b/software/archive/microcontroller/utils/map.h new file mode 100644 index 00000000..2fdbf35b --- /dev/null +++ b/software/archive/microcontroller/utils/map.h @@ -0,0 +1,71 @@ +#ifndef PEAPOD_UTILS_MAP_H_ +#define PEAPOD_UTILS_MAP_H_ + +#include + +#include + +#include +#include + +// Returns Sensor* +typedef Sensor* (*sensorfactory_t)(void* args); + +class SensorFactoryMap { + public: + template sensorfactory_t add(const char* const id); + sensorfactory_t find(const char* id); + private: + uint8_t numsensors = 0; + sensorfactory_t sensorFactories[NUM_SENSORS]; + const char* const* ids; +}; + +template sensorfactory_t SensorFactoryMap::add(const char* const id) { + uint8_t newsensor = numsensors; + ids[newsensor] = id; + sensorFactories[newsensor] = (sensorfactory_t)([void* args]{ return new S(args); }); + numsensors++; + return sensorFactories[newsensor]; +} + +sensorfactory_t SensorFactoryMap::find(const char* id) { + for (uint8_t i = 0; i < numsensors; i++) { + if (strcmp(id, ids[i]) == 0) { + return sensorFactories[i]; + } + } + return nullptr; +} + +// Returns Actuator* +typedef Actuator* (*actuatorfactory_t)(void* args); + +class ActuatorFactoryMap { + public: + template actuatorfactory_t add(const char* const id); + actuatorfactory_t find(const char* id); + private: + uint8_t numactuators = 0; + actuatorfactory_t actuatorFactories[NUM_ACTUATORS]; + const char* const* ids; +}; + +template actuatorfactory_t ActuatorFactoryMap::add(const char* const id) { + uint8_t newactuator = numactuators; + ids[newactuator] = id; + actuatorFactories[newactuator] = (actuatorfactory_t)([void* args]{ return new A(args); }); + numactuators++; + return actuatorFactories[newactuator]; +} + +actuatorfactory_t ActuatorFactoryMap::find(const char* id) { + for (uint8_t i = 0; i < numactuators; i++) { + if (strcmp(id, ids[i]) == 0) { + return actuatorFactories[i]; + } + } + return nullptr; +} + +#endif \ No newline at end of file diff --git a/software/index.ts b/software/index.ts index b2b1ef49..5270ddbf 100644 --- a/software/index.ts +++ b/software/index.ts @@ -11,32 +11,33 @@ import PeaPod from './src/peapod'; async function main(): Promise { Spinner.start(`Checking for ${chalk.blue('Internet')} connection...'`); - const internet = await checkInternet(); - if (internet) { - Spinner.succeed(`Connected to the ${chalk.blue('Internet')}!`); - } else { - Spinner.fail(`Failed to connect to the ${chalk.blue('Internet')}.`); - } + // const internet = await checkInternet(); + // const internet = false; + // if (internet) { + // Spinner.succeed(`Connected to the ${chalk.blue('Internet')}!`); + // } else { + // Spinner.fail(`Failed to connect to the ${chalk.blue('Internet')}.`); + // } // Get desired publishing mode - let publishingmode = ( - await inquirer.prompt<{ pm: PublishingMode }>([ - { - type: 'list', - name: 'pm', - message: 'Select publishing mode:', - choices: Object.entries(PublishingMode).map(pm => { - return { - // Object.entries casts `PublishingMode` values to `string`. This recasts them to PublishingMode enum values (TypeScript magic) - value: PublishingMode[pm[0] as keyof typeof PublishingMode], - name: pm[1] - }; - }) - } - ]) - ).pm; - - let peapod: PeaPod = new PeaPod(publishingmode); + // let publishingmode = ( + // await inquirer.prompt<{ pm: PublishingMode }>([ + // { + // type: 'list', + // name: 'pm', + // message: 'Select publishing mode:', + // choices: Object.entries(PublishingMode).map(pm => { + // return { + // // Object.entries casts `PublishingMode` values to `string`. This recasts them to PublishingMode enum values (TypeScript magic) + // value: PublishingMode[pm[0] as keyof typeof PublishingMode], + // name: pm[1] + // }; + // }) + // } + // ]) + // ).pm; + + let peapod: PeaPod = new PeaPod(PublishingMode.OFFLINE); let idleInterval = await peapod.idle(); diff --git a/software/microcontroller/lib/FiniteStateMachine/chrono.cc b/software/microcontroller/lib/FiniteStateMachine/chrono.cc new file mode 100644 index 00000000..099a0d03 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/chrono.cc @@ -0,0 +1,256 @@ +#include + +#include + +#include +#include + +Chronograph Chronos = Chronograph(); + +#ifdef FSM_DEBUG_SERIAL +// Helper +void debugTimestamp(const fsm_timestamp_t& timestamp); + +#endif + +IntervalCallback::IntervalCallback(const fsm_timestamp_t& delta, const void* cb, callback_type_t cbtype, bool invert) : now(delta), delta(delta), ConditionalCallback(CMP_GTR, delta, cb, cbtype, invert) { } + +IntervalCallback::IntervalCallback(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, const void* cb, callback_type_t cbtype, bool invert) : now(delta), delta(delta), phase(phase % delta), ConditionalCallback(CMP_GTR, delta, cb, cbtype, invert) { } + +const fsm_timestamp_t& IntervalCallback::childReference(const fsm_timestamp_t& val) { + // If the new value is way over the line, modulus and set now + if(val > (this->delta + this->phase)) { + this->now = val % this->delta; // leaves phase in + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Interval Reset!\n")); + DEBUG_DELAY(); + #endif // DEBUG + + // Trigger!! + return (refholder = 0); + } + return (refholder = (this->delta + this->phase)); +} + +fsm_timestamp_t IntervalCallback::get(void) { return this->now; } + +void IntervalCallback::set(const fsm_timestamp_t& val) { + this->now = val; + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Interval Set (")); + FSM_DEBUG_SERIAL.print(this->now); + FSM_DEBUG_SERIAL.print(_F("ms)\n")); + DEBUG_DELAY(); + #endif // DEBUG + this->operator()(this->now); +} + +FlagInterval::FlagInterval(const fsm_timestamp_t& delta, Flag* flag, bool invert) : flag(flag), IntervalCallback(delta, nullptr, CB_NONE, invert) { } + +FlagInterval::FlagInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert) : flag(flag), IntervalCallback(delta, phase, nullptr, CB_NONE, invert) { } + +void FlagInterval::childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Flag Interval Set (")); + FSM_DEBUG_SERIAL.print(comp ? "true" : "false"); + FSM_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + this->flag->set(comp); +} + +ChronoEvent::ChronoEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert) : ConditionalCallback(CMP_GEQ, timestamp, cb, invert) { } + +void ChronoEvent::callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { + bool result = compare(this->comparator, val, ref); + if(!this->triggered && result) { + this->triggered = true; + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Event Triggered!\n")); + DEBUG_DELAY(); + #endif // DEBUG + + this->executeCallback(val, ref); + } else if(!result) { + this->triggered = false; + } +} + +FlagEvent::FlagEvent(const fsm_timestamp_t& timestamp, Flag* flag, bool invert) : FlagSetCondition(CMP_GEQ, timestamp, flag, invert) { } + +void FlagEvent::callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { + bool result = compare(this->comparator, val, ref); + if(!this->triggered && result) { + this->triggered = true; + this->executeCallback(val, ref); + } else if(!result) { + this->triggered = false; + } +} +void FlagEvent::childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Flag Event Set (")); + FSM_DEBUG_SERIAL.print(comp ? "true" : "false"); + FSM_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + + this->flag->set(comp); +} + +Chronograph::Chronograph(const fsm_timestamp_t& start) : start(start), last(start), State(start) { + // Blank it + for(int i = 0; i < MAX_INTERVALS; i++) { + this->intervals[i] = nullptr; + } +} + +void Chronograph::set(const fsm_timestamp_t& _val) { + fsm_timestamp_t val = _val % TWENTYFOURHRS_MILLIS; + + const fsm_timestamp_t last = this->get(); + + // Null frame + const fsm_timestamp_t delta = val > last ? val - last : 0; + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Timer Delta: ")); + debugTimestamp(delta); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + + // Call super - let getRef handle overflow ;) + ((State*)this)->set(val); + + for(unsigned int i = 0; i < MAX_INTERVALS; i++) { + if(this->intervals[i] == nullptr) return; + + const fsm_timestamp_t last = intervals[i]->get(); + this->intervals[i]->set(last + delta); + } +} + +fsm_timestamp_t Chronograph::get(void) { return ((State*)this)->get(); } + +ConditionalCallback* Chronograph::addEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert) { return addConditional(new ChronoEvent(timestamp, cb, invert)); } + +ConditionalCallback* Chronograph::addEventFlag(const fsm_timestamp_t& timestamp, Flag* flag, bool invert) { return addConditional(new FlagEvent(timestamp, flag, invert)); } + +IntervalCallback* Chronograph::addInterval(const fsm_timestamp_t& delta, typename ConditionalCallback::cb_compval_t cb, bool invert) { + return this->addInterval(delta, 0, cb, invert); +} + +IntervalCallback* Chronograph::addInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, typename ConditionalCallback::cb_compval_t cb, bool invert) { + unsigned int n = 0; + while(intervals[n] != nullptr) { n++; if(n > MAX_INTERVALS) return nullptr;} + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("New Interval (")); + FSM_DEBUG_SERIAL.print(delta); + if(phase > 0) { + FSM_DEBUG_SERIAL.print("+"); + FSM_DEBUG_SERIAL.print(phase); + } + FSM_DEBUG_SERIAL.print("ms)\n"); + DEBUG_DELAY(); + #endif + + intervals[n] = new IntervalCallback(delta, phase, (const void*)cb, CB_COMPVAL, invert); + return intervals[n]; +} + +IntervalCallback* Chronograph::addIntervalFlag(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert) { + unsigned int n = 0; + while(intervals[n] != nullptr) { n++; if(n > MAX_INTERVALS) return nullptr;} + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("New Interval Flag (")); + FSM_DEBUG_SERIAL.print(delta); + if(phase > 0) { + FSM_DEBUG_SERIAL.print("+"); + FSM_DEBUG_SERIAL.print(phase); + } + FSM_DEBUG_SERIAL.print("ms)\n"); + DEBUG_DELAY(); + #endif + + intervals[n] = new FlagInterval(delta, phase, flag, invert); + return intervals[n]; +} + +IntervalCallback* Chronograph::addIntervalFlag(const fsm_timestamp_t& delta, Flag* flag, bool invert) { + return this->addIntervalFlag(delta, 0, flag, invert); +} + +IntervalCallback* Chronograph::addTwentyFourTimeout(const fsm_timestamp_t& phase, typename ConditionalCallback::cb_compval_t cb, bool invert) { + unsigned int n = 0; + while(intervals[n] != nullptr) { n++; if(n > MAX_INTERVALS) return nullptr;} + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("New 24Hr Interval Flag")); + if(phase > 0) { + FSM_DEBUG_SERIAL.print("(+"); + FSM_DEBUG_SERIAL.print(phase); + FSM_DEBUG_SERIAL.print("ms)\n"); + } + DEBUG_DELAY(); + #endif + + intervals[n] = new IntervalCallback(TWENTYFOURHRS_MILLIS, phase, (const void*)cb, CB_COMPVAL, invert); + return intervals[n]; +} + +#ifdef FSM_DEBUG_SERIAL + +void Chronograph::debugTimestamp(const fsm_timestamp_t& timestamp) { + unsigned long seconds = timestamp / 1000; + unsigned long res_ms = timestamp % 1000; + unsigned long minutes = seconds / 60; + unsigned long hours = minutes / 60; + + seconds -= minutes * 60; + minutes -= hours * 60; + + DEBUG_DELAY(); + if(hours > 0) { + if(hours < 10) FSM_DEBUG_SERIAL.print("0"); + FSM_DEBUG_SERIAL.print(hours); + FSM_DEBUG_SERIAL.print("h : "); + } + + if(minutes > 0) { + if(minutes < 10) FSM_DEBUG_SERIAL.print("0"); + FSM_DEBUG_SERIAL.print(minutes - hours * 60); + FSM_DEBUG_SERIAL.print("m : "); + } + + if(seconds < 10) FSM_DEBUG_SERIAL.print("0"); + FSM_DEBUG_SERIAL.print(seconds); + FSM_DEBUG_SERIAL.print("s"); + + if(minutes < 1) { + // ms + FSM_DEBUG_SERIAL.print(" : "); + if(res_ms < 100) FSM_DEBUG_SERIAL.print("0"); + if(res_ms < 10) FSM_DEBUG_SERIAL.print("0"); + FSM_DEBUG_SERIAL.print(res_ms); + FSM_DEBUG_SERIAL.print("ms"); + } + DEBUG_DELAY(); +} + +#endif + +// ConditionalCallback* _Timer::addLoggerCallback(void (* const cb)(bool comp, fsm_timestamp_t val)) { return addConditional(new ConditionalCallback(CMP_NEQ, -1, cb)); } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/chrono.h b/software/microcontroller/lib/FiniteStateMachine/chrono.h new file mode 100644 index 00000000..f92cbbbe --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/chrono.h @@ -0,0 +1,93 @@ +#ifndef FSM_TIMER_H_ +#define FSM_TIMER_H_ + +#include +#include + +#define MAX_INTERVALS 32 +#define INTERVAL_RESET_WINDOW 1.05 +#define TWENTYFOURHRS_MILLIS ((fsm_timestamp_t)86400000) + +class IntervalCallback : private ConditionalCallback { + private: + // Time since last reset (ms) + fsm_timestamp_t now = 0; + const fsm_timestamp_t phase = 0; + const fsm_timestamp_t delta; + + fsm_timestamp_t refholder; + protected: + const fsm_timestamp_t& childReference(const fsm_timestamp_t& val) override; + public: + IntervalCallback(const fsm_timestamp_t& delta, const void* cb, callback_type_t cbtype, bool invert = false); + + IntervalCallback(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, const void* cb, callback_type_t cbtype, bool invert = false); + + fsm_timestamp_t get(void); + void set(const fsm_timestamp_t& val); +}; + +class FlagInterval : public IntervalCallback { + private: + Flag* flag; + protected: + void childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; + public: + FlagInterval(const fsm_timestamp_t& delta, Flag* flag, bool invert = false); + FlagInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert = false); +}; + +class ChronoEvent : public ConditionalCallback { + private: + // Has the callback been triggered yet? + bool triggered = false; + protected: + void callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; + public: + ChronoEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert = false); +}; + +class FlagEvent : public FlagSetCondition { + private: + // Has the callback been triggered yet? + bool triggered = false; + protected: + void callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; + void childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; + public: + FlagEvent(const fsm_timestamp_t& timestamp, Flag* flag, bool invert = false); +}; + +class Chronograph : private State { + private: + const fsm_timestamp_t start; + const fsm_timestamp_t last; + + IntervalCallback* intervals[MAX_INTERVALS] = { nullptr }; + public: + Chronograph(const fsm_timestamp_t& start = 1000); + + ConditionalCallback* addEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert = false); + ConditionalCallback* addEventFlag(const fsm_timestamp_t& timestamp, Flag* flag, bool invert = false); + IntervalCallback* addInterval(const fsm_timestamp_t& delta, typename ConditionalCallback::cb_compval_t cb, bool invert = false); + IntervalCallback* addInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, typename ConditionalCallback::cb_compval_t cb, bool invert = false); + IntervalCallback* addIntervalFlag(const fsm_timestamp_t& delta, Flag* flag, bool invert = false); + IntervalCallback* addIntervalFlag(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert = false); + + IntervalCallback* addTwentyFourTimeout(const fsm_timestamp_t& delay, typename ConditionalCallback::cb_compval_t cb, bool invert = false); + + // EXPOSE SOME PUBLIC BASE CLASS MEMBERS + + void set(const fsm_timestamp_t& val); + fsm_timestamp_t get(void); + + // ConditionalCallback* addLoggerCallback(void (*cb)(bool, fsm_timestamp_t)); + + #ifdef FSM_DEBUG_SERIAL + static void debugTimestamp(const fsm_timestamp_t& timestamp); + #endif +}; + +extern Chronograph Chronos; + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/comparators.cc b/software/microcontroller/lib/FiniteStateMachine/comparators.cc new file mode 100644 index 00000000..b513038d --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/comparators.cc @@ -0,0 +1,283 @@ +#include + +#include +#include + +template <> bool ConditionalCallback::compare(comparators_t cmp, const bool& val, const bool& ref) { + bool result = false; + char ival = (val ? 1 : 0); + char iref = (ref ? 1 : 0); + + switch(cmp) { + case CMP_EQU: + result = (ival == iref); + break; + case CMP_NEQ: + result = !(ival == iref); + break; + case CMP_LES: + result = (ival < iref); + break; + case CMP_LEQ: + result = (ival <= iref); + break; + case CMP_GTR: + result = (ival > iref); + break; + case CMP_GEQ: + result = (ival >= iref); + break; + default: + result = NOP_COMP_BOOL; + break; + } + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Flag (")); + FSM_DEBUG_SERIAL.print(BOOLSTR(val)); + FSM_DEBUG_SERIAL.print(" | "); + FSM_DEBUG_SERIAL.print((int)ival); + FSM_DEBUG_SERIAL.print(") "); + FSM_DEBUG_SERIAL.print(parseComparator(cmp, result)); + FSM_DEBUG_SERIAL.print(" Ref ("); + FSM_DEBUG_SERIAL.print(BOOLSTR(ref)); + FSM_DEBUG_SERIAL.print(" | "); + FSM_DEBUG_SERIAL.print((int)iref); + FSM_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + return result; +} + +template <> bool ConditionalCallback::compare(comparators_t cmp, const Number& val, const Number& ref) { + if(ref.isNaN()) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("[Variable Ref NaN - Always True]\n")); + DEBUG_DELAY(); + #endif + return true; + } + + bool result = false; + switch(cmp) { + case CMP_EQU: + result = (val == ref); + break; + case CMP_NEQ: + result = (val != ref); + break; + case CMP_LES: + result = (val < ref); + break; + case CMP_LEQ: + result = (val <= ref); + break; + case CMP_GTR: + result = (val > ref); + break; + case CMP_GEQ: + result = (val >= ref); + break; + default: + result = NOP_COMP_NUMBER; + break; + } + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Variable (")); + FSM_DEBUG_SERIAL.print((double)val); + FSM_DEBUG_SERIAL.print(") "); + FSM_DEBUG_SERIAL.print(parseComparator(cmp, result)); + FSM_DEBUG_SERIAL.print(_F(" Ref (")); + FSM_DEBUG_SERIAL.print((double)ref); + FSM_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + return result; +} + +template <> bool ConditionalCallback::compare(comparators_t cmp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { + return ConditionalCallback::compare(cmp, Number(val), Number(ref)); +} + +BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, const void* rising, const void* falling, callback_type_t cbtype) : low(Number::minimum(lo, hi)), high(Number::maximum(lo, hi)), rising(rising), falling(falling), latchcbtype(cbtype), ConditionalCallback(CMP_LES, low, (double)hi < (double)lo) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang [")); + FSM_DEBUG_SERIAL.print((double)lo); + FSM_DEBUG_SERIAL.print(", "); + FSM_DEBUG_SERIAL.print((double)hi); + FSM_DEBUG_SERIAL.print("]\n"); + DEBUG_DELAY(); + #endif +} + +BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, cb_none_t rising, cb_none_t falling) : BangBangConditional(lo, hi, (const void*)rising, (const void*)falling, CB_NONE) { } + +BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, cb_none_t cb) : BangBangConditional(lo, hi, (const void*)cb, (const void*)cb, CB_NONE) { } + +BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, const void* cb, callback_type_t cbtype) : BangBangConditional(lo, hi, (const void*)cb, (const void*)cb, cbtype) { } + +void BangBangConditional::childCallback(bool comp, const Number& val, const Number& ref) { + this->state = (ref == this->high); + + const void* fn = this->state ? this->rising : this->falling; + #ifdef FSM_DEBUG_SERIAL + if(this->state) { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang (High)\n")); + DEBUG_DELAY(); + } else { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang (Low)\n")); + DEBUG_DELAY(); + } + #endif + + // state ? comp : !comp; + // T XOR T = T + // F XOR T = F + // T XOR F = F + // F XOR F = T + // comp = !(comp ^ this->state); + + bool _ = !(this->invert ^ this->state); + + // Cast void* cb fn pointer to appropriate type and call + if(fn != nullptr){ + switch(this->callbacktype) { + case CB_NONE: + // Called with no args + ((cb_none_t)fn)(); + break; + case CB_COMP: + ((cb_comp_t)fn)(_); + break; + case CB_COMPVAL: + ((cb_compval_t)fn)(_, val); + break; + case CB_COMPVALREF: + ((cb_compvalref_t)fn)(_, val, ref); + break; + } + } +} + +const Number& BangBangConditional::childReference(const Number& val) { + if (this->state && val <= this->low) { + this->setComparator(CMP_LES); + this->triggered = false; + this->state = false; + } else if (!this->state && val >= this->high) { + this->setComparator(CMP_GTR); + this->triggered = false; + this->state = true; + } + return this->state ? this->high : this->low; +} + +void BangBangConditional::callOperator(const Number& val, const Number& ref) { + bool result = compare(this->comparator, val, ref); + if(!this->triggered && result) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang Triggered\n")); + DEBUG_DELAY(); + #endif + this->triggered = true; + this->executeCallback(val, ref); + } else if(!result) { + // Reset + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang Trigger Reset!\n")); + DEBUG_DELAY(); + #endif + this->triggered = false; + } +} + +LatchingConditional::LatchingConditional(const void* rising, const void* falling, callback_type_t cbtype, bool invert) : rising(rising), falling(falling), latchcbtype(cbtype), ConditionalCallback(CMP_EQU, !invert, invert) { } + +LatchingConditional::LatchingConditional(void (* const rising)(void), void (* const falling)(void), bool invert) : LatchingConditional((const void*)rising, (const void*)falling, CB_NONE, invert) { } + +LatchingConditional::LatchingConditional(const void* cb, callback_type_t cbtype, bool invert) : LatchingConditional(cb, cb, cbtype, invert) { } + +LatchingConditional::LatchingConditional(void (* const cb)(void), bool invert) : LatchingConditional((const void*)cb, CB_NONE, invert) { } + +void LatchingConditional::callOperator(const bool& val, const bool& ref) { + bool result = compare(this->comparator, val, ref); + #ifdef FSM_DEBUG_SERIAL + if(!result && this->triggered) { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Trigger Reset!\n")); + DEBUG_DELAY(); + } + #endif + if(!this->triggered && result) { + this->triggered = true; + this->executeCallback(val, ref); + } else if(!result) { + // Reset + this->triggered = false; + } +} + +void LatchingConditional::childCallback(bool comp, const bool& val, const bool& ref) { + this->state = !ref; + this->triggered = false; + + const void* fn = val ? this->rising : this->falling; + #ifdef FSM_DEBUG_SERIAL + if(val) { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Latch Set (true)\n")); + DEBUG_DELAY(); + } else { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Latch Reset (false)\n")); + DEBUG_DELAY(); + } + #endif + + // Cast void* cb fn pointer to appropriate type and call + if(fn != nullptr){ + switch(this->callbacktype) { + case CB_NONE: + // Called with no args + ((cb_none_t)fn)(); + break; + case CB_COMP: + ((cb_comp_t)fn)(comp); + break; + case CB_COMPVAL: + ((cb_compval_t)fn)(comp, val); + break; + case CB_COMPVALREF: + ((cb_compvalref_t)fn)(comp, val, ref); + break; + } + } +} + +const bool& LatchingConditional::childReference(const bool& val) { return this->state; } + +const char* parseComparator(const comparators_t& cmp, bool result) { + switch(cmp) { + case CMP_EQU: + return result ? "==" : "!=="; + case CMP_NEQ: + return result ? "!=" : "!!="; + case CMP_LES: + return result ? "<" : "!<"; + case CMP_LEQ: + return result ? "<=" : "!<="; + case CMP_GTR: + return result ? ">" : "!>"; + case CMP_GEQ: + return result ? ">=" : "!>="; + default: + return result ? "??" : "!??"; + } +} \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/comparators.h b/software/microcontroller/lib/FiniteStateMachine/comparators.h new file mode 100644 index 00000000..172ffe28 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/comparators.h @@ -0,0 +1,176 @@ +#ifndef FSM_COMPARATORS_H_ +#define FSM_COMPARATORS_H_ + +#include + +#define NOP_COMP_BOOL true +#define NOP_COMP_NUMBER false + +typedef enum { + CMP_NOP = 0, + CMP_EQU, + CMP_NEQ, + CMP_LES, + CMP_LEQ, + CMP_GTR, + CMP_GEQ, +} comparators_t; + +// Helper Functions +const char* parseComparator(const comparators_t& cmp, bool result = true); + +typedef enum { + CB_NONE = 0, // void (* const)(void) - NO ARGS + CB_COMP, // void (* const)(bool) - ADD: CONDITION RESULT + CB_COMPVAL, // void (* const)(bool, T) - ADD: PASSED VALUE + CB_COMPVALREF, // void (* const)(bool, T, T) - ADD: REFERENCE VALUE +} callback_type_t; + +// template struct conditional_callbacks_t { +// typedef ; +// typedef ; +// typedef ); +// typedef void (* const cb_ref_t)(bool comp, A val, A ref); +// typedef A (* const cb_getref_t)(void); +// }; + +// TODO: Compound conditionals? + +// CONSTRUCTION: + + + +// USE: CONDITION - COMPARISON OF VALUE TO REFERENCE +// IF TRUE: EXECUTION + +/** + * @param T type of the two values to be compared +*/ +template class ConditionalCallback { + public: + typedef const T& (* cb_getref_t)(const T&); + typedef void (* const cb_none_t)(void); + typedef void (* const cb_comp_t)(bool); + typedef void (* const cb_compval_t)(bool, const T&); + typedef void (* const cb_compvalref_t)(bool, const T&, const T&); + + /** + * @param cmp COMPARATOR + * @param ref REFERENCE VALUE + * @param cb CALLBACK FUNCTION POINTER (CAST TO CONST VOID*) + * @param cbtype CALLBACK TYPE - dictates argument set, cb pointer recast + * REFERENCE UPDATOR (OPTIONAL) + **/ + ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, const T&(* const getReference)(const T&) = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, cb_getref_t getReference = nullptr); + + ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, bool invert, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, bool invert, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, bool invert, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, bool invert, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, bool invert, cb_getref_t getReference = nullptr); + + ConditionalCallback& operator=(ConditionalCallback const& other); + + void operator()(const T& val); + void operator()(const T& val, const T& ref); + + static bool compare(comparators_t cmp, const T& val, const T& ref); + + void disable(); + void enable(); + void enable(const T& val); + + protected: + // NO CALLBACK - ASSUMES childCallback is implemented + ConditionalCallback(comparators_t cmp, const T& ref, cb_getref_t getReference = nullptr); + ConditionalCallback(comparators_t cmp, const T& ref, bool invert, cb_getref_t getReference = nullptr); + + // FALLBACKS FOR FUNCPOINTERS + virtual void childCallback(bool comp, const T& val, const T& ref); // NOP + virtual const T& childReference(const T& val); // NOP + + // PROPERTIES + + void setComparator(comparators_t cmp); + + comparators_t comparator; + + virtual void callOperator(const T& val, const T& ref); + + T reference; + + const bool invert = false; + + bool disabled = false; + + const void* callback; + + const callback_type_t callbacktype; + + const cb_getref_t getReference = nullptr; + + void executeCallback(const T& val, const T& ref); +}; + +class BangBangConditional : public ConditionalCallback { + private: + bool state = false; + + // Has the callback been triggered yet? + bool triggered = false; + + const Number low; + const Number high; + + const void* rising; + const void* falling; + + const callback_type_t latchcbtype; + + protected: + void childCallback(bool comp, const Number& val, const Number& ref) override; + const Number& childReference(const Number& val) override; + void callOperator(const Number& val, const Number& ref) override; + public: + /** + * When val < `lo`, callback(true) + * When val > `hi`, callback(false) + * `invert` inverts argument value + * Args are parsed with min/max so that hi > lo + */ + BangBangConditional(const Number& lo, const Number& hi, cb_none_t rising, cb_none_t falling); + BangBangConditional(const Number& lo, const Number& hi, const void* rising, const void* falling, callback_type_t cbtype); + BangBangConditional(const Number& lo, const Number& hi, cb_none_t cb); + BangBangConditional(const Number& lo, const Number& hi, const void* cb, callback_type_t cbtype); +}; + +class LatchingConditional : public ConditionalCallback { + private: + bool state = true; + + // Has the callback been triggered yet? + bool triggered = false; + + const void* rising; + const void* falling; + + const callback_type_t latchcbtype; + + protected: + void childCallback(bool comp, const bool& val, const bool& ref) override; + const bool& childReference(const bool& val) override; + void callOperator(const bool& val, const bool& ref) override; + public: + LatchingConditional(cb_none_t rising, cb_none_t falling, bool invert = false); + LatchingConditional(const void* rising, const void* falling, callback_type_t cbtype, bool invert = false); + explicit LatchingConditional(cb_none_t cb, bool invert = false); + LatchingConditional(const void* cb, callback_type_t cbtype, bool invert = false); +}; + +#include + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/comparators.tpp b/software/microcontroller/lib/FiniteStateMachine/comparators.tpp new file mode 100644 index 00000000..8da40ac4 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/comparators.tpp @@ -0,0 +1,170 @@ +#ifndef FSM_COMPARATORS_H_ +#error __FILE__ should only be included AFTER +// #include +#endif + +#ifdef FSM_COMPARATORS_H_ + +#ifndef FSM_COMPARATORS_T_ +#define FSM_COMPARATORS_T_ + +// #error COMPARATORS.TPP + +#define BOOLSTR(b) (b ? "true" : "false") + +#include + +// TODO: All T args in callbacks -> const T& +// TODO: Replace all OneShot with Latching (duh) + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, bool invert, cb_getref_t getReference) : comparator(cmp), reference(ref), callback(cb), callbacktype(cbtype), invert(invert), getReference(getReference) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Callback Created: X ")); + FSM_DEBUG_SERIAL.print(parseComparator(cmp)); + FSM_DEBUG_SERIAL.print(" "); + FSM_DEBUG_SERIAL.print(ref); + if(invert) FSM_DEBUG_SERIAL.print(_F(" (Inverted) ")); + if(getReference != nullptr) FSM_DEBUG_SERIAL.print(_F(" (Dynamic Ref) ")); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif +} + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_NONE, invert, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMP, invert, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVAL, invert, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVALREF, invert, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, nullptr, CB_COMP, invert, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, cb_getref_t getReference) : comparator(cmp), reference(ref), callback(cb), callbacktype(cbtype), getReference(getReference) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Callback Created: X ")); + FSM_DEBUG_SERIAL.print(parseComparator(cmp)); + FSM_DEBUG_SERIAL.print(" "); + FSM_DEBUG_SERIAL.print(ref); + if(getReference != nullptr) FSM_DEBUG_SERIAL.print(_F(" (Dynamic Ref) ")); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif +} + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_NONE, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMP, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVAL, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVALREF, getReference) { } + +template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_getref_t getReference) : ConditionalCallback(cmp, ref, nullptr, CB_COMP, getReference) { } + +template ConditionalCallback& ConditionalCallback::operator=(ConditionalCallback const& other) { return ConditionalCallback(other.comparator, other.reference, other.callback, other.callbacktype, other.getReference); } + +template void ConditionalCallback::childCallback(bool comp, const T& val, const T& ref) { return; } +template const T& ConditionalCallback::childReference(const T& val) { return this->reference; } + +template void ConditionalCallback::operator()(const T& val) { + if(this->disabled) return; + + // Update the reference value + if (this->getReference != nullptr) { + this->reference = this->getReference(val); + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Got Reference: ")); + FSM_DEBUG_SERIAL.print(this->reference); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + } else { + this->reference = this->childReference(val); + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Got Child Reference: ")); + FSM_DEBUG_SERIAL.print(this->reference); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + } + this->callOperator(val, this->reference); +} + +template void ConditionalCallback::operator()(const T& val, const T& ref) { this->callOperator(val, ref); } + +template void ConditionalCallback::setComparator(comparators_t cmp) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Comparator Change: ")); + FSM_DEBUG_SERIAL.print(parseComparator(cmp)); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + this->comparator = cmp; +} + +template void ConditionalCallback::callOperator(const T& val, const T& ref) { + bool result = ConditionalCallback::compare(this->comparator, val, ref); + + if(result) this->executeCallback(val, ref); +} + +template void ConditionalCallback::executeCallback(const T& val, const T& ref) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(invert ? _F("Callback! (Inverted)") : _F("Callback!")); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + + bool _ = this->invert ? false : true; + + // #ifdef FSM_DEBUG_SERIAL + // DEBUG_DELAY(); + // FSM_DEBUG_SERIAL.print("Passing "); + // FSM_DEBUG_SERIAL.print(_ ? "true" : "false"); + // FSM_DEBUG_SERIAL.print("\n"); + // DEBUG_DELAY(); + // #endif + + if(this->callback == nullptr) { + // Assumes childCallback + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("No Callback - Calling Child: ")); + DEBUG_DELAY(); + #endif + this->childCallback(_, val, ref); + return; + }; + + // Cast void* cb fn pointer to appropriate type and call + switch(this->callbacktype) { + case CB_NONE: + // Called with no args + ((cb_none_t)this->callback)(); + break; + case CB_COMP: + ((cb_comp_t)this->callback)(_); + break; + case CB_COMPVAL: + ((cb_compval_t)this->callback)(_, val); + break; + case CB_COMPVALREF: + ((cb_compvalref_t)this->callback)(_, val, ref); + break; + } +} + +template void ConditionalCallback::disable() { this->disabled = true; } +template void ConditionalCallback::enable() { this->disabled = false; } +template void ConditionalCallback::enable(const T& val) { this->disabled = false; this->executeCallback(val, this->reference); } + +#endif + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/debug.h b/software/microcontroller/lib/FiniteStateMachine/debug.h new file mode 100644 index 00000000..b07b2ffc --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/debug.h @@ -0,0 +1,33 @@ +#ifndef FSM_DEBUG_SERIAL + +#include + +// Uncomment to enable debug +// #define DEBUG 1 + +// CROSS-LIBRARY DEBUG COMPATIBILITY +#ifdef DEBUG +#if DEBUG == true +#ifndef DEBUG_SERIAL +#define DEBUG_SERIAL Serial +#endif +#endif +#endif + +#ifdef DEBUG_SERIAL +// Just once +#define FSM_DEBUG_SERIAL DEBUG_SERIAL + +#ifndef DEBUG_DELAY +#define DEBUG_DELAY() { DEBUG_SERIAL.flush(); delay(10); } +#endif +#endif + +// #define DEBUG_DISABLE_FSTRINGS 1 +#ifdef DEBUG_DISABLE_FSTRINGS +#define _F(x) x +#else +#define _F(x) F(x) +#endif + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/library.properties b/software/microcontroller/lib/FiniteStateMachine/library.properties new file mode 100644 index 00000000..59472552 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/library.properties @@ -0,0 +1,10 @@ +name=FiniteStateMachine +version=0.1.0 +author=Jayden Lefebvre +maintainer=Jayden Lefebvre +sentence=A synchronous time-sliced state management library to build responsive "embedded apps" via callbacks and conditional signal chaining. +paragraph=Tested on Arduino Nano. +category=Device Control +url=https://github.com/PeaPodTechnologies/FiniteStateMachine/ +architectures=avr +includes=state.h, chrono.h \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/number.cc b/software/microcontroller/lib/FiniteStateMachine/number.cc new file mode 100644 index 00000000..56d2697a --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/number.cc @@ -0,0 +1,135 @@ +#include + +#include + +#include + +int Number::compare(const Number& other) const { + if(other.isNaN() && this->isNaN()) return 0; + if(other.isNaN()) return 1; + if(this->isNaN()) return -1; + + double diff = this->value - other.value; + return (diff < NUMBER_FLOP_EPSILON) ? (diff > -NUMBER_FLOP_EPSILON ? 0 : -1) : 1; +} + +// float Number::getValue() { return this->value; } +// float Number::isFloatingPoint() { return this->isFloating; } +// float Number::isSignedInteger() { return this->isSigned; } + +void Number::set(const double& val, bool isFloating, bool isSigned) { + this->value = val; + this->isFloating = isFloating; + this->isSigned = isSigned; +} + +void Number::setValue(const long& val) { this->set((double)val, false); } +void Number::setValue(const unsigned long& val) { this->set((double)val, false, false); } + +// Master Constructor +Number::Number(const double& val, bool isFloating, bool isSigned) : value(val), isFloating(isFloating), isSigned(isFloating || isSigned) { } + +// NaN Constructor +Number::Number() : value((double)NAN), isFloating(true), isSigned(true), notanumber(true) { + // delay(10); + // Serial.print("[NaN Constructor]\n"); + // delay(10); +} + +Number::Number(const unsigned long& val) : Number((double)val, false, false) { } +Number::Number(const long& val) : Number((double)val, false, val < 0) { } +Number::Number(const int& val) : Number((double)val, false, val < 0) { } +Number::Number(const Number& other) : Number(other.value, other.isFloating, other.isSigned) { + if(other.isNaN()) { + // delay(10); + // Serial.print("[NaN Copy Constructor]\n"); + // delay(10); + this->notanumber = true; + } +} + +Number::~Number() { } + +bool Number::isNaN(void) const { + // if(this->notanumber) { + // delay(10); + // Serial.print("[IS NAN]\n"); + // delay(10); + // } + return this->notanumber; +} + +double Number::operator()(void) const { return this->operator double(); } + +Number::operator double() const { return this->isNaN() ? (double)NAN : this->value; } + +void Number::operator=(const double& rhs) { this->set(rhs); } +void Number::operator=(const float& rhs) { this->set(rhs); } +void Number::operator=(const long& rhs) { this->set((double)rhs, false, rhs < 0); } +void Number::operator=(const int& rhs) { this->set((double)rhs, false, rhs < 0); } +void Number::operator=(const unsigned long& rhs) { this->set((double)rhs, false, false); } +void Number::operator=(const unsigned int& rhs) { this->set((double)rhs, false, false); } + +Number& Number::operator=(const Number& other) { + if(other.isNaN()) { + this->notanumber = true; + this->set((double)NAN, true); + } else { + this->set(other.value, other.isFloating, other.isSigned); + } + return (*this); +} + +bool Number::operator==(const Number& rhs) const { return (compare(rhs) == 0) && !(this->isFloating ^ rhs.isFloating); } + +bool Number::operator!=(const Number& rhs) const { return (compare(rhs) != 0); } + +bool Number::operator<(const Number& rhs) const { return (compare(rhs) < 0); } +bool Number::operator<=(const Number& rhs) const { return (compare(rhs) <= 0); } +bool Number::operator>(const Number& rhs) const { return (compare(rhs) > 0); } +bool Number::operator>=(const Number& rhs) const { return (compare(rhs) >= 0); } + +// Number::operator double() const { return this->value; } +// Number::operator long() const { return (this->isSigned ? (long)this->value : (unsigned long)this->value); } +// Number::operator int() const { return (this->isSigned ? (int)this->value : (unsigned int)this->value); } + +// NUMBER FLAG RULES + +// Addition/Subtraction: +// Is Floating: IFF this is floating OR rhs is floating +// Is Signed: IFF the result is floating OR signed + +Number Number::operator+(const Number& rhs) const { + if(rhs.isNaN() || this->isNaN()) return Number(); + + double result = rhs.value + this->value; + return Number(result, rhs.isFloating || this->isFloating, result < 0.0); +} + +Number Number::operator-(const Number& rhs) const { + if(rhs.isNaN() || this->isNaN()) return Number(); + + double result = this->value - rhs.value; + return Number(result, rhs.isFloating || this->isFloating, result < 0.0); +} + +// Multiplication/Division: +// Is Floating: IFF this is floating OR rhs is floating +// Is Signed: IFF this is floating OR rhs is floating OR this is signed OR rhs is signed + +Number Number::operator*(const Number& rhs) const { + if(rhs.isNaN() || this->isNaN()) return Number(); + + double result = rhs.value * this->value; + return Number(result, rhs.isFloating || this->isFloating, rhs.isSigned || this->isSigned); +} + +Number Number::operator/(const Number& rhs) const { + if(rhs.isNaN() || this->isNaN()) return Number(); + + return (rhs.value < NUMBER_FLOP_EPSILON && rhs.value > -NUMBER_FLOP_EPSILON) ? Number() : Number(this->value / rhs.value, true, rhs.isSigned || this->isSigned); +} + +const Number& Number::minimum(const Number& a, const Number& b) { return a > b ? b : a; } + +const Number& Number::maximum(const Number& a, const Number& b) { return a < b ? b : a; } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/number.h b/software/microcontroller/lib/FiniteStateMachine/number.h new file mode 100644 index 00000000..8b3e0849 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/number.h @@ -0,0 +1,74 @@ +#ifndef FSM_NUMBER_H_ +#define FSM_NUMBER_H_ + +#define NUMBER_FLOP_EPSILON 0.00001 + +#include + +typedef unsigned long fsm_timestamp_t; + +class Number { + private: + double value; + bool isFloating; + bool isSigned; + bool notanumber = false; + + protected: + int compare(const Number& other) const; + + // float getValue(void); + // float isFloatingPoint(void); + // float isSignedInteger(void); + + void set(const double& val, bool isFloating = true, bool isSigned = true); + void setValue(const long& val); + void setValue(const unsigned long& val); + + public: + Number(); // NaN Constructor + Number(const double& val, bool isFloating = true, bool isSigned = true); + Number(const int& val); + Number(const long& val); + Number(const unsigned long& val); + Number(const Number& other); + + ~Number(); + + bool isNaN(void) const; + + double operator()(void) const; + + void operator=(const double& rhs); + void operator=(const float& rhs); + void operator=(const long& rhs); + void operator=(const int& rhs); + void operator=(const unsigned long& rhs); + void operator=(const unsigned int& rhs); + + Number& operator=(const Number& other); + + bool operator==(const Number& rhs) const; + bool operator!=(const Number& rhs) const; + + bool operator<(const Number& rhs) const; + bool operator<=(const Number& rhs) const; + bool operator>(const Number& rhs) const; + bool operator>=(const Number& rhs) const; + + operator double() const; + explicit operator fsm_timestamp_t() const; + // operator long() const; + // operator int() const; + + Number operator+(const Number& rhs) const; + Number operator-(const Number& rhs) const; + Number operator*(const Number& rhs) const; + Number operator/(const Number& rhs) const; + + static const Number& minimum(const Number& a, const Number& b); + + static const Number& maximum(const Number& a, const Number& b); +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/state.cc b/software/microcontroller/lib/FiniteStateMachine/state.cc new file mode 100644 index 00000000..a5c9c107 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/state.cc @@ -0,0 +1,89 @@ +#define FSM_SRC 1 + +#include + +#include +#include +#include + +#ifdef FSM_DEBUG_SERIAL + +template <> void stateDebug(const Number& val) { + if(val.isNaN()) { + FSM_DEBUG_SERIAL.print("NaN"); + } else { + FSM_DEBUG_SERIAL.print(val.operator double()); + } +} + +template <> void stateDebug(const bool& val) { + FSM_DEBUG_SERIAL.print(val ? "true" : "false"); +} + +template <> void stateDebug(const fsm_timestamp_t& val) { + FSM_DEBUG_SERIAL.print(val); +} + +#endif + +template <> ConditionalCallback* State::addLoggerCallback(typename ConditionalCallback::cb_compval_t cb) { return addConditional(new ConditionalCallback(CMP_NEQ, Number(), cb)); } + +template <> ConditionalCallback* State::addLoggerCallback(typename ConditionalCallback::cb_compval_t cb) { return addConditional(new ConditionalCallback(CMP_NEQ, (fsm_timestamp_t)-1, cb)); } + +template <> ConditionalCallback* State::addLoggerCallback(typename ConditionalCallback::cb_compval_t cb) { return addConditional(new ConditionalCallback(CMP_NOP, false, cb)); } + +template <> ConditionalCallback* State::addLatchingSetFlag(const Number& on, const Number& off, Flag* flag) { + // if(on < off) { + // "heater" + return addConditional(new BangBangFlagSet(on, off, flag)); + // addConditional(oneshot ? new LatchFlagSet(CMP_LES, on, flag) : new FlagSetCondition(CMP_LES, on, flag)); + // addConditional(oneshot ? new LatchFlagSet(CMP_GTR, off, flag, true) : new FlagSetCondition(CMP_GTR, off, flag, true)); + // } else { + // "cooler" + // return addConditional(new BangBangFlagSet(off, on, flag)); + // addConditional(oneshot ? new LatchFlagSet(CMP_GTR, on, flag) : new FlagSetCondition(CMP_GTR, on, flag)); + // addConditional(oneshot ? new LatchFlagSet(CMP_LES, off, flag, true) : new FlagSetCondition(CMP_LES, off, flag, true)); + // } +} + +template <> ConditionalCallback* State::addLatchingConditional(const Number& on, const Number& off, const void* cb, callback_type_t cbtype) { + // if(on < off) { + // "heater" + return addConditional(new BangBangConditional(on, off, cb, cbtype)); + // addConditional(oneshot ? new OneShotConditional(CMP_LES, on, cb, cbtype) : new ConditionalCallback(CMP_LES, on, cb, cbtype)); + // addConditional(oneshot ? new OneShotConditional(CMP_GTR, off, cb, cbtype, true) : new ConditionalCallback(CMP_GTR, off, cb, cbtype, true)); + // } else { + // "cooler" + // return addConditional(new BangBangConditional(off, on, cb, cbtype)); + // addConditional(oneshot ? new OneShotConditional(CMP_GTR, on, cb, cbtype) : new ConditionalCallback(CMP_GTR, on, cb, cbtype)); + // addConditional(oneshot ? new OneShotConditional(CMP_LES, off, cb, cbtype, true) : new ConditionalCallback(CMP_LES, off, cb, cbtype, true)); + // } +} + +template <> ConditionalCallback* State::addLatchingSetFlag(const fsm_timestamp_t& on, const fsm_timestamp_t& off, Flag* flag) { + return (ConditionalCallback*)(new BangBangFlagSet(Number(on), Number(off), flag)); +} + +template <> ConditionalCallback* State::addLatchingConditional(const fsm_timestamp_t& on, const fsm_timestamp_t& off, const void* cb, callback_type_t cbtype) { + return (ConditionalCallback*)(new BangBangConditional(Number(on), Number(off), cb, cbtype)); +} + +// BOOLEAN HYSTERESIS = STEP CHANGE FILTER + +template <> ConditionalCallback* State::addLatchingConditional(const bool& on, const bool& off, const void* cb, callback_type_t cbtype) { + return addConditional(new LatchingConditional(cb, cbtype, (char)on < (char)off)); +} + +template <> ConditionalCallback* State::addLatchingSetFlag(const bool& on, const bool& off, Flag* flag) { + return addConditional(new LatchingFlagSet(flag, (char)on < (char)off)); +} + +Variable::Variable(const Number& value) : State(value) { } +Variable::Variable(const double& value) : Variable(Number(value)) { } +Variable::Variable(const float& value) : Variable(Number(value)) { } +Variable::Variable(const long& value) : Variable(Number(value)) { } +Variable::Variable(const int& value) : Variable(Number((long)value)) { } +Variable::Variable(const unsigned long& value) : Variable(Number(value)) { } +Variable::Variable(const unsigned int& value) : Variable(Number((unsigned long)value)) { } + +Flag::Flag(const bool& value) : State(value) { } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/state.h b/software/microcontroller/lib/FiniteStateMachine/state.h new file mode 100644 index 00000000..74af4229 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/state.h @@ -0,0 +1,112 @@ +#ifndef FSM_STATE_H_ +#define FSM_STATE_H_ + +// Class Forward-Declarations +class Flag; +class Variable; + +#include +#include + +#define MAX_CONDITIONALS 10 + +#ifdef FSM_DEBUG_SERIAL + +template void stateDebug(const T& val); + +#endif + +template class State { + private: + const char* key = nullptr; + + T value; + const T defaultValue; + + bool disabled = false; + + unsigned char numconditionals = 0; + protected: + ConditionalCallback* conditionals[MAX_CONDITIONALS] = { nullptr }; + public: + explicit State(const T& value); + State(const T& value, const char* key); + // ~State(); + + T get(void); + void set(const T& val); + + ConditionalCallback* addConditional(ConditionalCallback* conditional); + + ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_none_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); + + ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_comp_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); + + ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compval_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); + + ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compvalref_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); + + ConditionalCallback* addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference = nullptr); + + ConditionalCallback* addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference = nullptr); + + ConditionalCallback* addLatchingConditional(const T& on, const T& off, const void* cb, callback_type_t cbtype); + ConditionalCallback* addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compvalref_t cb); + ConditionalCallback* addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compval_t cb); + ConditionalCallback* addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_comp_t cb); + + ConditionalCallback* addLatchingSetFlag(const T& on, const T& off, Flag* flag); + + // ConditionalCallback* addConditionalSetFlag(comparators_t cmp, T ref, Flag* flag, bool oneshot = false); + + // ConditionalCallback* addConditionalResetFlag(comparators_t cmp, T ref, Flag* flag, T (*const getReference)(void)); + + // ConditionalCallback* addConditionalResetFlag(comparators_t cmp, T ref, Flag* flag, bool oneshot = false); + + ConditionalCallback* addLoggerCallback(typename ConditionalCallback::cb_compval_t cb); + + // virtual T get(void) = 0; + // virtual void set(T val) = 0; + + operator T() const { return value; }; + + bool isDisabled(); + + // Enable, No Set + void enable(); + // Enable, Re-Set + void resume(); + // Enable, Set + void enable(const T& val); + // Disable, No-Set + void disable(); + // Disable, Set + void disable(const T& val); +}; + +class Variable : public State { + private: + public: + explicit Variable(const Number& value); + explicit Variable(const double& value = 0.0f); + explicit Variable(const float& value); + explicit Variable(const long& value); + explicit Variable(const int& value); + explicit Variable(const unsigned long& value); + explicit Variable(const unsigned int& value); + // Number get(void) override; + // void set(Number val) override; +}; + +class Flag : public State { + private: + public: + explicit Flag(const bool& value = false); + // bool get(void) override; + // void set(bool val) override; +}; + +#include +#include + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/state.tpp b/software/microcontroller/lib/FiniteStateMachine/state.tpp new file mode 100644 index 00000000..709b89b4 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/state.tpp @@ -0,0 +1,105 @@ +#ifndef FSM_STATE_H_ +#error __FILE__ should only be included AFTER +// #include +#endif +#ifndef FSM_STATELINKER_H_ +#error __FILE__ should only be included AFTER +// #include +#endif + +#ifdef FSM_STATE_H_ +#ifdef FSM_STATELINKER_H_ + +#ifndef FSM_STATE_T_ +#define FSM_STATE_T_ + +// #error STATE.TPP + +#include + +template State::State(const T& value) : value(value), defaultValue(value) { } +template State::State(const T& value, const char* key) : value(value), defaultValue(value), key(key) { } + +template T State::get(void) { + return this->value; +} + +template void State::set(const T& val) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + if(this->key == nullptr) { + FSM_DEBUG_SERIAL.print(_F("State Set: ")); + } else { + FSM_DEBUG_SERIAL.print(this->key); + FSM_DEBUG_SERIAL.print(_F(" Set: ")); + } + stateDebug(val); + FSM_DEBUG_SERIAL.print("\n"); + FSM_DEBUG_SERIAL.print(_F("==== [ Conditionals ] ====")); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + + if(!this->disabled) { + for(unsigned int i = 0; i < MAX_CONDITIONALS; i++) { + if(this->conditionals[i] == nullptr) break; + + this->conditionals[i]->operator()(val); + } + } + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("==== [ End of Conditionals")); + if(this->key != nullptr) { + FSM_DEBUG_SERIAL.print(": "); + FSM_DEBUG_SERIAL.print(this->key); + } + FSM_DEBUG_SERIAL.print(_F(" ] ====\n")); + DEBUG_DELAY(); + #endif + + this->value = val; +} + +template ConditionalCallback* State::addConditional(ConditionalCallback* conditional) { + for (unsigned char i = 0; i < MAX_CONDITIONALS; i++) { + if(this->conditionals[i] == nullptr) { + this->conditionals[i] = conditional; + return this->conditionals[i]; + } + } + return nullptr; +} + +template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_none_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } + +template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_comp_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } + +template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compval_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } + +template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compvalref_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } + +template ConditionalCallback* State::addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new FlagSetCondition(cmp, ref, flag, getReference)); } + +template ConditionalCallback* State::addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new FlagSetCondition(cmp, ref, flag, invert, getReference)); } + +// OTHER ARGS + +template ConditionalCallback* State::addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_comp_t cb) { return addLatchingConditional(on, off, (const void*)cb, CB_COMP); } + +template ConditionalCallback* State::addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compval_t cb) { return addLatchingConditional(on, off, (const void*)cb, CB_COMPVAL); } + +template ConditionalCallback* State::addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compvalref_t cb) { return addLatchingConditional(on, off, (const void*)cb, CB_COMPVALREF); } + +template bool State::isDisabled() { return this->disabled; } +template void State::enable() { this->disabled = false; } +template void State::enable(const T& val) { this->disabled = false; this->set(val); } +template void State::disable() { this->disabled = true; } +template void State::disable(const T& val) { this->set(val); this->disabled = true; } +template void State::resume() { this->disabled = false; this->set(this->value); } + + +#endif +#endif +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/statelinker.cc b/software/microcontroller/lib/FiniteStateMachine/statelinker.cc new file mode 100644 index 00000000..aff35281 --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/statelinker.cc @@ -0,0 +1,148 @@ +#include + +#include +#include +#include +#include + +BangBangFlagSet::BangBangFlagSet(const Number& lo, const Number& hi, Flag* flag) : low(Number::minimum(lo, hi)), high(Number::maximum(lo, hi)), FlagSetCondition(CMP_LES, low, flag, (double)hi < (double)lo) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang FlagSet [")); + FSM_DEBUG_SERIAL.print((double)lo); + FSM_DEBUG_SERIAL.print(", "); + FSM_DEBUG_SERIAL.print((double)hi); + FSM_DEBUG_SERIAL.print("]\n"); + DEBUG_DELAY(); + #endif +} + +void BangBangFlagSet::childCallback(bool comp, const Number& val, const Number& ref) { + this->state = (ref == this->high); + + #ifdef FSM_DEBUG_SERIAL + if(state) { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang (High)\n")); + DEBUG_DELAY(); + } else { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang (Low)\n")); + DEBUG_DELAY(); + } + #endif + + // state ? comp : !comp; + // T XOR T = T + // F XOR T = F + // T XOR F = F + // F XOR F = T + // comp = !(comp ^ this->state); + // comp = this->state ? comp : !comp; + bool _ = !(this->invert ^ this->state); + + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Flag Set (")); + FSM_DEBUG_SERIAL.print(_ ? "true" : "false"); + FSM_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + this->flag->set(_); +} + +const Number& BangBangFlagSet::childReference(const Number& val) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang State: ")); + FSM_DEBUG_SERIAL.print(BOOLSTR(this->state)); + FSM_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + if (this->state && val <= this->low) { + this->setComparator(CMP_LES); + this->triggered = false; + this->state = false; + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang State: Expecting Low\n")); + DEBUG_DELAY(); + #endif + } else if (!this->state && val >= this->high) { + this->setComparator(CMP_GTR); + this->triggered = false; + this->state = true; + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang State: Expecting High\n")); + DEBUG_DELAY(); + #endif + } + return this->state ? this->high : this->low; +} + +void BangBangFlagSet::callOperator(const Number& val, const Number& ref) { + bool result = compare(this->comparator, val, ref); + if(!this->triggered && result) { + this->triggered = true; + this->executeCallback(val, ref); + } else if(!result) { + // Reset + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("BangBang Trigger Reset!\n")); + DEBUG_DELAY(); + #endif + this->triggered = false; + } +} + +void LatchingFlagSet::callOperator(const bool& val, const bool& ref) { + bool result = compare(this->comparator, val, ref); + if(!this->triggered && result) { + this->executeCallback(val, ref); + this->triggered = true; + } else if(!result) { + // Reset + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Trigger Reset!\n")); + DEBUG_DELAY(); + #endif + this->triggered = false; + } +} + +LatchingFlagSet::LatchingFlagSet(Flag* flag, bool invert) : FlagSetCondition(CMP_EQU, !invert, flag, invert) { } + + +void LatchingFlagSet::childCallback(bool comp, const bool& val, const bool& ref) { + this->state = !ref; + this->triggered = false; + + #ifdef FSM_DEBUG_SERIAL + if(val) { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Latch (High)\n")); + DEBUG_DELAY(); + } else { + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Latch (Low)\n")); + DEBUG_DELAY(); + } + #endif + + bool _ = val ^ ref; + + // Cast void* cb fn pointer to appropriate type and call + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Flag Set (")); + FSM_DEBUG_SERIAL.print(_ ? "true" : "false"); + FSM_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + this->flag->set(_); +} + +const bool& LatchingFlagSet::childReference(const bool& val) { return this->state; } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/statelinker.h b/software/microcontroller/lib/FiniteStateMachine/statelinker.h new file mode 100644 index 00000000..0dcf56ac --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/statelinker.h @@ -0,0 +1,70 @@ +#ifndef FSM_STATELINKER_H_ +#define FSM_STATELINKER_H_ + +// Class Forwarrd-Declarations (for ) +template class FlagSetCondition; +class LatchingFlagSet; +class BangBangFlagSet; + +#include +#include +#include + +template class FlagSetCondition : public ConditionalCallback { + protected: + Flag* flag; + + void childCallback(bool comp, const T& val, const T& ref) override; + + public: + FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference = nullptr); + FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference = nullptr); +}; + +class BangBangFlagSet : public FlagSetCondition { + private: + bool state = false; + + // Has the callback been triggered yet? + bool triggered = false; + + const Number low; + const Number high; + protected: + void callOperator(const Number& val, const Number& ref) override; + void childCallback(bool comp, const Number& val, const Number& ref) override; + const Number& childReference(const Number& val) override; + public: + BangBangFlagSet(const Number& lo, const Number& hi, Flag* flag); +}; + +// Mostly useless +class LatchingFlagSet : public FlagSetCondition { + private: + bool state = true; + + // Has the callback been triggered yet? + bool triggered = false; + protected: + void callOperator(const bool& val, const bool& ref) override; + void childCallback(bool comp, const bool& val, const bool& ref) override; + const bool& childReference(const bool& val) override; + public: + explicit LatchingFlagSet(Flag* flag, bool invert = false); +}; + +// template class OneShotFlagReset : public FlagSetCondition { +// private: +// bool triggered = false; + +// void callOperator(T val, T ref); +// public: +// OneShotFlagSet(comparators_t cmp, T ref, Flag* flag); + +// void operator()(T val); +// void operator()(T val, T ref); +// }; + +#include + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/statelinker.tpp b/software/microcontroller/lib/FiniteStateMachine/statelinker.tpp new file mode 100644 index 00000000..f8481a1f --- /dev/null +++ b/software/microcontroller/lib/FiniteStateMachine/statelinker.tpp @@ -0,0 +1,40 @@ +#ifndef FSM_STATELINKER_H_ +#error __FILE__ should only be included AFTER +// #include +#endif + +#ifndef FSM_STATE_H_ +#error __FILE__ should only be included AFTER +// #include +#endif + +#ifdef FSM_STATELINKER_H_ +#ifdef FSM_STATE_H_ + +#ifndef FSM_STATELINKER_T_ +#define FSM_STATELINKER_T_ + +#include + +// #error STATELINKER.TPP + +typedef void (*const flagSetCB_t)(bool); + +template FlagSetCondition::FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference) : flag(flag), ConditionalCallback(cmp, ref, getReference) { } + +template FlagSetCondition::FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference) : flag(flag), ConditionalCallback(cmp, ref, invert, getReference) { } + +template void FlagSetCondition::childCallback(bool comp, const T& val, const T& ref) { + #ifdef FSM_DEBUG_SERIAL + DEBUG_DELAY(); + FSM_DEBUG_SERIAL.print(_F("Flag Set (")); + FSM_DEBUG_SERIAL.print(comp ? "true" : "false"); + FSM_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + this->flag->set(comp); +} + +#endif +#endif +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/I2CIP.cc b/software/microcontroller/lib/I2CIP/I2CIP.cc new file mode 100644 index 00000000..b66ec336 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/I2CIP.cc @@ -0,0 +1,560 @@ +#include + +#include +#include + +using namespace I2CIP; + +// Module::Module(const i2cip_fqa_t& eeprom_fqa) { +// EEPROM* _eeprom = new EEPROM(eeprom_fqa); +// if(_eeprom->pingTimeout() == I2CIP_ERR_NONE) { + +// } +// } + +Module::Module(const uint8_t& wire, const uint8_t& mux, const uint8_t& eeprom_addr) : wire(wire), mux(mux), eeprom((EEPROM*)EEPROM::eepromFactory(createFQA(wire, mux, I2CIP_MUX_BUS_DEFAULT, eeprom_addr))) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Module ")); + I2CIP_DEBUG_SERIAL.print(mux, HEX); + I2CIP_DEBUG_SERIAL.print(F(" Constructed\n")); + DEBUG_DELAY(); + #endif + + eeprom->clearCache(); // ensure cache is nullptr + eeprom->resetFailsafe(); // ensure default EEPROM buffer is cached for next write + + // NOTE: I MOVED THIS STEP TO DISCOVER, FLAGGED WITH `eeprom_added`, & MADE DEVICEGROUPFACTORY PURE VIRTUAL + // This potentially useful if I decide to ping in deviceFactory + // this->add(*eeprom); // Add EEPROM to module - note that this will call Module::deviceGroupFactory() +} + +Module::Module(const i2cip_fqa_t& eeprom_fqa) : Module(I2CIP_FQA_SEG_I2CBUS(eeprom_fqa), I2CIP_FQA_SEG_MODULE(eeprom_fqa), I2CIP_EEPROM_ADDR) { } + +Module::~Module() { + // Delete all DeviceGroups, deleting all Devices (incl. EEPROM) + for(uint8_t i = 0; i < HASHTABLE_SLOTS; i++) { + if(this->devices_idgroups.hashtable[i] != nullptr) { + delete (&(this->devices_idgroups.hashtable[i]->value)); + } + } + + // BST, Hashtable are static and delete their own entries +} + +DeviceGroup* Module::deviceGroupFactory(i2cip_id_t id) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Builtin EEPROM DeviceGroup Factory: Matching\n")); + DEBUG_DELAY(); + #endif + + if(id == EEPROM::getStaticIDBuffer() || strcmp(id, EEPROM::getStaticIDBuffer()) == 0) { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F(" Match! Creating...\n")); + // DEBUG_DELAY(); + // #endif + + return new DeviceGroup(EEPROM::getStaticIDBuffer(), EEPROM::eepromFactory); + } + + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F(" EEPROM ID Mismatch!\n")); + // DEBUG_DELAY(); + // #endif + + return nullptr; +} + +// DeviceGroup* Module::deviceGroupFactory(const i2cip_id_t& id) { +// #ifdef I2CIP_DEBUG_SERIAL +// DEBUG_DELAY(); +// I2CIP_DEBUG_SERIAL.print(F("-> DeviceGroup Factory Not Implemented!\n")); +// DEBUG_DELAY(); +// #endif + +// return nullptr; +// } + +bool Module::discover(bool recurse) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Module ")); + I2CIP_DEBUG_SERIAL.print(getModuleNum(), HEX); + I2CIP_DEBUG_SERIAL.print(F(" Discovering...\n")); + DEBUG_DELAY(); + #endif + + if(!this->eeprom_added) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> First-Time EEPROM Addition\n")); + DEBUG_DELAY(); + #endif + Device* e = add(*eeprom); + if(e != eeprom) { + Serial.print(F("*** ABORT ***\n")); + delete e; + return false; + } + this->eeprom_added = true; + } + + // Read EEPROM + const uint16_t len = I2CIP_EEPROM_SIZE; + i2cip_errorlevel_t errlev = eeprom->getInput()->get(&len); + // eeprom->readContents(buf, len, I2CIP_EEPROM_SIZE); + + if(errlev != I2CIP_ERR_NONE || eeprom->getCache() == nullptr) return false; + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Module ")); + I2CIP_DEBUG_SERIAL.print(getModuleNum(), HEX); + I2CIP_DEBUG_SERIAL.print(F(" EEPROM Read! Parsing...\n")); + DEBUG_DELAY(); + #endif + + // Parse EEPROM contents into module devices + bool r = parseEEPROMContents(eeprom->getCache()); + if(r) return true; + else if (recurse) { + // BAD EEPROM CONTENT - OVERWRITE WITH FAILSAFE + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("EEPROM Parse Failed! Overwriting with Failsafe and Retrying...\n")); + DEBUG_DELAY(); + #endif + errlev = ((OutputSetter*)eeprom)->reset(); + if(errlev == I2CIP_ERR_HARD) return false; + + return discover(false); + } else { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("EEPROM Parse Failed! Aborting...\n")); + DEBUG_DELAY(); + #endif + return false; + } +} + +// bool Module::add(Device& device) { // Subnet match check +// if(this->isFQAinSubnet(device.getFQA()) && (*this)[device.getFQA()] != nullptr) return true; +// return false; +// } + +HashTableEntry* Module::addEmptyGroup(const char* id) { + if(this->devices_idgroups.get(id) != nullptr) return this->devices_idgroups.get(id); // Group already exists + + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("Creating new DeviceGroup '")); + // I2CIP_DEBUG_SERIAL.print(id); + // I2CIP_DEBUG_SERIAL.print("' @0x"); + // I2CIP_DEBUG_SERIAL.print((uint16_t)(&id[0]), HEX); + // I2CIP_DEBUG_SERIAL.print("\n"); + // DEBUG_DELAY(); + // #endif + + // Allocate new DeviceGroup + DeviceGroup* group = this->deviceGroupFactory(id); + if(group == nullptr) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("DeviceGroup Factory Failed!\n")); + DEBUG_DELAY(); + #endif + return nullptr; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("DeviceGroup Factory Success! Inserting into HashTable\n")); + DEBUG_DELAY(); + #endif + + // Insert into HashTable + return this->devices_idgroups.set(id, *group); +} + +bool Module::parseEEPROMContents(const char* contents) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("No Parsing Yet! EEPROM Contents '")); + I2CIP_DEBUG_SERIAL.print(contents); + I2CIP_DEBUG_SERIAL.print("' @0x"); + I2CIP_DEBUG_SERIAL.print((uint16_t)contents, HEX); + I2CIP_DEBUG_SERIAL.print("\n"); + DEBUG_DELAY(); + #endif + return true; +} + +Device* Module::add(Device& device, bool overwrite) { + const i2cip_fqa_t& fqa = device.getFQA(); + const char* id = device.getID(); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Module Add Device @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)&device, HEX); + I2CIP_DEBUG_SERIAL.print(F("(ID '")); + I2CIP_DEBUG_SERIAL.print(id); + I2CIP_DEBUG_SERIAL.print(F("' @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)id, HEX); + I2CIP_DEBUG_SERIAL.print(F("; FQA ")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); + I2CIP_DEBUG_SERIAL.print("):\n"); + DEBUG_DELAY(); + #endif + + // Search BST for Device + Device** dptr = this->devices_fqabst[fqa]; + if(dptr != nullptr && *dptr != nullptr) { + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("Device Already Exists (ID '")); + I2CIP_DEBUG_SERIAL.print((*dptr)->getID()); + I2CIP_DEBUG_SERIAL.print("')\n"); + DEBUG_DELAY(); + #endif + + // Return different ptr to invoke deletion + if (!overwrite) return *dptr; + } + + // Search HashTable for DeviceGroup + + HashTableEntry* entry = this->devices_idgroups[id]; + if(entry == nullptr) entry = addEmptyGroup(id); + if(entry == nullptr) { + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("Failed to Create DeviceGroup! Check Libraries.\n")); + DEBUG_DELAY(); + #endif + return nullptr; + } + + Device* existing = entry->value[fqa]; + + if(existing != nullptr) { + if(dptr != nullptr && *dptr != nullptr) { + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("Conflicting Entries, Removing.\n")); + DEBUG_DELAY(); + #endif + + // Delete old device + this->remove(existing, true); + } else { + // Was in HashTable but not BST + BSTNode* ptr = this->devices_fqabst.insert(fqa, existing); + #ifdef I2CIP_DEBUG_SERIAL + if(ptr != nullptr) I2CIP_DEBUG_SERIAL.print(F("-> Copied to BST from DeviceGroup\n")); + DEBUG_DELAY(); + #endif + } + } + + BSTNode* ptr = this->devices_fqabst.insert(fqa, &device); + if(ptr == nullptr) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Failed to Save Device!\n")); + DEBUG_DELAY(); + #endif + return nullptr; + } + if(ptr->value != &device || ptr->key != fqa) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> BST Node Mismatch!\n")); + DEBUG_DELAY(); + #endif + this->devices_fqabst.remove(fqa); + return nullptr; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> BST Success (FQA ")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(F(", Device @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)&device, HEX); + I2CIP_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + + // Insert into DeviceGroup (by pointer copy) + if(!entry->value.contains(device) && !entry->value.add(device)) { + // Abort! + this->devices_fqabst.remove(fqa); + return nullptr; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> HashTable Success (ID '")); + I2CIP_DEBUG_SERIAL.print(entry->value.key); + I2CIP_DEBUG_SERIAL.print(F("' @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)&entry->value.key[0], HEX); + I2CIP_DEBUG_SERIAL.print(F(", DeviceGroup[")); + I2CIP_DEBUG_SERIAL.print(entry->value.numdevices); + I2CIP_DEBUG_SERIAL.print(F("] @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)&entry->value, HEX); + I2CIP_DEBUG_SERIAL.print(F(", Factory @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)entry->value.factory, HEX); + I2CIP_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + + return ptr->value; +} + +Device* Module::operator[](const i2cip_fqa_t& fqa) const { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Module Device Lookup (FQA ")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); + I2CIP_DEBUG_SERIAL.print("):"); + DEBUG_DELAY(); + #endif + + Device** dptr = this->devices_fqabst[fqa]; + + #ifdef I2CIP_DEBUG_SERIAL + if(dptr == nullptr || *dptr == nullptr) { + I2CIP_DEBUG_SERIAL.print(F(" Not Found!\n")); + } else { + I2CIP_DEBUG_SERIAL.print(F(" Found!\n")); + } + DEBUG_DELAY(); + #endif + + return (dptr == nullptr) ? nullptr : *dptr; +} + +DeviceGroup* Module::operator[](i2cip_id_t id) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Module DeviceGroup Lookup (ID '")); + I2CIP_DEBUG_SERIAL.print(id); + I2CIP_DEBUG_SERIAL.print("'):"); + DEBUG_DELAY(); + #endif + HashTableEntry* entry = this->devices_idgroups[id]; + if(entry == nullptr) { + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F(" Not Found, Creating...\n")); + DEBUG_DELAY(); + #endif + + entry = addEmptyGroup(id); + return entry == nullptr ? nullptr : &(entry->value); + } + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F(" Found! (Factory @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)(entry->value.factory), HEX); + I2CIP_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + + return &(entry->value); +} + +void Module::remove(Device* device, bool del) { + if(device == nullptr) return; + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Removing Device... ")); + #endif + + // bool swap = false; + // for(int i = 0; i < this->numdevices-1; i++) { + // if(swap) { + // this->devices[i - 1] = this->devices[i]; + // } + // if(this->devices[i]->getFQA() == device->getFQA()) { + // this->devices[i] = nullptr; + // this->numdevices--; + // swap = true; + // } + // } + + i2cip_fqa_t fqa = device->getFQA(); + + // Lookup in BST + Device** dptr = this->devices_fqabst[fqa]; + if(dptr == nullptr) return; // Device not found + if(*dptr != device) return; // FQA points to different device + if((*dptr)->getFQA() != fqa) return; // Device doesn't match + + // Remove from BST + this->devices_fqabst.remove(fqa); + + // Lookup in HashTable + HashTableEntry* entry = this->devices_idgroups[device->getID()]; + if(entry == nullptr) return; + + // Remove from DeviceGroup + entry->value.remove(device); + + // Delete device + if(del) delete device; + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("Removed!\n")); + DEBUG_DELAY(); + #endif +} + +bool Module::isFQAinSubnet(const i2cip_fqa_t& fqa) { + bool match = I2CIP_FQA_SUBNET_MATCH(fqa, this->eeprom->getFQA()); + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + if(match) { + I2CIP_DEBUG_SERIAL.print(F("Subnet Match!\n")); + } else { + I2CIP_DEBUG_SERIAL.print(F("Subnet Mismatch!\n")); + } + DEBUG_DELAY(); + #endif + return match; +} + +// bool Module::contains(Device* device) { +// #ifdef I2CIP_DEBUG_SERIAL +// DEBUG_DELAY(); +// I2CIP_DEBUG_SERIAL.print(F("Module Contains Device Check!\n"); +// DEBUG_DELAY(); +// #endif + +// if(device == nullptr || !this->isFQAinSubnet(device->getFQA())) return false; +// for(int i = 0; i < this->numdevices; i++) { +// if(this->devices[i]->getFQA() == device->getFQA()) return true; +// } +// return false; +// } + +i2cip_errorlevel_t Module::operator()(void) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Module Self-Check!\n")); + DEBUG_DELAY(); + #endif + + // 1. Check MUX - If we have lost the switch the entire subnet is down! + i2cip_errorlevel_t errlev = MUX::pingMUX(this->eeprom->getFQA()) ? I2CIP_ERR_NONE : I2CIP_ERR_HARD; + I2CIP_ERR_BREAK(errlev); + + // 3. Ping EEPROM + return this->eeprom->pingTimeout(true, false); +} + +i2cip_errorlevel_t Module::operator()(const i2cip_fqa_t& fqa, bool update, bool fail) { + // 1. Self-check + // i2cip_errorlevel_t errlev = this->operator()(); + // I2CIP_ERR_BREAK(errlev); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Module Device Check (FQA ")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(':'); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(")!\n"); + DEBUG_DELAY(); + #endif + + // 2. Device check + if(!this->isFQAinSubnet(fqa)) return I2CIP_ERR_SOFT; + Device* device = this->operator[](fqa); + if(device == nullptr) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Device Not Found!\n")); + DEBUG_DELAY(); + #endif + return I2CIP_ERR_SOFT; + } + i2cip_errorlevel_t errlev = device->pingTimeout(); + // I2CIP_ERR_BREAK(errlev); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + if (errlev == I2CIP_ERR_NONE) { + I2CIP_DEBUG_SERIAL.print(F("-> Device Alive!\n")); + } else { + I2CIP_DEBUG_SERIAL.print(F("-> Device Dead!\n")); + } + DEBUG_DELAY(); + #endif + + if(update) { + // 3. Update device (optional) + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("-> Updating Device\n")); + DEBUG_DELAY(); + #endif + // Do Output, then Input + if(device->getOutput()) { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("Output Set:\n")); + // DEBUG_DELAY(); + // #endif + errlev = fail ? device->getOutput()->failSet() : device->getOutput()->set(); + I2CIP_ERR_BREAK(errlev); + } + if(device->getInput()) { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("Input Get:\n")); + // DEBUG_DELAY(); + // #endif + errlev = fail ? device->getInput()->failGet() : device->getInput()->get(); + I2CIP_ERR_BREAK(errlev); + } + } + + return errlev; +} + +// Device* Module::operator[](const i2cip_fqa_t& fqa) { +// if(!this->isFQAinSubnet(fqa)) return nullptr; +// for(int i = 0; i < this->numdevices; i++) { +// if(this->devices[i]->getFQA() == fqa) return this->devices[i]; +// } +// return nullptr; +// } + +uint8_t Module::getWireNum(void) const { return this->wire; } + +uint8_t Module::getModuleNum(void) const { return this->mux; } \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/I2CIP.h b/software/microcontroller/lib/I2CIP/I2CIP.h new file mode 100644 index 00000000..039c9121 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/I2CIP.h @@ -0,0 +1,110 @@ +#ifndef I2CIP_H_ +#define I2CIP_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// #define I2CIP_FQA_SUBNET_MATCH(fqa, wire, module) (bool)(I2CIP_FQA_SEG_I2CBUS(fqa) == wire && I2CIP_FQA_SEG_MODULE(fqa) == module) +#define I2CIP_FQA_SUBNET_MATCH(fqa, _fqa) (bool)(I2CIP_FQA_SEG_I2CBUS(fqa) == I2CIP_FQA_SEG_I2CBUS(_fqa) && I2CIP_FQA_SEG_MODULE(fqa) == I2CIP_FQA_SEG_MODULE(_fqa)) +#define I2CIP_FQA_MODULE_MATCH(fqa, wire, module) (bool)(I2CIP_FQA_SEG_I2CBUS(fqa) == (wire) && I2CIP_FQA_SEG_MODULE(fqa) == (module)) +#define I2CIP_FQA_BUSADR_MATCH(fqa, bus, addr) (bool)(I2CIP_FQA_SEG_MUXBUS(fqa) == (bus) && I2CIP_FQA_SEG_DEVADR(fqa) == (addr)) + +namespace I2CIP { + + // Enables fundamentals subnet communication and state awareness. + // State is reflected across two data structures: + // 1. A BST of Device* by FQA + // 2. A HashTable of DeviceGroup& by ID + + class Module { + private: + const uint8_t wire; // I2C Wire Number (Index of `wires[]`) + const uint8_t mux; // MUX/Module Number (0x00 - 0x07, address range 0x70 - 0x77) + + bool isFQAinSubnet(const i2cip_fqa_t& fqa); + + // Tables/trees are allocated STATICALLY, their entries are dynamic + BST devices_fqabst = BST(); + HashTable devices_idgroups = HashTable(); + + HashTableEntry* addEmptyGroup(const char* id); + + bool eeprom_added = false; + + protected: + EEPROM* const eeprom; // EEPROM device - to be added to `devices_fqabst` and `devices_idgroups` on construction + + public: + Module(const uint8_t& wire, const uint8_t& module, const uint8_t& eeprom_addr = I2CIP_EEPROM_ADDR); + Module(const i2cip_fqa_t& eeprom_fqa); + + virtual ~Module(); + + uint8_t getWireNum(void) const; + uint8_t getModuleNum(void) const; + + /** + * 1. Module Self-check + * 1a. Check MUX - If we have lost the switch the entire subnet is down! + * 1b. Rebuild EEPROM if necessary + * 1c. Ping EEPROM + * 1d. TODO: Ping Devices + */ + i2cip_errorlevel_t operator()(void); // Module Self-check + + /** + * 2. Device Discovery + * 2a. Read EEPROM + * 2b. Parse EEPROM + * Intended Implementation: + * 2b i. Read EEPROM (by Bus) + * 2b ii. Ping Devices (by ID) + * 2b iii. Create (new) Devices and `add` to DeviceGroups (Made Available Internal API via `operator[]` functions) + */ + + /** + * Discover devices on the module. + * Side effect: Adds its own EEPROM to the proper DeviceGroup + * Side effect: Recurses if EEPROM parse fails; attempts to overwrite with default contents and reparse + * @param recurse {bool} - Whether to recursively parse EEPROM or not + * @returns `false` IFF fail to add EEPROM | failed to ping EEPROM | failed to parse EEPROM; `true` otherwise + */ + bool discover(bool recurse = true); + virtual bool parseEEPROMContents(const char* contents); + Device* add(Device& device, bool overwrite = false); + // virtual DeviceGroup* deviceGroupFactory(const i2cip_id_t& id) = 0; + virtual DeviceGroup* deviceGroupFactory(i2cip_id_t id); + + /** + * 3. Device Lookup: HashTable by ID, BST by FQA + * Intended Implementation: + * 3a i. Check if FQA is in subnet + * 3a ii. Check if FQA is in DeviceGroups + * + * 3b i. Check if ID is in DeviceGroups + * 3b ii. Return DeviceGroup + * + */ + DeviceGroup* operator[](i2cip_id_t id); + Device* operator[](const i2cip_fqa_t& fqa) const; + + /** + * 4. Device Check + */ + i2cip_errorlevel_t operator()(const i2cip_fqa_t& fqa, bool update = false, bool fail = false); + + void remove(Device* device, bool del = true); + + inline operator const EEPROM&() const { return *this->eeprom; } + }; +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/bst.h b/software/microcontroller/lib/I2CIP/bst.h new file mode 100644 index 00000000..be07ef40 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/bst.h @@ -0,0 +1,71 @@ +#ifndef I2CIP_BST_H_ +#define I2CIP_BST_H_ + +#include + +template class BSTNode { + public: + BSTNode(K key, T value, BSTNode* left = nullptr, BSTNode* right = nullptr); + ~BSTNode(); + + K key; // Mutable (copied) key - passed in + T value; // REFERENCE to Value + BSTNode* left; // Pointer to left (lesser) node + BSTNode* right; // Pointer to right (greater) node +}; + +template class BST { + public: + BST(); + + ~BST(); + + // Pointer to root node - all nodes de/allocated on insert()/remove() + BSTNode* root = nullptr; + + /** + * Recursively finds a spot to insert a new node. + * @param key + * @param value + * @param root - Pass-by-ref, to be modified on insertion + * @return Pointer to the inserted node + */ + static BSTNode* insert(K key, T value, BSTNode*& root, bool overwrite = true); + BSTNode* insert(K key, T value, bool overwrite = true); + + /** + * Recursively finds a node by key and removes it. + * @param key + * @param root + */ + static BSTNode* remove(K key, BSTNode*& root); + BSTNode* remove(K key); + + /** + * Recursively finds a node by key. Breadth-first. + * @param key + * @param root + */ + static BSTNode* find(K key, BSTNode* const root); + BSTNode* find(K key) const; + + T* operator[](K key) const; + + /** + * Recursively finds the least-key node. Depth-first. + * @param root + */ + static BSTNode* findMin(BSTNode* root); + BSTNode* findMin(void); + + /** + * Recursively finds the greatest-key node. Depth-first. + * @param root + */ + static BSTNode* findMax(BSTNode* root); + BSTNode* findMax(void); +}; + +#include + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/bst.tpp b/software/microcontroller/lib/I2CIP/bst.tpp new file mode 100644 index 00000000..eda57920 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/bst.tpp @@ -0,0 +1,136 @@ +#ifndef I2CIP_BST_H_ +#error __FILE__ should only be included AFTER +#endif + +#ifdef I2CIP_BST_H_ + +#ifndef I2CIP_BST_T_ +#define I2CIP_BST_T_ + +// BST NODE + +template BSTNode::BSTNode(K key, T value, BSTNode* left, BSTNode* right) : key(key), value(value), left(left), right(right) { } + +template BSTNode::~BSTNode() { + delete(this->left); + delete(this->right); +} + +// BINARY SEARCH TREE + +template BST::BST() { } + +template BST::~BST() { + delete(this->root); +} + +template BSTNode* BST::insert(K key, T value, BSTNode*& root, bool overwrite) { + // Node empty? Allocate new. Otherwise, insert recursively + if(root == nullptr) { + root = new BSTNode(key, value); + #ifdef I2CIP_DEBUG_SERIAL + if(root == nullptr) I2CIP_DEBUG_SERIAL.print(F("BST(x) ENOMEM\n")); + #endif + } else if(key < root->key) { + return insert(key, value, root->left); + } else if(key > root->key) { + return insert(key, value, root->right); + } else if(overwrite) { + // Keys match, overwrite + root->value = value; + } + return root; +} + +template BSTNode* BST::insert(K key, T value, bool overwrite) { + return insert(key, value, this->root, overwrite); +} + +template BSTNode* BST::remove(K key, BSTNode*& root) { + BSTNode* const match = find(key, root); + if(match == nullptr) { + // Match not found + return nullptr; + } else { + // Match found, remove root properly + if(root->left && root->right) { + // Both branches exist, replace root with LEAST key on RIGHT branch + BSTNode* rightLeast = findMin(root->right); + + // Detach branches + BSTNode* newroot = new BSTNode(rightLeast->key, rightLeast->value, remove(rightLeast->key, root->right), root->left); + + // Detach and free old root + root->right = root->left = nullptr; + delete(root); + root = newroot; + } else { + // Branch(es) missing, replace with a branch node + if(root->left == nullptr) { + root = root->right; + } else if(root->right == nullptr) { + root = root->left; + } else { + // No branches on this match; delete + delete(root); + } + } + } + return root; +} + +template BSTNode* BST::remove(K key) { + return remove(key, this->root); +} + +template BSTNode* BST::find(K key, BSTNode* const root) { + if(root != nullptr) { + if(key < root->key) { + return find(key, root->left); + } else if(key > root->key) { + return find(key, root->right); + } + } + return root; +} + +template BSTNode* BST::find(K key) const { + return find(key, this->root); +} + +template T* BST::operator[](K key) const { + BSTNode* node = find(key); + #ifdef I2CIP_DEBUG_SERIAL + if(node == nullptr) I2CIP_DEBUG_SERIAL.print(F("BST[i] ENOENT\n")); + #endif + return node == nullptr ? nullptr : &(node->value); +} + +template BSTNode* BST::findMin(BSTNode* root) { + // If this is a leaf node, or there is no left node, return + if(root == nullptr || root->left == nullptr) { + return root; + } else { + return findMin(root->left); + } +} + +template BSTNode* BST::findMin(void) { + return findMin(this->root); +} + +template BSTNode* BST::findMax(BSTNode* root) { + // If this is a leaf node, or there is no right node, return + if(root == nullptr || root->right == nullptr) { + return root; + } else { + return findMin(root->right); + } +} + +template BSTNode* BST::findMax(void) { + return findMax(this->root); +} + +#endif +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/debug.h b/software/microcontroller/lib/I2CIP/debug.h new file mode 100644 index 00000000..90ca6cef --- /dev/null +++ b/software/microcontroller/lib/I2CIP/debug.h @@ -0,0 +1,28 @@ +#ifndef I2CIP_DEBUG_SERIAL + +#include + +// Uncomment to enable debug +// #define DEBUG 1 + +// CROSS-LIBRARY DEBUG COMPATIBILITY +// #ifdef DEBUG +// #if DEBUG == 1 +// #ifndef DEBUG_SERIAL +// #ifdef HAVE_HWSERIAL0 +// #define DEBUG_SERIAL Serial +// #endif +// #endif +// #endif +// #endif + +// #ifdef DEBUG_SERIAL +// // Just once +// #define I2CIP_DEBUG_SERIAL DEBUG_SERIAL + +// #ifndef DEBUG_DELAY +// #define DEBUG_DELAY() { delay(10); } +// #endif +// #endif + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/device.cc b/software/microcontroller/lib/I2CIP/device.cc new file mode 100644 index 00000000..066b8a70 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/device.cc @@ -0,0 +1,632 @@ +#include + +#include +#include +#include + +using namespace I2CIP; + +// CONSTRUCTORs AND PROPERTY GETTERS/SETTERS + +Device::Device(const i2cip_fqa_t& fqa, i2cip_id_t id) : fqa(fqa), id(id) { } + +Device::~Device() { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("~Device\n")); + // DEBUG_DELAY(); + // #endif +} + +void Device::setInput(InputGetter* input) { if(this->input != nullptr) { delete this->input; } this->input = input; } +void Device::setOutput(OutputSetter* output) { if(this->output != nullptr) { delete this->output; } this->output = output; } + +void Device::removeInput(void) { if(this->input != nullptr) { delete this->input; } this->input = nullptr; } +void Device::removeOutput(void) { if(this->output != nullptr) { delete this->output; } this->output = nullptr; } + +InputGetter* Device::getInput(void) const { return this->input; } +OutputSetter* Device::getOutput(void) const { return this->output; } + +const char InputGetter::failptr_get; +const char OutputSetter::failptr_set; + +InputGetter::~InputGetter() { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("~InputGetter")); + // DEBUG_DELAY(); + // #endif +} +OutputSetter::~OutputSetter() { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("~OutputSetter")); + // DEBUG_DELAY(); + // #endif +} + +i2cip_errorlevel_t Device::get(const void* args) { return (this->getInput() == nullptr) ? I2CIP_ERR_SOFT : this->input->get(args); } +i2cip_errorlevel_t Device::set(const void* value, const void* args) { return (this->output == nullptr) ? I2CIP_ERR_SOFT : this->output->set(value, args); } + +const i2cip_fqa_t& Device::getFQA(void) const { return this->fqa; } + +const i2cip_id_t& Device::getID(void) const { return this->id; } + +// STATIC CLASS-MEMBER FUNCTIONS (PRIVATE INTERNAL API) + +i2cip_errorlevel_t Device::ping(const i2cip_fqa_t& fqa, bool resetbus) { + // Switch MUX bus + i2cip_errorlevel_t errlev = MUX::setBus(fqa); + I2CIP_ERR_BREAK(errlev); + + // Begin transmission + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Ping... ")); + #endif + + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); + + // End transmission, check state + if(I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { + return I2CIP_ERR_HARD; + } + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.println("Pong!"); + DEBUG_DELAY(); + #endif + + // Switch MUX bus back + if (resetbus) { + errlev = MUX::resetBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + // If we made it this far, no errors occurred. + return I2CIP_ERR_NONE; +} + +i2cip_errorlevel_t Device::pingTimeout(const i2cip_fqa_t& fqa, bool setbus, bool resetbus, unsigned int timeout) { + if(setbus) { + i2cip_errorlevel_t errlev = MUX::setBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + // Check if it's actually lost + + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); + i2cip_errorlevel_t errlev = (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() == 0 ? I2CIP_ERR_NONE : I2CIP_ERR_HARD); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Ping... ")); + #endif + + unsigned long start = millis(); + + // Count down until out of time of found + while ((millis()-start) < timeout) { + #ifndef I2CIP_DEBUG_SERIAL + // Delta 1ms + delay(1); + #endif + + // Begin transmission + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); + + // End transmission, check state + errlev = (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() == 0 ? I2CIP_ERR_NONE : I2CIP_ERR_HARD); + + if (errlev == I2CIP_ERR_NONE) { + break; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Ping... ")); + #endif + } + + // Double check MUX before attempting to switch + if(errlev == I2CIP_ERR_HARD && !MUX::pingMUX(fqa)) { + return I2CIP_ERR_HARD; + } + + #ifdef I2CIP_DEBUG_SERIAL + if(errlev == I2CIP_ERR_HARD) { + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.println("Timed Out!"); + DEBUG_DELAY(); + } else { + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Pong! Ping Timeout: ")); + I2CIP_DEBUG_SERIAL.print(millis()-start); + I2CIP_DEBUG_SERIAL.print(F("ms\n")); + DEBUG_DELAY(); + } + #endif + + // Switch MUX bus back + if (resetbus) { + errlev = MUX::resetBus(fqa); + } + + return errlev; +} + +i2cip_errorlevel_t Device::writeByte(const i2cip_fqa_t& fqa, const uint8_t& value, bool setbus) { + i2cip_errorlevel_t errlev; + if (setbus) { + // Switch MUX bus + i2cip_errorlevel_t errlev = MUX::setBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + // Was the write performed successfully? + bool success = true; + + // Begin transmission + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); + + // Write the buffer + if (I2CIP_FQA_TO_WIRE(fqa)->write(value) != 1) { + success = false; + } + + // End transmission + if (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { + return I2CIP_ERR_HARD; + } + + // Reset MUX bus if `reset` == true + if (setbus) { + errlev = MUX::resetBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); +} + +i2cip_errorlevel_t Device::write(const i2cip_fqa_t& fqa, const uint8_t* buffer, size_t len, bool setbus) { + i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; + if (setbus) { + errlev = MUX::setBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + // Was the write performed successfully? + bool success = true; + + // Begin transmission + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); + + // Write the buffer + size_t sent = I2CIP_FQA_TO_WIRE(fqa)->write(buffer, len); + if (sent != len) { + success = false; + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("Write Failed (")); + // I2CIP_DEBUG_SERIAL.print(sent); + // I2CIP_DEBUG_SERIAL.print(F("/")); + // I2CIP_DEBUG_SERIAL.print(len); + // I2CIP_DEBUG_SERIAL.print(F(" bytes sent)\n")); + // DEBUG_DELAY(); + // #endif + } + + // End transmission + if (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("No ACK On Write! ")); + DEBUG_DELAY(); + #endif + return I2CIP_ERR_HARD; + } + + // Reset MUX bus if `reset` == true + if (setbus) { + errlev = MUX::resetBus(fqa); + } + + return ((success || errlev > I2CIP_ERR_NONE) ? errlev : I2CIP_ERR_SOFT); +} + +i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, const uint8_t& value, bool setbus) { + const uint8_t buf[2] = { reg, value }; + return write(fqa, buf, 2, setbus); +} + +i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, const uint8_t& value, bool setbus) { + const uint8_t buf[3] = { (uint8_t)(reg >> 8), (uint8_t)(reg & 0xFF), value }; + return write(fqa, buf, 3, setbus); +} + +i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* buffer, size_t len, bool setbus) { + uint8_t buff[len + 1] = { reg }; + for(size_t i = 0; i < len; i++) { + buff[i + 1] = buffer[i]; + } + return write(fqa, buff, len + 1, setbus); +} + +i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* buffer, size_t len, bool setbus) { + uint8_t buff[len + 2] = { (uint8_t)(reg >> 8), (uint8_t)(reg & 0xFF) }; + for(size_t i = 0; i < len; i++) { + buff[i + 2] = buffer[i]; + } + return write(fqa, buff, len + 2, setbus); +} + +i2cip_errorlevel_t Device::read(const i2cip_fqa_t& fqa, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { + // Device alive? + i2cip_errorlevel_t errlev = ping(fqa, false); + I2CIP_ERR_BREAK(errlev); + + // Read in chunks (buffer size limitation) + size_t pos = 0; + bool success = true; + while (pos < len) { + // Read whichever is greater: number of bytes remaining, or buffer size + uint8_t read_len = ((len - pos) > I2CIP_MAXBUFFER) ? I2CIP_MAXBUFFER : (len - pos); + + // Don't stop the bus unless we've read everything + bool read_stop = (pos + read_len >= len); + + // Request bytes; How many have we received? + size_t recv = I2CIP_FQA_TO_WIRE(fqa)->requestFrom(I2CIP_FQA_SEG_DEVADR(fqa), read_len, (uint8_t)read_stop); + + // We didn't get all the bytes we expected + if (recv != read_len) { + success = false; + len = pos; + break; + } + + // Read in all the bytes + for (uint16_t i = 0; i < read_len; i++) { + dest[pos + i] = I2CIP_FQA_TO_WIRE(fqa)->read(); + if(nullterminate && dest[pos + i] == '\0') { + len = pos + i; + goto endloop0; + } + } + + // Advance the index by the amount of bytes read + pos += read_len; + } +endloop0: + + // Reset MUX bus if `reset` == true + if (resetbus) { + errlev = MUX::resetBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + // Did we read all the bytes we hoped to? + return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); +} + +i2cip_errorlevel_t Device::readByte(const i2cip_fqa_t& fqa, uint8_t& dest, bool resetbus) { + size_t len = 1; + return read(fqa, &dest, len, resetbus); +} + +i2cip_errorlevel_t Device::readWord(const i2cip_fqa_t& fqa, uint16_t& dest, bool resetbus) { + size_t len = 2; + uint8_t buff[2]; + i2cip_errorlevel_t errlev = read(fqa, buff, len, resetbus); + I2CIP_ERR_BREAK(errlev); + dest = ((uint16_t)buff[1] << 8) | (uint16_t)buff[0]; + return errlev; +} + +i2cip_errorlevel_t Device::readRegisterByte(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t& dest, bool resetbus) { + size_t len = 1; + return readRegister(fqa, reg, &dest, len, false, resetbus); +} + +i2cip_errorlevel_t Device::readRegisterByte(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t& dest, bool resetbus) { + size_t len = 1; + return readRegister(fqa, reg, &dest, len, false, resetbus); +} + +i2cip_errorlevel_t Device::readRegisterWord(const i2cip_fqa_t& fqa, const uint8_t& reg, uint16_t& dest, bool resetbus) { + size_t len = 2; + uint8_t buff[2]; + i2cip_errorlevel_t errlev = readRegister(fqa, reg, buff, len, false, resetbus); + I2CIP_ERR_BREAK(errlev); + dest = ((uint16_t)buff[1] << 8) | (uint16_t)buff[0]; + return errlev; +} + +i2cip_errorlevel_t Device::readRegisterWord(const i2cip_fqa_t& fqa, const uint16_t& reg, uint16_t& dest, bool resetbus) { + size_t len = 2; + uint8_t buff[2]; + i2cip_errorlevel_t errlev = readRegister(fqa, reg, buff, len, false, resetbus); + I2CIP_ERR_BREAK(errlev); + dest = ((uint16_t)buff[1] << 8) | (uint16_t)buff[0]; + return errlev; +} + +i2cip_errorlevel_t Device::readRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { + // Device alive? + i2cip_errorlevel_t errlev = ping(fqa, false); + I2CIP_ERR_BREAK(errlev); + + // Read in chunks (buffer size limitation) + size_t pos = 0; + bool success = true; + while (pos < len) { + // Read whichever is greater: number of bytes remaining, or buffer size + uint8_t read_len = ((len - pos) > I2CIP_MAXBUFFER) ? I2CIP_MAXBUFFER : (len - pos); + + // Don't stop the bus unless we've read everything + bool read_stop = (pos + read_len >= len); + + // Request bytes; How many have we received? + size_t recv = I2CIP_FQA_TO_WIRE(fqa)->requestFrom(I2CIP_FQA_SEG_DEVADR(fqa), read_len, reg, 1, (uint8_t)read_stop); + + // We didn't get all the bytes we expected + if (recv != read_len) { + success = false; + len = pos; + break; + } + + // Read in all the bytes + for (uint16_t i = 0; i < read_len; i++) { + dest[pos + i] = I2CIP_FQA_TO_WIRE(fqa)->read(); + if(nullterminate && dest[pos + i] == '\0') { + len = pos + i; + goto endloop1; + } + } + + // Advance the index by the amount of bytes read + pos += read_len; + } +endloop1: + + // Reset MUX bus if `reset` == true + if (resetbus) { + errlev = MUX::resetBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + // Did we read all the bytes we hoped to? + return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); +} + +i2cip_errorlevel_t Device::readRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { + // Device alive? + i2cip_errorlevel_t errlev = ping(fqa, false); + I2CIP_ERR_BREAK(errlev); + + // Read in chunks (buffer size limitation) + size_t pos = 0; + bool success = true; + while (pos < len) { + // Read whichever is greater: number of bytes remaining, or buffer size + uint8_t read_len = min(len - pos, I2CIP_MAXBUFFER); + + // Don't stop the bus unless we've read everything + bool read_stop = (pos + read_len >= len); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Reading bytes ")); + I2CIP_DEBUG_SERIAL.print(pos); + I2CIP_DEBUG_SERIAL.print(F(" - ")); + I2CIP_DEBUG_SERIAL.print(pos+read_len); + I2CIP_DEBUG_SERIAL.print(F(": '")); + DEBUG_DELAY(); + #endif + + // Request bytes; How many have we received? + size_t recv = I2CIP_FQA_TO_WIRE(fqa)->requestFrom(I2CIP_FQA_SEG_DEVADR(fqa), read_len, reg, 2, (uint8_t)read_stop); + + // We didn't get all the bytes we expected + if (recv != read_len) { + success = false; + } + + // Read in all the bytes received + for (uint16_t i = 0; i < recv; i++) { + dest[pos + i] = I2CIP_FQA_TO_WIRE(fqa)->read(); + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print((char)dest[pos + i]); + #endif + if(nullterminate && dest[pos + i] == '\0') { + len = pos + i; + goto endloop2; + } + } + + // Advance the index by the amount of bytes received + pos += recv; + } +endloop2: + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.println('\''); + DEBUG_DELAY(); + #endif + + // Reset MUX bus if `reset` == true + if (resetbus) { + errlev = MUX::resetBus(fqa); + I2CIP_ERR_BREAK(errlev); + } + + // Did we read all the bytes we hoped to? + return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); +} + +// NON-STATIC OBJECT-MEMBER FUNCTIONS (PUBLIC EXTERNAL API) + +i2cip_errorlevel_t Device::ping(bool resetbus) { return Device::ping(this->fqa, resetbus); } +i2cip_errorlevel_t Device::pingTimeout(bool setbus, bool resetbus, unsigned int timeout) { return Device::pingTimeout(this->fqa, setbus, resetbus, timeout); } +i2cip_errorlevel_t Device::writeByte(const uint8_t& value, bool setbus) { return Device::writeByte(this->fqa, value, setbus); } +i2cip_errorlevel_t Device::write(const uint8_t* buffer, size_t len, bool setbus) { return Device::write(this->fqa, buffer, len, setbus); } +i2cip_errorlevel_t Device::writeRegister(const uint8_t& reg, const uint8_t& value, bool setbus) { return Device::writeRegister(this->fqa, reg, value, setbus); } +i2cip_errorlevel_t Device::writeRegister(const uint16_t& reg, const uint8_t& value, bool setbus) { return Device::writeRegister(this->fqa, reg, value, setbus); } +i2cip_errorlevel_t Device::writeRegister(const uint8_t& reg, uint8_t* buffer, size_t len, bool setbus) { return Device::writeRegister(this->fqa, reg, buffer, len, setbus); } +i2cip_errorlevel_t Device::writeRegister(const uint16_t& reg, uint8_t* buffer, size_t len, bool setbus) { return Device::writeRegister(this->fqa, reg, buffer, len, setbus); } +i2cip_errorlevel_t Device::read(uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { return Device::read(this->fqa, dest, len, nullterminate, resetbus); } +i2cip_errorlevel_t Device::readByte(uint8_t& dest, bool resetbus) { return Device::readByte(this->fqa, dest, resetbus); } +i2cip_errorlevel_t Device::readWord(uint16_t& dest, bool resetbus) { return Device::readWord(this->fqa, dest, resetbus); } +i2cip_errorlevel_t Device::readRegister(const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { return Device::readRegister(this->fqa, reg, dest, len, nullterminate, resetbus); } +i2cip_errorlevel_t Device::readRegister(const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { return Device::readRegister(this->fqa, reg, dest, len, nullterminate, resetbus); } +i2cip_errorlevel_t Device::readRegisterByte(const uint8_t& reg, uint8_t& dest, bool resetbus) { return Device::readRegisterByte(this->fqa, reg, dest, resetbus); } +i2cip_errorlevel_t Device::readRegisterByte(const uint16_t& reg, uint8_t& dest, bool resetbus) { return Device::readRegisterByte(this->fqa, reg, dest, resetbus); } +i2cip_errorlevel_t Device::readRegisterWord(const uint8_t& reg, uint16_t& dest, bool resetbus) { return Device::readRegisterWord(this->fqa, reg, dest, resetbus); } +i2cip_errorlevel_t Device::readRegisterWord(const uint16_t& reg, uint16_t& dest, bool resetbus) { return Device::readRegisterWord(this->fqa, reg, dest, resetbus); } + +DeviceGroup::DeviceGroup(i2cip_id_t key, factory_device_t factory) : key(key), factory(factory) { + for(uint8_t i = 0; i < I2CIP_DEVICES_PER_GROUP; i++) { + devices[i] = nullptr; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Constructed DeviceGroup('")); + I2CIP_DEBUG_SERIAL.print(this->key); + I2CIP_DEBUG_SERIAL.print(F("' @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)this->key, HEX); + I2CIP_DEBUG_SERIAL.print(F(", Device* (*)(fqa) @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)factory, HEX); + I2CIP_DEBUG_SERIAL.print(F(")\n")); + DEBUG_DELAY(); + #endif +} + +DeviceGroup::~DeviceGroup(void) { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("~DeviceGroup")); + // DEBUG_DELAY(); + // #endif + for(uint8_t i = 0; i < I2CIP_DEVICES_PER_GROUP; i++) { + if(devices[i] != nullptr) { + delete devices[i]; + } + } + +} + +bool DeviceGroup::add(Device& device) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("DeviceGroup (ID '")); + I2CIP_DEBUG_SERIAL.print(this->key); + I2CIP_DEBUG_SERIAL.print(F("' @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)this->key, HEX); + I2CIP_DEBUG_SERIAL.print(F(") Add Device (ID '")); + I2CIP_DEBUG_SERIAL.print(device.getID()); + I2CIP_DEBUG_SERIAL.print(F("' @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)device.getID(), HEX); + I2CIP_DEBUG_SERIAL.print(")\n"); + DEBUG_DELAY(); + #endif + if((const char*)device.getID() != (const char*)this->key && strcmp(device.getID(), this->key) != 0) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F(": Failed\n")); + DEBUG_DELAY(); + #endif + return false; + } + + if(this->contains(&device)) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F(": NOP\n")); + DEBUG_DELAY(); + #endif + return true; // Already added + } + + unsigned int n = 0; + while(this->devices[n] != nullptr) { n++; if(n > I2CIP_DEVICES_PER_GROUP) return false;} + + // Append new devices + this->devices[n] = &device; + this->numdevices = (n + 1); + return true; +} + +bool DeviceGroup::addGroup(Device* devices[], uint8_t numdevices) { + unsigned int n = 0; + while(this->devices[n] != nullptr) { n++; if(n + numdevices > I2CIP_DEVICES_PER_GROUP) return false; } + + // Append new devices + for(unsigned int i = 0; i < numdevices && (n + i) < I2CIP_DEVICES_PER_GROUP; i++) { + this->devices[n+i] = devices[i]; + this->numdevices++; + } + return true; +} + +void DeviceGroup::remove(Device* device) { + bool swap = false; + for(int i = 0; i < this->numdevices-1; i++) { + if(swap) { + this->devices[i - 1] = this->devices[i]; + } + if(this->devices[i] == nullptr) break; + if(this->devices[i]->getFQA() == device->getFQA()) { + this->devices[i] = nullptr; + this->numdevices--; + swap = true; + } + } +} + +bool DeviceGroup::contains(Device* device) const { + if(device == nullptr || ((const char*)device->getID() != (const char*)this->key && strcmp(device->getID(), this->key) != 0)) return false; + return this->contains(device->getFQA()); +} + +bool DeviceGroup::contains(const i2cip_fqa_t& fqa) const { + for(int i = 0; i < this->numdevices; i++) { + if(this->devices[i] == nullptr) break; + if(this->devices[i]->getFQA() == fqa) return true; + } + return false; +} + +Device* DeviceGroup::operator[](const i2cip_fqa_t& fqa) const { + for(int i = 0; i < this->numdevices; i++) { + if(this->devices[i] == nullptr) break; + if((i2cip_fqa_t)this->devices[i]->getFQA() == (i2cip_fqa_t)fqa) return this->devices[i]; + } + return nullptr; +} + +Device* DeviceGroup::operator()(const i2cip_fqa_t& fqa) { + if(this->contains(fqa)) { + return (*this)[fqa]; + } + + Device* device = this->factory(fqa); + + if(device != nullptr) { + bool b = this->add(*device); + + if(!b) { + delete device; + return nullptr; + } + } + + return device; +} + +// DeviceGroup& DeviceGroup::operator=(const DeviceGroup& rhs) { +// for(unsigned char i = 0; i < I2CIP_DEVICES_PER_GROUP; i++) this->devices[i] = rhs.devices[i]; +// this->numdevices = rhs.numdevices; +// this->factory = rhs.factory; +// this->key = rhs.key; +// return *this; +// } \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/device.h b/software/microcontroller/lib/I2CIP/device.h new file mode 100644 index 00000000..4797aaf0 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/device.h @@ -0,0 +1,398 @@ +#ifndef I2CIP_DEVICE_H_ +#define I2CIP_DEVICE_H_ + +#include +#include + +/** + * Shifts and masks a number's bits. + * @param data Data to shift/mask + * @param lsb LSB position + * @param bits Number of bits to keep + **/ +#define READ_BITS(data, lsb, bits) (((data) >> (lsb)) & ((1 << (bits)) - 1)) + +/** + * Overwrites some bits in existing data. + * @param existing Existing data + * @param data New data + * @param lsb Position to insert (LSB) + * @param bits Number of bits to overwrite + **/ +#define OVERWRITE_BITS(existing, data, lsb, bits) (((existing) & ~(((1 << (bits)) - 1) << (lsb))) | (((data) & ((1 << (bits)) - 1)) << (lsb))) + +#define I2CIP_DEVICES_PER_GROUP ((size_t)4) +#define I2CIP_ID_SIZE ((size_t)10) + +namespace ControlSystemsOS { + class Sensor; +} + +namespace I2CIP { + + class Module; + + // typedef enum { I2CIP_ITYPE_NULL = 0b00, I2CIP_ITYPE_INPUT = 0b01, I2CIP_ITYPE_OUTPUT = 0b10, I2CIP_ITYPE_IO = 0b11 } i2cip_itype_t; + + // Barebones template-less abstract classes expose voidptr hooks for the device to be used as an input or output + + class InputGetter { + protected: + static const char failptr_get = '\a'; + + ControlSystemsOS::Sensor* sensor = nullptr; + public: + virtual ~InputGetter() = 0; + virtual i2cip_errorlevel_t get(const void* args = nullptr) { return I2CIP_ERR_HARD; } // Unimplemented; delete this device + i2cip_errorlevel_t failGet(void) { return this->get(&failptr_get); } + + ControlSystemsOS::Sensor* getSensor(void) const { return this->sensor; } + void setSensor(ControlSystemsOS::Sensor* sensor) { this->sensor = sensor; } + }; + + class OutputSetter { + protected: + static const char failptr_set = '\a'; + public: + virtual ~OutputSetter() = 0; + virtual i2cip_errorlevel_t set(const void* value = nullptr, const void* args = nullptr) { return I2CIP_ERR_HARD; } // Unimplemented; delete this device + i2cip_errorlevel_t reset(const void* args = nullptr) { return this->set(&failptr_set, args); } + i2cip_errorlevel_t failSet(const void* value) { return this->set(value, &failptr_set); } + i2cip_errorlevel_t failSet(void) { return this->set(&failptr_set, &failptr_set); } + }; + + class Device { + protected: + const i2cip_fqa_t fqa; + i2cip_id_t id; + + // // Set by public API, deleted on deconstruction + InputGetter* input = nullptr; + OutputSetter* output = nullptr; + + Device(const i2cip_fqa_t& fqa, i2cip_id_t id); + + /** + * Attempt to communicate with a device. Always sets the bus. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | { resetbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param resetbus Should the bus be reset? (Default: `true`) + * @return Hardware failure: Device lost; no ACK (check MUX). Software failure: Failed to switch MUX bus. + */ + static i2cip_errorlevel_t ping(const i2cip_fqa_t& fqa, bool resetbus = true); + + /** + * Attempt to communicate with a device repeatedly until timeout. Always sets the bus. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | { until ACK or timeout: DEV ADDR (7) | ACK? | } { failed? : | MUX ADDR (7) | ACK? | }{ resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | + * @param fqa FQA of the device + * @param setbus Should the bus be set? (Default: `true`, set false if checking EEPROM write!) + * @param resetbus Should the bus be reset? (Default: `true`, set false if checking EEPROM write!) + * @param timeout Attempt duration (ms) + * @return Hardware failure: Device unreachable, module check. Software failure: Failed to switch MUX bus + */ + static i2cip_errorlevel_t pingTimeout(const i2cip_fqa_t& fqa, bool setbus = true, bool resetbus = true, unsigned int timeout = 100); + + /** + * Write one byte to a device. + * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | DATA BYTE (8) | ACK? | { setbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param value Byte to be written + * @param setbus Should the MUX be set and reset? (Default: `true`) + * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus + */ + static i2cip_errorlevel_t writeByte(const i2cip_fqa_t& fqa, const uint8_t& value, bool setbus = true); + + /** + * Write data to a device. + * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | DATA BYTE (8 * len) | ACK? | { setbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param buffer Bytes to be sent + * @param len Number of bytes (Default: `1`) + * @param setbus Should the MUX be set and reset? (Default: `true`) + * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus + */ + static i2cip_errorlevel_t write(const i2cip_fqa_t& fqa, const uint8_t* buffer, size_t len = 1, bool setbus = true); + + /** + * Write one byte to a device's register. Effectively adds ONE prefix byte. + * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | REG ADDR (16) | DATA BYTE (8) | ACK? | { setbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param reg Register address + * @param value Byte to be written + * @param setbus Should the MUX be reset? (Default: `true`) + * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus + */ + static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, const uint8_t& value, bool setbus = true); + + /** + * Write one byte to a device's register (16-bit register address). Effectively adds TWO prefix bytes. + * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | REG ADDR (16) | DATA BYTE (8) | ACK? | { setbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param reg Register address + * @param value Byte to be written + * @param setbus Should the MUX be reset? (Default: `true`) + * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus + */ + static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, const uint8_t& value, bool setbus = true); + + static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); + + static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); + + /** + * Request and read in data from a device. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | READ BYTES (8*len) | + * resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | + * @param fqa FQA of the device + * @param dest Bytes to read into + * @param len Number of bytes to read (Default: `1`) + * @param setbus Should the MUX be reset? (Default: `true`) + */ + static i2cip_errorlevel_t read(const i2cip_fqa_t& fqa, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); + + /** + * Read one byte of data from the device. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | READ BYTES (8) | + * resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | + * @param fqa FQA of the device + * @param dest Byte to read into + * @param setbus Should the MUX be reset? (Default: `true`) + */ + static i2cip_errorlevel_t readByte(const i2cip_fqa_t& fqa, uint8_t& dest, bool resetbus = true); + + /** + * Read one byte of data from the device. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | READ BYTES (16) | + * resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | + * @param fqa FQA of the device + * @param dest Word to read into + * @param setbus Should the MUX be reset? (Default: `true`) + */ + static i2cip_errorlevel_t readWord(const i2cip_fqa_t& fqa, uint16_t& dest, bool resetbus = true); + + static i2cip_errorlevel_t readRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); + + static i2cip_errorlevel_t readRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); + + + /** + * Read one byte of data from the device. Effectively adds a prefix byte. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (8) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param dest Bytes to read into + * @param setbus Should the MUX be reset? (Default: `true`) + */ + static i2cip_errorlevel_t readRegisterByte(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t& dest, bool resetbus = true); + + /** + * Read one byte of data from the device. Effectively adds TWO prefix bytes. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (16) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param dest Bytes to read into + * @param setbus Should the MUX be reset? (Default: `true`) + **/ + static i2cip_errorlevel_t readRegisterByte(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t& dest, bool resetbus = true); + + /** + * Read two bytes of data from the device. Effectively adds one prefix bytes. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (16) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param dest Bytes to read into + * @param setbus Should the MUX be reset? (Default: `true`) + **/ + static i2cip_errorlevel_t readRegisterWord(const i2cip_fqa_t& fqa, const uint8_t& reg, uint16_t& dest, bool resetbus = true); + + /** + * Read two bytes of data from the device. Effectively adds TWO prefix bytes. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (16) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } + * @param fqa FQA of the device + * @param dest Bytes to read into + * @param setbus Should the MUX be reset? (Default: `true`) + **/ + static i2cip_errorlevel_t readRegisterWord(const i2cip_fqa_t& fqa, const uint16_t& reg, uint16_t& dest, bool resetbus = true); + + public: + virtual ~Device() = 0; + + void setInput(InputGetter* input); + void setOutput(OutputSetter* output); + + void removeInput(void); + void removeOutput(void); + + InputGetter* getInput(void) const; + OutputSetter* getOutput(void) const; + + i2cip_errorlevel_t get(const void* args = nullptr); + i2cip_errorlevel_t set(const void* value = nullptr, const void* args = nullptr); + + const i2cip_fqa_t& getFQA(void) const; + const i2cip_id_t& getID(void) const; + + i2cip_errorlevel_t ping(bool resetbus = true); + i2cip_errorlevel_t pingTimeout(bool setbus = true, bool resetbus = true, unsigned int timeout = 100); + i2cip_errorlevel_t writeByte(const uint8_t& value, bool setbus = true); + i2cip_errorlevel_t write(const uint8_t* buffer, size_t len = 1, bool setbus = true); + i2cip_errorlevel_t writeRegister(const uint8_t& reg, const uint8_t& value, bool setbus = true); + i2cip_errorlevel_t writeRegister(const uint16_t& reg, const uint8_t& value, bool setbus = true); + i2cip_errorlevel_t writeRegister(const uint8_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); + i2cip_errorlevel_t writeRegister(const uint16_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); + i2cip_errorlevel_t read(uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); + i2cip_errorlevel_t readByte(uint8_t& dest, bool resetbus = true); + i2cip_errorlevel_t readWord(uint16_t& dest, bool resetbus = true); + i2cip_errorlevel_t readRegister(const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); + i2cip_errorlevel_t readRegister(const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); + i2cip_errorlevel_t readRegisterByte(const uint8_t& reg, uint8_t& dest, bool resetbus = true); + i2cip_errorlevel_t readRegisterByte(const uint16_t& reg, uint8_t& dest, bool resetbus = true); + i2cip_errorlevel_t readRegisterWord(const uint8_t& reg, uint16_t& dest, bool resetbus = true); + i2cip_errorlevel_t readRegisterWord(const uint16_t& reg, uint16_t& dest, bool resetbus = true); + + inline operator i2cip_fqa_t() const { return this->fqa; } + }; + + typedef Device* (* factory_device_t)(const i2cip_fqa_t& fqa); + + class DeviceGroup { + friend class Module; + + protected: + bool add(Device& device); + bool addGroup(Device* devices[], uint8_t numdevices); + void remove(Device* device); + + public: + i2cip_id_t key; + uint8_t numdevices = 0; + Device* devices[I2CIP_DEVICES_PER_GROUP] = { nullptr }; + + factory_device_t factory; + + DeviceGroup(i2cip_id_t key, factory_device_t factory); + + ~DeviceGroup(); + + bool contains(Device* device) const; + bool contains(const i2cip_fqa_t& fqa) const; + + Device* operator[](const i2cip_fqa_t& fqa) const; + + // DeviceGroup& operator=(const DeviceGroup& rhs); + + Device* operator()(const i2cip_fqa_t& fqa); + }; + + /** + * An I2CIP peripheral used for input/state "getting". + * @param G type used for "get" variable + * @param A type used for "get" arguments + **/ + template class InputInterface : public InputGetter { + private: + G cache; // Last RECIEVED value + A argsA; // Last passed arguments + + bool argsAset = false; + protected: + void setCache(G value); + void setArgsA(A args); + + /** + * Gets the default arguments used for the "get" operation. + * A constant reference. + * To be implemented by the child class. + */ + virtual const A& getDefaultA(void) const = 0; + public: + InputInterface(Device* device); + virtual ~InputInterface() = 0; + + i2cip_errorlevel_t get(const void* args = nullptr) override; + + /** + * Gets the last recieved value. + */ + G getCache(void) const; + + /** + * Sets the cache to the default "zero" value. + * To be implemented by the child class. + */ + virtual void clearCache(void); + + /** + * Gets the arguments used for the last "get" operation. + */ + A getArgsA(void) const; + + /** + * Gets the input device's state. + **/ + virtual i2cip_errorlevel_t get(G& dest, const A& args) { return I2CIP_ERR_HARD; } // Unimplemented; Disable this device + }; + + /** + * An I2CIP peripheral used for output/state "setting". + * @param S type used for "set" value + * @param B type used for "set" arguments + **/ + template class OutputInterface : public OutputSetter { + private: + S value; // Last SET value (not PASSED value) + B argsB; // Last passed arguments + + bool argsBset = false; + protected: + void setValue(S value); + void setArgsB(B args); + + /** + * Gets the default arguments used for the "set" operation. + * To be implemented by the child class. + */ + virtual const B& getDefaultB(void) const = 0; + public: + OutputInterface(Device* device); + + virtual ~OutputInterface() = 0; + + i2cip_errorlevel_t set(const void* value = nullptr, const void* args = nullptr) override; + + /** + * Gets the arguments used for the last "set" operation. + */ + B getArgsB(void) const; + + /** + * Gets the last set value. + */ + S getValue(void) const; + + /** + * Gets the default "zero"/off-state value. + * To be implemented by the child class. + */ + virtual void resetFailsafe(void); + + /** + * Sets the output device's state. + **/ + virtual i2cip_errorlevel_t set(const S& value, const B& args) { return I2CIP_ERR_HARD; } // Unimplemented; Disable this device + }; + + /** + * An I2CIP peripheral used for input/state "getting" as well as output/state "setting". + * @param G type used for "get" variable + * @param A type used for "get" arguments + * @param S type used for "set" value + * @param B type used for "set" arguments + **/ + template class IOInterface : public InputInterface, public OutputInterface { + public: + IOInterface(Device* device); + virtual ~IOInterface() = 0; + }; +}; + +#include + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/device.tpp b/software/microcontroller/lib/I2CIP/device.tpp new file mode 100644 index 00000000..113eb39c --- /dev/null +++ b/software/microcontroller/lib/I2CIP/device.tpp @@ -0,0 +1,147 @@ +#ifndef I2CIP_DEVICE_H_ +#error __FILE__ should only be included AFTER +// #include +#endif + +#ifdef I2CIP_DEVICE_H_ + +#ifndef I2CIP_DEVICE_T_ +#define I2CIP_DEVICE_T_ + +#include + +using I2CIP::i2cip_errorlevel_t; +using I2CIP::InputGetter; +using I2CIP::OutputSetter; +using I2CIP::InputInterface; +using I2CIP::OutputInterface; +using I2CIP::IOInterface; + +template InputInterface::InputInterface(Device* device) { device->setInput(this); } + +template InputInterface::~InputInterface() { } + +template G InputInterface::getCache(void) const { return this->cache; } + +template void InputInterface::setCache(G value) { this->cache = value; } + +template void InputInterface::clearCache(void) { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("InputInterface::clearCache() Not Implemented; Nothing Done\n")); + // DEBUG_DELAY(); + // #endif +} + +template void InputInterface::setArgsA(A args) { this->argsA = args; } + +template A InputInterface::getArgsA(void) const { return this->argsA; } + +template i2cip_errorlevel_t InputInterface::get(const void* args) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("InputInterface::get(")); + if(args == &InputGetter::failptr_get) I2CIP_DEBUG_SERIAL.print(F("fail")); + else if(args == nullptr) I2CIP_DEBUG_SERIAL.print(F("null")); + else { + I2CIP_DEBUG_SERIAL.print(*(A* const)args); + I2CIP_DEBUG_SERIAL.print(F(" @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)args, HEX); + } + I2CIP_DEBUG_SERIAL.print(F(")\n")); + DEBUG_DELAY(); + #endif + + if (args == &InputGetter::failptr_get) this->clearCache(); + G temp = this->cache; + + if(!this->argsAset) { this->argsA = this->getDefaultA(); this->argsAset = true; } + A arg = (args == &InputGetter::failptr_get) ? this->getDefaultA() : ((args == nullptr) ? this->getArgsA() : *(A* const)args); + + i2cip_errorlevel_t errlev = this->get(temp, arg); + + // If successful, update last cache + if(errlev == I2CIP::i2cip_errorlevel_t::I2CIP_ERR_NONE) { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F(" -> ")); + // I2CIP_DEBUG_SERIAL.print(temp); + // I2CIP_DEBUG_SERIAL.print(F(" @0x")); + // I2CIP_DEBUG_SERIAL.print((uint16_t)temp, HEX); + // I2CIP_DEBUG_SERIAL.print(F(", setting cache\n")); + // DEBUG_DELAY(); + // #endif + // this->clearCache(); + this->cache = temp; this->argsA = arg; + } + return errlev; +} + +template OutputInterface::OutputInterface(Device* device) { device->setOutput(this); } + +template OutputInterface::~OutputInterface() { } + +template void OutputInterface::setValue(S value) { this->value = value; } + +template void OutputInterface::resetFailsafe(void) { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("OutputInterface::resetFailsafe() Not Implemented; Nothing Done\n")); + // DEBUG_DELAY(); + // #endif +} + +template S OutputInterface::getValue(void) const { return this->value; } + +template void OutputInterface::setArgsB(B args) { this->argsB = args; } + +template B OutputInterface::getArgsB(void) const { return this->argsB; } + +template i2cip_errorlevel_t OutputInterface::set(const void* value, const void* args) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("OutputInterface::set(")); + if(value == &OutputSetter::failptr_set) I2CIP_DEBUG_SERIAL.print(F("fail")); + else if(value == nullptr) I2CIP_DEBUG_SERIAL.print(F("null")); + else { + I2CIP_DEBUG_SERIAL.print(*(S* const)value); + I2CIP_DEBUG_SERIAL.print(F(" @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)value, HEX); + } + I2CIP_DEBUG_SERIAL.print(F(", ")); + if(args == &OutputSetter::failptr_set) I2CIP_DEBUG_SERIAL.print(F("fail")); + else if(args == nullptr) I2CIP_DEBUG_SERIAL.print(F("null")); + else { + I2CIP_DEBUG_SERIAL.print(*(B* const)args); + I2CIP_DEBUG_SERIAL.print(F(" @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)args, HEX); + } + I2CIP_DEBUG_SERIAL.print(F(")\n")); + DEBUG_DELAY(); + #endif + + // If fail, reset to failsafe value + if (value == &OutputSetter::failptr_set) this->resetFailsafe(); + // 1. If `set` value is not given, repeat last action + S val = ((value == nullptr || value == &OutputSetter::failptr_set) ? this->getValue() : *(S* const)value); + + if(!this->argsBset) { this->argsB = this->getDefaultB(); this->argsBset = true; } + + // 2. If `set` args are not given, use last args + B arg = (args == &OutputSetter::failptr_set) ? this->getDefaultB() : ((args == nullptr || value == nullptr) ? this->getArgsB() : *(B* const)args); + + // 3. Attempt `set` + i2cip_errorlevel_t errlev = this->set(val, arg); + + // 4. If successful, update cached `value` and `args` + if(errlev == I2CIP::i2cip_errorlevel_t::I2CIP_ERR_NONE) { this->value = val; this->argsB = arg; }; + + return errlev; +} + +template IOInterface::IOInterface(Device* device) : InputInterface(device), OutputInterface(device) { } + +template IOInterface::~IOInterface() { } + +#endif +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/eeprom.cc b/software/microcontroller/lib/I2CIP/eeprom.cc new file mode 100644 index 00000000..7624d46b --- /dev/null +++ b/software/microcontroller/lib/I2CIP/eeprom.cc @@ -0,0 +1,368 @@ +#include + +#include +#include +#include + +bool I2CIP::EEPROM::_id_set; +char I2CIP::EEPROM::_id[I2CIP_ID_SIZE]; +bool I2CIP::EEPROM::_failsafe_set; +char I2CIP::EEPROM::_failsafe[I2CIP_EEPROM_SIZE]; +uint16_t I2CIP::EEPROM::_failsafe_b; + +using namespace I2CIP; + +// Handles ID pointer assignment too +// NEVER returns nullptr, unless out of memory +Device* EEPROM::eepromFactory(const i2cip_fqa_t& fqa, const i2cip_id_t& id) { + if(!EEPROM::_id_set) { + uint8_t idlen = strlen_P(i2cip_eeprom_id_progmem); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("PROGMEM ID EEPROM Loaded: char[")); + I2CIP_DEBUG_SERIAL.print(idlen+1); + I2CIP_DEBUG_SERIAL.print(F("] = {'")); + #endif + + // Read in PROGMEM + for (uint8_t k = 0; k < idlen; k++) { + char c = pgm_read_byte_near(i2cip_eeprom_id_progmem + k); + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_SERIAL.print(c); + #endif + EEPROM::_id[k] = c; + } + + EEPROM::_id[idlen] = '\0'; + EEPROM::_id_set = true; + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print("'} @0x"); + I2CIP_DEBUG_SERIAL.print((uint16_t)(&(EEPROM::_id[0])), HEX); + DEBUG_SERIAL.print('\n'); + DEBUG_DELAY(); + #endif + } + + return (Device*)(new EEPROM(fqa, id)); +} + +Device* EEPROM::eepromFactory(const i2cip_fqa_t& fqa) { return eepromFactory(fqa, EEPROM::_id); } + +EEPROM::EEPROM(const i2cip_fqa_t& fqa, const i2cip_id_t& id) : Device(fqa, &(EEPROM::_id[0])), IOInterface((Device*)this) { + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("EEPROM Constructed (ID '")); + I2CIP_DEBUG_SERIAL.print(this->id); + I2CIP_DEBUG_SERIAL.print(F("' @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)&(this->id[0]), HEX); + I2CIP_DEBUG_SERIAL.print(F("; FQA ")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(F(":")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(F(":")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(F(":")); + I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); + I2CIP_DEBUG_SERIAL.print(F(")\n")); + DEBUG_DELAY(); + #endif +} + +EEPROM::~EEPROM() { + // #ifdef I2CIP_DEBUG_SERIAL + // DEBUG_DELAY(); + // I2CIP_DEBUG_SERIAL.print(F("EEPROM Destructed (ID '")); + // I2CIP_DEBUG_SERIAL.print(this->id); + // I2CIP_DEBUG_SERIAL.print(F("' @0x")); + // I2CIP_DEBUG_SERIAL.print((uint16_t)&(this->id[0]), HEX); + // I2CIP_DEBUG_SERIAL.print(F("; FQA ")); + // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(this->fqa), HEX); + // I2CIP_DEBUG_SERIAL.print(F(":")); + // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(this->fqa), HEX); + // I2CIP_DEBUG_SERIAL.print(F(":")); + // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(this->fqa), HEX); + // I2CIP_DEBUG_SERIAL.print(F(":")); + // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(this->fqa), HEX); + // I2CIP_DEBUG_SERIAL.print(F(")\n")); + // DEBUG_DELAY(); + // #endif + + // Cleanup + if(this->getValue() != nullptr && this->getValue() != _failsafe) delete this->getValue(); +} + +// EEPROM::EEPROM(const uint8_t& wire, const uint8_t& module, const uint8_t& addr) : EEPROM(I2CIP_FQA_CREATE(wire, module, I2CIP_MUX_BUS_DEFAULT, addr)) { } + +i2cip_errorlevel_t EEPROM::readContents(uint8_t* dest, size_t& num_read, size_t max_read) { + size_t bytes_read = max_read; + i2cip_errorlevel_t errlev = Device::readRegister(fqa, (uint16_t)0, dest, bytes_read, true, false); + num_read = bytes_read; + return errlev; +} + +i2cip_errorlevel_t EEPROM::clearContents(bool setbus, uint16_t numbytes) { + i2cip_errorlevel_t errlev = pingTimeout(setbus, false); + I2CIP_ERR_BREAK(errlev); + + uint8_t zeroes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + for (uint16_t bytes_written = 0; bytes_written < numbytes; bytes_written+=8) { + const uint8_t pagelen = min(numbytes - bytes_written, 8); + + errlev = writeRegister(bytes_written, zeroes, pagelen, false); + if(errlev == I2CIP_ERR_SOFT) { + // Actual failed write + return errlev; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Cleared EEPROM bytes ")); + I2CIP_DEBUG_SERIAL.print(bytes_written); + I2CIP_DEBUG_SERIAL.print(F(" - ")); + I2CIP_DEBUG_SERIAL.println((bytes_written + pagelen - 1)); + DEBUG_DELAY(); + #endif + + // Note: Timeout ping before each byte write to await completion of last write cycle + errlev = pingTimeout(false, false); + I2CIP_ERR_BREAK(errlev); + } + + return errlev; +} + +i2cip_errorlevel_t EEPROM::overwriteContents(const char* contents, bool clear, bool setbus) { + for(size_t i = 0; i < I2CIP_EEPROM_SIZE; i++) { + if(contents[i] == '\0') { + return overwriteContents((uint8_t*)contents, i, clear, setbus); + } + } + return I2CIP_ERR_SOFT; +} + +i2cip_errorlevel_t EEPROM::overwriteContents(uint8_t* buffer, size_t len, bool clear, bool setbus) { + i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; + if(clear) { + errlev = clearContents(setbus, len); + } else if(setbus) { + errlev = MUX::setBus(this->fqa); + } + I2CIP_ERR_BREAK(errlev); + + for (uint16_t bytes_written = 0; bytes_written < len; bytes_written+=8) { + const uint8_t pagelen = min(len - bytes_written, 8); + errlev = writeRegister(bytes_written, buffer+bytes_written, pagelen, false); + if(errlev == I2CIP_ERR_SOFT) { + // Actual failed write + return errlev; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Bytes ")); + I2CIP_DEBUG_SERIAL.print(bytes_written); + I2CIP_DEBUG_SERIAL.print(F(" - ")); + I2CIP_DEBUG_SERIAL.print(bytes_written + pagelen); + I2CIP_DEBUG_SERIAL.print(F(" written '")); + for(int i = 0; i < pagelen; i++) { + I2CIP_DEBUG_SERIAL.print((char)((buffer+bytes_written)[i])); + } + I2CIP_DEBUG_SERIAL.print(F("'... ")); + DEBUG_DELAY(); + #endif + + // Note: Timeout ping before each byte write to await completion of last write cycle + errlev = pingTimeout(false, false); + I2CIP_ERR_BREAK(errlev); + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.println("... ACK'd"); + DEBUG_DELAY(); + #endif + + if(buffer[bytes_written] == '\0') { + // Null terminator + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.println("Overwrite stopped - null terminator"); + DEBUG_DELAY(); + #endif + break; + } + } + + return errlev; +} + +i2cip_errorlevel_t EEPROM::get(char*& dest, const uint16_t& args) { + // 0. Check args + if(args > I2CIP_EEPROM_SIZE) { + return I2CIP_ERR_SOFT; + } + + // 1. Read register (until null terminator or max bytes, arg-dependant) into a local buffer + size_t len = args == 0 ? I2CIP_EEPROM_SIZE-1 : args; + uint8_t buffer[len]; + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("EEPROM Get (up to ")); + I2CIP_DEBUG_SERIAL.print(len); + I2CIP_DEBUG_SERIAL.print(F(" bytes)\n")); + DEBUG_DELAY(); + #endif + + i2cip_errorlevel_t errlev = readRegister((uint16_t)0, buffer, len); + I2CIP_ERR_BREAK(errlev); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(len); + I2CIP_DEBUG_SERIAL.print(F("+'\\0' bytes read from EEPROM\n")); + DEBUG_DELAY(); + #endif + + // if((uint16_t)len != args) return I2CIP_ERR_SOFT; + + // 2. Copy local buffer to heap buffer (null-terminated) + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Reading to static heap buffer @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)(&this->readBuffer[0]), HEX); + I2CIP_DEBUG_SERIAL.print(F(" (")); + I2CIP_DEBUG_SERIAL.print(len+1); + I2CIP_DEBUG_SERIAL.print(F(" bytes) '")); + #endif + + for(size_t i = 0; i < len; i++) { + this->readBuffer[i] = (char)buffer[i]; + } + this->readBuffer[len] = '\0'; + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(this->readBuffer); + I2CIP_DEBUG_SERIAL.print("'\n"); + DEBUG_DELAY(); + #endif + + if(errlev == I2CIP_ERR_NONE) { + dest = &this->readBuffer[0]; + } + return errlev; +} + +i2cip_errorlevel_t EEPROM::set(const char * const& value, const uint16_t& args) { + if(args > I2CIP_EEPROM_SIZE) { + return I2CIP_ERR_SOFT; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("EEPROM Set (")); + I2CIP_DEBUG_SERIAL.print(args); + I2CIP_DEBUG_SERIAL.print(F(" bytes @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)value, HEX); + I2CIP_DEBUG_SERIAL.print(F(") '")); + I2CIP_DEBUG_SERIAL.print(value); + I2CIP_DEBUG_SERIAL.print("'...\n"); + #endif + + // Write register + size_t len = args; + i2cip_errorlevel_t errlev = writeRegister((uint16_t)0, (uint8_t*)value, len); + I2CIP_ERR_BREAK(errlev); + + // Pre-caching Cleanup + if(this->getValue() != value && this->getValue() != nullptr && this->getValue()) { delete this->getValue(); } + + // if((uint16_t)len != args) return I2CIP_ERR_SOFT; + return errlev; +} + +// Args: len (2 bytes) +// int EEPROM::read(char*& dest, uint8_t argc, uint8_t[] argv) { +// size_t len = 0; +// if(argc == 0 || argv == nullptr) { +// this->readContents((uint8_t*&)dest, len); +// } else { +// this->readContents((uint8_t*&)dest, len, argc > 1 ? ((argv[1] << 8) & argv[0]) : argv[0]); + +// argv[0] = (len & 0xFF); +// if(argc > 1) argv[1] = (len >> 8); +// } + +// return (len > 0 ? 0 : 1); +// } + +// G - Getter type: char* (null-terminated; writable heap) +void EEPROM::clearCache(void) { + this->setCache(nullptr); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("EEPROM Cache Cleared\n")); + DEBUG_DELAY(); + #endif +} + +// A - Getter argument type: uint16_t (max bytes to read) +const uint16_t& EEPROM::getDefaultA(void) const { + return i2cip_eeprom_capacity; +} + +// S - Setter type: const char* (null-terminated; immutable) +void EEPROM::resetFailsafe(void) { + if(_failsafe_set && this->getValue() == _failsafe) return; // Already set + if(this->getValue() != nullptr && this->getValue() != _failsafe) delete this->getValue(); + + // Load from PROGMEM + if(!_failsafe_set) { + uint8_t len = strlen_P(i2cip_eeprom_default); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("Loading Failsafe PROGMEM Static Heap @0x")); + I2CIP_DEBUG_SERIAL.print((uint16_t)(&_failsafe[0]), HEX); + I2CIP_DEBUG_SERIAL.print(F(" (")); + I2CIP_DEBUG_SERIAL.print(len+1); + I2CIP_DEBUG_SERIAL.print(F(" bytes) '")); + #endif + + // Read in PROGMEM + for (uint8_t k = 0; k < len; k++) { + char c = pgm_read_byte_near(i2cip_eeprom_default + k); + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(c); + #endif + _failsafe[k] = c; + } + + _failsafe[len] = '\0'; + + _failsafe_b = len; + _failsafe_set = true; + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print("'\n"); + DEBUG_DELAY(); + #endif + } + + + this->setValue(_failsafe); + this->setArgsB(_failsafe_b); + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("EEPROM Value Reset\n")); + DEBUG_DELAY(); + #endif +} + +// B - Setter argument type: uint16_t (max bytes to write) +const uint16_t& EEPROM::getDefaultB(void) const { + return i2cip_eeprom_capacity; +} \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/eeprom.h b/software/microcontroller/lib/I2CIP/eeprom.h new file mode 100644 index 00000000..85856099 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/eeprom.h @@ -0,0 +1,93 @@ +#ifndef I2CIP_EEPROM_H_ +#define I2CIP_EEPROM_H_ + +#include +#include + +#define I2CIP_EEPROM_SIZE 100 // EEPROM size in bytes +#define I2CIP_EEPROM_ADDR 0x50 // SPRT EEPROM address +#define I2CIP_EEPROM_TIMEOUT 100 // How long to wait for a write to complete (ms) + +#define I2CIP_EEPROM_ID "24LC32" +#define I2CIP_EEPROM_DEFAULT "[{\"" I2CIP_EEPROM_ID "\":[80],\"SHT31\":[68]}]" + +// Future-Proofing ;) +// namespace ControlSystemsOS { +// class Linker; +// } + +namespace I2CIP { + + class Device; + template class IOInterface; + + const char i2cip_eeprom_default[] PROGMEM = {I2CIP_EEPROM_DEFAULT}; + const uint16_t i2cip_eeprom_capacity = I2CIP_EEPROM_SIZE; + const char i2cip_eeprom_id_progmem[] PROGMEM = {I2CIP_EEPROM_ID}; + + /** + * 24LC32 EEPROM + * + * This class is a wrapper for on- and off-module EEPROM as an I2CIP device and a high-level null-terminated UTF-8 C-string I/O peripheral. + * NOTE: This object handles all C-string memory management, including allocation and deallocation. All pass-by-ref(!) pointer arguments are reassigned. Similar array-valued interfaces should follow the same pattern. + * + * G - Getter type: char* (null-terminated; writable heap) + * A - Getter argument type: uint16_t (max bytes to read) + * S - Setter type: const char* (null-terminated; immutable) + * B - Setter argument type: uint16_t (max bytes to write) + */ + class EEPROM : public Device, public IOInterface { + // friend Device* I2CIP::eepromFactory(const i2cip_fqa_t& fqa); + // friend class Module; + // friend class ControlSystemsOS::Linker; // Future-Proofing ;) + + EEPROM(const i2cip_fqa_t& fqa, const i2cip_id_t& id); + private: + static bool _id_set; + static char _id[]; // to be loaded from progmem + + static bool _failsafe_set; + static char _failsafe[]; // to be loaded from progmem + static uint16_t _failsafe_b; + + char readBuffer[I2CIP_EEPROM_SIZE+1] = { '\0' }; + public: + static Device* eepromFactory(const i2cip_fqa_t& fqa, const i2cip_id_t& id); + static Device* eepromFactory(const i2cip_fqa_t& fqa); + + ~EEPROM(); + + i2cip_errorlevel_t readContents(uint8_t* dest, size_t& num_read, size_t max_read = I2CIP_EEPROM_SIZE); + + i2cip_errorlevel_t writeByte(const uint16_t& bytenum, const uint8_t& value, bool setbus = true); + + i2cip_errorlevel_t clearContents(bool setbus = true, uint16_t numbytes = I2CIP_EEPROM_SIZE); + + i2cip_errorlevel_t overwriteContents(const char* contents, bool clear = true, bool setbus = true); + + i2cip_errorlevel_t overwriteContents(uint8_t* buffer, size_t len, bool clear = true, bool setbus = true); + + /** + * Read a section from EEPROM. + * @param dest Destination heap (pointer reassigned, not overwritten) + * @param args Number of bytes to read + **/ + i2cip_errorlevel_t get(char*& dest, const uint16_t& args) override; + + /** + * Write to a section of EEPROM. + * @param value Value to write (null-terminated) + * @param args Number of bytes to write + **/ + i2cip_errorlevel_t set(const char * const& value, const uint16_t& args) override; + + void clearCache(void) override; + const uint16_t& getDefaultA(void) const override; + void resetFailsafe(void) override; + const uint16_t& getDefaultB(void) const override; + + static const char* getStaticIDBuffer() { return EEPROM::_id_set ? &(EEPROM::_id[0]) : nullptr; } + }; +} + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/fqa.cc b/software/microcontroller/lib/I2CIP/fqa.cc new file mode 100644 index 00000000..e0c343da --- /dev/null +++ b/software/microcontroller/lib/I2CIP/fqa.cc @@ -0,0 +1,32 @@ +#include + +#include + +TwoWire* const I2CIP::wires[I2CIP_NUM_WIRES] = { &Wire }; + +// Has wire N been wires[N].begin() yet? +bool wiresBegun[I2CIP_NUM_WIRES] = { false }; + +i2cip_fqa_t I2CIP::createFQA(const uint8_t& wire, const uint8_t& mux, const uint8_t& bus, const uint8_t& addr) { + if (( wire < I2CIP_FQA_I2CBUS_MAX ) && + ( mux < I2CIP_FQA_MODULE_MAX ) && + ( bus < I2CIP_FQA_MUXBUS_MAX ) && + ( addr < I2CIP_FQA_DEVADR_MAX ) + ) { + return I2CIP_FQA_CREATE(wire, mux, bus, addr); + } + return (i2cip_fqa_t)0; +} + +void I2CIP::beginWire(const uint8_t& wire) { + if(!wiresBegun[wire]) { + wires[wire]->begin(); + wiresBegun[wire] = true; + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("INIT I2C WIRE ")); + I2CIP_DEBUG_SERIAL.println(wire); + DEBUG_DELAY(); + #endif + } +} \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/fqa.h b/software/microcontroller/lib/I2CIP/fqa.h new file mode 100644 index 00000000..2ef830ba --- /dev/null +++ b/software/microcontroller/lib/I2CIP/fqa.h @@ -0,0 +1,84 @@ +#ifndef I2CIP_FQA_H_ +#define I2CIP_FQA_H_ + +#include +#include + +// -------------------------------- +// FQA: Fully Qualified Addressing +// -------------------------------- + +// Two useful typedefs +typedef uint16_t i2cip_fqa_t; +typedef const char* i2cip_id_t; + +// Address segments: Least Significant Bit positions, lengths, and maximum values +#define I2CIP_FQA_I2CBUS_LSB 13 +#define I2CIP_FQA_I2CBUS_LEN 3 +#define I2CIP_FQA_I2CBUS_MAX ((1 << I2CIP_FQA_I2CBUS_LEN) - 1) +#define I2CIP_FQA_MODULE_LSB 10 +#define I2CIP_FQA_MODULE_LEN 3 +#define I2CIP_FQA_MODULE_MAX ((1 << I2CIP_FQA_MODULE_LEN) - 1) +#define I2CIP_FQA_MUXBUS_LSB 7 +#define I2CIP_FQA_MUXBUS_LEN 3 +#define I2CIP_FQA_MUXBUS_MAX ((1 << I2CIP_FQA_MUXBUS_LEN) - 1) +#define I2CIP_FQA_DEVADR_LSB 0 +#define I2CIP_FQA_DEVADR_LEN 7 +#define I2CIP_FQA_DEVADR_MAX ((1 << I2CIP_FQA_DEVADR_LEN) - 1) + +#define I2CIP_FQA_CREATE(wire, module, bus, addr) (i2cip_fqa_t)((wire << (16 - I2CIP_FQA_I2CBUS_LEN)) | (module << (16 - I2CIP_FQA_I2CBUS_LEN - I2CIP_FQA_MODULE_LEN)) | (bus << (16 - I2CIP_FQA_I2CBUS_LEN - I2CIP_FQA_MODULE_LEN - I2CIP_FQA_MUXBUS_LEN)) | addr) + +/** + * Segment extraction. Shifts all bits so LSB is at index 0. Returns as uint8_t. + * @param fqa FQA of interest + * @param lsb Right-most (least significant) bit position + * @param len Length of the segment + */ +#define I2CIP_FQA_SEG(fqa, lsb, len) ((uint8_t)((fqa >> lsb) & (0xFFFF >> (16 - len)))) + +// Shorthands +#define I2CIP_FQA_SEG_DEVADR(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_DEVADR_LSB, I2CIP_FQA_DEVADR_LEN) // Extracts the device address segment from an FQA +#define I2CIP_FQA_SEG_MUXBUS(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_MUXBUS_LSB, I2CIP_FQA_MUXBUS_LEN) // Extracts the MUX bus number segment from an FQA +#define I2CIP_FQA_SEG_MODULE(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_MODULE_LSB, I2CIP_FQA_MODULE_LEN) // Extracts the MUX number segment from an FQA +#define I2CIP_FQA_SEG_I2CBUS(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_I2CBUS_LSB, I2CIP_FQA_I2CBUS_LEN) // Extracts the I2C bus number segment from an FQA + +// I2C Wire Implementation +#define I2CIP_MAXBUFFER 32 // I2C buffer size +#define I2CIP_NUM_WIRES 1 // Number of I2C wires - TODO: autodetect and populate `wires[]` based on hardware spec macros + +extern TwoWire Wire; + +#define I2CIP_FQA_TO_WIRE(fqa) (I2CIP::wires[I2CIP_FQA_SEG_I2CBUS(fqa)]) + +#define I2CIP_ERR_BREAK(errlev) if((errlev) != I2CIP::I2CIP_ERR_NONE) { return (errlev); } + +namespace I2CIP { + /** + * Errorlevels for I2CIP communication. + */ + typedef enum { + I2CIP_ERR_NONE = 0x0, // No error + I2CIP_ERR_SOFT = 0x1, // Communications error, device still reachable + I2CIP_ERR_HARD = 0x2, // Device unreachable + } i2cip_errorlevel_t; + + /** + * Create an FQA from segments. Validates. + * @param wire I2C bus number + * @param mux MUX number + * @param bus MUX bus number + * @param addr Device address + * @return A valid FQA, or 0 if any segment has an invalid value. + */ + i2cip_fqa_t createFQA(const uint8_t& wire, const uint8_t& mux, const uint8_t& bus, const uint8_t& addr); + + extern TwoWire* const wires[I2CIP_NUM_WIRES]; + + /** + * Initialize an I2C interface (if it has not already been initialized) + * @param wire + */ + void beginWire(const uint8_t& wire); +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/hashtable.h b/software/microcontroller/lib/I2CIP/hashtable.h new file mode 100644 index 00000000..c6c1ce8e --- /dev/null +++ b/software/microcontroller/lib/I2CIP/hashtable.h @@ -0,0 +1,66 @@ +#ifndef I2CIP_HASHTABLE_H_ +#define I2CIP_HASHTABLE_H_ + +#include +#include + +#include + +#include + +// Constants +#define HASHTABLE_OFFSET 7 +#define HASHTABLE_SLOTS 4 // Number of unique ID slots to include in the hashtable (for optimization purposes) + +// Hash table entry +template class HashTableEntry { + public: + HashTableEntry(const char* key, T value, HashTableEntry* last); + ~HashTableEntry(); + + const char* key; // Pointer to the IMMUTABLE (copied) key - de/allocated on de/construction + T value; // REFERENCE to the value - passed in + HashTableEntry* next; // Pointer to the previous entry - passed in +}; + +// TODO: Class 'HashTableEntry < DeviceGroup & >' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s). +// TODO: Class 'HashTableEntry < DeviceGroup & >' does not have a operator= which is recommended since it has dynamic memory/resource allocation(s). + +// A hash table. Yup. +template class HashTable { + public: + // Slots are static, entries are dynamic + HashTableEntry* hashtable[HASHTABLE_SLOTS] = { nullptr }; // FIXED POINTER to the entries - de/allocated on de/construction + + HashTable(); + ~HashTable(); + + /** + * Put {key: value} in the hash table. + * @param key String key + * @param value Pointer to value + * @param overwrite Overwrite existing value if found? Default: `true` + * @return Pointer to the new entry + */ + HashTableEntry* set(const char* key, T value, bool overwrite = true); + + /** + * Look at the index for that key, down the chain until either key=key or next = nullptr + * @param key to look for + * @return Pointer if found, nullptr otherwise + */ + HashTableEntry* get(const char* key); + + /** + * Index operator by key. + * @param key + * @return Pointer to entry + */ + HashTableEntry* operator[](const char* key); + + bool remove(const char* key); +}; + +#include + +#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/hashtable.tpp b/software/microcontroller/lib/I2CIP/hashtable.tpp new file mode 100644 index 00000000..6b27ccf8 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/hashtable.tpp @@ -0,0 +1,115 @@ +#ifndef I2CIP_HASHTABLE_H_ +#error __FILE__ should only be included AFTER +#endif + +#ifdef I2CIP_HASHTABLE_H_ + +#ifndef I2CIP_HASHTABLE_T_ +#define I2CIP_HASHTABLE_T_ + +inline static uint8_t hash(const char* s) { + unsigned index; + for (index = 0; *s != '\0'; s++) { + index = *s + HASHTABLE_OFFSET * index; + } + return index % HASHTABLE_SLOTS; +} + +// HASH TABLE ENTRY + +template HashTableEntry::HashTableEntry(const char* key, T value, HashTableEntry* last) : key(key), value(value), next(last) { } + +template HashTableEntry::~HashTableEntry() { + // Free our key then trigger the next entry + delete this->key; + delete this->next; +} + +// HASH TABLE + +template HashTable::HashTable() { + for(int i = 0; i < HASHTABLE_SLOTS; i++) { + hashtable[i] = nullptr; + } +} + +template HashTable::~HashTable() { + // Free all allocated entries (and their keys) recursively; slots are static + for (uint8_t i = 0; i < HASHTABLE_SLOTS; i++) { + delete(this->hashtable[i]); + } +} + +template HashTableEntry* HashTable::operator[](const char* key) { + return get(key); +} + +// Public methods + +template HashTableEntry* HashTable::set(const char* key, T value, bool overwrite) { + HashTableEntry* head = get(key); + + // Match found? + if (head != nullptr) { + if (overwrite) head->value = value; + return head; + } + // No match, allocate new; point "next" to the top entry + uint8_t index = hash(key); + head = new HashTableEntry(key, value, hashtable[index]); + + // If allocation was successful: + if (head != nullptr && head->key != nullptr) { + // Point the top of the hashtable to the new entry + hashtable[index] = head; + } + #ifdef I2CIP_DEBUG_SERIAL + else { I2CIP_DEBUG_SERIAL.print(F("HashTable(x) ENOMEM\n")); } + #endif + + return head; +} + +template HashTableEntry* HashTable::get(const char* key) { + HashTableEntry* np; + for (np = hashtable[hash(key)]; np != nullptr; np = np->next) { + if (strcmp(key, np->key) == 0) { + return np; /* found */ + } + } + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("HashTable[''] ENOENT\n")); + #endif + return nullptr; /* not found */ +} + +template bool HashTable::remove(const char* key) { + // Find ptr.next.key == key + uint8_t index = hash(key); + HashTableEntry* ptr = hashtable[index]; + + // Check top-level + if (strcmp(key, ptr->key) == 0) { + // Found; point the slot to next + hashtable[index] = ptr->next; + ptr->next = nullptr; + delete(ptr); + return true; + } + + for (; ptr->next != nullptr; ptr = ptr->next) { + if (strcmp(key, ptr->next->key) == 0) { + // Found; point the preceding entry to the subsequent one + HashTableEntry* entry = ptr->next; + ptr->next = ptr->next->next; + entry->next = nullptr; + delete(entry); + return true; + } + } + // Not found + return false; +} + +#endif +#endif diff --git a/software/microcontroller/lib/I2CIP/library.properties b/software/microcontroller/lib/I2CIP/library.properties new file mode 100644 index 00000000..b280ca6b --- /dev/null +++ b/software/microcontroller/lib/I2CIP/library.properties @@ -0,0 +1,10 @@ +name=I2CIP +version=1.0.0 +author=Jayden Lefebvre +maintainer=Jayden Lefebvre +sentence=A library of protocols for interfacing with I2C devices on a modular switched network. +paragraph=See README for compatible hardware specifications. State management for plug-and-play compatibility. Tested on Arduino Nano. +category=Communication +url=https://github.com/PeaPodTechnologies/I2CIP/ +architectures=avr +includes=I2CIP.h \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/mux.cc b/software/microcontroller/lib/I2CIP/mux.cc new file mode 100644 index 00000000..9c0a7e6f --- /dev/null +++ b/software/microcontroller/lib/I2CIP/mux.cc @@ -0,0 +1,105 @@ +#include + +#include +#include + +namespace I2CIP { + + namespace MUX { + bool pingMUX(const uint8_t& wire, const uint8_t& module) { + beginWire(wire); + wires[wire]->beginTransmission(I2CIP_MODULE_TO_MUXADDR(module)); + uint8_t code = wires[wire]->endTransmission(); + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("MUX Ping: Code 0x")); + I2CIP_DEBUG_SERIAL.print(code, HEX); + I2CIP_DEBUG_SERIAL.print(F("\n")); + DEBUG_DELAY(); + #endif + return (code == 0); + } + + bool pingMUX(const i2cip_fqa_t& fqa) { + beginWire(I2CIP_FQA_SEG_I2CBUS(fqa)); + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_MODULE_TO_MUXADDR(I2CIP_FQA_SEG_MODULE(fqa))); + uint8_t code = I2CIP_FQA_TO_WIRE(fqa)->endTransmission(); + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("MUX Ping: Code 0x")); + I2CIP_DEBUG_SERIAL.print(code, HEX); + I2CIP_DEBUG_SERIAL.print(F("\n")); + DEBUG_DELAY(); + #endif + return (code == 0); + } + + i2cip_errorlevel_t setBus(const i2cip_fqa_t& fqa) { + // Note: no need to ping MUX, we'll see in real time what the result is + beginWire(I2CIP_FQA_SEG_I2CBUS(fqa)); + + // Was the bus switched successfully? + bool success = true; + + // Begin transmission + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_MODULE_TO_MUXADDR(I2CIP_FQA_SEG_MODULE(fqa))); + + // Write the bus switch instruction + uint8_t instruction = I2CIP_MUX_BUS_TO_INSTR(I2CIP_FQA_SEG_MUXBUS(fqa)); + if (I2CIP_FQA_TO_WIRE(fqa)->write(&instruction, 1) != 1) { + success = false; + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("MUX Write Failed\n")); + #endif + } + + // End transmission + uint8_t r = I2CIP_FQA_TO_WIRE(fqa)->endTransmission(); + if (r != 0) { + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("MUX Transmission Failed 0x")); + I2CIP_DEBUG_SERIAL.print(r, HEX); + I2CIP_DEBUG_SERIAL.print('\n'); + DEBUG_DELAY(); + #endif + return I2CIP_ERR_HARD; + } + + #ifdef I2CIP_DEBUG_SERIAL + I2CIP_DEBUG_SERIAL.print(F("MUX Bus Set\n")); + DEBUG_DELAY(); + #endif + + return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); + } + + i2cip_errorlevel_t resetBus(const i2cip_fqa_t& fqa) { + // Note: no need to ping MUX, we'll see in real time what the result is + beginWire(I2CIP_FQA_SEG_I2CBUS(fqa)); + + // Begin transmission + I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_MODULE_TO_MUXADDR(I2CIP_FQA_SEG_MODULE(fqa))); + + // Write the "inactive" bus switch instruction + const uint8_t instruction = I2CIP_MUX_INSTR_RST; + if (I2CIP_FQA_TO_WIRE(fqa)->write(&instruction, 1) != 1) { + return I2CIP_ERR_SOFT; + } + + // End transmission + if (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { + return I2CIP_ERR_HARD; + } + + #ifdef I2CIP_DEBUG_SERIAL + DEBUG_DELAY(); + I2CIP_DEBUG_SERIAL.print(F("MUX Bus Reset\n")); + DEBUG_DELAY(); + #endif + + return I2CIP_ERR_NONE; + } + }; +}; \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/mux.h b/software/microcontroller/lib/I2CIP/mux.h new file mode 100644 index 00000000..5caf6283 --- /dev/null +++ b/software/microcontroller/lib/I2CIP/mux.h @@ -0,0 +1,67 @@ +#ifndef I2CIP_MUX_H_ +#define I2CIP_MUX_H_ + +#include + +// ---------------------- +// MUX: I2C Multiplexers +// ---------------------- + +#define I2CIP_MUX_ADDR_MIN 0x70 // The lowest device address +#define I2CIP_MUX_ADDR_MAX 0x77 // The highest device address +#define I2CIP_MUX_COUNT (I2CIP_MUX_ADDR_MAX - I2CIP_MUX_ADDR_MIN + 1) +#define I2CIP_MUX_BUS_MIN 0x00 // The lowest bus number +#define I2CIP_MUX_BUS_MAX 0x07 // The highest bus number +#define I2CIP_MUX_BUS_COUNT (I2CIP_MUX_BUS_MAX - I2CIP_MUX_BUS_MIN + 1) +#define I2CIP_MUX_BUS_DEFAULT 0x00 // The default bus for integrated on-module interfaces. +#define I2CIP_MUX_INSTR_RST 0x00 // Disable all busses + +/** + * Converts a bus number to a MUX instruction. + * @param bus Bus number (0-7) + */ +#define I2CIP_MUX_BUS_TO_INSTR(bus) (uint8_t)(1 << bus) + +/** + * Converts a module number to its MUX address. + * @param num MUX number (0-7) + */ +#define I2CIP_MODULE_TO_MUXADDR(module) (module + I2CIP_MUX_ADDR_MIN) + +namespace I2CIP { + namespace MUX { + /** + * Pings the MUX. Begins the Wire. + * | MUX ADDR (7) | ACK? | + * @param fqa FQA of a device this MUX is in front of. + * @return ACK? + */ + bool pingMUX(const i2cip_fqa_t& fqa); + + /** + * Pings the MUX. Begins the Wire. + * | MUX ADDR (7) | ACK? | + * @param fqa FQA of a device this MUX is in front of. + * @return ACK? + */ + bool pingMUX(const uint8_t& wire, const uint8_t& module); + + /** + * Sets the MUX bus. + * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | + * @param fqa FQA of a device that is on the target Subnet. + * @return Hardware failure: No ACK; Module lost. Software failure: Failed to write to MUX. + */ + i2cip_errorlevel_t setBus(const i2cip_fqa_t& fqa); + + /** + * Reset the MUX to the "inactive" bus. + * | MUX ADDR (7) | MUX RESET (8) | ACK? | + * @param fqa FQA of a device that this MUX is in front of + * @return Hardware failure: No ACK; Module lost. Software failure: Failed to write to MUX. + */ + i2cip_errorlevel_t resetBus(const i2cip_fqa_t& fqa); + }; +}; + +#endif \ No newline at end of file diff --git a/software/microcontroller/library.properties b/software/microcontroller/library.properties new file mode 100644 index 00000000..4e8a1eeb --- /dev/null +++ b/software/microcontroller/library.properties @@ -0,0 +1,11 @@ +name=ControlSystemsOS +version=0.2.2 +author=Jayden Lefebvre +maintainer=Jayden Lefebvre +sentence=A robotics library for interfacing with a dynamic set of plug-and-play low-level I2C modules through a high-level control systems API. +paragraph=Currently supports a variety of I2C middleware devices ("Interfaces", i.e. 16-pin GPIO expander, 12-bit ADC, autonomous PWM) for communicating with sensors and actuators. Intra-module linking info for device detection is read from on-module EEPROM. Tested on Arduino Nano. +category=Device Control +url=https://github.com/PeaPodTechnologies/ControlSystemsOS/ +architectures=avr +depends=I2CIP (>=1.0.0), FiniteStateMachine (>=0.1.0), ArduinoJson (>=6.19.4) +includes=ControlSystemsOS.h diff --git a/software/microcontroller/platformio.ini b/software/microcontroller/platformio.ini index 463c1f67..f5e15593 100644 --- a/software/microcontroller/platformio.ini +++ b/software/microcontroller/platformio.ini @@ -13,7 +13,7 @@ default_envs = peapod [env:peapod] platform = atmelavr -board = nanoatmega328 +board = nanoatmega328new framework = arduino upload_protocol = custom upload_command = sudo avrdude -p $BOARD_MCU -C avrdude_gpio.conf -c peapod -v -U flash:w:$SOURCE:i @@ -23,12 +23,11 @@ test_build_src = true monitor_port = /dev/ttyS0 monitor_speed = 115200 lib_deps = - adafruit/Adafruit BusIO@^1.13.2 bblanchon/ArduinoJson@^6.19.4 [env:direct] platform = atmelavr -board = nanoatmega328 +board = nanoatmega328new framework = arduino test_port = /dev/ttyS0 test_speed = 115200 @@ -36,5 +35,14 @@ test_build_src = true monitor_port = /dev/ttyS0 monitor_speed = 115200 lib_deps = - adafruit/Adafruit BusIO@^1.13.2 bblanchon/ArduinoJson@^6.19.4 + +[env:autousb] +platform = atmelavr +board = nanoatmega328new +framework = arduino +test_speed = 115200 +test_build_src = true +monitor_speed = 115200 +lib_deps = + bblanchon/ArduinoJson@^6.19.4 \ No newline at end of file diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc deleted file mode 100644 index d5f5b94a..00000000 --- a/software/microcontroller/src/main.cc +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef UNIT_TEST - -#include - -#include - -#include -#include -#include -#include - -// DECLARATIONS -bool post(void); - -// GLOBAL VARIABLES -uint8_t sensor = 0, actuator = 0; - -// FUNCTIONS -void setup(void) { - // Status LED - pinMode(PIN_STATUS, OUTPUT); - digitalWrite(PIN_STATUS, LOW); - - // Await serial start - Serial.begin(BAUDRATE); - while (!Serial) { - delay(1); - } - - // Send software revision first - JSONMessenger::sendRevision(REVISION); - - // Await valid initial instruction set - String in; - do { - // Await instructions - while (!Serial.available()); - in = Serial.readStringUntil('\n'); - - // Trim whitespace, newline terminator - in.trim(); - } while (InstructionHandler::handleSet(in, &matrix) != ERR_NONE); - - // Serial communications established, initialize sensors and actuators - if (!post()) { - // Failed POST, blink - while (true) { - delay(250); - digitalWrite(PIN_STATUS, HIGH); - delay(250); - digitalWrite(PIN_STATUS, LOW); - } - } - - // Enable the watchdog timer - #if ENABLE_WATCHDOG - wdt_enable(WDTO); - #endif - digitalWrite(PIN_STATUS, HIGH); -} - -void loop(void) { - // Check for instructions - if (Serial.available()) { - String in = Serial.readStringUntil('\n'); - in.trim(); - InstructionHandler::handleSet(in, &matrix); - } - - // Read sensor - SensorState* sstate = sensors[sensor]->update(); - if (sstate->error == ERR_NONE) { - if (sstate->debug == DS_SUCCESS) { - for (int j = 0; j < sstate->numdata; ++j) { - JSONMessenger::sendData(sstate->data[j].label, sstate->data[j].value); - } - } // else do nothing - } else if (sstate->error == ERR_WARNING) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + sensors[sensor]->toString() + " (non-fatal).")); - } else if (sstate->error == ERR_FATAL) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to read from sensor " + sensors[sensor]->toString() + " (FATAL, SENSOR DISABLED!).")); - } - - // Update actuator - ActuatorState* astate = actuators[actuator]->update(); - if (astate->error == ERR_WARNING) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + actuators[actuator]->toString() + " (non-fatal).")); - } else if (astate->error == ERR_FATAL) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to update actuator " + actuators[actuator]->toString() + " (FATAL, ACTUATOR DISABLED!).")); - } - - // Increment - sensor = (sensor+1) % NUM_SENSORS; - actuator = (actuator+1) % NUM_ACTUATORS; - - #if ENABLE_WATCHDOG - wdt_enable(WDTO); - #endif -} - -bool post(void) { - bool success = true; - for (int i = 0; i < NUM_SENSORS; ++i) { - SensorState* state = sensors[i]->begin(); - bool latest = (state->debug >= DS_INITIALIZED && state->error == ERR_NONE); - if (latest) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Sensor " + sensors[i]->toString() + " initialized successfully.")); - } else { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, String("Failed to initialize sensor " + sensors[i]->toString() + ". Check wiring.")); - } - success &= latest; - } - for (int i = 0; i < NUM_ACTUATORS; ++i) { - ActuatorState* state = actuators[i]->begin(); - bool latest = (state->debug >= DS_INITIALIZED && state->error == ERR_NONE); - if (latest) { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Actuator " + actuators[i]->toString() + " initialized successfully.")); - } else { - JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, String("Failed to initialize actuator " + actuators[i]->toString() + ". Check wiring.")); - } - success &= latest; - } - return success; -} - -#endif \ No newline at end of file diff --git a/software/microcontroller/src/sensors/sensor.cc b/software/microcontroller/src/sensors/sensor.cc deleted file mode 100644 index be9e4368..00000000 --- a/software/microcontroller/src/sensors/sensor.cc +++ /dev/null @@ -1,103 +0,0 @@ -// HEADERS - -#include - -#include - -#include - -#include - -// CONSTRUCTOR - -Sensor::Sensor(const char* const* id, const SensorDataSetup* setup, uint32_t delta) : delta(delta), _id(id) { - // Initial error and debug states - state.error = ERR_NONE; - state.debug = DS_DISABLED; - - // Allocate data state - state.data = (DataPoint*)malloc(sizeof(DataPoint)*(setup->numdata)); - state.numdata = setup->numdata; - for (int i = 0; i < setup->numdata; ++i) { - // Since setup->labels[i] is a const char*, we can just reassign our pointer - state.data[i].label = setup->labels[i]; - } -} - -// PUBLIC METHODS - -SensorState* Sensor::begin(void) { - state.error = initialize(); - if (state.error > ERR_NONE) { - // Failed - state.debug = DS_DISABLED; - } else { - // Success - state.debug = DS_INITIALIZED; - // Refresh read delay - state.timestamp = millis(); - } - return &state; -} - -SensorState* Sensor::update(void) { - // Allocate new data buffer on stack - float buffer[state.numdata]; - - // Check state preconditions - if (state.error < ERR_FATAL && state.debug >= DS_INITIALIZED) { - // Check timing - if (millis() - lastread > delta) { - // Read and refresh read delay - state.error = read(buffer, state.numdata); - lastread = millis(); - - switch (state.error) { - case ERR_NONE: - // Success! - // Indicate that new data is available - state.debug = DS_SUCCESS; - state.timestamp = lastread; - - // Copy from buffer to state data - for (int i = 0; i < state.numdata; ++i) { - state.data[i].value = buffer[i]; - } - break; - - case ERR_WARNING: - // Read didn't go as planned, non-fatal - // DO NOT UPDATE ANY STATE VALUES - break; - - case ERR_FATAL: - // Read failed catastrophically - state.debug = DS_DISABLED; - break; - } - } else { - // Attempted to update between valid read cycles - state.debug = DS_WAITING; - } - } - return &state; -} - -SensorState* Sensor::getState(void) { - return &state; -} - -String Sensor::toString(void) { - String s = String((const char*)(_id)) + " ("; - for (int i = 0; i < state.numdata; i++) { - s += state.data[i].label; - if (i < state.numdata - 1) { - s += ", "; - } - } - return s + ")"; -} - -String Sensor::getID(void) { - return String((const char*)_id); -} \ No newline at end of file diff --git a/software/microcontroller/src/sensors/sensor.h b/software/microcontroller/src/sensors/sensor.h deleted file mode 100644 index e6553ecc..00000000 --- a/software/microcontroller/src/sensors/sensor.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef PEAPOD_SENSORS_SENSOR_H_ -#define PEAPOD_SENSORS_SENSOR_H_ - -// HEADERS - -#include - -#include - -// DECLARATIONS - -// Single datapoint -typedef struct DataPoint { - // Value - float value; - - // Dataset label (string literal) - const char* label; -} DataPoint; - -// All sensor state info + latest datum - used as the "cache" in the sensor object. -typedef struct SensorState { - // Error level - errorlevel_t error; - - // Other state information - debuglevel_t debug; - - // Last data update (millis since program start) - NOT THE SAME AS `lastread`, an unsuccessful read attempt will NOT update `state.timestamp`. - uint32_t timestamp; - - // Array of datapoints - DataPoint* data; - - // Length of `data` array - uint8_t numdata; -} SensorState; - -typedef struct SensorDataSetup { - // Number of datasets recorded by this sensor - const uint8_t numdata; - - // Array of string literals corresponding to the labels for each dataset - const char* const* labels; -} SensorDataSetup; - -// CLASS -// Abstract base class for all sensor interfaces -class Sensor { - public: - /** - * Sensor interface constructor. - * @param setup Pointer to datasets setup - * @param delta Minimum delay (in milliseconds) between sensor read attempts - */ - Sensor(const char* const* id, const SensorDataSetup* setup, uint32_t delta); - - /** Wrapper function for `initialize()`. Sets debug state to indicate initialization success or failure. - * @return Pointer to sensor state - */ - SensorState* begin(void); - - /** - * Wrapper function for `read()`. Checks debug state (initialized), ensures `delta` compliance, and sets `lastread` and `data` flags on read success. - * @return Pointer to sensor state - */ - SensorState* update(void); - - // @return Pointer to sensor state - SensorState* getState(void); - - // @return String representation - String toString(void); - - String getID(void); - - protected: - /** - * Initializes sensor. To be implemented by the child class. - * @return Error level for this initialization attempt - */ - virtual errorlevel_t initialize(void) = 0; - - /** - * Reads data from sensor. To be implemented by the child class. - * @param data The data array to be read into - * @param numdata Length of the data array - * @return Error level for this read attempt - */ - virtual errorlevel_t read(float* data, uint8_t numdata) = 0; - - private: - // Stores all the latest state data for this sensor. - SensorState state; - - // Minimum delay (in milliseconds) between sensor read attempts - const uint32_t delta; - - // Last read attempt (milliseconds since program start) - NOT THE SAME AS `state.timestamp`, an unsuccessful read attempt will still update `lastread`. - uint32_t lastread; - - // Sensor ID - const char* const* _id; -}; - -#endif diff --git a/software/microcontroller/src/sensors/sht31.cc b/software/microcontroller/src/sensors/sht31.cc deleted file mode 100644 index 2b8dedda..00000000 --- a/software/microcontroller/src/sensors/sht31.cc +++ /dev/null @@ -1,141 +0,0 @@ -// HEADERS - -#include - -#include -#include - -#include -#include - -// DECLARATIONS - -static uint8_t crc8(const uint8_t *data, int len); - -// CONSTRUCTOR - -SHT31::SHT31(TwoWire *wire, uint8_t address) : Sensor(&id, &datasetup, SHT31_DELTA), address(address), wire(wire) { } - -// PUBLIC METHODS - -errorlevel_t SHT31::initialize(void) { - wire->begin(address); - reset(); - if (readStatus() == 0xFFFF) { - return ERR_FATAL; - } else { - return ERR_NONE; - } -} - -errorlevel_t SHT31::read(float* data, uint8_t numdata) { - uint8_t buffer[6]; - - // Ready up - writeCommand(SHT31_MEAS_HIGHREP); - delay(20); - wire->requestFrom(address, sizeof(buffer)); - - // Invalid size - if (wire->available() != sizeof(buffer)) { - return ERR_WARNING; - } - - // Read into buffer - for (size_t i = 0; i < sizeof(buffer); ++i) { - buffer[i] = wire->read(); - } - - // Checksum - if (buffer[2] != crc8(buffer, 2) || buffer[5] != crc8(buffer + 3, 2)) { - return ERR_WARNING; - } - - // Temperature conversion - int32_t stemp = (int32_t)(((uint32_t)buffer[0] << 8) | buffer[1]); - stemp = ((4375 * stemp) >> 14) - 4500; - data[0] = (float)stemp / 100.0f; - - // Humidity conversion - uint32_t shum = ((uint32_t)buffer[3] << 8) | buffer[4]; - shum = (625 * shum) >> 12; - data[1] = (float)shum / 100.0f; - - return ERR_NONE; -} - -// HELPER FUNCTIONS - -void SHT31::writeCommand(uint16_t cmd) { - wire->beginTransmission(address); - wire->write(cmd >> 8); - wire->write(cmd & 0xFF); - wire->endTransmission(); -} - -uint16_t SHT31::readStatus(void) { - writeCommand(SHT31_READSTATUS); - wire->requestFrom(address, (uint8_t)3); - uint16_t stat = wire->read(); - stat <<= 8; - stat |= wire->read(); - return stat; -} - -void SHT31::reset(void) { - writeCommand(SHT31_SOFTRESET); - delay(10); -} - -/** - * Performs a CRC8 calculation on the supplied values. - * - * @param data Pointer to the data to use when calculating the CRC8. - * @param len The number of bytes in 'data'. - * - * @return The computed CRC8 value. - */ -static uint8_t crc8(const uint8_t *data, int len) { - /* - * - * CRC-8 formula from page 14 of SHT spec pdf - * - * Test data 0xBE, 0xEF should yield 0x92 - * - * Initialization data 0xFF - * Polynomial 0x31 (x8 + x5 +x4 +1) - * Final XOR 0x00 - */ - - const uint8_t POLYNOMIAL(0x31); - uint8_t crc(0xFF); - - for (int j = len; j; --j) { - crc ^= *data++; - - for (int i = 8; i; --i) { - crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1); - } - } - return crc; -} - -// UNUSED - -// void SHT31::heater(bool h) { -// if (h) { -// writeCommand(SHT31_HEATEREN); -// } else { -// writeCommand(SHT31_HEATERDIS); -// } -// delay(1); -// } - -/** - * @brief Return sensor heater state - * @return heater state (TRUE = enabled, FALSE = disabled) - */ -// bool SHT31::isHeaterEnabled() { -// uint16_t regValue = readStatus(); -// return (bool)bitRead(regValue, SHT31_REG_HEATER_BIT); -// } diff --git a/software/microcontroller/src/sensors/sht31.h b/software/microcontroller/src/sensors/sht31.h deleted file mode 100644 index 2009f405..00000000 --- a/software/microcontroller/src/sensors/sht31.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef PEAPOD_SENSORS_SHT31_H_ -#define PEAPOD_SENSORS_SHT31_H_ - -// HEADERS - -#include -#include - -#include -#include - -// MACROS - -// Registers -#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Measurement High Repeatability with Clock Stretch Enabled -#define SHT31_MEAS_MEDREP_STRETCH 0x2C0D // Measurement Medium Repeatability with Clock Stretch Enabled -#define SHT31_MEAS_LOWREP_STRETCH 0x2C10 // Measurement Low Repeatability with Clock Stretch Enabled -#define SHT31_MEAS_HIGHREP 0x2400 // Measurement High Repeatability with Clock Stretch Disabled -#define SHT31_MEAS_MEDREP 0x240B // Measurement Medium Repeatability with Clock Stretch Disabled -#define SHT31_MEAS_LOWREP 0x2416 // Measurement Low Repeatability with Clock Stretch Disabled -#define SHT31_READSTATUS 0xF32D // Read Out of Status Register -#define SHT31_CLEARSTATUS 0x3041 // Clear Status -#define SHT31_SOFTRESET 0x30A2 // Soft Reset -#define SHT31_HEATEREN 0x306D // Heater Enable -#define SHT31_HEATERDIS 0x3066 // Heater Disable -#define SHT31_REG_HEATER_BIT 0x000d // Status Register Heater Bit - -// Settings -#define SHT31_ADDR 0x44 // SHT31 Default Address -#define SHT31_DELTA 1000 // read() once every delta milliseconds - - -// GLOBALS -// Used when you need a pointer to static PROGMEM - these variables are ONLY INITIALIZED in the .cc SOURCE file that INCLUDES this header file -#ifndef GLOBALS // Double-declaration blocker - #define GLOBALS - // Descriptors - static const PROGMEM char* const id = "SHT31"; - - // Data setup - static const PROGMEM char* const labels[2] = { "air_temperature", "air_humidity" }; - static const PROGMEM SensorDataSetup datasetup = { - .numdata = 2, - .labels = labels - }; -#endif - -// Import the default I2C interface provided by Arduino. -extern TwoWire Wire; - -// CLASS - -// Interface for the Adafruit SHT31-D Temperature and Humidity breakout board. -class SHT31 : public Sensor { - public: - /** - * Constructor. - * @param wire I2C interface. Defaults to the provided `Wire` interface (by reference). - * @param address I2C address. Defaults to the default SHT31 address - */ - SHT31(TwoWire* wire = &Wire, uint8_t address = SHT31_ADDR); - - /** - * UNUSED - * Enables or disabled the heating element. - * @param h True to enable the heater, False to disable it. - */ - // void heater(bool h); - - /** - * UNUSED - * Gets the current status register heater bit. - * @return Boolean value, True = enabled, False = disabled. - */ - // bool isHeaterEnabled(void); - - private: - errorlevel_t initialize(void) override; - - errorlevel_t read(float* data, uint8_t numdata) override; - - /** - * Internal function to perform and I2C write. - * @param cmd The 16-bit command ID to send. - */ - void writeCommand(uint16_t cmd); - - /** - * Gets the current status register contents. - * @return The 16-bit status register. - */ - uint16_t readStatus(void); - - // Performs a reset of the sensor to put it into a known state. - void reset(void); - - // I2C address - const uint8_t address; - - // Wire object - TwoWire* wire; -}; - -#endif diff --git a/software/microcontroller/test/README.md b/software/microcontroller/test/README.md deleted file mode 100644 index 120e3927..00000000 --- a/software/microcontroller/test/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Arduino Software Testing Suite - -### Arduino Compilation & Upload Test - `blink.ino` - -Test the functionality of the `arduino-cli` and `avrdude` utilities (and their custom configurations) by compilation the script and flashing the binary. - -### Communications Protocol Test - `revision.ino` - -Test communications between the Arduino and Raspberry Pi including timeout and revision checking. Essentially a skeleton of the main `PeaPodOS-Arduino.ino` script, with no sensors or actuators. \ No newline at end of file diff --git a/software/microcontroller/test/config.h b/software/microcontroller/test/config.h new file mode 100644 index 00000000..c8b11bce --- /dev/null +++ b/software/microcontroller/test/config.h @@ -0,0 +1,8 @@ +#ifndef I2CIP_TESTS_TEST_H_ +#define I2CIP_TESTS_TEST_H_ + +// TESTING PARAMETERS + +#define I2CIP_TEST_BUFFERSIZE 100 // Need to limit this, or else crash; I think Unity takes up a lot of stack space + +#endif \ No newline at end of file diff --git a/software/microcontroller/test/test_blink/test_blink.cc b/software/microcontroller/test/test_0_blink/test_0_blink.cc similarity index 100% rename from software/microcontroller/test/test_blink/test_blink.cc rename to software/microcontroller/test/test_0_blink/test_0_blink.cc diff --git a/software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc b/software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc new file mode 100644 index 00000000..f4230117 --- /dev/null +++ b/software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc @@ -0,0 +1,61 @@ +#include +#include + +#include +#include "../config.h" + +#define CSOS_TEST_EEPROM_BYTE '[' // This should be the first character of ANY valid SPRT EEPROM + +i2cip_fqa_t eeprom_fqa = I2CIP::createFQA(WIRENUM, MUXNUM, CSOS_MUX_BUS_DEFAULT, CSOS_EEPROM_ADDR); +char buffer[I2CIP_TEST_BUFFERSIZE] = { '\0' }; +uint16_t bufferlen = 0; + +// ALL MACROS + +void test_eeprom_ping(void) { + char msg[30]; + sprintf(msg, "Device unreachable (%01X.%01X.%01X.%02X)", CSOS_FQA_SEG_I2CBUS(eeprom_fqa), CSOS_FQA_SEG_MUXNUM(eeprom_fqa), CSOS_FQA_SEG_MUXBUS(eeprom_fqa), CSOS_FQA_SEG_DEVADR(eeprom_fqa)); + I2CIP::i2cip_errorlevel_t result = I2CIP::Device::ping(eeprom_fqa); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, msg); +} + +void test_eeprom_read_byte(void) { + uint8_t c = '\0'; + I2CIP::i2cip_errorlevel_t result = I2CIP::Device::readRegisterByte(eeprom_fqa, (uint16_t)0, c); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Read Byte"); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_TEST_EEPROM_BYTE, c, "EEPROM Read Byte (Match)"); +} + +void test_eeprom_write_byte(void) { + I2CIP::i2cip_errorlevel_t result = I2CIP::Device::writeRegister(eeprom_fqa, (uint16_t)0, CSOS_TEST_EEPROM_BYTE); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Write Byte"); +} + +void test_eeprom_read_contents(void) { + I2CIP::i2cip_errorlevel_t result = I2CIP::EEPROM::readContents(eeprom_fqa, (uint8_t*)buffer, bufferlen, CSOS_TEST_BUFFERSIZE); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Read Contents"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, bufferlen, "EEPROM Read Contents (Empty)"); +} + +void test_eeprom_overwrite_contents(void) { + I2CIP::i2cip_errorlevel_t result = I2CIP::EEPROM::overwriteContents(eeprom_fqa, (uint8_t*)buffer, bufferlen); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Overwrite Contents"); +} + +void setup() { + delay(2000); + + UNITY_BEGIN(); + + RUN_TEST(test_eeprom_ping); + RUN_TEST(test_eeprom_read_byte); + // RUN_TEST(test_eeprom_write_byte); + RUN_TEST(test_eeprom_read_contents); + // RUN_TEST(test_eeprom_overwrite_contents); + + UNITY_END(); +} + +void loop() {w + +} \ No newline at end of file diff --git a/software/microcontroller/test/test_2_json/test_2_json.cc b/software/microcontroller/test/test_2_json/test_2_json.cc new file mode 100644 index 00000000..0e5b2452 --- /dev/null +++ b/software/microcontroller/test/test_2_json/test_2_json.cc @@ -0,0 +1,110 @@ +#include +#include +#include + +#include +#include "../config.h" + +// GLOBALS +i2cip_fqa_t eeprom_fqa = I2CIP::createFQA(WIRENUM, MUXNUM, CSOS_MUX_BUS_DEFAULT, CSOS_EEPROM_ADDR); +char buffer[I2CIP_TEST_BUFFERSIZE] = { '\0' }; +StaticJsonDocument eeprom_json; // EEPROM JSON doc +uint8_t totaldevices = 0; // Number of devices in EEPROM +i2cip_fqa_t* devices; // Array of EEPROM device FQAs +uint8_t d = 0; // Loop variable + +/** + * Read and deserialize the EEPROM. + **/ +void test_sprt_json_deser(void) { + // READ EEPROM + uint16_t size = 0; + I2CIP::i2cip_errorlevel_t result = I2CIP::EEPROM::readContents(eeprom_fqa, (uint8_t*)buffer, size, CSOS_TEST_BUFFERSIZE); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "Failed to read EEPROM"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, size, "EEPROM empty!"); + + // DESERIALIZE + DeserializationError jsonerr = deserializeJson(eeprom_json, buffer); + + TEST_ASSERT_TRUE_MESSAGE(jsonerr.code() == DeserializationError::Code::Ok, buffer); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, eeprom_json.memoryUsage(), buffer); + TEST_ASSERT_FALSE_MESSAGE(eeprom_json.overflowed(), buffer); +} + +/** + * Parse the JSON, count devices and busses. + **/ +void test_sprt_json_valid(void) { + JsonArray arr = eeprom_json.as(); + + uint8_t buscount = 0; + for (JsonObject bus : arr) { + // Count reachable devices in each device group + uint8_t devicecount = 0; + for (JsonPair device : bus) { + buscount++; + + // Device addresses + JsonArray addresses = device.value().as(); + + for (JsonVariant address : addresses) { + devicecount++; + } + } + + // Add the number of devices on this bus to the tally + totaldevices += devicecount; + } + + TEST_ASSERT_NOT_EQUAL(0, buscount); + + TEST_ASSERT_NOT_EQUAL(0, totaldevices); +} + +/** + * Attempt to reach a device. + **/ +void test_device_reachable(void) { + char msg[30]; + sprintf(msg, "Device unreachable (%01X.%01X.%01X.%02X)", CSOS_FQA_SEG_I2CBUS(devices[d]), CSOS_FQA_SEG_MUXNUM(devices[d]), CSOS_FQA_SEG_MUXBUS(devices[d]), CSOS_FQA_SEG_DEVADR(devices[d])); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, I2CIP::Device::ping(devices[d]), msg); +} + +void setup() { + delay(2000); + + UNITY_BEGIN(); + + RUN_TEST(test_sprt_json_deser); + + RUN_TEST(test_sprt_json_valid); + + devices = new i2cip_fqa_t[totaldevices]; + + JsonArray arr = eeprom_json.as(); + + uint8_t busnum = 0, i = 0; + for (JsonObject bus : arr) { + for (JsonPair device : bus) { + + // Device addresses + JsonArray addresses = device.value().as(); + + for (JsonVariant address : addresses) { + devices[i] = I2CIP::createFQA(WIRENUM, MUXNUM, busnum, address.as()); + i++; + } + + busnum++; + } + } +} + +void loop() { + if(d >= totaldevices) { + UNITY_END(); + delete devices; + } + RUN_TEST(test_device_reachable); + d++; +} \ No newline at end of file diff --git a/software/microcontroller/test/test_3_csos/test_3_csos.cc b/software/microcontroller/test/test_3_csos/test_3_csos.cc new file mode 100644 index 00000000..a7c22356 --- /dev/null +++ b/software/microcontroller/test/test_3_csos/test_3_csos.cc @@ -0,0 +1,60 @@ +#include +#include + +#include "../config.h" + +#include +#include + +using namespace ControlSystemsOS; + +CSOSModule* m; // to be initialized in setup() + +void test_csos_map(void) { } +void test_csos_build(void) { + #ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("==== [ BUILD ] ====")); + DEBUG_DELAY(); + #endif + + i2cip_errorlevel_t errlev = ControlSystemsOS::update(true); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "Build failed!"); + + // TODO: Test Module Contents! (Against what?) +} + +void test_csos_update(void) { + #ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("==== [ UPDATE ] ====")); + DEBUG_DELAY(); + #endif + + i2cip_errorlevel_t errlev = ControlSystemsOS::update(false); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "Update failed!"); +} + +void setup(void) { + Serial.begin(115200); + + delay(2000); + + UNITY_BEGIN(); + + RUN_TEST(test_csos_build); + + delay(1000); +} + +uint8_t count = 1; +void loop(void) { + + RUN_TEST(test_csos_update); + + if(count > 10) { + UNITY_END(); + while(true); + } + + delay(1000); + count++; +} diff --git a/software/microcontroller/test/test_instructions/test_instructions.cc b/software/microcontroller/test/test_instructions/test_instructions.cc deleted file mode 100644 index 6c2de6a9..00000000 --- a/software/microcontroller/test/test_instructions/test_instructions.cc +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include - -#include -#include -#include - -class TestActuator : public Actuator { - public: - TestActuator(void); - private: - errorlevel_t initialize(void) override; - errorlevel_t set(float target) override; -}; - -const char* const id = "Test Actuator"; - -TestActuator::TestActuator(void) : Actuator(&id, 0) { } - -errorlevel_t TestActuator::initialize(void) { - return ERR_NONE; -} - -errorlevel_t TestActuator::set(float target) { - return ERR_NONE; -} - -TestActuator actuatorA = TestActuator(); -TestActuator actuatorB = TestActuator(); - -const char* ins[2] = { "A", "B" }; -Actuator* acts[2] = { &actuatorA, &actuatorB }; - -static const InstructionActuatorMatrix matrix = { - .numActuators = 2, - .actuators = acts, - .instructions = ins -}; - -void test_non_json(void) { - uint8_t result = InstructionHandler::handleSet("hello world", &matrix); - TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); -} - -void test_incomplete_json(void) { - uint8_t result = InstructionHandler::handleSet("{\"incomplete", &matrix); - TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); -} - -void test_invalid_target(void) { - uint8_t result = InstructionHandler::handleSet("{\"incomplete\":abc", &matrix); - TEST_ASSERT_EQUAL_UINT8(ERR_FATAL, result); -} - -void test_bad_instruction(void) { - uint8_t result = InstructionHandler::handleSet("{\"C\":0}", &matrix); - TEST_ASSERT_EQUAL_UINT8(ERR_WARNING, result); -} - -void test_empty_instruction_set(void) { - uint8_t result = InstructionHandler::handleSet("{}", &matrix); - TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); -} - -void test_one_instruction(void) { - uint8_t result = InstructionHandler::handleSet("{\"A\":1.3}", &matrix); - TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); - float target = actuatorA.update()->lasttarget; - TEST_ASSERT_EQUAL_FLOAT(1.3, target); -} - -void test_multiple_instructions(void) { - uint8_t result = InstructionHandler::handleSet("{\"A\":2.4,\"B\":4.3}", &matrix); - TEST_ASSERT_EQUAL_UINT8(ERR_NONE, result); - float target = actuatorA.update()->lasttarget; - TEST_ASSERT_EQUAL_FLOAT(2.4, target); - target = actuatorB.update()->lasttarget; - TEST_ASSERT_EQUAL_FLOAT(4.3, target); -} - -void setup(void) { - delay(2000); - - UNITY_BEGIN(); - - RUN_TEST(test_non_json); - RUN_TEST(test_incomplete_json); - RUN_TEST(test_invalid_target); - RUN_TEST(test_bad_instruction); - RUN_TEST(test_empty_instruction_set); - RUN_TEST(test_one_instruction); - RUN_TEST(test_multiple_instructions); - - UNITY_END(); -} - -void loop(void) { } \ No newline at end of file diff --git a/software/microcontroller/test/test_k30/test_k30.cc b/software/microcontroller/test/test_k30/test_k30.cc deleted file mode 100644 index 11037470..00000000 --- a/software/microcontroller/test/test_k30/test_k30.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include - -#include -#include -#include -#include "../tests.h" - -Sensor* sensor = new K30(); - -void setup(void) { - delay(2000); - - UNITY_BEGIN(); - - RUN_TEST(SensorTests::test_begin); -} - -int i = 0; - -void loop(void) { - if (i < 3) { - delay(K30_DELTA * 1.5); - - RUN_TEST(SensorTests::test_update); - - delay(K30_DELTA * 1.5); - - RUN_TEST(SensorTests::test_delta); - i++; - } else { - UNITY_END(); - } -} \ No newline at end of file diff --git a/software/microcontroller/test/test_messenger/test_messenger.cc b/software/microcontroller/test/test_messenger/test_messenger.cc deleted file mode 100644 index 06a16c53..00000000 --- a/software/microcontroller/test/test_messenger/test_messenger.cc +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -#include -#include - -void test_revision(void) { - const char* result = JSONMessenger::sendRevision(63).c_str(); - TEST_ASSERT_EQUAL_STRING("{\"type\":\"revision\",\"data\":63}\n", result); -} - -void test_debug(void) { - const char* result = JSONMessenger::sendMessage(JSONMessenger::MESSAGE_DEBUG, "hello world").c_str(); - TEST_ASSERT_EQUAL_STRING("{\"type\":\"debug\",\"data\":\"hello world\"}\n", result); -} - -void test_error(void) { - const char* result = JSONMessenger::sendMessage(JSONMessenger::MESSAGE_ERROR, "oh no").c_str(); - TEST_ASSERT_EQUAL_STRING("{\"type\":\"error\",\"data\":\"oh no\"}\n", result); -} - -void test_data(void) { - const char* result = JSONMessenger::sendData("testlabel", 32.4).c_str(); - TEST_ASSERT_EQUAL_STRING("{\"type\":\"data\",\"data\":{\"label\":\"testlabel\",\"value\":32.40}}\n", result); -} - -void setup(void) { - delay(2000); - - UNITY_BEGIN(); - - RUN_TEST(test_revision); - RUN_TEST(test_debug); - RUN_TEST(test_error); - RUN_TEST(test_data); - - UNITY_END(); -} - -void loop(void) { } \ No newline at end of file diff --git a/software/microcontroller/test/test_sht31/test_sht31.cc b/software/microcontroller/test/test_sht31/test_sht31.cc deleted file mode 100644 index a0fda98d..00000000 --- a/software/microcontroller/test/test_sht31/test_sht31.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include - -#include -#include -#include -#include "../tests.h" - -Sensor* sensor = new SHT31(); - -void setup(void) { - delay(2000); - - UNITY_BEGIN(); - - RUN_TEST(SensorTests::test_begin); -} - -int i = 0; - -void loop(void) { - if (i < 3) { - delay(SHT31_DELTA * 1.5); - - RUN_TEST(SensorTests::test_update); - - delay(SHT31_DELTA * 1.5); - - RUN_TEST(SensorTests::test_delta); - i++; - } else { - UNITY_END(); - } -} \ No newline at end of file diff --git a/software/microcontroller/test/tests.cc b/software/microcontroller/test/tests.cc deleted file mode 100644 index 26b2219d..00000000 --- a/software/microcontroller/test/tests.cc +++ /dev/null @@ -1,34 +0,0 @@ -// HEADERS -#include "tests.h" - -#include -#include - -#include -#include - -// DECLARATIONS - -void SensorTests::test_begin(void) { - SensorState* state = sensor->begin(); - TEST_ASSERT_EQUAL(DS_INITIALIZED, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} - -void SensorTests::test_update(void) { - SensorState* state = sensor->update(); - TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} - -void SensorTests::test_delta(void) { - SensorState* state = sensor->update(); - TEST_ASSERT_EQUAL(DS_SUCCESS, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); - - // No delay - - state = sensor->update(); - TEST_ASSERT_EQUAL(DS_WAITING, state->debug); - TEST_ASSERT_EQUAL(ERR_NONE, state->error); -} \ No newline at end of file diff --git a/software/microcontroller/test/tests.h b/software/microcontroller/test/tests.h deleted file mode 100644 index 90af46dd..00000000 --- a/software/microcontroller/test/tests.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef PEAPOD_TESTS_H_ -#define PEAPOD_TESTS_H_ - -// HEADERS - -#include -#include - -#include -#include - -// Requires `sensor` to be defined in the test source file -extern Sensor* sensor; - -// DECLARATAIONS - -namespace SensorTests { - /** - * Ensures state compliance with `begin()` - */ - void test_begin(void); - - /** - * Ensures state compliance with `update()` - */ - void test_update(void); - - /** - * Ensures state compliance when `delta` is skipped - */ - void test_delta(void); -} - -#endif \ No newline at end of file diff --git a/software/package-lock.json b/software/package-lock.json index f0709a7c..7795fb9c 100644 --- a/software/package-lock.json +++ b/software/package-lock.json @@ -1,7 +1,7 @@ { "name": "@peapodtech/peapodos", "version": "0.1.1", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -45,59 +45,61 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", - "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.7", - "@babel/parser": "^7.17.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -107,220 +109,210 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", - "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -364,13 +356,13 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" @@ -389,9 +381,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", - "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -548,12 +540,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", - "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -563,34 +555,34 @@ } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -598,12 +590,13 @@ } }, "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -617,14 +610,14 @@ "dev": true }, "node_modules/@firebase/analytics": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.8.tgz", - "integrity": "sha512-W38Zy/jf64LKpPi+mGNNETIjz4eq/YXBE0Uu2bzstqUwlhvFn1WlRBK4vzgtZMRaGW04CQp9FXYv6ZTRo/Xbyw==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.0.tgz", + "integrity": "sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -632,14 +625,14 @@ } }, "node_modules/@firebase/analytics-compat": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.9.tgz", - "integrity": "sha512-HYKMAZvfU589WVvK5XKY9Pl+axXFISabouAFw2VHpJm/TO1mAXAy0+eIjqQ3j8z3L1OEfCeOV/oY9eh8rpJZ5w==", - "dependencies": { - "@firebase/analytics": "0.7.8", - "@firebase/analytics-types": "0.7.0", - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz", + "integrity": "sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-types": "0.8.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -647,29 +640,30 @@ } }, "node_modules/@firebase/analytics-types": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.7.0.tgz", - "integrity": "sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ==" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.0.tgz", + "integrity": "sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==" }, "node_modules/@firebase/app": { - "version": "0.7.22", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.22.tgz", - "integrity": "sha512-v3AXSCwAvZyIFzOGgPAYtzjltm1M9R4U4yqsIBPf5B4ryaT1EGK+3ETZUOckNl5y2YwdKRJVPDDore+B2xg0Ug==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "version": "0.9.13", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.13.tgz", + "integrity": "sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", "tslib": "^2.1.0" } }, "node_modules/@firebase/app-check": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.7.tgz", - "integrity": "sha512-ByfjzbWCg+f42TeS++70pmEmYBtouJbHem/yH0vgF8+E90LeZugMx18oZxa/+4GVJRmDrMyhJHzWYQlqsG4q2Q==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.0.tgz", + "integrity": "sha512-dRDnhkcaC2FspMiRK/Vbp+PfsOAEP6ZElGm9iGFJ9fDqHoPs0HOPn7dwpJ51lCFi1+2/7n5pRPGhqF/F03I97g==", "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -677,15 +671,15 @@ } }, "node_modules/@firebase/app-check-compat": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.7.tgz", - "integrity": "sha512-RK3JViHfaIfwLLWETJWY7STYsegXJXK1GACu06tv2WLEJGMXbvWftxpVi7VMSVjgCFDRDnUEhim5Exn4Z73sOg==", - "dependencies": { - "@firebase/app-check": "0.5.7", - "@firebase/app-check-types": "0.4.0", - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.7.tgz", + "integrity": "sha512-cW682AxsyP1G+Z0/P7pO/WT2CzYlNxoNe5QejVarW2o5ZxeWSSPAiVEwpEpQR/bUlUmdeWThYTMvBWaopdBsqw==", + "dependencies": { + "@firebase/app-check": "0.8.0", + "@firebase/app-check-types": "0.5.0", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -693,42 +687,41 @@ } }, "node_modules/@firebase/app-check-interop-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.1.0.tgz", - "integrity": "sha512-uZfn9s4uuRsaX5Lwx+gFP3B6YsyOKUE+Rqa6z9ojT4VSRAsZFko9FRn6OxQUA1z5t5d08fY4pf+/+Dkd5wbdbA==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" }, "node_modules/@firebase/app-check-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.4.0.tgz", - "integrity": "sha512-SsWafqMABIOu7zLgWbmwvHGOeQQVQlwm42kwwubsmfLmL4Sf5uGpBfDhQ0CAkpi7bkJ/NwNFKafNDL9prRNP0Q==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.0.tgz", + "integrity": "sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==" }, "node_modules/@firebase/app-compat": { - "version": "0.1.23", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.23.tgz", - "integrity": "sha512-c0QOhU2UVxZ7N5++nLQgKZ899ZC8+/ESa8VCzsQDwBw1T3MFAD1cG40KhB+CGtp/uYk/w6Jtk8k0xyZu6O2LOg==", - "dependencies": { - "@firebase/app": "0.7.22", - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.13.tgz", + "integrity": "sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==", + "dependencies": { + "@firebase/app": "0.9.13", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" } }, "node_modules/@firebase/app-types": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", - "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" }, "node_modules/@firebase/auth": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.12.tgz", - "integrity": "sha512-39/eJBmq5Ne+HoCJuQXlhaOH2e8qySxYUa5Z25mhcam8nmAMrBh7Ph1yZjUeSfLsSJiSXANMHK5dnVE+1TROXw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.23.2.tgz", + "integrity": "sha512-dM9iJ0R6tI1JczuGSxXmQbXAgtYie0K4WvKcuyuSTCu9V8eEDiz4tfa1sO3txsfvwg7nOY3AjoCyMYEdqZ8hdg==", "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "node-fetch": "2.6.7", - "selenium-webdriver": "4.0.0-rc-1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -736,98 +729,166 @@ } }, "node_modules/@firebase/auth-compat": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.12.tgz", - "integrity": "sha512-LKeKylktRj03xgW5ilSOW1c4AsMig15ogf5hDKa820t6Bp6MNabj8yq2TV0/Q4SP4Ox/yrTISJGVvk+TJuBecQ==", - "dependencies": { - "@firebase/auth": "0.19.12", - "@firebase/auth-types": "0.11.0", - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.4.2.tgz", + "integrity": "sha512-Q30e77DWXFmXEt5dg5JbqEDpjw9y3/PcP9LslDPR7fARmAOTIY9MM6HXzm9KC+dlrKH/+p6l8g9ifJiam9mc4A==", + "dependencies": { + "@firebase/auth": "0.23.2", + "@firebase/auth-types": "0.12.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", "node-fetch": "2.6.7", - "selenium-webdriver": "^4.0.0-beta.2", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, - "node_modules/@firebase/auth-interop-types": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", - "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", + "node_modules/@firebase/auth-compat/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@firebase/auth-compat/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@firebase/auth-compat/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" + }, "node_modules/@firebase/auth-types": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.11.0.tgz", - "integrity": "sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", + "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, + "node_modules/@firebase/auth/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/auth/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@firebase/auth/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@firebase/auth/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/@firebase/component": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.13.tgz", - "integrity": "sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", + "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", "dependencies": { - "@firebase/util": "1.5.2", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" } }, "node_modules/@firebase/database": { - "version": "0.12.8", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.8.tgz", - "integrity": "sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA==", - "dependencies": { - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.14.4.tgz", + "integrity": "sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "faye-websocket": "0.11.4", "tslib": "^2.1.0" } }, "node_modules/@firebase/database-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.8.tgz", - "integrity": "sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/database": "0.12.8", - "@firebase/database-types": "0.9.7", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz", + "integrity": "sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/database": "0.14.4", + "@firebase/database-types": "0.10.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" } }, "node_modules/@firebase/database-types": { - "version": "0.9.7", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.7.tgz", - "integrity": "sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz", + "integrity": "sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==", "dependencies": { - "@firebase/app-types": "0.7.0", - "@firebase/util": "1.5.2" + "@firebase/app-types": "0.9.0", + "@firebase/util": "1.9.3" } }, "node_modules/@firebase/firestore": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.4.8.tgz", - "integrity": "sha512-qjrI22TrqSGsOVBkYpRcpY48eSFj+hvleWEaFn3bDxy+QNUiZS08cicSlBOxdosKi5LRMQVGyHKcqHExup02+A==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "@firebase/webchannel-wrapper": "0.6.1", - "@grpc/grpc-js": "^1.3.2", - "@grpc/proto-loader": "^0.6.0", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.13.0.tgz", + "integrity": "sha512-NwcnU+madJXQ4fbLkGx1bWvL612IJN/qO6bZ6dlPmyf7QRyu5azUosijdAN675r+bOOJxMtP1Bv981bHBXAbUg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "@firebase/webchannel-wrapper": "0.10.1", + "@grpc/grpc-js": "~1.7.0", + "@grpc/proto-loader": "^0.6.13", "node-fetch": "2.6.7", "tslib": "^2.1.0" }, @@ -839,14 +900,14 @@ } }, "node_modules/@firebase/firestore-compat": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.17.tgz", - "integrity": "sha512-hTLgq2WXUE6bb3/IqYlwY0Q6FdbZB2JwDoZHexIQmK69XuuK3j+JbE/NixV3mBo232tNSU+QeamfbAd6A1Agfw==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/firestore": "3.4.8", - "@firebase/firestore-types": "2.5.0", - "@firebase/util": "1.5.2", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.12.tgz", + "integrity": "sha512-mazuNGAx5Kt9Nph0pm6ULJFp/+j7GSsx+Ncw1GrnKl+ft1CQ4q2LcUssXnjqkX2Ry0fNGqUzC1mfIUrk9bYtjQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/firestore": "3.13.0", + "@firebase/firestore-types": "2.5.1", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -854,24 +915,62 @@ } }, "node_modules/@firebase/firestore-types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.5.0.tgz", - "integrity": "sha512-I6c2m1zUhZ5SH0cWPmINabDyH5w0PPFHk2UHsjBpKdZllzJZ2TwTkXbDtpHUZNmnc/zAa0WNMNMvcvbb/xJLKA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.5.1.tgz", + "integrity": "sha512-xG0CA6EMfYo8YeUxC8FeDzf6W3FX1cLlcAGBYV6Cku12sZRI81oWcu61RSKM66K6kUENP+78Qm8mvroBcm1whw==", "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, + "node_modules/@firebase/firestore/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/firestore/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@firebase/firestore/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@firebase/firestore/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/@firebase/functions": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.8.0.tgz", - "integrity": "sha512-QewfP/QY4jifjqLV6xrAnksZz+BwJe4XfMNoohhz7etu403yu+ZxAatev7brq1XtgvHePwa+vpQ8ppSeX6TIpA==", - "dependencies": { - "@firebase/app-check-interop-types": "0.1.0", - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.13", - "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.5.2", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.10.0.tgz", + "integrity": "sha512-2U+fMNxTYhtwSpkkR6WbBcuNMOVaI7MaH3cZ6UAeNfj7AgEwHwMIFLPpC13YNZhno219F0lfxzTAA0N62ndWzA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", "node-fetch": "2.6.7", "tslib": "^2.1.0" }, @@ -880,14 +979,14 @@ } }, "node_modules/@firebase/functions-compat": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.2.0.tgz", - "integrity": "sha512-jbrjTNgWRgcRegZlA3cQV1NLhvMBIlG0JFc8o34OhSvFAurraOX0uPBOCHruR8Pk+lJaP61Gqk3eeRhenVdX5w==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/functions": "0.8.0", - "@firebase/functions-types": "0.5.0", - "@firebase/util": "1.5.2", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.5.tgz", + "integrity": "sha512-uD4jwgwVqdWf6uc3NRKF8cSZ0JwGqSlyhPgackyUPe+GAtnERpS4+Vr66g0b3Gge0ezG4iyHo/EXW/Hjx7QhHw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/functions": "0.10.0", + "@firebase/functions-types": "0.6.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -895,40 +994,108 @@ } }, "node_modules/@firebase/functions-types": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.5.0.tgz", - "integrity": "sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA==" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.0.tgz", + "integrity": "sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==" + }, + "node_modules/@firebase/functions/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/functions/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@firebase/functions/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@firebase/functions/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } }, "node_modules/@firebase/installations": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.8.tgz", - "integrity": "sha512-u/lAOVhgYFg1e38rNrVzFrWxdKzTOIromx574Hi2AccFA230hSlXFY7pRaCpgs11VDzmpt4lhhOrQOX7886cKw==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.4.tgz", + "integrity": "sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==", "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "idb": "7.0.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.4.tgz", + "integrity": "sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/installations-types": "0.5.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.0.tgz", + "integrity": "sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/installations/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + }, "node_modules/@firebase/logger": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.2.tgz", - "integrity": "sha512-lzLrcJp9QBWpo40OcOM9B8QEtBw2Fk1zOZQdvv+rWS6gKmhQBCEMc4SMABQfWdjsylBcDfniD1Q+fUX1dcBTXA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", + "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@firebase/messaging": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.12.tgz", - "integrity": "sha512-qfLW7SZRZVKscI1GSyWc3WPtjAUDUk3gcEfPkdz9fzzQwj98V8xF++g4wL+9cuEuRzYf8ki2kCN/aqKRYUrxag==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.5.2", + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.4.tgz", + "integrity": "sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "idb": "7.0.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -936,13 +1103,13 @@ } }, "node_modules/@firebase/messaging-compat": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.12.tgz", - "integrity": "sha512-Cfv4ZQaxiMx4DcpDkFX1yKHFGQtnyMA6pcLplcC3uHkSVCyNRW6pFYSoO0/Uae03ixxIYNwle1ZVaVUZ2L5ddA==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.4.tgz", + "integrity": "sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==", "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/messaging": "0.9.12", - "@firebase/util": "1.5.2", + "@firebase/component": "0.6.4", + "@firebase/messaging": "0.12.4", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -950,19 +1117,24 @@ } }, "node_modules/@firebase/messaging-interop-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.1.0.tgz", - "integrity": "sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ==" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz", + "integrity": "sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==" + }, + "node_modules/@firebase/messaging/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" }, "node_modules/@firebase/performance": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.8.tgz", - "integrity": "sha512-IN5MWdGRn0jglSdv1UHEDMklm1SOfF1IZ1pGNxVyO5CpF3a08I54I60fuwEfMUcsU6OAfzMl3zI+bnW5IgKdPg==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.4.tgz", + "integrity": "sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -970,15 +1142,15 @@ } }, "node_modules/@firebase/performance-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.8.tgz", - "integrity": "sha512-lMLKFcOB99+tb6dVHJlJ8s19JFjxqpAqPGXCG8evTODPUW3BluBbfG4YS7JRESVA7wc/6kkuQIOx9q7l+bBZtQ==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/performance": "0.5.8", - "@firebase/performance-types": "0.1.0", - "@firebase/util": "1.5.2", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.4.tgz", + "integrity": "sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/performance": "0.6.4", + "@firebase/performance-types": "0.2.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -986,29 +1158,19 @@ } }, "node_modules/@firebase/performance-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.1.0.tgz", - "integrity": "sha512-6p1HxrH0mpx+622Ql6fcxFxfkYSBpE3LSuwM7iTtYU2nw91Hj6THC8Bc8z4nboIq7WvgsT/kOTYVVZzCSlXl8w==" - }, - "node_modules/@firebase/polyfill": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.36.tgz", - "integrity": "sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg==", - "dependencies": { - "core-js": "3.6.5", - "promise-polyfill": "8.1.3", - "whatwg-fetch": "2.0.4" - } + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.0.tgz", + "integrity": "sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==" }, "node_modules/@firebase/remote-config": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.7.tgz", - "integrity": "sha512-gQaGzQCBOkS35b/aXC5Y9/zsPenqs6+axnChYYyfU7CqMG5FGfNbVi2rezYwB4G3+fH4rGO1s6xqcI535Fvy/A==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.4.tgz", + "integrity": "sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==", "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1016,15 +1178,15 @@ } }, "node_modules/@firebase/remote-config-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.8.tgz", - "integrity": "sha512-lU9t7PMVpgE6q1vG8AuFenFhfUnx0H+eeiIQTi4dtuLDMx9BsI14c9VuiVjRIi7xC2DCDRNQCRL1kRD8bzgJNg==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/remote-config": "0.3.7", - "@firebase/remote-config-types": "0.2.0", - "@firebase/util": "1.5.2", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz", + "integrity": "sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-types": "0.3.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1032,17 +1194,17 @@ } }, "node_modules/@firebase/remote-config-types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.2.0.tgz", - "integrity": "sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz", + "integrity": "sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==" }, "node_modules/@firebase/storage": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.9.5.tgz", - "integrity": "sha512-+nCDNIT2pNovlHnLOQPofn8jdOyJ4akUWPGn4ydAoFrfVt1/lINx5Qe+jS3/tKLROfYabqBYxfFUjHQKZBYwvg==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.11.2.tgz", + "integrity": "sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==", "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", "node-fetch": "2.6.7", "tslib": "^2.1.0" }, @@ -1051,14 +1213,14 @@ } }, "node_modules/@firebase/storage-compat": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.13.tgz", - "integrity": "sha512-MdubKh+xe3Xpi34WaXBKtim8H2aauO5sqqmATTc2WgSmSAqTmNSjQfNqIdf139Mp9ZCnpZAxiwiwzQtfckLYWg==", - "dependencies": { - "@firebase/component": "0.5.13", - "@firebase/storage": "0.9.5", - "@firebase/storage-types": "0.6.0", - "@firebase/util": "1.5.2", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.2.tgz", + "integrity": "sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-types": "0.8.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1066,49 +1228,170 @@ } }, "node_modules/@firebase/storage-types": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.6.0.tgz", - "integrity": "sha512-1LpWhcCb1ftpkP/akhzjzeFxgVefs6eMD2QeKiJJUGH1qOiows2w5o0sKCUSQrvrRQS1lz3SFGvNR1Ck/gqxeA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.0.tgz", + "integrity": "sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==", "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, + "node_modules/@firebase/storage/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/storage/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@firebase/storage/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@firebase/storage/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/@firebase/util": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.2.tgz", - "integrity": "sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", + "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@firebase/webchannel-wrapper": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.6.1.tgz", - "integrity": "sha512-9FqhNjKQWpQ3fGnSOCovHOm+yhhiorKEqYLAfd525jWavunDJcx8rOW6i6ozAh+FbwcYMkL7b+3j4UR/30MpoQ==" + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.1.tgz", + "integrity": "sha512-Dq5rYfEpdeel0bLVN+nfD1VWmzCkK+pJbSjIawGE+RY4+NIJqhbUDDQjvV0NUK84fMfwxvtFoCtEe70HfZjFcw==" }, "node_modules/@grpc/grpc-js": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz", - "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", + "integrity": "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==", "dependencies": { - "@grpc/proto-loader": "^0.6.4", + "@grpc/proto-loader": "^0.7.0", "@types/node": ">=12.12.47" }, "engines": { "node": "^8.13.0 || >=10.10.0" } }, - "node_modules/@grpc/proto-loader": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.11.tgz", - "integrity": "sha512-MRiPjTjNgKxMupQ0M8mM9Mcljb2aZvE3Y/oEv+dacozIs2TwTdiPbvfkZpMeghfjGtoDJhDjyCtmFzJcjdDTUQ==", + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz", + "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==", "dependencies": { - "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^4.0.0 || ^5.2.0", - "protobufjs": "^6.10.0", - "yargs": "^16.2.0" + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/grpc-js/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/grpc-js/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/@grpc/grpc-js/node_modules/protobufjs": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/grpc-js/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.13.tgz", + "integrity": "sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==", + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.11.3", + "yargs": "^16.2.0" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" @@ -1382,40 +1665,63 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", - "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "dependencies": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", "make-dir": "^3.1.0", - "node-fetch": "^2.6.5", + "node-fetch": "^2.6.7", "nopt": "^5.0.0", "npmlog": "^5.0.1", "rimraf": "^3.0.2", @@ -1426,20 +1732,6 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@peapodtech/firebasedeviceflow": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@peapodtech/firebasedeviceflow/-/firebasedeviceflow-0.2.0.tgz", @@ -1457,7 +1749,7 @@ "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", @@ -1472,12 +1764,12 @@ "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -1486,27 +1778,27 @@ "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@serialport/binding-mock": { "version": "10.2.2", @@ -1521,15 +1813,15 @@ } }, "node_modules/@serialport/bindings-cpp": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.7.0.tgz", - "integrity": "sha512-Xx1wA2UCG2loS32hxNvWJI4smCzGKhWqE85//fLRzHoGgE1lSLe3Nk7W40/ebrlGFHWRbQZmeaIF4chb2XLliA==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.8.0.tgz", + "integrity": "sha512-OMQNJz5kJblbmZN5UgJXLwi2XNtVLxSKmq5VyWuXQVsUIJD4l9UGHnLPqM5LD9u3HPZgDI5w7iYN7gxkQNZJUw==", "hasInstallScript": true, "dependencies": { - "@serialport/bindings-interface": "1.2.1", + "@serialport/bindings-interface": "1.2.2", "@serialport/parser-readline": "^10.2.1", "debug": "^4.3.2", - "node-addon-api": "^4.3.0", + "node-addon-api": "^5.0.0", "node-gyp-build": "^4.3.0" }, "engines": { @@ -1540,9 +1832,9 @@ } }, "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", - "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.5.0.tgz", + "integrity": "sha512-/uR/yT3jmrcwnl2FJU/2ySvwgo5+XpksDUR4NF/nwTS5i3CcuKS+FKi/tLzy1k8F+rCx5JzpiK+koqPqOUWArA==", "engines": { "node": ">=12.0.0" }, @@ -1551,11 +1843,11 @@ } }, "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", - "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.5.0.tgz", + "integrity": "sha512-0aXJknodcl94W9zSjvU+sLdXiyEG2rqjQmvBWZCr8wJZjWEtv3RgrnYiWq4i2OTOyC8C/oPK8ZjpBjQptRsoJQ==", "dependencies": { - "@serialport/parser-delimiter": "10.3.0" + "@serialport/parser-delimiter": "10.5.0" }, "engines": { "node": ">=12.0.0" @@ -1565,17 +1857,17 @@ } }, "node_modules/@serialport/bindings-interface": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.1.tgz", - "integrity": "sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", + "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", "engines": { "node": "^12.22 || ^14.13 || >=16" } }, "node_modules/@serialport/parser-byte-length": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.3.0.tgz", - "integrity": "sha512-pJ/VoFemzKRRNDHLhFfPThwP40QrGaEnm9TtwL7o2GihEPwzBg3T0bN13ew5TpbbUYZdMpUtpm3CGfl6av9rUQ==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.5.0.tgz", + "integrity": "sha512-eHhr4lHKboq1OagyaXAqkemQ1XyoqbLQC8XJbvccm95o476TmEdW5d7AElwZV28kWprPW68ZXdGF2VXCkJgS2w==", "engines": { "node": ">=12.0.0" }, @@ -1584,9 +1876,9 @@ } }, "node_modules/@serialport/parser-cctalk": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.3.0.tgz", - "integrity": "sha512-8ujmk8EvVbDPrNF4mM33bWvUYJOZ0wXbY3WCRazHRWvyCdL0VO0DQvW81ZqgoTpiDQZm5r8wQu9rmuemahF6vQ==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.5.0.tgz", + "integrity": "sha512-Iwsdr03xmCKAiibLSr7b3w6ZUTBNiS+PwbDQXdKU/clutXjuoex83XvsOtYVcNZmwJlVNhAUbkG+FJzWwIa4DA==", "engines": { "node": ">=12.0.0" }, @@ -1607,9 +1899,9 @@ } }, "node_modules/@serialport/parser-inter-byte-timeout": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.3.0.tgz", - "integrity": "sha512-wKP0QK85NHgvT6BBB1qBfKBBU4pf8kespNXAZBUYmFT+P4n8r8IZE2mqigCD+AiZcfWNQoAizwOsT/Jx/qeVig==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.5.0.tgz", + "integrity": "sha512-WPvVlSx98HmmUF9jjK6y9mMp3Wnv6JQA0cUxLeZBgS74TibOuYG3fuUxUWGJALgAXotOYMxfXSezJ/vSnQrkhQ==", "engines": { "node": ">=12.0.0" }, @@ -1618,9 +1910,9 @@ } }, "node_modules/@serialport/parser-packet-length": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.3.0.tgz", - "integrity": "sha512-bj0cWzt8YSQj/E5fRQVYdi4TsfTlZQrXlXrUwjyTsCONv8IPOHzsz+yY0fw5SEMiJtaLyqvPkCHLsttOd/zFsg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.5.0.tgz", + "integrity": "sha512-jkpC/8w4/gUBRa2Teyn7URv1D7T//0lGj27/4u9AojpDVXsR6dtdcTG7b7dNirXDlOrSLvvN7aS5/GNaRlEByw==", "engines": { "node": ">=8.6.0" } @@ -1641,9 +1933,9 @@ } }, "node_modules/@serialport/parser-ready": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.3.0.tgz", - "integrity": "sha512-1owywJ4p592dJyVrEJZPIh6pUZ3/y/LN6kGTDH2wxdewRUITo/sGvDy0er5i2+dJD3yuowiAz0dOHSdz8tevJA==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.5.0.tgz", + "integrity": "sha512-QIf65LTvUoxqWWHBpgYOL+soldLIIyD1bwuWelukem2yDZVWwEjR288cLQ558BgYxH4U+jLAQahhqoyN1I7BaA==", "engines": { "node": ">=12.0.0" }, @@ -1652,9 +1944,9 @@ } }, "node_modules/@serialport/parser-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.3.0.tgz", - "integrity": "sha512-tIogTs7CvTH+UUFnsvE7i33MSISyTPTGPWlglWYH2/5coipXY503jlaYS1YGe818wWNcSx6YAjMZRdhTWwM39w==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.5.0.tgz", + "integrity": "sha512-9jnr9+PCxRoLjtGs7uxwsFqvho+rxuJlW6ZWSB7oqfzshEZWXtTJgJRgac/RuLft4hRlrmRz5XU40i3uoL4HKw==", "engines": { "node": ">=12.0.0" }, @@ -1663,9 +1955,9 @@ } }, "node_modules/@serialport/parser-slip-encoder": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.3.0.tgz", - "integrity": "sha512-JI0ILF5sylWn8f0MuMzHFBix/iMUTa79/Z95KaPZYnVaEdA7h7hh/o21Jmon/26P3RJwL1SNJCjZ81zfan+LtQ==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.5.0.tgz", + "integrity": "sha512-wP8m+uXQdkWSa//3n+VvfjLthlabwd9NiG6kegf0fYweLWio8j4pJRL7t9eTh2Lbc7zdxuO0r8ducFzO0m8CQw==", "engines": { "node": ">=12.0.0" }, @@ -1674,9 +1966,9 @@ } }, "node_modules/@serialport/parser-spacepacket": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.3.0.tgz", - "integrity": "sha512-PDF73ClEPsClD1FEJZHNuBevDKsJCkqy/XD5+S5eA6+tY5D4HLrVgSWsg+3qqB6+dlpwf2CzHe+uO8D3teuKHA==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.5.0.tgz", + "integrity": "sha512-BEZ/HAEMwOd8xfuJSeI/823IR/jtnThovh7ils90rXD4DPL1ZmrP4abAIEktwe42RobZjIPfA4PaVfyO0Fjfhg==", "engines": { "node": ">=12.0.0" }, @@ -1685,11 +1977,11 @@ } }, "node_modules/@serialport/stream": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.3.0.tgz", - "integrity": "sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.5.0.tgz", + "integrity": "sha512-gbcUdvq9Kyv2HsnywS7QjnEB28g+6OGB5Z8TLP7X+UPpoMIWoUsoQIq5Kt0ZTgMoWn3JGM2lqwTsSHF+1qhniA==", "dependencies": { - "@serialport/bindings-interface": "1.2.1", + "@serialport/bindings-interface": "1.2.2", "debug": "^4.3.2" }, "engines": { @@ -1700,9 +1992,9 @@ } }, "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" @@ -1727,31 +2019,31 @@ } }, "node_modules/@types/babel__core": { - "version": "7.1.18", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", - "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -1759,27 +2051,27 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/blessed": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.19.tgz", - "integrity": "sha512-r4qnseYWBsi/kxo5AAlCS22EnTXFbGpnvuXUubJikVeRnYB3e5HwV3NtcwJ0Sk5KOGaLvo9Rtwb8hzxfbqbQPg==", + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.25.tgz", + "integrity": "sha512-kQsjBgtsbJLmG6CJA+Z6Nujj+tq1fcSE3UIowbDvzQI4wWmoTV7djUDhSo5lDjgwpIN0oRvks0SA5mMdKE5eFg==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -1795,33 +2087,33 @@ } }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { - "version": "27.5.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.0.tgz", - "integrity": "sha512-9RBFx7r4k+msyj/arpfaa0WOOEcaAZNmN+j80KFbFCoSqCJGHTz7YMAMGQW9Xmqm5w6l5c25vbSjMwlikJi5+g==", + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", "dev": true, "dependencies": { "jest-matcher-utils": "^27.0.0", @@ -1829,9 +2121,9 @@ } }, "node_modules/@types/jsonwebtoken": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.5.tgz", - "integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", + "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", "dev": true, "dependencies": { "@types/node": "*" @@ -1843,74 +2135,75 @@ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "node_modules/@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==" + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" }, "node_modules/@types/pi-camera": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@types/pi-camera/-/pi-camera-1.5.3.tgz", - "integrity": "sha512-9CGFEc7Cr0QDWdhqL+Bjb8+0xcYX8L9iv7YlQw+F5ntBh9GwZImDbaeJsFZg76TLGsTMLDTrowfHA7WWMX0csQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/pi-camera/-/pi-camera-1.5.5.tgz", + "integrity": "sha512-H6DI1FKHkKwNwvSELUTxQQ+OMMwjSZ1w6UaG/sQ3dcXFwmJXrQvh+h7AQxXZgcu1NYy/4X4vej9wtHB7sH6yGQ==", "dev": true }, "node_modules/@types/prettier": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", - "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "node_modules/@types/project-name-generator": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/project-name-generator/-/project-name-generator-2.1.1.tgz", - "integrity": "sha512-VLd5FEVTJs8hNa/WF4pZRcFvv0OAcIGeTyki4RDcaID0TUhTc5/Xe/btYM2XIHwVCb67ila8wUJYKJNa5dVABw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@types/project-name-generator/-/project-name-generator-2.1.3.tgz", + "integrity": "sha512-uomLFTmq6/eyLlGLTw1KPcdAMes8WPqcPtoZIA3FaJ6fJR3snXqY3TmIzsKNa2b4WV5ZaN5brh2xgGkWKLGPoQ==", "dev": true }, "node_modules/@types/serialport": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@types/serialport/-/serialport-8.0.2.tgz", - "integrity": "sha512-z4b1I8/vdZE3upgCcAL9VAWlVVFUVn5uo3faAHavkVfK/Hb1LUxKwp9YCtA5AZqEUCWoSWl20SRTOvAI/5fQWQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@types/serialport/-/serialport-8.0.5.tgz", + "integrity": "sha512-Lw082WIy46fYLnOzyf+8QN/vZaR3d9ol9WNyEGHsKJJ1pmZogFbloHGbnXyNcxfV9aTbgviWU8jktrIjRheYFQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, "node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", "dev": true }, "node_modules/abbrev": { @@ -1919,9 +2212,9 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2009,9 +2302,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -2050,7 +2343,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "node_modules/axios": { @@ -2234,9 +2527,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -2246,14 +2539,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -2309,7 +2605,7 @@ "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "node_modules/buffer-from": { "version": "1.1.2", @@ -2335,14 +2631,24 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001317", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", - "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", + "version": "1.0.30001606", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001606.tgz", + "integrity": "sha512-LPbwnW4vfpJId225pwjZJOgX1m9sGfbw/RKJvw/t0QhYOOaTXHvkjVGFGPpvwEzufrjvTlsULnVTxdy4/6cqkg==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, "node_modules/chalk": { "version": "4.1.2", @@ -2382,15 +2688,24 @@ } }, "node_modules/ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "node_modules/cli-cursor": { @@ -2405,9 +2720,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "engines": { "node": ">=6" }, @@ -2436,7 +2751,7 @@ "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "engines": { "node": ">=0.8" } @@ -2444,7 +2759,7 @@ "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "engines": { "iojs": ">= 1.0.0", @@ -2452,9 +2767,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, "node_modules/color-convert": { @@ -2502,10 +2817,18 @@ "minimist": "^1.1.0" } }, + "node_modules/commist/node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { "version": "2.0.0", @@ -2524,38 +2847,14 @@ "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "node_modules/core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2608,45 +2907,10 @@ "node": ">=10" } }, - "node_modules/data-urls/node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/data-urls/node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -2660,44 +2924,41 @@ } }, "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "engines": { "node": ">=0.4.0" @@ -2706,17 +2967,14 @@ "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/detect-newline": { @@ -2738,19 +2996,26 @@ } }, "node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, "dependencies": { - "webidl-conversions": "^4.0.2" + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/domexception/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/dotenv": { "version": "10.0.0", @@ -2761,14 +3026,14 @@ } }, "node_modules/duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" + "stream-shift": "^1.0.2" } }, "node_modules/ecdsa-sig-formatter": { @@ -2780,9 +3045,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.86", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz", - "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==", + "version": "1.4.729", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz", + "integrity": "sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==", "dev": true }, "node_modules/emittery": { @@ -2811,13 +3076,13 @@ } }, "node_modules/epoll": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/epoll/-/epoll-4.0.1.tgz", - "integrity": "sha512-BgCq0nEsk+XI7y9qjrRtt9uXsyFEdvevvq42xl6t/hKZjxLSDZreD9rTZ0pU40V//c3Zzk2PZGuIsn8YJHSJ4g==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/epoll/-/epoll-4.0.2.tgz", + "integrity": "sha512-4KtBBSBYAfyta2/4TIFT4P7pZdDjU6UPvdzWcF4Fy6Q300V08Wdj/82ovAZBUDra6TTjsKsfy12Qxuwc3ob+Qw==", "hasInstallScript": true, "dependencies": { "bindings": "^1.5.0", - "nan": "^2.14.2" + "nan": "^2.17.0" }, "engines": { "node": ">=10.0.0" @@ -2833,9 +3098,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "engines": { "node": ">=6" } @@ -2843,21 +3108,20 @@ "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { "node": ">=0.8.0" } }, "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", @@ -2927,7 +3191,7 @@ "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, "engines": { "node": ">= 0.8.0" @@ -2967,12 +3231,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, "node_modules/faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -2985,9 +3243,9 @@ } }, "node_modules/fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "dependencies": { "bser": "2.1.1" @@ -3038,42 +3296,42 @@ } }, "node_modules/firebase": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.7.0.tgz", - "integrity": "sha512-IsBZ11b8MbB2YyulNii+vhNMpqmfNwI9iGWG9GVgn+Pzdi+Ta+eaGI3+VbeSZoqC/nkKOzSR89cpz7kXd4+mgg==", - "dependencies": { - "@firebase/analytics": "0.7.8", - "@firebase/analytics-compat": "0.1.9", - "@firebase/app": "0.7.22", - "@firebase/app-check": "0.5.7", - "@firebase/app-check-compat": "0.2.7", - "@firebase/app-compat": "0.1.23", - "@firebase/app-types": "0.7.0", - "@firebase/auth": "0.19.12", - "@firebase/auth-compat": "0.2.12", - "@firebase/database": "0.12.8", - "@firebase/database-compat": "0.1.8", - "@firebase/firestore": "3.4.8", - "@firebase/firestore-compat": "0.1.17", - "@firebase/functions": "0.8.0", - "@firebase/functions-compat": "0.2.0", - "@firebase/installations": "0.5.8", - "@firebase/messaging": "0.9.12", - "@firebase/messaging-compat": "0.1.12", - "@firebase/performance": "0.5.8", - "@firebase/performance-compat": "0.1.8", - "@firebase/polyfill": "0.3.36", - "@firebase/remote-config": "0.3.7", - "@firebase/remote-config-compat": "0.1.8", - "@firebase/storage": "0.9.5", - "@firebase/storage-compat": "0.1.13", - "@firebase/util": "1.5.2" + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.23.0.tgz", + "integrity": "sha512-/4lUVY0lUvBDIaeY1q6dUYhS8Sd18Qb9CgWkPZICUo9IXpJNCEagfNZXBBFCkMTTN5L5gx2Hjr27y21a9NzUcA==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-compat": "0.2.6", + "@firebase/app": "0.9.13", + "@firebase/app-check": "0.8.0", + "@firebase/app-check-compat": "0.3.7", + "@firebase/app-compat": "0.2.13", + "@firebase/app-types": "0.9.0", + "@firebase/auth": "0.23.2", + "@firebase/auth-compat": "0.4.2", + "@firebase/database": "0.14.4", + "@firebase/database-compat": "0.3.4", + "@firebase/firestore": "3.13.0", + "@firebase/firestore-compat": "0.3.12", + "@firebase/functions": "0.10.0", + "@firebase/functions-compat": "0.3.5", + "@firebase/installations": "0.6.4", + "@firebase/installations-compat": "0.2.4", + "@firebase/messaging": "0.12.4", + "@firebase/messaging-compat": "0.2.4", + "@firebase/performance": "0.6.4", + "@firebase/performance-compat": "0.2.4", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-compat": "0.2.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-compat": "0.3.2", + "@firebase/util": "1.9.3" } }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -3114,15 +3372,31 @@ "node": ">= 8" } }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3134,10 +3408,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gauge": { "version": "3.0.2", @@ -3197,14 +3474,14 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -3225,23 +3502,11 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3253,7 +3518,19 @@ "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/help-me": { "version": "3.0.0", @@ -3283,9 +3560,9 @@ "dev": true }, "node_modules/http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==" + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" }, "node_modules/http-proxy-agent": { "version": "4.0.1", @@ -3302,9 +3579,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dependencies": { "agent-base": "6", "debug": "4" @@ -3333,6 +3610,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3352,11 +3634,6 @@ } ] }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" - }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -3379,7 +3656,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -3388,7 +3665,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3425,16 +3702,16 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3495,7 +3772,7 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "node_modules/is-unicode-supported": { @@ -3509,30 +3786,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", @@ -3546,26 +3818,41 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/istanbul-lib-source-maps": { @@ -3583,9 +3870,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -3944,9 +4231,9 @@ } }, "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "engines": { "node": ">=6" @@ -4115,21 +4402,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -4176,15 +4448,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-validate/node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/jest-watcher": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", @@ -4232,6 +4495,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4297,62 +4569,6 @@ } } }, - "node_modules/jsdom/node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdom/node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdom/node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdom/node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -4372,13 +4588,10 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -4407,7 +4620,15 @@ "npm": ">=1.4.28" } }, - "node_modules/jsonwebtoken/node_modules/jwa": { + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", @@ -4417,7 +4638,7 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/jsonwebtoken/node_modules/jws": { + "node_modules/jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", @@ -4426,44 +4647,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/jszip": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz", - "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4474,32 +4657,12 @@ } }, "node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dependencies": { - "immediate": "~3.0.5" + "node": ">=6" } }, "node_modules/lines-and-columns": { @@ -4528,7 +4691,7 @@ "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -4538,43 +4701,43 @@ "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, "node_modules/log-symbols": { "version": "4.1.0", @@ -4592,19 +4755,17 @@ } }, "node_modules/long": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", - "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, "node_modules/make-dir": { @@ -4622,9 +4783,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -4651,13 +4812,13 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -4693,9 +4854,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4704,17 +4865,17 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "dependencies": { - "yallist": "^4.0.0" - }, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "engines": { "node": ">=8" } @@ -4731,6 +4892,22 @@ "node": ">= 8" } }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -4743,9 +4920,9 @@ } }, "node_modules/mqtt": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", - "integrity": "sha512-DJYjlXODVXtSDecN8jnNzi6ItX3+ufGsEs9OB3YV24HtkRrh7kpx8L5M1LuyF0KzaiGtWr2PzDcMGAY60KGOSA==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.8.tgz", + "integrity": "sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw==", "dependencies": { "commist": "^1.0.0", "concat-stream": "^2.0.0", @@ -4753,13 +4930,16 @@ "duplexify": "^4.1.1", "help-me": "^3.0.0", "inherits": "^2.0.3", + "lru-cache": "^6.0.0", "minimist": "^1.2.5", "mqtt-packet": "^6.8.0", + "number-allocator": "^1.0.9", "pump": "^3.0.0", "readable-stream": "^3.6.0", "reinterval": "^1.1.0", + "rfdc": "^1.3.0", "split2": "^3.1.0", - "ws": "^7.5.0", + "ws": "^7.5.5", "xtend": "^4.0.2" }, "bin": { @@ -4781,6 +4961,22 @@ "process-nextick-args": "^2.0.1" } }, + "node_modules/mqtt/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mqtt/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4792,25 +4988,25 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, "node_modules/nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -4826,10 +5022,29 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp-build": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", - "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -4839,13 +5054,13 @@ "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/nopt": { @@ -4894,16 +5109,25 @@ "set-blocking": "^2.0.0" } }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, "node_modules/nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } @@ -4911,7 +5135,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { "wrappy": "1" } @@ -4942,23 +5166,6 @@ "node": ">=10.0.0" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -4984,7 +5191,7 @@ "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "engines": { "node": ">=0.10.0" } @@ -5025,11 +5232,6 @@ "node": ">=6" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -5066,7 +5268,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "engines": { "node": ">=0.10.0" } @@ -5087,9 +5289,9 @@ "dev": true }, "node_modules/pi-camera": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.6.0.tgz", - "integrity": "sha512-WrnJ4R1X/5qA5/vXeou/Ty97c7urrnla03j5FhctF9nZy1lXnfYkPOsL+qDZAZcvg40cbkTE217WlHNnMMftaw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.7.0.tgz", + "integrity": "sha512-96LdjJeh/1sZKYdBugURUnhoTdIUu5u8oDbVW2EFtxiGpz6Vs4nATmQHY4jibopZNfDhH9He9j0iDmS6w0L2/Q==", "engines": { "node": ">=6.10.0" } @@ -5113,9 +5315,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -5133,15 +5335,6 @@ "node": ">=8" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -5173,11 +5366,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/promise-polyfill": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", - "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==" - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -5192,9 +5380,9 @@ } }, "node_modules/protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -5216,15 +5404,10 @@ "pbts": "bin/pbts" } }, - "node_modules/protobufjs/node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "node_modules/pump": { @@ -5237,14 +5420,20 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -5252,9 +5441,9 @@ "dev": true }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -5267,23 +5456,29 @@ "node_modules/reinterval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", - "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -5316,9 +5511,9 @@ } }, "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true, "engines": { "node": ">=10" @@ -5336,6 +5531,11 @@ "node": ">=8" } }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -5410,57 +5610,54 @@ "node": ">=10" } }, - "node_modules/selenium-webdriver": { - "version": "4.0.0-rc-1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-rc-1.tgz", - "integrity": "sha512-bcrwFPRax8fifRP60p7xkWDGSJJoMkPAzufMlk5K2NyLPht/YZzR2WcIk1+3gR8VOCLlst1P2PI+MXACaFzpIw==", + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { - "jszip": "^3.6.0", - "rimraf": "^3.0.2", - "tmp": "^0.2.1", - "ws": ">=7.4.6" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 10.15.0" + "node": ">=10" } }, - "node_modules/selenium-webdriver/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { - "rimraf": "^3.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8.17.0" + "node": ">=10" } }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/serialport": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.4.0.tgz", - "integrity": "sha512-PszPM5SnFMgSXom60PkKS2A9nMlNbHkuoyRBlzdSWw9rmgOn258+V0dYbWMrETJMM+TJV32vqBzjg5MmmUMwMw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.5.0.tgz", + "integrity": "sha512-7OYLDsu5i6bbv3lU81pGy076xe0JwpK6b49G6RjNvGibstUqQkI+I3/X491yBGtf4gaqUdOgoU1/5KZ/XxL4dw==", "dependencies": { "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "10.7.0", - "@serialport/parser-byte-length": "10.3.0", - "@serialport/parser-cctalk": "10.3.0", - "@serialport/parser-delimiter": "10.3.0", - "@serialport/parser-inter-byte-timeout": "10.3.0", - "@serialport/parser-packet-length": "10.3.0", - "@serialport/parser-readline": "10.3.0", - "@serialport/parser-ready": "10.3.0", - "@serialport/parser-regex": "10.3.0", - "@serialport/parser-slip-encoder": "10.3.0", - "@serialport/parser-spacepacket": "10.3.0", - "@serialport/stream": "10.3.0", + "@serialport/bindings-cpp": "10.8.0", + "@serialport/parser-byte-length": "10.5.0", + "@serialport/parser-cctalk": "10.5.0", + "@serialport/parser-delimiter": "10.5.0", + "@serialport/parser-inter-byte-timeout": "10.5.0", + "@serialport/parser-packet-length": "10.5.0", + "@serialport/parser-readline": "10.5.0", + "@serialport/parser-ready": "10.5.0", + "@serialport/parser-regex": "10.5.0", + "@serialport/parser-slip-encoder": "10.5.0", + "@serialport/parser-spacepacket": "10.5.0", + "@serialport/stream": "10.5.0", "debug": "^4.3.3" }, "engines": { @@ -5471,9 +5668,9 @@ } }, "node_modules/serialport/node_modules/@serialport/parser-delimiter": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", - "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.5.0.tgz", + "integrity": "sha512-/uR/yT3jmrcwnl2FJU/2ySvwgo5+XpksDUR4NF/nwTS5i3CcuKS+FKi/tLzy1k8F+rCx5JzpiK+koqPqOUWArA==", "engines": { "node": ">=12.0.0" }, @@ -5482,11 +5679,11 @@ } }, "node_modules/serialport/node_modules/@serialport/parser-readline": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", - "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.5.0.tgz", + "integrity": "sha512-0aXJknodcl94W9zSjvU+sLdXiyEG2rqjQmvBWZCr8wJZjWEtv3RgrnYiWq4i2OTOyC8C/oPK8ZjpBjQptRsoJQ==", "dependencies": { - "@serialport/parser-delimiter": "10.3.0" + "@serialport/parser-delimiter": "10.5.0" }, "engines": { "node": ">=12.0.0" @@ -5498,15 +5695,7 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "engines": { - "node": ">=0.10.0" - } + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -5530,9 +5719,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sisteransi": { "version": "1.0.5", @@ -5579,13 +5768,13 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" @@ -5604,9 +5793,9 @@ } }, "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" }, "node_modules/string_decoder": { "version": "1.3.0", @@ -5695,9 +5884,9 @@ } }, "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, "dependencies": { "has-flag": "^4.0.0", @@ -5726,21 +5915,26 @@ "dev": true }, "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">=10" } }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -5772,15 +5966,15 @@ } }, "node_modules/throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, "node_modules/tmp": { "version": "0.0.33", @@ -5802,7 +5996,7 @@ "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, "engines": { "node": ">=4" @@ -5821,28 +6015,36 @@ } }, "node_modules/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", - "universalify": "^0.1.2" + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { "node": ">=6" } }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "version": "27.1.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", + "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -5864,7 +6066,6 @@ "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", - "esbuild": "~0.14.0", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, @@ -5883,37 +6084,10 @@ } } }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/type-detect": { "version": "4.0.8", @@ -5938,7 +6112,7 @@ "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", @@ -5950,9 +6124,9 @@ } }, "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5962,18 +6136,58 @@ } }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, "engines": { "node": ">= 4.0.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { "version": "8.3.2", @@ -5998,9 +6212,9 @@ } }, "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, "engines": { "node": ">= 8" @@ -6010,6 +6224,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", "dev": true, "dependencies": { "browser-process-hrtime": "^1.0.0" @@ -6039,15 +6254,19 @@ "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dependencies": { "defaults": "^1.0.3" } }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } }, "node_modules/websocket-driver": { "version": "0.7.4", @@ -6079,11 +6298,6 @@ "iconv-lite": "0.4.24" } }, - "node_modules/whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - }, "node_modules/whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", @@ -6091,12 +6305,17 @@ "dev": true }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/which": { @@ -6122,15 +6341,6 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -6150,7 +6360,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -6182,10 +6392,26 @@ "domexception": "^1.0.1" } }, + "node_modules/wrtc/node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "deprecated": "Use your platform's native DOMException instead", + "optional": true, + "dependencies": { + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/wrtc/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "optional": true + }, "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "engines": { "node": ">=8.3.0" }, @@ -6231,9 +6457,10 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yargs": { "version": "16.2.0", @@ -6260,4798 +6487,5 @@ "node": ">=10" } } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.0" - } - }, - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "dev": true - }, - "@babel/core": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", - "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.7", - "@babel/parser": "^7.17.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true - }, - "@babel/helpers": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", - "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - } - }, - "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", - "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", - "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@firebase/analytics": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.8.tgz", - "integrity": "sha512-W38Zy/jf64LKpPi+mGNNETIjz4eq/YXBE0Uu2bzstqUwlhvFn1WlRBK4vzgtZMRaGW04CQp9FXYv6ZTRo/Xbyw==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/analytics-compat": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.9.tgz", - "integrity": "sha512-HYKMAZvfU589WVvK5XKY9Pl+axXFISabouAFw2VHpJm/TO1mAXAy0+eIjqQ3j8z3L1OEfCeOV/oY9eh8rpJZ5w==", - "requires": { - "@firebase/analytics": "0.7.8", - "@firebase/analytics-types": "0.7.0", - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/analytics-types": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.7.0.tgz", - "integrity": "sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ==" - }, - "@firebase/app": { - "version": "0.7.22", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.22.tgz", - "integrity": "sha512-v3AXSCwAvZyIFzOGgPAYtzjltm1M9R4U4yqsIBPf5B4ryaT1EGK+3ETZUOckNl5y2YwdKRJVPDDore+B2xg0Ug==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/app-check": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.5.7.tgz", - "integrity": "sha512-ByfjzbWCg+f42TeS++70pmEmYBtouJbHem/yH0vgF8+E90LeZugMx18oZxa/+4GVJRmDrMyhJHzWYQlqsG4q2Q==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/app-check-compat": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.2.7.tgz", - "integrity": "sha512-RK3JViHfaIfwLLWETJWY7STYsegXJXK1GACu06tv2WLEJGMXbvWftxpVi7VMSVjgCFDRDnUEhim5Exn4Z73sOg==", - "requires": { - "@firebase/app-check": "0.5.7", - "@firebase/app-check-types": "0.4.0", - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/app-check-interop-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.1.0.tgz", - "integrity": "sha512-uZfn9s4uuRsaX5Lwx+gFP3B6YsyOKUE+Rqa6z9ojT4VSRAsZFko9FRn6OxQUA1z5t5d08fY4pf+/+Dkd5wbdbA==" - }, - "@firebase/app-check-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.4.0.tgz", - "integrity": "sha512-SsWafqMABIOu7zLgWbmwvHGOeQQVQlwm42kwwubsmfLmL4Sf5uGpBfDhQ0CAkpi7bkJ/NwNFKafNDL9prRNP0Q==" - }, - "@firebase/app-compat": { - "version": "0.1.23", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.23.tgz", - "integrity": "sha512-c0QOhU2UVxZ7N5++nLQgKZ899ZC8+/ESa8VCzsQDwBw1T3MFAD1cG40KhB+CGtp/uYk/w6Jtk8k0xyZu6O2LOg==", - "requires": { - "@firebase/app": "0.7.22", - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/app-types": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", - "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" - }, - "@firebase/auth": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.12.tgz", - "integrity": "sha512-39/eJBmq5Ne+HoCJuQXlhaOH2e8qySxYUa5Z25mhcam8nmAMrBh7Ph1yZjUeSfLsSJiSXANMHK5dnVE+1TROXw==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "node-fetch": "2.6.7", - "selenium-webdriver": "4.0.0-rc-1", - "tslib": "^2.1.0" - } - }, - "@firebase/auth-compat": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.12.tgz", - "integrity": "sha512-LKeKylktRj03xgW5ilSOW1c4AsMig15ogf5hDKa820t6Bp6MNabj8yq2TV0/Q4SP4Ox/yrTISJGVvk+TJuBecQ==", - "requires": { - "@firebase/auth": "0.19.12", - "@firebase/auth-types": "0.11.0", - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", - "node-fetch": "2.6.7", - "selenium-webdriver": "^4.0.0-beta.2", - "tslib": "^2.1.0" - } - }, - "@firebase/auth-interop-types": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", - "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", - "requires": {} - }, - "@firebase/auth-types": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.11.0.tgz", - "integrity": "sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==", - "requires": {} - }, - "@firebase/component": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.13.tgz", - "integrity": "sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w==", - "requires": { - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/database": { - "version": "0.12.8", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.8.tgz", - "integrity": "sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA==", - "requires": { - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "faye-websocket": "0.11.4", - "tslib": "^2.1.0" - } - }, - "@firebase/database-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.8.tgz", - "integrity": "sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/database": "0.12.8", - "@firebase/database-types": "0.9.7", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/database-types": { - "version": "0.9.7", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.7.tgz", - "integrity": "sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw==", - "requires": { - "@firebase/app-types": "0.7.0", - "@firebase/util": "1.5.2" - } - }, - "@firebase/firestore": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.4.8.tgz", - "integrity": "sha512-qjrI22TrqSGsOVBkYpRcpY48eSFj+hvleWEaFn3bDxy+QNUiZS08cicSlBOxdosKi5LRMQVGyHKcqHExup02+A==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "@firebase/webchannel-wrapper": "0.6.1", - "@grpc/grpc-js": "^1.3.2", - "@grpc/proto-loader": "^0.6.0", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - } - }, - "@firebase/firestore-compat": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.17.tgz", - "integrity": "sha512-hTLgq2WXUE6bb3/IqYlwY0Q6FdbZB2JwDoZHexIQmK69XuuK3j+JbE/NixV3mBo232tNSU+QeamfbAd6A1Agfw==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/firestore": "3.4.8", - "@firebase/firestore-types": "2.5.0", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/firestore-types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.5.0.tgz", - "integrity": "sha512-I6c2m1zUhZ5SH0cWPmINabDyH5w0PPFHk2UHsjBpKdZllzJZ2TwTkXbDtpHUZNmnc/zAa0WNMNMvcvbb/xJLKA==", - "requires": {} - }, - "@firebase/functions": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.8.0.tgz", - "integrity": "sha512-QewfP/QY4jifjqLV6xrAnksZz+BwJe4XfMNoohhz7etu403yu+ZxAatev7brq1XtgvHePwa+vpQ8ppSeX6TIpA==", - "requires": { - "@firebase/app-check-interop-types": "0.1.0", - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.13", - "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.5.2", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - } - }, - "@firebase/functions-compat": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.2.0.tgz", - "integrity": "sha512-jbrjTNgWRgcRegZlA3cQV1NLhvMBIlG0JFc8o34OhSvFAurraOX0uPBOCHruR8Pk+lJaP61Gqk3eeRhenVdX5w==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/functions": "0.8.0", - "@firebase/functions-types": "0.5.0", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/functions-types": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.5.0.tgz", - "integrity": "sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA==" - }, - "@firebase/installations": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.8.tgz", - "integrity": "sha512-u/lAOVhgYFg1e38rNrVzFrWxdKzTOIromx574Hi2AccFA230hSlXFY7pRaCpgs11VDzmpt4lhhOrQOX7886cKw==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/logger": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.2.tgz", - "integrity": "sha512-lzLrcJp9QBWpo40OcOM9B8QEtBw2Fk1zOZQdvv+rWS6gKmhQBCEMc4SMABQfWdjsylBcDfniD1Q+fUX1dcBTXA==", - "requires": { - "tslib": "^2.1.0" - } - }, - "@firebase/messaging": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.12.tgz", - "integrity": "sha512-qfLW7SZRZVKscI1GSyWc3WPtjAUDUk3gcEfPkdz9fzzQwj98V8xF++g4wL+9cuEuRzYf8ki2kCN/aqKRYUrxag==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/messaging-interop-types": "0.1.0", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/messaging-compat": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.12.tgz", - "integrity": "sha512-Cfv4ZQaxiMx4DcpDkFX1yKHFGQtnyMA6pcLplcC3uHkSVCyNRW6pFYSoO0/Uae03ixxIYNwle1ZVaVUZ2L5ddA==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/messaging": "0.9.12", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/messaging-interop-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.1.0.tgz", - "integrity": "sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ==" - }, - "@firebase/performance": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.8.tgz", - "integrity": "sha512-IN5MWdGRn0jglSdv1UHEDMklm1SOfF1IZ1pGNxVyO5CpF3a08I54I60fuwEfMUcsU6OAfzMl3zI+bnW5IgKdPg==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/performance-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.8.tgz", - "integrity": "sha512-lMLKFcOB99+tb6dVHJlJ8s19JFjxqpAqPGXCG8evTODPUW3BluBbfG4YS7JRESVA7wc/6kkuQIOx9q7l+bBZtQ==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/performance": "0.5.8", - "@firebase/performance-types": "0.1.0", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/performance-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.1.0.tgz", - "integrity": "sha512-6p1HxrH0mpx+622Ql6fcxFxfkYSBpE3LSuwM7iTtYU2nw91Hj6THC8Bc8z4nboIq7WvgsT/kOTYVVZzCSlXl8w==" - }, - "@firebase/polyfill": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.36.tgz", - "integrity": "sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg==", - "requires": { - "core-js": "3.6.5", - "promise-polyfill": "8.1.3", - "whatwg-fetch": "2.0.4" - } - }, - "@firebase/remote-config": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.7.tgz", - "integrity": "sha512-gQaGzQCBOkS35b/aXC5Y9/zsPenqs6+axnChYYyfU7CqMG5FGfNbVi2rezYwB4G3+fH4rGO1s6xqcI535Fvy/A==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/installations": "0.5.8", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/remote-config-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.8.tgz", - "integrity": "sha512-lU9t7PMVpgE6q1vG8AuFenFhfUnx0H+eeiIQTi4dtuLDMx9BsI14c9VuiVjRIi7xC2DCDRNQCRL1kRD8bzgJNg==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/remote-config": "0.3.7", - "@firebase/remote-config-types": "0.2.0", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/remote-config-types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.2.0.tgz", - "integrity": "sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw==" - }, - "@firebase/storage": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.9.5.tgz", - "integrity": "sha512-+nCDNIT2pNovlHnLOQPofn8jdOyJ4akUWPGn4ydAoFrfVt1/lINx5Qe+jS3/tKLROfYabqBYxfFUjHQKZBYwvg==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - } - }, - "@firebase/storage-compat": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.13.tgz", - "integrity": "sha512-MdubKh+xe3Xpi34WaXBKtim8H2aauO5sqqmATTc2WgSmSAqTmNSjQfNqIdf139Mp9ZCnpZAxiwiwzQtfckLYWg==", - "requires": { - "@firebase/component": "0.5.13", - "@firebase/storage": "0.9.5", - "@firebase/storage-types": "0.6.0", - "@firebase/util": "1.5.2", - "tslib": "^2.1.0" - } - }, - "@firebase/storage-types": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.6.0.tgz", - "integrity": "sha512-1LpWhcCb1ftpkP/akhzjzeFxgVefs6eMD2QeKiJJUGH1qOiows2w5o0sKCUSQrvrRQS1lz3SFGvNR1Ck/gqxeA==", - "requires": {} - }, - "@firebase/util": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.2.tgz", - "integrity": "sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==", - "requires": { - "tslib": "^2.1.0" - } - }, - "@firebase/webchannel-wrapper": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.6.1.tgz", - "integrity": "sha512-9FqhNjKQWpQ3fGnSOCovHOm+yhhiorKEqYLAfd525jWavunDJcx8rOW6i6ozAh+FbwcYMkL7b+3j4UR/30MpoQ==" - }, - "@grpc/grpc-js": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz", - "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==", - "requires": { - "@grpc/proto-loader": "^0.6.4", - "@types/node": ">=12.12.47" - } - }, - "@grpc/proto-loader": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.11.tgz", - "integrity": "sha512-MRiPjTjNgKxMupQ0M8mM9Mcljb2aZvE3Y/oEv+dacozIs2TwTdiPbvfkZpMeghfjGtoDJhDjyCtmFzJcjdDTUQ==", - "requires": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0 || ^5.2.0", - "protobufjs": "^6.10.0", - "yargs": "^16.2.0" - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - } - }, - "@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - } - }, - "@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - } - }, - "@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - } - }, - "@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - } - }, - "@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@mapbox/node-pre-gyp": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", - "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", - "requires": { - "detect-libc": "^1.0.3", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.5", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@peapodtech/firebasedeviceflow": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@peapodtech/firebasedeviceflow/-/firebasedeviceflow-0.2.0.tgz", - "integrity": "sha512-IOkEZt2P7hcMZX7a7URSvtOu4K3Bw8QhA2REsLHqBLywHkslm1dXx0PCZY12Dk2dBdMdY9iUWAfJtjANPWs5og==", - "requires": { - "@types/inquirer": "^7.3.1", - "axios": "^0.21.1", - "chalk": "^4.1.0", - "firebase": "^9.4.0", - "inquirer": "^7.3.3", - "ora": "^5.2.0", - "typescript": "^4.1.3" - } - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" - }, - "@serialport/binding-mock": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", - "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", - "requires": { - "@serialport/bindings-interface": "^1.2.1", - "debug": "^4.3.3" - } - }, - "@serialport/bindings-cpp": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.7.0.tgz", - "integrity": "sha512-Xx1wA2UCG2loS32hxNvWJI4smCzGKhWqE85//fLRzHoGgE1lSLe3Nk7W40/ebrlGFHWRbQZmeaIF4chb2XLliA==", - "requires": { - "@serialport/bindings-interface": "1.2.1", - "@serialport/parser-readline": "^10.2.1", - "debug": "^4.3.2", - "node-addon-api": "^4.3.0", - "node-gyp-build": "^4.3.0" - }, - "dependencies": { - "@serialport/parser-delimiter": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", - "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==" - }, - "@serialport/parser-readline": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", - "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", - "requires": { - "@serialport/parser-delimiter": "10.3.0" - } - } - } - }, - "@serialport/bindings-interface": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.1.tgz", - "integrity": "sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==" - }, - "@serialport/parser-byte-length": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.3.0.tgz", - "integrity": "sha512-pJ/VoFemzKRRNDHLhFfPThwP40QrGaEnm9TtwL7o2GihEPwzBg3T0bN13ew5TpbbUYZdMpUtpm3CGfl6av9rUQ==" - }, - "@serialport/parser-cctalk": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.3.0.tgz", - "integrity": "sha512-8ujmk8EvVbDPrNF4mM33bWvUYJOZ0wXbY3WCRazHRWvyCdL0VO0DQvW81ZqgoTpiDQZm5r8wQu9rmuemahF6vQ==" - }, - "@serialport/parser-delimiter": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-9.2.4.tgz", - "integrity": "sha512-4nvTAoYAgkxFiXrkI+3CA49Yd43CODjeszh89EK+I9c8wOZ+etZduRCzINYPiy26g7zO+GRAb9FoPCsY+sYcbQ==", - "dev": true - }, - "@serialport/parser-inter-byte-timeout": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.3.0.tgz", - "integrity": "sha512-wKP0QK85NHgvT6BBB1qBfKBBU4pf8kespNXAZBUYmFT+P4n8r8IZE2mqigCD+AiZcfWNQoAizwOsT/Jx/qeVig==" - }, - "@serialport/parser-packet-length": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.3.0.tgz", - "integrity": "sha512-bj0cWzt8YSQj/E5fRQVYdi4TsfTlZQrXlXrUwjyTsCONv8IPOHzsz+yY0fw5SEMiJtaLyqvPkCHLsttOd/zFsg==" - }, - "@serialport/parser-readline": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-9.2.4.tgz", - "integrity": "sha512-Z1/qrZTQUVhNSJP1hd9YfDvq0o7d87rNwAjjRKbVpa7Qi51tG5BnKt43IV3NFMyBlVcRe0rnIb3tJu57E0SOwg==", - "dev": true, - "requires": { - "@serialport/parser-delimiter": "9.2.4" - } - }, - "@serialport/parser-ready": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.3.0.tgz", - "integrity": "sha512-1owywJ4p592dJyVrEJZPIh6pUZ3/y/LN6kGTDH2wxdewRUITo/sGvDy0er5i2+dJD3yuowiAz0dOHSdz8tevJA==" - }, - "@serialport/parser-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.3.0.tgz", - "integrity": "sha512-tIogTs7CvTH+UUFnsvE7i33MSISyTPTGPWlglWYH2/5coipXY503jlaYS1YGe818wWNcSx6YAjMZRdhTWwM39w==" - }, - "@serialport/parser-slip-encoder": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.3.0.tgz", - "integrity": "sha512-JI0ILF5sylWn8f0MuMzHFBix/iMUTa79/Z95KaPZYnVaEdA7h7hh/o21Jmon/26P3RJwL1SNJCjZ81zfan+LtQ==" - }, - "@serialport/parser-spacepacket": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.3.0.tgz", - "integrity": "sha512-PDF73ClEPsClD1FEJZHNuBevDKsJCkqy/XD5+S5eA6+tY5D4HLrVgSWsg+3qqB6+dlpwf2CzHe+uO8D3teuKHA==" - }, - "@serialport/stream": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.3.0.tgz", - "integrity": "sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==", - "requires": { - "@serialport/bindings-interface": "1.2.1", - "debug": "^4.3.2" - } - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@types/babel__core": { - "version": "7.1.18", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", - "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/blessed": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.19.tgz", - "integrity": "sha512-r4qnseYWBsi/kxo5AAlCS22EnTXFbGpnvuXUubJikVeRnYB3e5HwV3NtcwJ0Sk5KOGaLvo9Rtwb8hzxfbqbQPg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", - "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "27.5.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.0.tgz", - "integrity": "sha512-9RBFx7r4k+msyj/arpfaa0WOOEcaAZNmN+j80KFbFCoSqCJGHTz7YMAMGQW9Xmqm5w6l5c25vbSjMwlikJi5+g==", - "dev": true, - "requires": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - } - }, - "@types/jsonwebtoken": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.5.tgz", - "integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==" - }, - "@types/pi-camera": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@types/pi-camera/-/pi-camera-1.5.3.tgz", - "integrity": "sha512-9CGFEc7Cr0QDWdhqL+Bjb8+0xcYX8L9iv7YlQw+F5ntBh9GwZImDbaeJsFZg76TLGsTMLDTrowfHA7WWMX0csQ==", - "dev": true - }, - "@types/prettier": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", - "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", - "dev": true - }, - "@types/project-name-generator": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/project-name-generator/-/project-name-generator-2.1.1.tgz", - "integrity": "sha512-VLd5FEVTJs8hNa/WF4pZRcFvv0OAcIGeTyki4RDcaID0TUhTc5/Xe/btYM2XIHwVCb67ila8wUJYKJNa5dVABw==", - "dev": true - }, - "@types/serialport": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@types/serialport/-/serialport-8.0.2.tgz", - "integrity": "sha512-z4b1I8/vdZE3upgCcAL9VAWlVVFUVn5uo3faAHavkVfK/Hb1LUxKwp9YCtA5AZqEUCWoSWl20SRTOvAI/5fQWQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", - "requires": { - "@types/node": "*" - } - }, - "@types/uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", - "dev": true - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" - }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, - "babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dev": true, - "requires": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "blessed": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", - "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001317", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", - "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==" - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", - "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", - "requires": { - "leven": "^2.1.0", - "minimist": "^1.1.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "dependencies": { - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - } - } - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "requires": { - "clone": "^1.0.2" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "requires": { - "webidl-conversions": "^4.0.2" - }, - "dependencies": { - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - } - } - }, - "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" - }, - "duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", - "requires": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "electron-to-chromium": { - "version": "1.4.86", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz", - "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==", - "dev": true - }, - "emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "epoll": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/epoll/-/epoll-4.0.1.tgz", - "integrity": "sha512-BgCq0nEsk+XI7y9qjrRtt9uXsyFEdvevvq42xl6t/hKZjxLSDZreD9rTZ0pU40V//c3Zzk2PZGuIsn8YJHSJ4g==", - "requires": { - "bindings": "^1.5.0", - "nan": "^2.14.2" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "firebase": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.7.0.tgz", - "integrity": "sha512-IsBZ11b8MbB2YyulNii+vhNMpqmfNwI9iGWG9GVgn+Pzdi+Ta+eaGI3+VbeSZoqC/nkKOzSR89cpz7kXd4+mgg==", - "requires": { - "@firebase/analytics": "0.7.8", - "@firebase/analytics-compat": "0.1.9", - "@firebase/app": "0.7.22", - "@firebase/app-check": "0.5.7", - "@firebase/app-check-compat": "0.2.7", - "@firebase/app-compat": "0.1.23", - "@firebase/app-types": "0.7.0", - "@firebase/auth": "0.19.12", - "@firebase/auth-compat": "0.2.12", - "@firebase/database": "0.12.8", - "@firebase/database-compat": "0.1.8", - "@firebase/firestore": "3.4.8", - "@firebase/firestore-compat": "0.1.17", - "@firebase/functions": "0.8.0", - "@firebase/functions-compat": "0.2.0", - "@firebase/installations": "0.5.8", - "@firebase/messaging": "0.9.12", - "@firebase/messaging-compat": "0.1.12", - "@firebase/performance": "0.5.8", - "@firebase/performance-compat": "0.1.8", - "@firebase/polyfill": "0.3.36", - "@firebase/remote-config": "0.3.7", - "@firebase/remote-config-compat": "0.1.8", - "@firebase/storage": "0.9.5", - "@firebase/storage-compat": "0.1.13", - "@firebase/util": "1.5.2" - } - }, - "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "help-me": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", - "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", - "requires": { - "glob": "^7.1.6", - "readable-stream": "^3.6.0" - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==" - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - } - }, - "jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - } - }, - "jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - } - }, - "jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - } - }, - "jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dev": true, - "requires": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - } - }, - "jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - } - }, - "jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dev": true, - "requires": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "dev": true - }, - "jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - } - }, - "jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - } - }, - "jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - } - }, - "jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dev": true, - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - } - } - }, - "jszip": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz", - "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==", - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "requires": { - "immediate": "~3.0.5" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "long": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", - "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "mqtt": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", - "integrity": "sha512-DJYjlXODVXtSDecN8jnNzi6ItX3+ufGsEs9OB3YV24HtkRrh7kpx8L5M1LuyF0KzaiGtWr2PzDcMGAY60KGOSA==", - "requires": { - "commist": "^1.0.0", - "concat-stream": "^2.0.0", - "debug": "^4.1.1", - "duplexify": "^4.1.1", - "help-me": "^3.0.0", - "inherits": "^2.0.3", - "minimist": "^1.2.5", - "mqtt-packet": "^6.8.0", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "reinterval": "^1.1.0", - "split2": "^3.1.0", - "ws": "^7.5.0", - "xtend": "^4.0.2" - } - }, - "mqtt-packet": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", - "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", - "requires": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-gyp-build": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", - "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==" - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", - "dev": true - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "onoff": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/onoff/-/onoff-6.0.3.tgz", - "integrity": "sha512-xtVlwRDzswYM69bzzIui/qzu7QHsFnjsQiCV1iYVA/HXt5xdc9utc97SYAlXzK8wAhIN7+H7MaVqh2vpfdKk9A==", - "requires": { - "epoll": "^4.0.1", - "lodash.debounce": "^4.0.8" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "pi-camera": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.6.0.tgz", - "integrity": "sha512-WrnJ4R1X/5qA5/vXeou/Ty97c7urrnla03j5FhctF9nZy1lXnfYkPOsL+qDZAZcvg40cbkTE217WlHNnMMftaw==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "promise-polyfill": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", - "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==" - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "dependencies": { - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - } - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "reinterval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", - "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "selenium-webdriver": { - "version": "4.0.0-rc-1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-rc-1.tgz", - "integrity": "sha512-bcrwFPRax8fifRP60p7xkWDGSJJoMkPAzufMlk5K2NyLPht/YZzR2WcIk1+3gR8VOCLlst1P2PI+MXACaFzpIw==", - "requires": { - "jszip": "^3.6.0", - "rimraf": "^3.0.2", - "tmp": "^0.2.1", - "ws": ">=7.4.6" - }, - "dependencies": { - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - } - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "serialport": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.4.0.tgz", - "integrity": "sha512-PszPM5SnFMgSXom60PkKS2A9nMlNbHkuoyRBlzdSWw9rmgOn258+V0dYbWMrETJMM+TJV32vqBzjg5MmmUMwMw==", - "requires": { - "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "10.7.0", - "@serialport/parser-byte-length": "10.3.0", - "@serialport/parser-cctalk": "10.3.0", - "@serialport/parser-delimiter": "10.3.0", - "@serialport/parser-inter-byte-timeout": "10.3.0", - "@serialport/parser-packet-length": "10.3.0", - "@serialport/parser-readline": "10.3.0", - "@serialport/parser-ready": "10.3.0", - "@serialport/parser-regex": "10.3.0", - "@serialport/parser-slip-encoder": "10.3.0", - "@serialport/parser-spacepacket": "10.3.0", - "@serialport/stream": "10.3.0", - "debug": "^4.3.3" - }, - "dependencies": { - "@serialport/parser-delimiter": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.3.0.tgz", - "integrity": "sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==" - }, - "@serialport/parser-readline": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.3.0.tgz", - "integrity": "sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==", - "requires": { - "@serialport/parser-delimiter": "10.3.0" - } - } - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "requires": { - "readable-stream": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^27.0.0", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "20.x" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==" - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "requires": { - "defaults": "^1.0.3" - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "wrtc": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz", - "integrity": "sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g==", - "requires": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - } - }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "requires": {} - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" - } } } diff --git a/software/src/peapod.ts b/software/src/peapod.ts index 3ef2b21a..ef52da12 100644 --- a/software/src/peapod.ts +++ b/software/src/peapod.ts @@ -114,7 +114,7 @@ export default class PeaPod { // CONTROLLER FIELDS - private controlSystems?: { [key: string]: ControlSystem }; + // private controlSystems?: { [key: string]: ControlSystem }; /** * Latest controller instruction set. @@ -168,7 +168,7 @@ export default class PeaPod { Screen.render(); break; case 'revision': - this.controller.write(instructions); + this.controller.write(this.instructions); break; default: // TODO: Console box? @@ -207,23 +207,23 @@ export default class PeaPod { switch (msg.type) { case 'data': // Initialize batch array - if (batch[msg.data.label] === undefined) batch[msg.data.label] = []; + if (this.batch[msg.data.label] === undefined) this.batch[msg.data.label] = []; // Accumulate data into batches - batch[msg.data.label].push({ + this.batch[msg.data.label].push({ timestamp: Date.now(), value: msg.data.value }); // Set actuator values - if (controlSystems[msg.data.label] !== undefined) { - controlSystems[msg.data.label].setValue(msg.data.value); - } + // if (!!this.controlSystems && !!(this.controlSystems[msg.data.label])) { + // this.controlSystems[msg.data.label].setValue(msg.data.value); + // } break; case 'revision': - this.controller.write(instructions); + this.controller.write(this.instructions); break; default: Spinner.log( - `[${chalk.blueBright('CONTROLLER')} | ${msg.type.toUpperCase()}] - ${JSON.stringify( + `[${chalk.blueBright('CONTROLLER')}${msg.type ? ' | ' + msg.type.toUpperCase() : ''} - ${JSON.stringify( msg.data )}` ); @@ -287,14 +287,16 @@ export default class PeaPod { // Start schedule phase 0 let phasePromises = []; - for (const parameter of Object.keys(this.schedule.parameters)) { - phasePromises.push(this.startPhase(schedule, parameter)); + if(!!this.schedule) { + for (const parameter of Object.keys(this.schedule.parameters)) { + phasePromises.push(this.startPhase(this.schedule, parameter)); + } } // Reset - batch = {}; + this.batch = {}; - batchInterval = setInterval(() => { + this.batchInterval = setInterval(() => { // Publish entire batch try { this.publisher.publish({ @@ -304,7 +306,7 @@ export default class PeaPod { project: projectid, run }, - data: batch + data: this.batch }); } catch { Spinner.fail('Batch publish failed, will retry...'); @@ -312,36 +314,38 @@ export default class PeaPod { } Spinner.log( - `[${chalk.magenta('PUBLISH')}] - Batch of ${Object.values(batch).reduce( + `[${chalk.magenta('PUBLISH')}] - Batch of ${Object.values(this.batch).reduce( (sum, entry) => sum + entry.length, 0 )} datapoints published.` ); // Reset batch to empty - batch = {}; + this.batch = {}; }, BATCH_PUBLISH_INTERVAL * 1000); // Refresh control system values and targets, update actuator instructions, send new instructions - const refreshInterval = setInterval(() => { - let instruction: ControllerInstructions = {}; - for (const parameter of Object.keys(targets)) { - const cs = controlSystems[parameter]; - cs.setTarget(targets[parameter]); - instruction[cs.actuator] = cs.refresh(); - } - this.controller.write(instruction); - }, REFRESH_INTERVAL); + // const refreshInterval = setInterval(() => { + // let instruction: ControllerInstructions = {}; + // for (const parameter of Object.keys(targets)) { + // const cs = this.controlSystems[parameter]; + // cs.setTarget(this.targets[parameter]); + // instruction[cs.actuator] = cs.refresh(); + // } + // this.controller.write(instruction); + // }, REFRESH_INTERVAL); // When all phases for all parameters are complete, clear refresh interval, send "off" instruction, resolve this promise - return Promise.all(phasePromises).then(() => { - clearInterval(refreshInterval); - let off: ControllerInstructions = {}; - for (const parameter of Object.keys(targets)) { - off[controlSystems[parameter].actuator] = 0; - } - this.controller.write(off); - }); + // return Promise.all(phasePromises).then(() => { + // clearInterval(refreshInterval); + // let off: ControllerInstructions = {}; + // if(!!this.controlSystems){ + // for (const parameter of Object.keys(this.targets)) { + // off[this.controlSystems[parameter].actuator] = 0; + // } + // } + // this.controller.write(off); + // }); } /** @@ -374,7 +378,7 @@ export default class PeaPod { for (const value of phase.values) { let t = setTimeout(() => { // Assign value - targets[parameter] = value.value; + this.targets[parameter] = value.value; // Remove this timeout if/when it self-clears timeouts.splice( timeouts.findIndex(v => v == t), @@ -395,7 +399,7 @@ export default class PeaPod { intervals.push( setInterval(() => { // Assign value - targets[parameter] = value.value; + this.targets[parameter] = value.value; }, phase.period) ); // Remove this timeout if/when it self-clears diff --git a/software/src/publisher.ts b/software/src/publisher.ts index 7076146c..9e1d7259 100644 --- a/software/src/publisher.ts +++ b/software/src/publisher.ts @@ -339,7 +339,7 @@ export default class PubSubPublisher implements Publisher { username: 'unused', password, protocol: 'mqtts', - secureProtocol: 'TLSv1_2_method', + // secureProtocol: 'TLSv1_2_method', ca: [servercert] }); diff --git a/software/src/utils.ts b/software/src/utils.ts index 8efcebf0..7d2a65ae 100644 --- a/software/src/utils.ts +++ b/software/src/utils.ts @@ -3,6 +3,10 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; import axios from 'axios'; import { spawn } from 'child_process'; +const PATHSTEM_IMAGES = '~/img/'; + +const dateFormat = (d: Date) => (`${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}_${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}`); + /** * Checks the internet connectivity. * @param timeout Timeout in milliseconds. Default: 5000 @@ -105,3 +109,23 @@ export function execute(command: string, failureCodes: number[] = []): Promise { + return new Promise((res, rej) => { + const p = `${PATHSTEM_IMAGES}-${dateFormat(new Date())}`; + execute(`libcamera-jpeg -o ${p}.jpg`) + .catch(err => { + rej(err); + }) + .then(() => { + res(`${p}.jpg`); + }); + }); +} \ No newline at end of file From 406a16bf0aa13f931e57d7ec023b90bdff4aae0c Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 29 Aug 2025 15:39:31 -0400 Subject: [PATCH 36/85] Clean Slate --- .../lib/FiniteStateMachine/chrono.cc | 256 ------- .../lib/FiniteStateMachine/chrono.h | 93 --- .../lib/FiniteStateMachine/comparators.cc | 283 -------- .../lib/FiniteStateMachine/comparators.h | 176 ----- .../lib/FiniteStateMachine/comparators.tpp | 170 ----- .../lib/FiniteStateMachine/debug.h | 33 - .../lib/FiniteStateMachine/library.properties | 10 - .../lib/FiniteStateMachine/number.cc | 135 ---- .../lib/FiniteStateMachine/number.h | 74 -- .../lib/FiniteStateMachine/state.cc | 89 --- .../lib/FiniteStateMachine/state.h | 112 ---- .../lib/FiniteStateMachine/state.tpp | 105 --- .../lib/FiniteStateMachine/statelinker.cc | 148 ---- .../lib/FiniteStateMachine/statelinker.h | 70 -- .../lib/FiniteStateMachine/statelinker.tpp | 40 -- software/microcontroller/lib/I2CIP/I2CIP.cc | 560 ---------------- software/microcontroller/lib/I2CIP/I2CIP.h | 110 --- software/microcontroller/lib/I2CIP/bst.h | 71 -- software/microcontroller/lib/I2CIP/bst.tpp | 136 ---- software/microcontroller/lib/I2CIP/debug.h | 28 - software/microcontroller/lib/I2CIP/device.cc | 632 ------------------ software/microcontroller/lib/I2CIP/device.h | 398 ----------- software/microcontroller/lib/I2CIP/device.tpp | 147 ---- software/microcontroller/lib/I2CIP/eeprom.cc | 368 ---------- software/microcontroller/lib/I2CIP/eeprom.h | 93 --- software/microcontroller/lib/I2CIP/fqa.cc | 32 - software/microcontroller/lib/I2CIP/fqa.h | 84 --- .../microcontroller/lib/I2CIP/hashtable.h | 66 -- .../microcontroller/lib/I2CIP/hashtable.tpp | 115 ---- .../lib/I2CIP/library.properties | 10 - software/microcontroller/lib/I2CIP/mux.cc | 105 --- software/microcontroller/lib/I2CIP/mux.h | 67 -- software/microcontroller/library.properties | 11 - software/microcontroller/test/config.h | 8 - .../test/test_0_blink/test_0_blink.cc | 39 -- .../test/test_1_eeprom/test_1_eeprom.cc | 61 -- .../test/test_2_json/test_2_json.cc | 110 --- .../test/test_3_csos/test_3_csos.cc | 60 -- 38 files changed, 5105 deletions(-) delete mode 100644 software/microcontroller/lib/FiniteStateMachine/chrono.cc delete mode 100644 software/microcontroller/lib/FiniteStateMachine/chrono.h delete mode 100644 software/microcontroller/lib/FiniteStateMachine/comparators.cc delete mode 100644 software/microcontroller/lib/FiniteStateMachine/comparators.h delete mode 100644 software/microcontroller/lib/FiniteStateMachine/comparators.tpp delete mode 100644 software/microcontroller/lib/FiniteStateMachine/debug.h delete mode 100644 software/microcontroller/lib/FiniteStateMachine/library.properties delete mode 100644 software/microcontroller/lib/FiniteStateMachine/number.cc delete mode 100644 software/microcontroller/lib/FiniteStateMachine/number.h delete mode 100644 software/microcontroller/lib/FiniteStateMachine/state.cc delete mode 100644 software/microcontroller/lib/FiniteStateMachine/state.h delete mode 100644 software/microcontroller/lib/FiniteStateMachine/state.tpp delete mode 100644 software/microcontroller/lib/FiniteStateMachine/statelinker.cc delete mode 100644 software/microcontroller/lib/FiniteStateMachine/statelinker.h delete mode 100644 software/microcontroller/lib/FiniteStateMachine/statelinker.tpp delete mode 100644 software/microcontroller/lib/I2CIP/I2CIP.cc delete mode 100644 software/microcontroller/lib/I2CIP/I2CIP.h delete mode 100644 software/microcontroller/lib/I2CIP/bst.h delete mode 100644 software/microcontroller/lib/I2CIP/bst.tpp delete mode 100644 software/microcontroller/lib/I2CIP/debug.h delete mode 100644 software/microcontroller/lib/I2CIP/device.cc delete mode 100644 software/microcontroller/lib/I2CIP/device.h delete mode 100644 software/microcontroller/lib/I2CIP/device.tpp delete mode 100644 software/microcontroller/lib/I2CIP/eeprom.cc delete mode 100644 software/microcontroller/lib/I2CIP/eeprom.h delete mode 100644 software/microcontroller/lib/I2CIP/fqa.cc delete mode 100644 software/microcontroller/lib/I2CIP/fqa.h delete mode 100644 software/microcontroller/lib/I2CIP/hashtable.h delete mode 100644 software/microcontroller/lib/I2CIP/hashtable.tpp delete mode 100644 software/microcontroller/lib/I2CIP/library.properties delete mode 100644 software/microcontroller/lib/I2CIP/mux.cc delete mode 100644 software/microcontroller/lib/I2CIP/mux.h delete mode 100644 software/microcontroller/library.properties delete mode 100644 software/microcontroller/test/config.h delete mode 100644 software/microcontroller/test/test_0_blink/test_0_blink.cc delete mode 100644 software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc delete mode 100644 software/microcontroller/test/test_2_json/test_2_json.cc delete mode 100644 software/microcontroller/test/test_3_csos/test_3_csos.cc diff --git a/software/microcontroller/lib/FiniteStateMachine/chrono.cc b/software/microcontroller/lib/FiniteStateMachine/chrono.cc deleted file mode 100644 index 099a0d03..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/chrono.cc +++ /dev/null @@ -1,256 +0,0 @@ -#include - -#include - -#include -#include - -Chronograph Chronos = Chronograph(); - -#ifdef FSM_DEBUG_SERIAL -// Helper -void debugTimestamp(const fsm_timestamp_t& timestamp); - -#endif - -IntervalCallback::IntervalCallback(const fsm_timestamp_t& delta, const void* cb, callback_type_t cbtype, bool invert) : now(delta), delta(delta), ConditionalCallback(CMP_GTR, delta, cb, cbtype, invert) { } - -IntervalCallback::IntervalCallback(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, const void* cb, callback_type_t cbtype, bool invert) : now(delta), delta(delta), phase(phase % delta), ConditionalCallback(CMP_GTR, delta, cb, cbtype, invert) { } - -const fsm_timestamp_t& IntervalCallback::childReference(const fsm_timestamp_t& val) { - // If the new value is way over the line, modulus and set now - if(val > (this->delta + this->phase)) { - this->now = val % this->delta; // leaves phase in - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Interval Reset!\n")); - DEBUG_DELAY(); - #endif // DEBUG - - // Trigger!! - return (refholder = 0); - } - return (refholder = (this->delta + this->phase)); -} - -fsm_timestamp_t IntervalCallback::get(void) { return this->now; } - -void IntervalCallback::set(const fsm_timestamp_t& val) { - this->now = val; - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Interval Set (")); - FSM_DEBUG_SERIAL.print(this->now); - FSM_DEBUG_SERIAL.print(_F("ms)\n")); - DEBUG_DELAY(); - #endif // DEBUG - this->operator()(this->now); -} - -FlagInterval::FlagInterval(const fsm_timestamp_t& delta, Flag* flag, bool invert) : flag(flag), IntervalCallback(delta, nullptr, CB_NONE, invert) { } - -FlagInterval::FlagInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert) : flag(flag), IntervalCallback(delta, phase, nullptr, CB_NONE, invert) { } - -void FlagInterval::childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Flag Interval Set (")); - FSM_DEBUG_SERIAL.print(comp ? "true" : "false"); - FSM_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - this->flag->set(comp); -} - -ChronoEvent::ChronoEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert) : ConditionalCallback(CMP_GEQ, timestamp, cb, invert) { } - -void ChronoEvent::callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { - bool result = compare(this->comparator, val, ref); - if(!this->triggered && result) { - this->triggered = true; - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Event Triggered!\n")); - DEBUG_DELAY(); - #endif // DEBUG - - this->executeCallback(val, ref); - } else if(!result) { - this->triggered = false; - } -} - -FlagEvent::FlagEvent(const fsm_timestamp_t& timestamp, Flag* flag, bool invert) : FlagSetCondition(CMP_GEQ, timestamp, flag, invert) { } - -void FlagEvent::callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { - bool result = compare(this->comparator, val, ref); - if(!this->triggered && result) { - this->triggered = true; - this->executeCallback(val, ref); - } else if(!result) { - this->triggered = false; - } -} -void FlagEvent::childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Flag Event Set (")); - FSM_DEBUG_SERIAL.print(comp ? "true" : "false"); - FSM_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - - this->flag->set(comp); -} - -Chronograph::Chronograph(const fsm_timestamp_t& start) : start(start), last(start), State(start) { - // Blank it - for(int i = 0; i < MAX_INTERVALS; i++) { - this->intervals[i] = nullptr; - } -} - -void Chronograph::set(const fsm_timestamp_t& _val) { - fsm_timestamp_t val = _val % TWENTYFOURHRS_MILLIS; - - const fsm_timestamp_t last = this->get(); - - // Null frame - const fsm_timestamp_t delta = val > last ? val - last : 0; - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Timer Delta: ")); - debugTimestamp(delta); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - - // Call super - let getRef handle overflow ;) - ((State*)this)->set(val); - - for(unsigned int i = 0; i < MAX_INTERVALS; i++) { - if(this->intervals[i] == nullptr) return; - - const fsm_timestamp_t last = intervals[i]->get(); - this->intervals[i]->set(last + delta); - } -} - -fsm_timestamp_t Chronograph::get(void) { return ((State*)this)->get(); } - -ConditionalCallback* Chronograph::addEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert) { return addConditional(new ChronoEvent(timestamp, cb, invert)); } - -ConditionalCallback* Chronograph::addEventFlag(const fsm_timestamp_t& timestamp, Flag* flag, bool invert) { return addConditional(new FlagEvent(timestamp, flag, invert)); } - -IntervalCallback* Chronograph::addInterval(const fsm_timestamp_t& delta, typename ConditionalCallback::cb_compval_t cb, bool invert) { - return this->addInterval(delta, 0, cb, invert); -} - -IntervalCallback* Chronograph::addInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, typename ConditionalCallback::cb_compval_t cb, bool invert) { - unsigned int n = 0; - while(intervals[n] != nullptr) { n++; if(n > MAX_INTERVALS) return nullptr;} - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("New Interval (")); - FSM_DEBUG_SERIAL.print(delta); - if(phase > 0) { - FSM_DEBUG_SERIAL.print("+"); - FSM_DEBUG_SERIAL.print(phase); - } - FSM_DEBUG_SERIAL.print("ms)\n"); - DEBUG_DELAY(); - #endif - - intervals[n] = new IntervalCallback(delta, phase, (const void*)cb, CB_COMPVAL, invert); - return intervals[n]; -} - -IntervalCallback* Chronograph::addIntervalFlag(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert) { - unsigned int n = 0; - while(intervals[n] != nullptr) { n++; if(n > MAX_INTERVALS) return nullptr;} - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("New Interval Flag (")); - FSM_DEBUG_SERIAL.print(delta); - if(phase > 0) { - FSM_DEBUG_SERIAL.print("+"); - FSM_DEBUG_SERIAL.print(phase); - } - FSM_DEBUG_SERIAL.print("ms)\n"); - DEBUG_DELAY(); - #endif - - intervals[n] = new FlagInterval(delta, phase, flag, invert); - return intervals[n]; -} - -IntervalCallback* Chronograph::addIntervalFlag(const fsm_timestamp_t& delta, Flag* flag, bool invert) { - return this->addIntervalFlag(delta, 0, flag, invert); -} - -IntervalCallback* Chronograph::addTwentyFourTimeout(const fsm_timestamp_t& phase, typename ConditionalCallback::cb_compval_t cb, bool invert) { - unsigned int n = 0; - while(intervals[n] != nullptr) { n++; if(n > MAX_INTERVALS) return nullptr;} - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("New 24Hr Interval Flag")); - if(phase > 0) { - FSM_DEBUG_SERIAL.print("(+"); - FSM_DEBUG_SERIAL.print(phase); - FSM_DEBUG_SERIAL.print("ms)\n"); - } - DEBUG_DELAY(); - #endif - - intervals[n] = new IntervalCallback(TWENTYFOURHRS_MILLIS, phase, (const void*)cb, CB_COMPVAL, invert); - return intervals[n]; -} - -#ifdef FSM_DEBUG_SERIAL - -void Chronograph::debugTimestamp(const fsm_timestamp_t& timestamp) { - unsigned long seconds = timestamp / 1000; - unsigned long res_ms = timestamp % 1000; - unsigned long minutes = seconds / 60; - unsigned long hours = minutes / 60; - - seconds -= minutes * 60; - minutes -= hours * 60; - - DEBUG_DELAY(); - if(hours > 0) { - if(hours < 10) FSM_DEBUG_SERIAL.print("0"); - FSM_DEBUG_SERIAL.print(hours); - FSM_DEBUG_SERIAL.print("h : "); - } - - if(minutes > 0) { - if(minutes < 10) FSM_DEBUG_SERIAL.print("0"); - FSM_DEBUG_SERIAL.print(minutes - hours * 60); - FSM_DEBUG_SERIAL.print("m : "); - } - - if(seconds < 10) FSM_DEBUG_SERIAL.print("0"); - FSM_DEBUG_SERIAL.print(seconds); - FSM_DEBUG_SERIAL.print("s"); - - if(minutes < 1) { - // ms - FSM_DEBUG_SERIAL.print(" : "); - if(res_ms < 100) FSM_DEBUG_SERIAL.print("0"); - if(res_ms < 10) FSM_DEBUG_SERIAL.print("0"); - FSM_DEBUG_SERIAL.print(res_ms); - FSM_DEBUG_SERIAL.print("ms"); - } - DEBUG_DELAY(); -} - -#endif - -// ConditionalCallback* _Timer::addLoggerCallback(void (* const cb)(bool comp, fsm_timestamp_t val)) { return addConditional(new ConditionalCallback(CMP_NEQ, -1, cb)); } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/chrono.h b/software/microcontroller/lib/FiniteStateMachine/chrono.h deleted file mode 100644 index f92cbbbe..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/chrono.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef FSM_TIMER_H_ -#define FSM_TIMER_H_ - -#include -#include - -#define MAX_INTERVALS 32 -#define INTERVAL_RESET_WINDOW 1.05 -#define TWENTYFOURHRS_MILLIS ((fsm_timestamp_t)86400000) - -class IntervalCallback : private ConditionalCallback { - private: - // Time since last reset (ms) - fsm_timestamp_t now = 0; - const fsm_timestamp_t phase = 0; - const fsm_timestamp_t delta; - - fsm_timestamp_t refholder; - protected: - const fsm_timestamp_t& childReference(const fsm_timestamp_t& val) override; - public: - IntervalCallback(const fsm_timestamp_t& delta, const void* cb, callback_type_t cbtype, bool invert = false); - - IntervalCallback(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, const void* cb, callback_type_t cbtype, bool invert = false); - - fsm_timestamp_t get(void); - void set(const fsm_timestamp_t& val); -}; - -class FlagInterval : public IntervalCallback { - private: - Flag* flag; - protected: - void childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; - public: - FlagInterval(const fsm_timestamp_t& delta, Flag* flag, bool invert = false); - FlagInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert = false); -}; - -class ChronoEvent : public ConditionalCallback { - private: - // Has the callback been triggered yet? - bool triggered = false; - protected: - void callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; - public: - ChronoEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert = false); -}; - -class FlagEvent : public FlagSetCondition { - private: - // Has the callback been triggered yet? - bool triggered = false; - protected: - void callOperator(const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; - void childCallback(bool comp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) override; - public: - FlagEvent(const fsm_timestamp_t& timestamp, Flag* flag, bool invert = false); -}; - -class Chronograph : private State { - private: - const fsm_timestamp_t start; - const fsm_timestamp_t last; - - IntervalCallback* intervals[MAX_INTERVALS] = { nullptr }; - public: - Chronograph(const fsm_timestamp_t& start = 1000); - - ConditionalCallback* addEvent(const fsm_timestamp_t& timestamp, typename ConditionalCallback::cb_compval_t cb, bool invert = false); - ConditionalCallback* addEventFlag(const fsm_timestamp_t& timestamp, Flag* flag, bool invert = false); - IntervalCallback* addInterval(const fsm_timestamp_t& delta, typename ConditionalCallback::cb_compval_t cb, bool invert = false); - IntervalCallback* addInterval(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, typename ConditionalCallback::cb_compval_t cb, bool invert = false); - IntervalCallback* addIntervalFlag(const fsm_timestamp_t& delta, Flag* flag, bool invert = false); - IntervalCallback* addIntervalFlag(const fsm_timestamp_t& delta, const fsm_timestamp_t& phase, Flag* flag, bool invert = false); - - IntervalCallback* addTwentyFourTimeout(const fsm_timestamp_t& delay, typename ConditionalCallback::cb_compval_t cb, bool invert = false); - - // EXPOSE SOME PUBLIC BASE CLASS MEMBERS - - void set(const fsm_timestamp_t& val); - fsm_timestamp_t get(void); - - // ConditionalCallback* addLoggerCallback(void (*cb)(bool, fsm_timestamp_t)); - - #ifdef FSM_DEBUG_SERIAL - static void debugTimestamp(const fsm_timestamp_t& timestamp); - #endif -}; - -extern Chronograph Chronos; - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/comparators.cc b/software/microcontroller/lib/FiniteStateMachine/comparators.cc deleted file mode 100644 index b513038d..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/comparators.cc +++ /dev/null @@ -1,283 +0,0 @@ -#include - -#include -#include - -template <> bool ConditionalCallback::compare(comparators_t cmp, const bool& val, const bool& ref) { - bool result = false; - char ival = (val ? 1 : 0); - char iref = (ref ? 1 : 0); - - switch(cmp) { - case CMP_EQU: - result = (ival == iref); - break; - case CMP_NEQ: - result = !(ival == iref); - break; - case CMP_LES: - result = (ival < iref); - break; - case CMP_LEQ: - result = (ival <= iref); - break; - case CMP_GTR: - result = (ival > iref); - break; - case CMP_GEQ: - result = (ival >= iref); - break; - default: - result = NOP_COMP_BOOL; - break; - } - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Flag (")); - FSM_DEBUG_SERIAL.print(BOOLSTR(val)); - FSM_DEBUG_SERIAL.print(" | "); - FSM_DEBUG_SERIAL.print((int)ival); - FSM_DEBUG_SERIAL.print(") "); - FSM_DEBUG_SERIAL.print(parseComparator(cmp, result)); - FSM_DEBUG_SERIAL.print(" Ref ("); - FSM_DEBUG_SERIAL.print(BOOLSTR(ref)); - FSM_DEBUG_SERIAL.print(" | "); - FSM_DEBUG_SERIAL.print((int)iref); - FSM_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - return result; -} - -template <> bool ConditionalCallback::compare(comparators_t cmp, const Number& val, const Number& ref) { - if(ref.isNaN()) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("[Variable Ref NaN - Always True]\n")); - DEBUG_DELAY(); - #endif - return true; - } - - bool result = false; - switch(cmp) { - case CMP_EQU: - result = (val == ref); - break; - case CMP_NEQ: - result = (val != ref); - break; - case CMP_LES: - result = (val < ref); - break; - case CMP_LEQ: - result = (val <= ref); - break; - case CMP_GTR: - result = (val > ref); - break; - case CMP_GEQ: - result = (val >= ref); - break; - default: - result = NOP_COMP_NUMBER; - break; - } - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Variable (")); - FSM_DEBUG_SERIAL.print((double)val); - FSM_DEBUG_SERIAL.print(") "); - FSM_DEBUG_SERIAL.print(parseComparator(cmp, result)); - FSM_DEBUG_SERIAL.print(_F(" Ref (")); - FSM_DEBUG_SERIAL.print((double)ref); - FSM_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - return result; -} - -template <> bool ConditionalCallback::compare(comparators_t cmp, const fsm_timestamp_t& val, const fsm_timestamp_t& ref) { - return ConditionalCallback::compare(cmp, Number(val), Number(ref)); -} - -BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, const void* rising, const void* falling, callback_type_t cbtype) : low(Number::minimum(lo, hi)), high(Number::maximum(lo, hi)), rising(rising), falling(falling), latchcbtype(cbtype), ConditionalCallback(CMP_LES, low, (double)hi < (double)lo) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang [")); - FSM_DEBUG_SERIAL.print((double)lo); - FSM_DEBUG_SERIAL.print(", "); - FSM_DEBUG_SERIAL.print((double)hi); - FSM_DEBUG_SERIAL.print("]\n"); - DEBUG_DELAY(); - #endif -} - -BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, cb_none_t rising, cb_none_t falling) : BangBangConditional(lo, hi, (const void*)rising, (const void*)falling, CB_NONE) { } - -BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, cb_none_t cb) : BangBangConditional(lo, hi, (const void*)cb, (const void*)cb, CB_NONE) { } - -BangBangConditional::BangBangConditional(const Number& lo, const Number& hi, const void* cb, callback_type_t cbtype) : BangBangConditional(lo, hi, (const void*)cb, (const void*)cb, cbtype) { } - -void BangBangConditional::childCallback(bool comp, const Number& val, const Number& ref) { - this->state = (ref == this->high); - - const void* fn = this->state ? this->rising : this->falling; - #ifdef FSM_DEBUG_SERIAL - if(this->state) { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang (High)\n")); - DEBUG_DELAY(); - } else { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang (Low)\n")); - DEBUG_DELAY(); - } - #endif - - // state ? comp : !comp; - // T XOR T = T - // F XOR T = F - // T XOR F = F - // F XOR F = T - // comp = !(comp ^ this->state); - - bool _ = !(this->invert ^ this->state); - - // Cast void* cb fn pointer to appropriate type and call - if(fn != nullptr){ - switch(this->callbacktype) { - case CB_NONE: - // Called with no args - ((cb_none_t)fn)(); - break; - case CB_COMP: - ((cb_comp_t)fn)(_); - break; - case CB_COMPVAL: - ((cb_compval_t)fn)(_, val); - break; - case CB_COMPVALREF: - ((cb_compvalref_t)fn)(_, val, ref); - break; - } - } -} - -const Number& BangBangConditional::childReference(const Number& val) { - if (this->state && val <= this->low) { - this->setComparator(CMP_LES); - this->triggered = false; - this->state = false; - } else if (!this->state && val >= this->high) { - this->setComparator(CMP_GTR); - this->triggered = false; - this->state = true; - } - return this->state ? this->high : this->low; -} - -void BangBangConditional::callOperator(const Number& val, const Number& ref) { - bool result = compare(this->comparator, val, ref); - if(!this->triggered && result) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang Triggered\n")); - DEBUG_DELAY(); - #endif - this->triggered = true; - this->executeCallback(val, ref); - } else if(!result) { - // Reset - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang Trigger Reset!\n")); - DEBUG_DELAY(); - #endif - this->triggered = false; - } -} - -LatchingConditional::LatchingConditional(const void* rising, const void* falling, callback_type_t cbtype, bool invert) : rising(rising), falling(falling), latchcbtype(cbtype), ConditionalCallback(CMP_EQU, !invert, invert) { } - -LatchingConditional::LatchingConditional(void (* const rising)(void), void (* const falling)(void), bool invert) : LatchingConditional((const void*)rising, (const void*)falling, CB_NONE, invert) { } - -LatchingConditional::LatchingConditional(const void* cb, callback_type_t cbtype, bool invert) : LatchingConditional(cb, cb, cbtype, invert) { } - -LatchingConditional::LatchingConditional(void (* const cb)(void), bool invert) : LatchingConditional((const void*)cb, CB_NONE, invert) { } - -void LatchingConditional::callOperator(const bool& val, const bool& ref) { - bool result = compare(this->comparator, val, ref); - #ifdef FSM_DEBUG_SERIAL - if(!result && this->triggered) { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Trigger Reset!\n")); - DEBUG_DELAY(); - } - #endif - if(!this->triggered && result) { - this->triggered = true; - this->executeCallback(val, ref); - } else if(!result) { - // Reset - this->triggered = false; - } -} - -void LatchingConditional::childCallback(bool comp, const bool& val, const bool& ref) { - this->state = !ref; - this->triggered = false; - - const void* fn = val ? this->rising : this->falling; - #ifdef FSM_DEBUG_SERIAL - if(val) { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Latch Set (true)\n")); - DEBUG_DELAY(); - } else { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Latch Reset (false)\n")); - DEBUG_DELAY(); - } - #endif - - // Cast void* cb fn pointer to appropriate type and call - if(fn != nullptr){ - switch(this->callbacktype) { - case CB_NONE: - // Called with no args - ((cb_none_t)fn)(); - break; - case CB_COMP: - ((cb_comp_t)fn)(comp); - break; - case CB_COMPVAL: - ((cb_compval_t)fn)(comp, val); - break; - case CB_COMPVALREF: - ((cb_compvalref_t)fn)(comp, val, ref); - break; - } - } -} - -const bool& LatchingConditional::childReference(const bool& val) { return this->state; } - -const char* parseComparator(const comparators_t& cmp, bool result) { - switch(cmp) { - case CMP_EQU: - return result ? "==" : "!=="; - case CMP_NEQ: - return result ? "!=" : "!!="; - case CMP_LES: - return result ? "<" : "!<"; - case CMP_LEQ: - return result ? "<=" : "!<="; - case CMP_GTR: - return result ? ">" : "!>"; - case CMP_GEQ: - return result ? ">=" : "!>="; - default: - return result ? "??" : "!??"; - } -} \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/comparators.h b/software/microcontroller/lib/FiniteStateMachine/comparators.h deleted file mode 100644 index 172ffe28..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/comparators.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef FSM_COMPARATORS_H_ -#define FSM_COMPARATORS_H_ - -#include - -#define NOP_COMP_BOOL true -#define NOP_COMP_NUMBER false - -typedef enum { - CMP_NOP = 0, - CMP_EQU, - CMP_NEQ, - CMP_LES, - CMP_LEQ, - CMP_GTR, - CMP_GEQ, -} comparators_t; - -// Helper Functions -const char* parseComparator(const comparators_t& cmp, bool result = true); - -typedef enum { - CB_NONE = 0, // void (* const)(void) - NO ARGS - CB_COMP, // void (* const)(bool) - ADD: CONDITION RESULT - CB_COMPVAL, // void (* const)(bool, T) - ADD: PASSED VALUE - CB_COMPVALREF, // void (* const)(bool, T, T) - ADD: REFERENCE VALUE -} callback_type_t; - -// template struct conditional_callbacks_t { -// typedef ; -// typedef ; -// typedef ); -// typedef void (* const cb_ref_t)(bool comp, A val, A ref); -// typedef A (* const cb_getref_t)(void); -// }; - -// TODO: Compound conditionals? - -// CONSTRUCTION: - - - -// USE: CONDITION - COMPARISON OF VALUE TO REFERENCE -// IF TRUE: EXECUTION - -/** - * @param T type of the two values to be compared -*/ -template class ConditionalCallback { - public: - typedef const T& (* cb_getref_t)(const T&); - typedef void (* const cb_none_t)(void); - typedef void (* const cb_comp_t)(bool); - typedef void (* const cb_compval_t)(bool, const T&); - typedef void (* const cb_compvalref_t)(bool, const T&, const T&); - - /** - * @param cmp COMPARATOR - * @param ref REFERENCE VALUE - * @param cb CALLBACK FUNCTION POINTER (CAST TO CONST VOID*) - * @param cbtype CALLBACK TYPE - dictates argument set, cb pointer recast - * REFERENCE UPDATOR (OPTIONAL) - **/ - ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, const T&(* const getReference)(const T&) = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, cb_getref_t getReference = nullptr); - - ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, bool invert, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, bool invert, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, bool invert, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, bool invert, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, bool invert, cb_getref_t getReference = nullptr); - - ConditionalCallback& operator=(ConditionalCallback const& other); - - void operator()(const T& val); - void operator()(const T& val, const T& ref); - - static bool compare(comparators_t cmp, const T& val, const T& ref); - - void disable(); - void enable(); - void enable(const T& val); - - protected: - // NO CALLBACK - ASSUMES childCallback is implemented - ConditionalCallback(comparators_t cmp, const T& ref, cb_getref_t getReference = nullptr); - ConditionalCallback(comparators_t cmp, const T& ref, bool invert, cb_getref_t getReference = nullptr); - - // FALLBACKS FOR FUNCPOINTERS - virtual void childCallback(bool comp, const T& val, const T& ref); // NOP - virtual const T& childReference(const T& val); // NOP - - // PROPERTIES - - void setComparator(comparators_t cmp); - - comparators_t comparator; - - virtual void callOperator(const T& val, const T& ref); - - T reference; - - const bool invert = false; - - bool disabled = false; - - const void* callback; - - const callback_type_t callbacktype; - - const cb_getref_t getReference = nullptr; - - void executeCallback(const T& val, const T& ref); -}; - -class BangBangConditional : public ConditionalCallback { - private: - bool state = false; - - // Has the callback been triggered yet? - bool triggered = false; - - const Number low; - const Number high; - - const void* rising; - const void* falling; - - const callback_type_t latchcbtype; - - protected: - void childCallback(bool comp, const Number& val, const Number& ref) override; - const Number& childReference(const Number& val) override; - void callOperator(const Number& val, const Number& ref) override; - public: - /** - * When val < `lo`, callback(true) - * When val > `hi`, callback(false) - * `invert` inverts argument value - * Args are parsed with min/max so that hi > lo - */ - BangBangConditional(const Number& lo, const Number& hi, cb_none_t rising, cb_none_t falling); - BangBangConditional(const Number& lo, const Number& hi, const void* rising, const void* falling, callback_type_t cbtype); - BangBangConditional(const Number& lo, const Number& hi, cb_none_t cb); - BangBangConditional(const Number& lo, const Number& hi, const void* cb, callback_type_t cbtype); -}; - -class LatchingConditional : public ConditionalCallback { - private: - bool state = true; - - // Has the callback been triggered yet? - bool triggered = false; - - const void* rising; - const void* falling; - - const callback_type_t latchcbtype; - - protected: - void childCallback(bool comp, const bool& val, const bool& ref) override; - const bool& childReference(const bool& val) override; - void callOperator(const bool& val, const bool& ref) override; - public: - LatchingConditional(cb_none_t rising, cb_none_t falling, bool invert = false); - LatchingConditional(const void* rising, const void* falling, callback_type_t cbtype, bool invert = false); - explicit LatchingConditional(cb_none_t cb, bool invert = false); - LatchingConditional(const void* cb, callback_type_t cbtype, bool invert = false); -}; - -#include - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/comparators.tpp b/software/microcontroller/lib/FiniteStateMachine/comparators.tpp deleted file mode 100644 index 8da40ac4..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/comparators.tpp +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef FSM_COMPARATORS_H_ -#error __FILE__ should only be included AFTER -// #include -#endif - -#ifdef FSM_COMPARATORS_H_ - -#ifndef FSM_COMPARATORS_T_ -#define FSM_COMPARATORS_T_ - -// #error COMPARATORS.TPP - -#define BOOLSTR(b) (b ? "true" : "false") - -#include - -// TODO: All T args in callbacks -> const T& -// TODO: Replace all OneShot with Latching (duh) - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, bool invert, cb_getref_t getReference) : comparator(cmp), reference(ref), callback(cb), callbacktype(cbtype), invert(invert), getReference(getReference) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Callback Created: X ")); - FSM_DEBUG_SERIAL.print(parseComparator(cmp)); - FSM_DEBUG_SERIAL.print(" "); - FSM_DEBUG_SERIAL.print(ref); - if(invert) FSM_DEBUG_SERIAL.print(_F(" (Inverted) ")); - if(getReference != nullptr) FSM_DEBUG_SERIAL.print(_F(" (Dynamic Ref) ")); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif -} - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_NONE, invert, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMP, invert, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVAL, invert, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVALREF, invert, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, bool invert, cb_getref_t getReference) : ConditionalCallback(cmp, ref, nullptr, CB_COMP, invert, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, const void* cb, callback_type_t cbtype, cb_getref_t getReference) : comparator(cmp), reference(ref), callback(cb), callbacktype(cbtype), getReference(getReference) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Callback Created: X ")); - FSM_DEBUG_SERIAL.print(parseComparator(cmp)); - FSM_DEBUG_SERIAL.print(" "); - FSM_DEBUG_SERIAL.print(ref); - if(getReference != nullptr) FSM_DEBUG_SERIAL.print(_F(" (Dynamic Ref) ")); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif -} - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_none_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_NONE, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_comp_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMP, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compval_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVAL, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_compvalref_t cb, cb_getref_t getReference) : ConditionalCallback(cmp, ref, (const void*)cb, CB_COMPVALREF, getReference) { } - -template ConditionalCallback::ConditionalCallback(comparators_t cmp, const T& ref, cb_getref_t getReference) : ConditionalCallback(cmp, ref, nullptr, CB_COMP, getReference) { } - -template ConditionalCallback& ConditionalCallback::operator=(ConditionalCallback const& other) { return ConditionalCallback(other.comparator, other.reference, other.callback, other.callbacktype, other.getReference); } - -template void ConditionalCallback::childCallback(bool comp, const T& val, const T& ref) { return; } -template const T& ConditionalCallback::childReference(const T& val) { return this->reference; } - -template void ConditionalCallback::operator()(const T& val) { - if(this->disabled) return; - - // Update the reference value - if (this->getReference != nullptr) { - this->reference = this->getReference(val); - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Got Reference: ")); - FSM_DEBUG_SERIAL.print(this->reference); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - } else { - this->reference = this->childReference(val); - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Got Child Reference: ")); - FSM_DEBUG_SERIAL.print(this->reference); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - } - this->callOperator(val, this->reference); -} - -template void ConditionalCallback::operator()(const T& val, const T& ref) { this->callOperator(val, ref); } - -template void ConditionalCallback::setComparator(comparators_t cmp) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Comparator Change: ")); - FSM_DEBUG_SERIAL.print(parseComparator(cmp)); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - this->comparator = cmp; -} - -template void ConditionalCallback::callOperator(const T& val, const T& ref) { - bool result = ConditionalCallback::compare(this->comparator, val, ref); - - if(result) this->executeCallback(val, ref); -} - -template void ConditionalCallback::executeCallback(const T& val, const T& ref) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(invert ? _F("Callback! (Inverted)") : _F("Callback!")); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - - bool _ = this->invert ? false : true; - - // #ifdef FSM_DEBUG_SERIAL - // DEBUG_DELAY(); - // FSM_DEBUG_SERIAL.print("Passing "); - // FSM_DEBUG_SERIAL.print(_ ? "true" : "false"); - // FSM_DEBUG_SERIAL.print("\n"); - // DEBUG_DELAY(); - // #endif - - if(this->callback == nullptr) { - // Assumes childCallback - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("No Callback - Calling Child: ")); - DEBUG_DELAY(); - #endif - this->childCallback(_, val, ref); - return; - }; - - // Cast void* cb fn pointer to appropriate type and call - switch(this->callbacktype) { - case CB_NONE: - // Called with no args - ((cb_none_t)this->callback)(); - break; - case CB_COMP: - ((cb_comp_t)this->callback)(_); - break; - case CB_COMPVAL: - ((cb_compval_t)this->callback)(_, val); - break; - case CB_COMPVALREF: - ((cb_compvalref_t)this->callback)(_, val, ref); - break; - } -} - -template void ConditionalCallback::disable() { this->disabled = true; } -template void ConditionalCallback::enable() { this->disabled = false; } -template void ConditionalCallback::enable(const T& val) { this->disabled = false; this->executeCallback(val, this->reference); } - -#endif - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/debug.h b/software/microcontroller/lib/FiniteStateMachine/debug.h deleted file mode 100644 index b07b2ffc..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/debug.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef FSM_DEBUG_SERIAL - -#include - -// Uncomment to enable debug -// #define DEBUG 1 - -// CROSS-LIBRARY DEBUG COMPATIBILITY -#ifdef DEBUG -#if DEBUG == true -#ifndef DEBUG_SERIAL -#define DEBUG_SERIAL Serial -#endif -#endif -#endif - -#ifdef DEBUG_SERIAL -// Just once -#define FSM_DEBUG_SERIAL DEBUG_SERIAL - -#ifndef DEBUG_DELAY -#define DEBUG_DELAY() { DEBUG_SERIAL.flush(); delay(10); } -#endif -#endif - -// #define DEBUG_DISABLE_FSTRINGS 1 -#ifdef DEBUG_DISABLE_FSTRINGS -#define _F(x) x -#else -#define _F(x) F(x) -#endif - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/library.properties b/software/microcontroller/lib/FiniteStateMachine/library.properties deleted file mode 100644 index 59472552..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=FiniteStateMachine -version=0.1.0 -author=Jayden Lefebvre -maintainer=Jayden Lefebvre -sentence=A synchronous time-sliced state management library to build responsive "embedded apps" via callbacks and conditional signal chaining. -paragraph=Tested on Arduino Nano. -category=Device Control -url=https://github.com/PeaPodTechnologies/FiniteStateMachine/ -architectures=avr -includes=state.h, chrono.h \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/number.cc b/software/microcontroller/lib/FiniteStateMachine/number.cc deleted file mode 100644 index 56d2697a..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/number.cc +++ /dev/null @@ -1,135 +0,0 @@ -#include - -#include - -#include - -int Number::compare(const Number& other) const { - if(other.isNaN() && this->isNaN()) return 0; - if(other.isNaN()) return 1; - if(this->isNaN()) return -1; - - double diff = this->value - other.value; - return (diff < NUMBER_FLOP_EPSILON) ? (diff > -NUMBER_FLOP_EPSILON ? 0 : -1) : 1; -} - -// float Number::getValue() { return this->value; } -// float Number::isFloatingPoint() { return this->isFloating; } -// float Number::isSignedInteger() { return this->isSigned; } - -void Number::set(const double& val, bool isFloating, bool isSigned) { - this->value = val; - this->isFloating = isFloating; - this->isSigned = isSigned; -} - -void Number::setValue(const long& val) { this->set((double)val, false); } -void Number::setValue(const unsigned long& val) { this->set((double)val, false, false); } - -// Master Constructor -Number::Number(const double& val, bool isFloating, bool isSigned) : value(val), isFloating(isFloating), isSigned(isFloating || isSigned) { } - -// NaN Constructor -Number::Number() : value((double)NAN), isFloating(true), isSigned(true), notanumber(true) { - // delay(10); - // Serial.print("[NaN Constructor]\n"); - // delay(10); -} - -Number::Number(const unsigned long& val) : Number((double)val, false, false) { } -Number::Number(const long& val) : Number((double)val, false, val < 0) { } -Number::Number(const int& val) : Number((double)val, false, val < 0) { } -Number::Number(const Number& other) : Number(other.value, other.isFloating, other.isSigned) { - if(other.isNaN()) { - // delay(10); - // Serial.print("[NaN Copy Constructor]\n"); - // delay(10); - this->notanumber = true; - } -} - -Number::~Number() { } - -bool Number::isNaN(void) const { - // if(this->notanumber) { - // delay(10); - // Serial.print("[IS NAN]\n"); - // delay(10); - // } - return this->notanumber; -} - -double Number::operator()(void) const { return this->operator double(); } - -Number::operator double() const { return this->isNaN() ? (double)NAN : this->value; } - -void Number::operator=(const double& rhs) { this->set(rhs); } -void Number::operator=(const float& rhs) { this->set(rhs); } -void Number::operator=(const long& rhs) { this->set((double)rhs, false, rhs < 0); } -void Number::operator=(const int& rhs) { this->set((double)rhs, false, rhs < 0); } -void Number::operator=(const unsigned long& rhs) { this->set((double)rhs, false, false); } -void Number::operator=(const unsigned int& rhs) { this->set((double)rhs, false, false); } - -Number& Number::operator=(const Number& other) { - if(other.isNaN()) { - this->notanumber = true; - this->set((double)NAN, true); - } else { - this->set(other.value, other.isFloating, other.isSigned); - } - return (*this); -} - -bool Number::operator==(const Number& rhs) const { return (compare(rhs) == 0) && !(this->isFloating ^ rhs.isFloating); } - -bool Number::operator!=(const Number& rhs) const { return (compare(rhs) != 0); } - -bool Number::operator<(const Number& rhs) const { return (compare(rhs) < 0); } -bool Number::operator<=(const Number& rhs) const { return (compare(rhs) <= 0); } -bool Number::operator>(const Number& rhs) const { return (compare(rhs) > 0); } -bool Number::operator>=(const Number& rhs) const { return (compare(rhs) >= 0); } - -// Number::operator double() const { return this->value; } -// Number::operator long() const { return (this->isSigned ? (long)this->value : (unsigned long)this->value); } -// Number::operator int() const { return (this->isSigned ? (int)this->value : (unsigned int)this->value); } - -// NUMBER FLAG RULES - -// Addition/Subtraction: -// Is Floating: IFF this is floating OR rhs is floating -// Is Signed: IFF the result is floating OR signed - -Number Number::operator+(const Number& rhs) const { - if(rhs.isNaN() || this->isNaN()) return Number(); - - double result = rhs.value + this->value; - return Number(result, rhs.isFloating || this->isFloating, result < 0.0); -} - -Number Number::operator-(const Number& rhs) const { - if(rhs.isNaN() || this->isNaN()) return Number(); - - double result = this->value - rhs.value; - return Number(result, rhs.isFloating || this->isFloating, result < 0.0); -} - -// Multiplication/Division: -// Is Floating: IFF this is floating OR rhs is floating -// Is Signed: IFF this is floating OR rhs is floating OR this is signed OR rhs is signed - -Number Number::operator*(const Number& rhs) const { - if(rhs.isNaN() || this->isNaN()) return Number(); - - double result = rhs.value * this->value; - return Number(result, rhs.isFloating || this->isFloating, rhs.isSigned || this->isSigned); -} - -Number Number::operator/(const Number& rhs) const { - if(rhs.isNaN() || this->isNaN()) return Number(); - - return (rhs.value < NUMBER_FLOP_EPSILON && rhs.value > -NUMBER_FLOP_EPSILON) ? Number() : Number(this->value / rhs.value, true, rhs.isSigned || this->isSigned); -} - -const Number& Number::minimum(const Number& a, const Number& b) { return a > b ? b : a; } - -const Number& Number::maximum(const Number& a, const Number& b) { return a < b ? b : a; } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/number.h b/software/microcontroller/lib/FiniteStateMachine/number.h deleted file mode 100644 index 8b3e0849..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/number.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef FSM_NUMBER_H_ -#define FSM_NUMBER_H_ - -#define NUMBER_FLOP_EPSILON 0.00001 - -#include - -typedef unsigned long fsm_timestamp_t; - -class Number { - private: - double value; - bool isFloating; - bool isSigned; - bool notanumber = false; - - protected: - int compare(const Number& other) const; - - // float getValue(void); - // float isFloatingPoint(void); - // float isSignedInteger(void); - - void set(const double& val, bool isFloating = true, bool isSigned = true); - void setValue(const long& val); - void setValue(const unsigned long& val); - - public: - Number(); // NaN Constructor - Number(const double& val, bool isFloating = true, bool isSigned = true); - Number(const int& val); - Number(const long& val); - Number(const unsigned long& val); - Number(const Number& other); - - ~Number(); - - bool isNaN(void) const; - - double operator()(void) const; - - void operator=(const double& rhs); - void operator=(const float& rhs); - void operator=(const long& rhs); - void operator=(const int& rhs); - void operator=(const unsigned long& rhs); - void operator=(const unsigned int& rhs); - - Number& operator=(const Number& other); - - bool operator==(const Number& rhs) const; - bool operator!=(const Number& rhs) const; - - bool operator<(const Number& rhs) const; - bool operator<=(const Number& rhs) const; - bool operator>(const Number& rhs) const; - bool operator>=(const Number& rhs) const; - - operator double() const; - explicit operator fsm_timestamp_t() const; - // operator long() const; - // operator int() const; - - Number operator+(const Number& rhs) const; - Number operator-(const Number& rhs) const; - Number operator*(const Number& rhs) const; - Number operator/(const Number& rhs) const; - - static const Number& minimum(const Number& a, const Number& b); - - static const Number& maximum(const Number& a, const Number& b); -}; - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/state.cc b/software/microcontroller/lib/FiniteStateMachine/state.cc deleted file mode 100644 index a5c9c107..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/state.cc +++ /dev/null @@ -1,89 +0,0 @@ -#define FSM_SRC 1 - -#include - -#include -#include -#include - -#ifdef FSM_DEBUG_SERIAL - -template <> void stateDebug(const Number& val) { - if(val.isNaN()) { - FSM_DEBUG_SERIAL.print("NaN"); - } else { - FSM_DEBUG_SERIAL.print(val.operator double()); - } -} - -template <> void stateDebug(const bool& val) { - FSM_DEBUG_SERIAL.print(val ? "true" : "false"); -} - -template <> void stateDebug(const fsm_timestamp_t& val) { - FSM_DEBUG_SERIAL.print(val); -} - -#endif - -template <> ConditionalCallback* State::addLoggerCallback(typename ConditionalCallback::cb_compval_t cb) { return addConditional(new ConditionalCallback(CMP_NEQ, Number(), cb)); } - -template <> ConditionalCallback* State::addLoggerCallback(typename ConditionalCallback::cb_compval_t cb) { return addConditional(new ConditionalCallback(CMP_NEQ, (fsm_timestamp_t)-1, cb)); } - -template <> ConditionalCallback* State::addLoggerCallback(typename ConditionalCallback::cb_compval_t cb) { return addConditional(new ConditionalCallback(CMP_NOP, false, cb)); } - -template <> ConditionalCallback* State::addLatchingSetFlag(const Number& on, const Number& off, Flag* flag) { - // if(on < off) { - // "heater" - return addConditional(new BangBangFlagSet(on, off, flag)); - // addConditional(oneshot ? new LatchFlagSet(CMP_LES, on, flag) : new FlagSetCondition(CMP_LES, on, flag)); - // addConditional(oneshot ? new LatchFlagSet(CMP_GTR, off, flag, true) : new FlagSetCondition(CMP_GTR, off, flag, true)); - // } else { - // "cooler" - // return addConditional(new BangBangFlagSet(off, on, flag)); - // addConditional(oneshot ? new LatchFlagSet(CMP_GTR, on, flag) : new FlagSetCondition(CMP_GTR, on, flag)); - // addConditional(oneshot ? new LatchFlagSet(CMP_LES, off, flag, true) : new FlagSetCondition(CMP_LES, off, flag, true)); - // } -} - -template <> ConditionalCallback* State::addLatchingConditional(const Number& on, const Number& off, const void* cb, callback_type_t cbtype) { - // if(on < off) { - // "heater" - return addConditional(new BangBangConditional(on, off, cb, cbtype)); - // addConditional(oneshot ? new OneShotConditional(CMP_LES, on, cb, cbtype) : new ConditionalCallback(CMP_LES, on, cb, cbtype)); - // addConditional(oneshot ? new OneShotConditional(CMP_GTR, off, cb, cbtype, true) : new ConditionalCallback(CMP_GTR, off, cb, cbtype, true)); - // } else { - // "cooler" - // return addConditional(new BangBangConditional(off, on, cb, cbtype)); - // addConditional(oneshot ? new OneShotConditional(CMP_GTR, on, cb, cbtype) : new ConditionalCallback(CMP_GTR, on, cb, cbtype)); - // addConditional(oneshot ? new OneShotConditional(CMP_LES, off, cb, cbtype, true) : new ConditionalCallback(CMP_LES, off, cb, cbtype, true)); - // } -} - -template <> ConditionalCallback* State::addLatchingSetFlag(const fsm_timestamp_t& on, const fsm_timestamp_t& off, Flag* flag) { - return (ConditionalCallback*)(new BangBangFlagSet(Number(on), Number(off), flag)); -} - -template <> ConditionalCallback* State::addLatchingConditional(const fsm_timestamp_t& on, const fsm_timestamp_t& off, const void* cb, callback_type_t cbtype) { - return (ConditionalCallback*)(new BangBangConditional(Number(on), Number(off), cb, cbtype)); -} - -// BOOLEAN HYSTERESIS = STEP CHANGE FILTER - -template <> ConditionalCallback* State::addLatchingConditional(const bool& on, const bool& off, const void* cb, callback_type_t cbtype) { - return addConditional(new LatchingConditional(cb, cbtype, (char)on < (char)off)); -} - -template <> ConditionalCallback* State::addLatchingSetFlag(const bool& on, const bool& off, Flag* flag) { - return addConditional(new LatchingFlagSet(flag, (char)on < (char)off)); -} - -Variable::Variable(const Number& value) : State(value) { } -Variable::Variable(const double& value) : Variable(Number(value)) { } -Variable::Variable(const float& value) : Variable(Number(value)) { } -Variable::Variable(const long& value) : Variable(Number(value)) { } -Variable::Variable(const int& value) : Variable(Number((long)value)) { } -Variable::Variable(const unsigned long& value) : Variable(Number(value)) { } -Variable::Variable(const unsigned int& value) : Variable(Number((unsigned long)value)) { } - -Flag::Flag(const bool& value) : State(value) { } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/state.h b/software/microcontroller/lib/FiniteStateMachine/state.h deleted file mode 100644 index 74af4229..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/state.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef FSM_STATE_H_ -#define FSM_STATE_H_ - -// Class Forward-Declarations -class Flag; -class Variable; - -#include -#include - -#define MAX_CONDITIONALS 10 - -#ifdef FSM_DEBUG_SERIAL - -template void stateDebug(const T& val); - -#endif - -template class State { - private: - const char* key = nullptr; - - T value; - const T defaultValue; - - bool disabled = false; - - unsigned char numconditionals = 0; - protected: - ConditionalCallback* conditionals[MAX_CONDITIONALS] = { nullptr }; - public: - explicit State(const T& value); - State(const T& value, const char* key); - // ~State(); - - T get(void); - void set(const T& val); - - ConditionalCallback* addConditional(ConditionalCallback* conditional); - - ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_none_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); - - ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_comp_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); - - ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compval_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); - - ConditionalCallback* addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compvalref_t cb, typename ConditionalCallback::cb_getref_t getReference = nullptr); - - ConditionalCallback* addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference = nullptr); - - ConditionalCallback* addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference = nullptr); - - ConditionalCallback* addLatchingConditional(const T& on, const T& off, const void* cb, callback_type_t cbtype); - ConditionalCallback* addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compvalref_t cb); - ConditionalCallback* addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compval_t cb); - ConditionalCallback* addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_comp_t cb); - - ConditionalCallback* addLatchingSetFlag(const T& on, const T& off, Flag* flag); - - // ConditionalCallback* addConditionalSetFlag(comparators_t cmp, T ref, Flag* flag, bool oneshot = false); - - // ConditionalCallback* addConditionalResetFlag(comparators_t cmp, T ref, Flag* flag, T (*const getReference)(void)); - - // ConditionalCallback* addConditionalResetFlag(comparators_t cmp, T ref, Flag* flag, bool oneshot = false); - - ConditionalCallback* addLoggerCallback(typename ConditionalCallback::cb_compval_t cb); - - // virtual T get(void) = 0; - // virtual void set(T val) = 0; - - operator T() const { return value; }; - - bool isDisabled(); - - // Enable, No Set - void enable(); - // Enable, Re-Set - void resume(); - // Enable, Set - void enable(const T& val); - // Disable, No-Set - void disable(); - // Disable, Set - void disable(const T& val); -}; - -class Variable : public State { - private: - public: - explicit Variable(const Number& value); - explicit Variable(const double& value = 0.0f); - explicit Variable(const float& value); - explicit Variable(const long& value); - explicit Variable(const int& value); - explicit Variable(const unsigned long& value); - explicit Variable(const unsigned int& value); - // Number get(void) override; - // void set(Number val) override; -}; - -class Flag : public State { - private: - public: - explicit Flag(const bool& value = false); - // bool get(void) override; - // void set(bool val) override; -}; - -#include -#include - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/state.tpp b/software/microcontroller/lib/FiniteStateMachine/state.tpp deleted file mode 100644 index 709b89b4..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/state.tpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef FSM_STATE_H_ -#error __FILE__ should only be included AFTER -// #include -#endif -#ifndef FSM_STATELINKER_H_ -#error __FILE__ should only be included AFTER -// #include -#endif - -#ifdef FSM_STATE_H_ -#ifdef FSM_STATELINKER_H_ - -#ifndef FSM_STATE_T_ -#define FSM_STATE_T_ - -// #error STATE.TPP - -#include - -template State::State(const T& value) : value(value), defaultValue(value) { } -template State::State(const T& value, const char* key) : value(value), defaultValue(value), key(key) { } - -template T State::get(void) { - return this->value; -} - -template void State::set(const T& val) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - if(this->key == nullptr) { - FSM_DEBUG_SERIAL.print(_F("State Set: ")); - } else { - FSM_DEBUG_SERIAL.print(this->key); - FSM_DEBUG_SERIAL.print(_F(" Set: ")); - } - stateDebug(val); - FSM_DEBUG_SERIAL.print("\n"); - FSM_DEBUG_SERIAL.print(_F("==== [ Conditionals ] ====")); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - - if(!this->disabled) { - for(unsigned int i = 0; i < MAX_CONDITIONALS; i++) { - if(this->conditionals[i] == nullptr) break; - - this->conditionals[i]->operator()(val); - } - } - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("==== [ End of Conditionals")); - if(this->key != nullptr) { - FSM_DEBUG_SERIAL.print(": "); - FSM_DEBUG_SERIAL.print(this->key); - } - FSM_DEBUG_SERIAL.print(_F(" ] ====\n")); - DEBUG_DELAY(); - #endif - - this->value = val; -} - -template ConditionalCallback* State::addConditional(ConditionalCallback* conditional) { - for (unsigned char i = 0; i < MAX_CONDITIONALS; i++) { - if(this->conditionals[i] == nullptr) { - this->conditionals[i] = conditional; - return this->conditionals[i]; - } - } - return nullptr; -} - -template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_none_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } - -template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_comp_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } - -template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compval_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } - -template ConditionalCallback* State::addConditional(comparators_t cmp, const T& ref, typename ConditionalCallback::cb_compvalref_t cb, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new ConditionalCallback(cmp, ref, cb, getReference)); } - -template ConditionalCallback* State::addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new FlagSetCondition(cmp, ref, flag, getReference)); } - -template ConditionalCallback* State::addConditionalSetFlag(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference) { return addConditional(new FlagSetCondition(cmp, ref, flag, invert, getReference)); } - -// OTHER ARGS - -template ConditionalCallback* State::addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_comp_t cb) { return addLatchingConditional(on, off, (const void*)cb, CB_COMP); } - -template ConditionalCallback* State::addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compval_t cb) { return addLatchingConditional(on, off, (const void*)cb, CB_COMPVAL); } - -template ConditionalCallback* State::addLatchingConditional(const T& on, const T& off, typename ConditionalCallback::cb_compvalref_t cb) { return addLatchingConditional(on, off, (const void*)cb, CB_COMPVALREF); } - -template bool State::isDisabled() { return this->disabled; } -template void State::enable() { this->disabled = false; } -template void State::enable(const T& val) { this->disabled = false; this->set(val); } -template void State::disable() { this->disabled = true; } -template void State::disable(const T& val) { this->set(val); this->disabled = true; } -template void State::resume() { this->disabled = false; this->set(this->value); } - - -#endif -#endif -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/statelinker.cc b/software/microcontroller/lib/FiniteStateMachine/statelinker.cc deleted file mode 100644 index aff35281..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/statelinker.cc +++ /dev/null @@ -1,148 +0,0 @@ -#include - -#include -#include -#include -#include - -BangBangFlagSet::BangBangFlagSet(const Number& lo, const Number& hi, Flag* flag) : low(Number::minimum(lo, hi)), high(Number::maximum(lo, hi)), FlagSetCondition(CMP_LES, low, flag, (double)hi < (double)lo) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang FlagSet [")); - FSM_DEBUG_SERIAL.print((double)lo); - FSM_DEBUG_SERIAL.print(", "); - FSM_DEBUG_SERIAL.print((double)hi); - FSM_DEBUG_SERIAL.print("]\n"); - DEBUG_DELAY(); - #endif -} - -void BangBangFlagSet::childCallback(bool comp, const Number& val, const Number& ref) { - this->state = (ref == this->high); - - #ifdef FSM_DEBUG_SERIAL - if(state) { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang (High)\n")); - DEBUG_DELAY(); - } else { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang (Low)\n")); - DEBUG_DELAY(); - } - #endif - - // state ? comp : !comp; - // T XOR T = T - // F XOR T = F - // T XOR F = F - // F XOR F = T - // comp = !(comp ^ this->state); - // comp = this->state ? comp : !comp; - bool _ = !(this->invert ^ this->state); - - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Flag Set (")); - FSM_DEBUG_SERIAL.print(_ ? "true" : "false"); - FSM_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - this->flag->set(_); -} - -const Number& BangBangFlagSet::childReference(const Number& val) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang State: ")); - FSM_DEBUG_SERIAL.print(BOOLSTR(this->state)); - FSM_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - if (this->state && val <= this->low) { - this->setComparator(CMP_LES); - this->triggered = false; - this->state = false; - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang State: Expecting Low\n")); - DEBUG_DELAY(); - #endif - } else if (!this->state && val >= this->high) { - this->setComparator(CMP_GTR); - this->triggered = false; - this->state = true; - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang State: Expecting High\n")); - DEBUG_DELAY(); - #endif - } - return this->state ? this->high : this->low; -} - -void BangBangFlagSet::callOperator(const Number& val, const Number& ref) { - bool result = compare(this->comparator, val, ref); - if(!this->triggered && result) { - this->triggered = true; - this->executeCallback(val, ref); - } else if(!result) { - // Reset - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("BangBang Trigger Reset!\n")); - DEBUG_DELAY(); - #endif - this->triggered = false; - } -} - -void LatchingFlagSet::callOperator(const bool& val, const bool& ref) { - bool result = compare(this->comparator, val, ref); - if(!this->triggered && result) { - this->executeCallback(val, ref); - this->triggered = true; - } else if(!result) { - // Reset - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Trigger Reset!\n")); - DEBUG_DELAY(); - #endif - this->triggered = false; - } -} - -LatchingFlagSet::LatchingFlagSet(Flag* flag, bool invert) : FlagSetCondition(CMP_EQU, !invert, flag, invert) { } - - -void LatchingFlagSet::childCallback(bool comp, const bool& val, const bool& ref) { - this->state = !ref; - this->triggered = false; - - #ifdef FSM_DEBUG_SERIAL - if(val) { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Latch (High)\n")); - DEBUG_DELAY(); - } else { - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Latch (Low)\n")); - DEBUG_DELAY(); - } - #endif - - bool _ = val ^ ref; - - // Cast void* cb fn pointer to appropriate type and call - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Flag Set (")); - FSM_DEBUG_SERIAL.print(_ ? "true" : "false"); - FSM_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - this->flag->set(_); -} - -const bool& LatchingFlagSet::childReference(const bool& val) { return this->state; } \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/statelinker.h b/software/microcontroller/lib/FiniteStateMachine/statelinker.h deleted file mode 100644 index 0dcf56ac..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/statelinker.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef FSM_STATELINKER_H_ -#define FSM_STATELINKER_H_ - -// Class Forwarrd-Declarations (for ) -template class FlagSetCondition; -class LatchingFlagSet; -class BangBangFlagSet; - -#include -#include -#include - -template class FlagSetCondition : public ConditionalCallback { - protected: - Flag* flag; - - void childCallback(bool comp, const T& val, const T& ref) override; - - public: - FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference = nullptr); - FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference = nullptr); -}; - -class BangBangFlagSet : public FlagSetCondition { - private: - bool state = false; - - // Has the callback been triggered yet? - bool triggered = false; - - const Number low; - const Number high; - protected: - void callOperator(const Number& val, const Number& ref) override; - void childCallback(bool comp, const Number& val, const Number& ref) override; - const Number& childReference(const Number& val) override; - public: - BangBangFlagSet(const Number& lo, const Number& hi, Flag* flag); -}; - -// Mostly useless -class LatchingFlagSet : public FlagSetCondition { - private: - bool state = true; - - // Has the callback been triggered yet? - bool triggered = false; - protected: - void callOperator(const bool& val, const bool& ref) override; - void childCallback(bool comp, const bool& val, const bool& ref) override; - const bool& childReference(const bool& val) override; - public: - explicit LatchingFlagSet(Flag* flag, bool invert = false); -}; - -// template class OneShotFlagReset : public FlagSetCondition { -// private: -// bool triggered = false; - -// void callOperator(T val, T ref); -// public: -// OneShotFlagSet(comparators_t cmp, T ref, Flag* flag); - -// void operator()(T val); -// void operator()(T val, T ref); -// }; - -#include - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/FiniteStateMachine/statelinker.tpp b/software/microcontroller/lib/FiniteStateMachine/statelinker.tpp deleted file mode 100644 index f8481a1f..00000000 --- a/software/microcontroller/lib/FiniteStateMachine/statelinker.tpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef FSM_STATELINKER_H_ -#error __FILE__ should only be included AFTER -// #include -#endif - -#ifndef FSM_STATE_H_ -#error __FILE__ should only be included AFTER -// #include -#endif - -#ifdef FSM_STATELINKER_H_ -#ifdef FSM_STATE_H_ - -#ifndef FSM_STATELINKER_T_ -#define FSM_STATELINKER_T_ - -#include - -// #error STATELINKER.TPP - -typedef void (*const flagSetCB_t)(bool); - -template FlagSetCondition::FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, typename ConditionalCallback::cb_getref_t getReference) : flag(flag), ConditionalCallback(cmp, ref, getReference) { } - -template FlagSetCondition::FlagSetCondition(comparators_t cmp, const T& ref, Flag* flag, bool invert, typename ConditionalCallback::cb_getref_t getReference) : flag(flag), ConditionalCallback(cmp, ref, invert, getReference) { } - -template void FlagSetCondition::childCallback(bool comp, const T& val, const T& ref) { - #ifdef FSM_DEBUG_SERIAL - DEBUG_DELAY(); - FSM_DEBUG_SERIAL.print(_F("Flag Set (")); - FSM_DEBUG_SERIAL.print(comp ? "true" : "false"); - FSM_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - this->flag->set(comp); -} - -#endif -#endif -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/I2CIP.cc b/software/microcontroller/lib/I2CIP/I2CIP.cc deleted file mode 100644 index b66ec336..00000000 --- a/software/microcontroller/lib/I2CIP/I2CIP.cc +++ /dev/null @@ -1,560 +0,0 @@ -#include - -#include -#include - -using namespace I2CIP; - -// Module::Module(const i2cip_fqa_t& eeprom_fqa) { -// EEPROM* _eeprom = new EEPROM(eeprom_fqa); -// if(_eeprom->pingTimeout() == I2CIP_ERR_NONE) { - -// } -// } - -Module::Module(const uint8_t& wire, const uint8_t& mux, const uint8_t& eeprom_addr) : wire(wire), mux(mux), eeprom((EEPROM*)EEPROM::eepromFactory(createFQA(wire, mux, I2CIP_MUX_BUS_DEFAULT, eeprom_addr))) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Module ")); - I2CIP_DEBUG_SERIAL.print(mux, HEX); - I2CIP_DEBUG_SERIAL.print(F(" Constructed\n")); - DEBUG_DELAY(); - #endif - - eeprom->clearCache(); // ensure cache is nullptr - eeprom->resetFailsafe(); // ensure default EEPROM buffer is cached for next write - - // NOTE: I MOVED THIS STEP TO DISCOVER, FLAGGED WITH `eeprom_added`, & MADE DEVICEGROUPFACTORY PURE VIRTUAL - // This potentially useful if I decide to ping in deviceFactory - // this->add(*eeprom); // Add EEPROM to module - note that this will call Module::deviceGroupFactory() -} - -Module::Module(const i2cip_fqa_t& eeprom_fqa) : Module(I2CIP_FQA_SEG_I2CBUS(eeprom_fqa), I2CIP_FQA_SEG_MODULE(eeprom_fqa), I2CIP_EEPROM_ADDR) { } - -Module::~Module() { - // Delete all DeviceGroups, deleting all Devices (incl. EEPROM) - for(uint8_t i = 0; i < HASHTABLE_SLOTS; i++) { - if(this->devices_idgroups.hashtable[i] != nullptr) { - delete (&(this->devices_idgroups.hashtable[i]->value)); - } - } - - // BST, Hashtable are static and delete their own entries -} - -DeviceGroup* Module::deviceGroupFactory(i2cip_id_t id) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Builtin EEPROM DeviceGroup Factory: Matching\n")); - DEBUG_DELAY(); - #endif - - if(id == EEPROM::getStaticIDBuffer() || strcmp(id, EEPROM::getStaticIDBuffer()) == 0) { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F(" Match! Creating...\n")); - // DEBUG_DELAY(); - // #endif - - return new DeviceGroup(EEPROM::getStaticIDBuffer(), EEPROM::eepromFactory); - } - - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F(" EEPROM ID Mismatch!\n")); - // DEBUG_DELAY(); - // #endif - - return nullptr; -} - -// DeviceGroup* Module::deviceGroupFactory(const i2cip_id_t& id) { -// #ifdef I2CIP_DEBUG_SERIAL -// DEBUG_DELAY(); -// I2CIP_DEBUG_SERIAL.print(F("-> DeviceGroup Factory Not Implemented!\n")); -// DEBUG_DELAY(); -// #endif - -// return nullptr; -// } - -bool Module::discover(bool recurse) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Module ")); - I2CIP_DEBUG_SERIAL.print(getModuleNum(), HEX); - I2CIP_DEBUG_SERIAL.print(F(" Discovering...\n")); - DEBUG_DELAY(); - #endif - - if(!this->eeprom_added) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> First-Time EEPROM Addition\n")); - DEBUG_DELAY(); - #endif - Device* e = add(*eeprom); - if(e != eeprom) { - Serial.print(F("*** ABORT ***\n")); - delete e; - return false; - } - this->eeprom_added = true; - } - - // Read EEPROM - const uint16_t len = I2CIP_EEPROM_SIZE; - i2cip_errorlevel_t errlev = eeprom->getInput()->get(&len); - // eeprom->readContents(buf, len, I2CIP_EEPROM_SIZE); - - if(errlev != I2CIP_ERR_NONE || eeprom->getCache() == nullptr) return false; - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Module ")); - I2CIP_DEBUG_SERIAL.print(getModuleNum(), HEX); - I2CIP_DEBUG_SERIAL.print(F(" EEPROM Read! Parsing...\n")); - DEBUG_DELAY(); - #endif - - // Parse EEPROM contents into module devices - bool r = parseEEPROMContents(eeprom->getCache()); - if(r) return true; - else if (recurse) { - // BAD EEPROM CONTENT - OVERWRITE WITH FAILSAFE - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("EEPROM Parse Failed! Overwriting with Failsafe and Retrying...\n")); - DEBUG_DELAY(); - #endif - errlev = ((OutputSetter*)eeprom)->reset(); - if(errlev == I2CIP_ERR_HARD) return false; - - return discover(false); - } else { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("EEPROM Parse Failed! Aborting...\n")); - DEBUG_DELAY(); - #endif - return false; - } -} - -// bool Module::add(Device& device) { // Subnet match check -// if(this->isFQAinSubnet(device.getFQA()) && (*this)[device.getFQA()] != nullptr) return true; -// return false; -// } - -HashTableEntry* Module::addEmptyGroup(const char* id) { - if(this->devices_idgroups.get(id) != nullptr) return this->devices_idgroups.get(id); // Group already exists - - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("Creating new DeviceGroup '")); - // I2CIP_DEBUG_SERIAL.print(id); - // I2CIP_DEBUG_SERIAL.print("' @0x"); - // I2CIP_DEBUG_SERIAL.print((uint16_t)(&id[0]), HEX); - // I2CIP_DEBUG_SERIAL.print("\n"); - // DEBUG_DELAY(); - // #endif - - // Allocate new DeviceGroup - DeviceGroup* group = this->deviceGroupFactory(id); - if(group == nullptr) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("DeviceGroup Factory Failed!\n")); - DEBUG_DELAY(); - #endif - return nullptr; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("DeviceGroup Factory Success! Inserting into HashTable\n")); - DEBUG_DELAY(); - #endif - - // Insert into HashTable - return this->devices_idgroups.set(id, *group); -} - -bool Module::parseEEPROMContents(const char* contents) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("No Parsing Yet! EEPROM Contents '")); - I2CIP_DEBUG_SERIAL.print(contents); - I2CIP_DEBUG_SERIAL.print("' @0x"); - I2CIP_DEBUG_SERIAL.print((uint16_t)contents, HEX); - I2CIP_DEBUG_SERIAL.print("\n"); - DEBUG_DELAY(); - #endif - return true; -} - -Device* Module::add(Device& device, bool overwrite) { - const i2cip_fqa_t& fqa = device.getFQA(); - const char* id = device.getID(); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Module Add Device @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)&device, HEX); - I2CIP_DEBUG_SERIAL.print(F("(ID '")); - I2CIP_DEBUG_SERIAL.print(id); - I2CIP_DEBUG_SERIAL.print(F("' @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)id, HEX); - I2CIP_DEBUG_SERIAL.print(F("; FQA ")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); - I2CIP_DEBUG_SERIAL.print("):\n"); - DEBUG_DELAY(); - #endif - - // Search BST for Device - Device** dptr = this->devices_fqabst[fqa]; - if(dptr != nullptr && *dptr != nullptr) { - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("Device Already Exists (ID '")); - I2CIP_DEBUG_SERIAL.print((*dptr)->getID()); - I2CIP_DEBUG_SERIAL.print("')\n"); - DEBUG_DELAY(); - #endif - - // Return different ptr to invoke deletion - if (!overwrite) return *dptr; - } - - // Search HashTable for DeviceGroup - - HashTableEntry* entry = this->devices_idgroups[id]; - if(entry == nullptr) entry = addEmptyGroup(id); - if(entry == nullptr) { - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("Failed to Create DeviceGroup! Check Libraries.\n")); - DEBUG_DELAY(); - #endif - return nullptr; - } - - Device* existing = entry->value[fqa]; - - if(existing != nullptr) { - if(dptr != nullptr && *dptr != nullptr) { - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("Conflicting Entries, Removing.\n")); - DEBUG_DELAY(); - #endif - - // Delete old device - this->remove(existing, true); - } else { - // Was in HashTable but not BST - BSTNode* ptr = this->devices_fqabst.insert(fqa, existing); - #ifdef I2CIP_DEBUG_SERIAL - if(ptr != nullptr) I2CIP_DEBUG_SERIAL.print(F("-> Copied to BST from DeviceGroup\n")); - DEBUG_DELAY(); - #endif - } - } - - BSTNode* ptr = this->devices_fqabst.insert(fqa, &device); - if(ptr == nullptr) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Failed to Save Device!\n")); - DEBUG_DELAY(); - #endif - return nullptr; - } - if(ptr->value != &device || ptr->key != fqa) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> BST Node Mismatch!\n")); - DEBUG_DELAY(); - #endif - this->devices_fqabst.remove(fqa); - return nullptr; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> BST Success (FQA ")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(F(", Device @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)&device, HEX); - I2CIP_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - - // Insert into DeviceGroup (by pointer copy) - if(!entry->value.contains(device) && !entry->value.add(device)) { - // Abort! - this->devices_fqabst.remove(fqa); - return nullptr; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> HashTable Success (ID '")); - I2CIP_DEBUG_SERIAL.print(entry->value.key); - I2CIP_DEBUG_SERIAL.print(F("' @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)&entry->value.key[0], HEX); - I2CIP_DEBUG_SERIAL.print(F(", DeviceGroup[")); - I2CIP_DEBUG_SERIAL.print(entry->value.numdevices); - I2CIP_DEBUG_SERIAL.print(F("] @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)&entry->value, HEX); - I2CIP_DEBUG_SERIAL.print(F(", Factory @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)entry->value.factory, HEX); - I2CIP_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - - return ptr->value; -} - -Device* Module::operator[](const i2cip_fqa_t& fqa) const { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Module Device Lookup (FQA ")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); - I2CIP_DEBUG_SERIAL.print("):"); - DEBUG_DELAY(); - #endif - - Device** dptr = this->devices_fqabst[fqa]; - - #ifdef I2CIP_DEBUG_SERIAL - if(dptr == nullptr || *dptr == nullptr) { - I2CIP_DEBUG_SERIAL.print(F(" Not Found!\n")); - } else { - I2CIP_DEBUG_SERIAL.print(F(" Found!\n")); - } - DEBUG_DELAY(); - #endif - - return (dptr == nullptr) ? nullptr : *dptr; -} - -DeviceGroup* Module::operator[](i2cip_id_t id) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Module DeviceGroup Lookup (ID '")); - I2CIP_DEBUG_SERIAL.print(id); - I2CIP_DEBUG_SERIAL.print("'):"); - DEBUG_DELAY(); - #endif - HashTableEntry* entry = this->devices_idgroups[id]; - if(entry == nullptr) { - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F(" Not Found, Creating...\n")); - DEBUG_DELAY(); - #endif - - entry = addEmptyGroup(id); - return entry == nullptr ? nullptr : &(entry->value); - } - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F(" Found! (Factory @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)(entry->value.factory), HEX); - I2CIP_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - - return &(entry->value); -} - -void Module::remove(Device* device, bool del) { - if(device == nullptr) return; - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Removing Device... ")); - #endif - - // bool swap = false; - // for(int i = 0; i < this->numdevices-1; i++) { - // if(swap) { - // this->devices[i - 1] = this->devices[i]; - // } - // if(this->devices[i]->getFQA() == device->getFQA()) { - // this->devices[i] = nullptr; - // this->numdevices--; - // swap = true; - // } - // } - - i2cip_fqa_t fqa = device->getFQA(); - - // Lookup in BST - Device** dptr = this->devices_fqabst[fqa]; - if(dptr == nullptr) return; // Device not found - if(*dptr != device) return; // FQA points to different device - if((*dptr)->getFQA() != fqa) return; // Device doesn't match - - // Remove from BST - this->devices_fqabst.remove(fqa); - - // Lookup in HashTable - HashTableEntry* entry = this->devices_idgroups[device->getID()]; - if(entry == nullptr) return; - - // Remove from DeviceGroup - entry->value.remove(device); - - // Delete device - if(del) delete device; - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("Removed!\n")); - DEBUG_DELAY(); - #endif -} - -bool Module::isFQAinSubnet(const i2cip_fqa_t& fqa) { - bool match = I2CIP_FQA_SUBNET_MATCH(fqa, this->eeprom->getFQA()); - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - if(match) { - I2CIP_DEBUG_SERIAL.print(F("Subnet Match!\n")); - } else { - I2CIP_DEBUG_SERIAL.print(F("Subnet Mismatch!\n")); - } - DEBUG_DELAY(); - #endif - return match; -} - -// bool Module::contains(Device* device) { -// #ifdef I2CIP_DEBUG_SERIAL -// DEBUG_DELAY(); -// I2CIP_DEBUG_SERIAL.print(F("Module Contains Device Check!\n"); -// DEBUG_DELAY(); -// #endif - -// if(device == nullptr || !this->isFQAinSubnet(device->getFQA())) return false; -// for(int i = 0; i < this->numdevices; i++) { -// if(this->devices[i]->getFQA() == device->getFQA()) return true; -// } -// return false; -// } - -i2cip_errorlevel_t Module::operator()(void) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Module Self-Check!\n")); - DEBUG_DELAY(); - #endif - - // 1. Check MUX - If we have lost the switch the entire subnet is down! - i2cip_errorlevel_t errlev = MUX::pingMUX(this->eeprom->getFQA()) ? I2CIP_ERR_NONE : I2CIP_ERR_HARD; - I2CIP_ERR_BREAK(errlev); - - // 3. Ping EEPROM - return this->eeprom->pingTimeout(true, false); -} - -i2cip_errorlevel_t Module::operator()(const i2cip_fqa_t& fqa, bool update, bool fail) { - // 1. Self-check - // i2cip_errorlevel_t errlev = this->operator()(); - // I2CIP_ERR_BREAK(errlev); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Module Device Check (FQA ")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(':'); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(")!\n"); - DEBUG_DELAY(); - #endif - - // 2. Device check - if(!this->isFQAinSubnet(fqa)) return I2CIP_ERR_SOFT; - Device* device = this->operator[](fqa); - if(device == nullptr) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Device Not Found!\n")); - DEBUG_DELAY(); - #endif - return I2CIP_ERR_SOFT; - } - i2cip_errorlevel_t errlev = device->pingTimeout(); - // I2CIP_ERR_BREAK(errlev); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - if (errlev == I2CIP_ERR_NONE) { - I2CIP_DEBUG_SERIAL.print(F("-> Device Alive!\n")); - } else { - I2CIP_DEBUG_SERIAL.print(F("-> Device Dead!\n")); - } - DEBUG_DELAY(); - #endif - - if(update) { - // 3. Update device (optional) - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("-> Updating Device\n")); - DEBUG_DELAY(); - #endif - // Do Output, then Input - if(device->getOutput()) { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("Output Set:\n")); - // DEBUG_DELAY(); - // #endif - errlev = fail ? device->getOutput()->failSet() : device->getOutput()->set(); - I2CIP_ERR_BREAK(errlev); - } - if(device->getInput()) { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("Input Get:\n")); - // DEBUG_DELAY(); - // #endif - errlev = fail ? device->getInput()->failGet() : device->getInput()->get(); - I2CIP_ERR_BREAK(errlev); - } - } - - return errlev; -} - -// Device* Module::operator[](const i2cip_fqa_t& fqa) { -// if(!this->isFQAinSubnet(fqa)) return nullptr; -// for(int i = 0; i < this->numdevices; i++) { -// if(this->devices[i]->getFQA() == fqa) return this->devices[i]; -// } -// return nullptr; -// } - -uint8_t Module::getWireNum(void) const { return this->wire; } - -uint8_t Module::getModuleNum(void) const { return this->mux; } \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/I2CIP.h b/software/microcontroller/lib/I2CIP/I2CIP.h deleted file mode 100644 index 039c9121..00000000 --- a/software/microcontroller/lib/I2CIP/I2CIP.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef I2CIP_H_ -#define I2CIP_H_ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -// #define I2CIP_FQA_SUBNET_MATCH(fqa, wire, module) (bool)(I2CIP_FQA_SEG_I2CBUS(fqa) == wire && I2CIP_FQA_SEG_MODULE(fqa) == module) -#define I2CIP_FQA_SUBNET_MATCH(fqa, _fqa) (bool)(I2CIP_FQA_SEG_I2CBUS(fqa) == I2CIP_FQA_SEG_I2CBUS(_fqa) && I2CIP_FQA_SEG_MODULE(fqa) == I2CIP_FQA_SEG_MODULE(_fqa)) -#define I2CIP_FQA_MODULE_MATCH(fqa, wire, module) (bool)(I2CIP_FQA_SEG_I2CBUS(fqa) == (wire) && I2CIP_FQA_SEG_MODULE(fqa) == (module)) -#define I2CIP_FQA_BUSADR_MATCH(fqa, bus, addr) (bool)(I2CIP_FQA_SEG_MUXBUS(fqa) == (bus) && I2CIP_FQA_SEG_DEVADR(fqa) == (addr)) - -namespace I2CIP { - - // Enables fundamentals subnet communication and state awareness. - // State is reflected across two data structures: - // 1. A BST of Device* by FQA - // 2. A HashTable of DeviceGroup& by ID - - class Module { - private: - const uint8_t wire; // I2C Wire Number (Index of `wires[]`) - const uint8_t mux; // MUX/Module Number (0x00 - 0x07, address range 0x70 - 0x77) - - bool isFQAinSubnet(const i2cip_fqa_t& fqa); - - // Tables/trees are allocated STATICALLY, their entries are dynamic - BST devices_fqabst = BST(); - HashTable devices_idgroups = HashTable(); - - HashTableEntry* addEmptyGroup(const char* id); - - bool eeprom_added = false; - - protected: - EEPROM* const eeprom; // EEPROM device - to be added to `devices_fqabst` and `devices_idgroups` on construction - - public: - Module(const uint8_t& wire, const uint8_t& module, const uint8_t& eeprom_addr = I2CIP_EEPROM_ADDR); - Module(const i2cip_fqa_t& eeprom_fqa); - - virtual ~Module(); - - uint8_t getWireNum(void) const; - uint8_t getModuleNum(void) const; - - /** - * 1. Module Self-check - * 1a. Check MUX - If we have lost the switch the entire subnet is down! - * 1b. Rebuild EEPROM if necessary - * 1c. Ping EEPROM - * 1d. TODO: Ping Devices - */ - i2cip_errorlevel_t operator()(void); // Module Self-check - - /** - * 2. Device Discovery - * 2a. Read EEPROM - * 2b. Parse EEPROM - * Intended Implementation: - * 2b i. Read EEPROM (by Bus) - * 2b ii. Ping Devices (by ID) - * 2b iii. Create (new) Devices and `add` to DeviceGroups (Made Available Internal API via `operator[]` functions) - */ - - /** - * Discover devices on the module. - * Side effect: Adds its own EEPROM to the proper DeviceGroup - * Side effect: Recurses if EEPROM parse fails; attempts to overwrite with default contents and reparse - * @param recurse {bool} - Whether to recursively parse EEPROM or not - * @returns `false` IFF fail to add EEPROM | failed to ping EEPROM | failed to parse EEPROM; `true` otherwise - */ - bool discover(bool recurse = true); - virtual bool parseEEPROMContents(const char* contents); - Device* add(Device& device, bool overwrite = false); - // virtual DeviceGroup* deviceGroupFactory(const i2cip_id_t& id) = 0; - virtual DeviceGroup* deviceGroupFactory(i2cip_id_t id); - - /** - * 3. Device Lookup: HashTable by ID, BST by FQA - * Intended Implementation: - * 3a i. Check if FQA is in subnet - * 3a ii. Check if FQA is in DeviceGroups - * - * 3b i. Check if ID is in DeviceGroups - * 3b ii. Return DeviceGroup - * - */ - DeviceGroup* operator[](i2cip_id_t id); - Device* operator[](const i2cip_fqa_t& fqa) const; - - /** - * 4. Device Check - */ - i2cip_errorlevel_t operator()(const i2cip_fqa_t& fqa, bool update = false, bool fail = false); - - void remove(Device* device, bool del = true); - - inline operator const EEPROM&() const { return *this->eeprom; } - }; -}; - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/bst.h b/software/microcontroller/lib/I2CIP/bst.h deleted file mode 100644 index be07ef40..00000000 --- a/software/microcontroller/lib/I2CIP/bst.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef I2CIP_BST_H_ -#define I2CIP_BST_H_ - -#include - -template class BSTNode { - public: - BSTNode(K key, T value, BSTNode* left = nullptr, BSTNode* right = nullptr); - ~BSTNode(); - - K key; // Mutable (copied) key - passed in - T value; // REFERENCE to Value - BSTNode* left; // Pointer to left (lesser) node - BSTNode* right; // Pointer to right (greater) node -}; - -template class BST { - public: - BST(); - - ~BST(); - - // Pointer to root node - all nodes de/allocated on insert()/remove() - BSTNode* root = nullptr; - - /** - * Recursively finds a spot to insert a new node. - * @param key - * @param value - * @param root - Pass-by-ref, to be modified on insertion - * @return Pointer to the inserted node - */ - static BSTNode* insert(K key, T value, BSTNode*& root, bool overwrite = true); - BSTNode* insert(K key, T value, bool overwrite = true); - - /** - * Recursively finds a node by key and removes it. - * @param key - * @param root - */ - static BSTNode* remove(K key, BSTNode*& root); - BSTNode* remove(K key); - - /** - * Recursively finds a node by key. Breadth-first. - * @param key - * @param root - */ - static BSTNode* find(K key, BSTNode* const root); - BSTNode* find(K key) const; - - T* operator[](K key) const; - - /** - * Recursively finds the least-key node. Depth-first. - * @param root - */ - static BSTNode* findMin(BSTNode* root); - BSTNode* findMin(void); - - /** - * Recursively finds the greatest-key node. Depth-first. - * @param root - */ - static BSTNode* findMax(BSTNode* root); - BSTNode* findMax(void); -}; - -#include - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/bst.tpp b/software/microcontroller/lib/I2CIP/bst.tpp deleted file mode 100644 index eda57920..00000000 --- a/software/microcontroller/lib/I2CIP/bst.tpp +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef I2CIP_BST_H_ -#error __FILE__ should only be included AFTER -#endif - -#ifdef I2CIP_BST_H_ - -#ifndef I2CIP_BST_T_ -#define I2CIP_BST_T_ - -// BST NODE - -template BSTNode::BSTNode(K key, T value, BSTNode* left, BSTNode* right) : key(key), value(value), left(left), right(right) { } - -template BSTNode::~BSTNode() { - delete(this->left); - delete(this->right); -} - -// BINARY SEARCH TREE - -template BST::BST() { } - -template BST::~BST() { - delete(this->root); -} - -template BSTNode* BST::insert(K key, T value, BSTNode*& root, bool overwrite) { - // Node empty? Allocate new. Otherwise, insert recursively - if(root == nullptr) { - root = new BSTNode(key, value); - #ifdef I2CIP_DEBUG_SERIAL - if(root == nullptr) I2CIP_DEBUG_SERIAL.print(F("BST(x) ENOMEM\n")); - #endif - } else if(key < root->key) { - return insert(key, value, root->left); - } else if(key > root->key) { - return insert(key, value, root->right); - } else if(overwrite) { - // Keys match, overwrite - root->value = value; - } - return root; -} - -template BSTNode* BST::insert(K key, T value, bool overwrite) { - return insert(key, value, this->root, overwrite); -} - -template BSTNode* BST::remove(K key, BSTNode*& root) { - BSTNode* const match = find(key, root); - if(match == nullptr) { - // Match not found - return nullptr; - } else { - // Match found, remove root properly - if(root->left && root->right) { - // Both branches exist, replace root with LEAST key on RIGHT branch - BSTNode* rightLeast = findMin(root->right); - - // Detach branches - BSTNode* newroot = new BSTNode(rightLeast->key, rightLeast->value, remove(rightLeast->key, root->right), root->left); - - // Detach and free old root - root->right = root->left = nullptr; - delete(root); - root = newroot; - } else { - // Branch(es) missing, replace with a branch node - if(root->left == nullptr) { - root = root->right; - } else if(root->right == nullptr) { - root = root->left; - } else { - // No branches on this match; delete - delete(root); - } - } - } - return root; -} - -template BSTNode* BST::remove(K key) { - return remove(key, this->root); -} - -template BSTNode* BST::find(K key, BSTNode* const root) { - if(root != nullptr) { - if(key < root->key) { - return find(key, root->left); - } else if(key > root->key) { - return find(key, root->right); - } - } - return root; -} - -template BSTNode* BST::find(K key) const { - return find(key, this->root); -} - -template T* BST::operator[](K key) const { - BSTNode* node = find(key); - #ifdef I2CIP_DEBUG_SERIAL - if(node == nullptr) I2CIP_DEBUG_SERIAL.print(F("BST[i] ENOENT\n")); - #endif - return node == nullptr ? nullptr : &(node->value); -} - -template BSTNode* BST::findMin(BSTNode* root) { - // If this is a leaf node, or there is no left node, return - if(root == nullptr || root->left == nullptr) { - return root; - } else { - return findMin(root->left); - } -} - -template BSTNode* BST::findMin(void) { - return findMin(this->root); -} - -template BSTNode* BST::findMax(BSTNode* root) { - // If this is a leaf node, or there is no right node, return - if(root == nullptr || root->right == nullptr) { - return root; - } else { - return findMin(root->right); - } -} - -template BSTNode* BST::findMax(void) { - return findMax(this->root); -} - -#endif -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/debug.h b/software/microcontroller/lib/I2CIP/debug.h deleted file mode 100644 index 90ca6cef..00000000 --- a/software/microcontroller/lib/I2CIP/debug.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef I2CIP_DEBUG_SERIAL - -#include - -// Uncomment to enable debug -// #define DEBUG 1 - -// CROSS-LIBRARY DEBUG COMPATIBILITY -// #ifdef DEBUG -// #if DEBUG == 1 -// #ifndef DEBUG_SERIAL -// #ifdef HAVE_HWSERIAL0 -// #define DEBUG_SERIAL Serial -// #endif -// #endif -// #endif -// #endif - -// #ifdef DEBUG_SERIAL -// // Just once -// #define I2CIP_DEBUG_SERIAL DEBUG_SERIAL - -// #ifndef DEBUG_DELAY -// #define DEBUG_DELAY() { delay(10); } -// #endif -// #endif - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/device.cc b/software/microcontroller/lib/I2CIP/device.cc deleted file mode 100644 index 066b8a70..00000000 --- a/software/microcontroller/lib/I2CIP/device.cc +++ /dev/null @@ -1,632 +0,0 @@ -#include - -#include -#include -#include - -using namespace I2CIP; - -// CONSTRUCTORs AND PROPERTY GETTERS/SETTERS - -Device::Device(const i2cip_fqa_t& fqa, i2cip_id_t id) : fqa(fqa), id(id) { } - -Device::~Device() { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("~Device\n")); - // DEBUG_DELAY(); - // #endif -} - -void Device::setInput(InputGetter* input) { if(this->input != nullptr) { delete this->input; } this->input = input; } -void Device::setOutput(OutputSetter* output) { if(this->output != nullptr) { delete this->output; } this->output = output; } - -void Device::removeInput(void) { if(this->input != nullptr) { delete this->input; } this->input = nullptr; } -void Device::removeOutput(void) { if(this->output != nullptr) { delete this->output; } this->output = nullptr; } - -InputGetter* Device::getInput(void) const { return this->input; } -OutputSetter* Device::getOutput(void) const { return this->output; } - -const char InputGetter::failptr_get; -const char OutputSetter::failptr_set; - -InputGetter::~InputGetter() { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("~InputGetter")); - // DEBUG_DELAY(); - // #endif -} -OutputSetter::~OutputSetter() { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("~OutputSetter")); - // DEBUG_DELAY(); - // #endif -} - -i2cip_errorlevel_t Device::get(const void* args) { return (this->getInput() == nullptr) ? I2CIP_ERR_SOFT : this->input->get(args); } -i2cip_errorlevel_t Device::set(const void* value, const void* args) { return (this->output == nullptr) ? I2CIP_ERR_SOFT : this->output->set(value, args); } - -const i2cip_fqa_t& Device::getFQA(void) const { return this->fqa; } - -const i2cip_id_t& Device::getID(void) const { return this->id; } - -// STATIC CLASS-MEMBER FUNCTIONS (PRIVATE INTERNAL API) - -i2cip_errorlevel_t Device::ping(const i2cip_fqa_t& fqa, bool resetbus) { - // Switch MUX bus - i2cip_errorlevel_t errlev = MUX::setBus(fqa); - I2CIP_ERR_BREAK(errlev); - - // Begin transmission - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Ping... ")); - #endif - - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); - - // End transmission, check state - if(I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { - return I2CIP_ERR_HARD; - } - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.println("Pong!"); - DEBUG_DELAY(); - #endif - - // Switch MUX bus back - if (resetbus) { - errlev = MUX::resetBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - // If we made it this far, no errors occurred. - return I2CIP_ERR_NONE; -} - -i2cip_errorlevel_t Device::pingTimeout(const i2cip_fqa_t& fqa, bool setbus, bool resetbus, unsigned int timeout) { - if(setbus) { - i2cip_errorlevel_t errlev = MUX::setBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - // Check if it's actually lost - - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); - i2cip_errorlevel_t errlev = (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() == 0 ? I2CIP_ERR_NONE : I2CIP_ERR_HARD); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Ping... ")); - #endif - - unsigned long start = millis(); - - // Count down until out of time of found - while ((millis()-start) < timeout) { - #ifndef I2CIP_DEBUG_SERIAL - // Delta 1ms - delay(1); - #endif - - // Begin transmission - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); - - // End transmission, check state - errlev = (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() == 0 ? I2CIP_ERR_NONE : I2CIP_ERR_HARD); - - if (errlev == I2CIP_ERR_NONE) { - break; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Ping... ")); - #endif - } - - // Double check MUX before attempting to switch - if(errlev == I2CIP_ERR_HARD && !MUX::pingMUX(fqa)) { - return I2CIP_ERR_HARD; - } - - #ifdef I2CIP_DEBUG_SERIAL - if(errlev == I2CIP_ERR_HARD) { - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.println("Timed Out!"); - DEBUG_DELAY(); - } else { - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Pong! Ping Timeout: ")); - I2CIP_DEBUG_SERIAL.print(millis()-start); - I2CIP_DEBUG_SERIAL.print(F("ms\n")); - DEBUG_DELAY(); - } - #endif - - // Switch MUX bus back - if (resetbus) { - errlev = MUX::resetBus(fqa); - } - - return errlev; -} - -i2cip_errorlevel_t Device::writeByte(const i2cip_fqa_t& fqa, const uint8_t& value, bool setbus) { - i2cip_errorlevel_t errlev; - if (setbus) { - // Switch MUX bus - i2cip_errorlevel_t errlev = MUX::setBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - // Was the write performed successfully? - bool success = true; - - // Begin transmission - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); - - // Write the buffer - if (I2CIP_FQA_TO_WIRE(fqa)->write(value) != 1) { - success = false; - } - - // End transmission - if (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { - return I2CIP_ERR_HARD; - } - - // Reset MUX bus if `reset` == true - if (setbus) { - errlev = MUX::resetBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); -} - -i2cip_errorlevel_t Device::write(const i2cip_fqa_t& fqa, const uint8_t* buffer, size_t len, bool setbus) { - i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; - if (setbus) { - errlev = MUX::setBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - // Was the write performed successfully? - bool success = true; - - // Begin transmission - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_FQA_SEG_DEVADR(fqa)); - - // Write the buffer - size_t sent = I2CIP_FQA_TO_WIRE(fqa)->write(buffer, len); - if (sent != len) { - success = false; - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("Write Failed (")); - // I2CIP_DEBUG_SERIAL.print(sent); - // I2CIP_DEBUG_SERIAL.print(F("/")); - // I2CIP_DEBUG_SERIAL.print(len); - // I2CIP_DEBUG_SERIAL.print(F(" bytes sent)\n")); - // DEBUG_DELAY(); - // #endif - } - - // End transmission - if (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("No ACK On Write! ")); - DEBUG_DELAY(); - #endif - return I2CIP_ERR_HARD; - } - - // Reset MUX bus if `reset` == true - if (setbus) { - errlev = MUX::resetBus(fqa); - } - - return ((success || errlev > I2CIP_ERR_NONE) ? errlev : I2CIP_ERR_SOFT); -} - -i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, const uint8_t& value, bool setbus) { - const uint8_t buf[2] = { reg, value }; - return write(fqa, buf, 2, setbus); -} - -i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, const uint8_t& value, bool setbus) { - const uint8_t buf[3] = { (uint8_t)(reg >> 8), (uint8_t)(reg & 0xFF), value }; - return write(fqa, buf, 3, setbus); -} - -i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* buffer, size_t len, bool setbus) { - uint8_t buff[len + 1] = { reg }; - for(size_t i = 0; i < len; i++) { - buff[i + 1] = buffer[i]; - } - return write(fqa, buff, len + 1, setbus); -} - -i2cip_errorlevel_t Device::writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* buffer, size_t len, bool setbus) { - uint8_t buff[len + 2] = { (uint8_t)(reg >> 8), (uint8_t)(reg & 0xFF) }; - for(size_t i = 0; i < len; i++) { - buff[i + 2] = buffer[i]; - } - return write(fqa, buff, len + 2, setbus); -} - -i2cip_errorlevel_t Device::read(const i2cip_fqa_t& fqa, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { - // Device alive? - i2cip_errorlevel_t errlev = ping(fqa, false); - I2CIP_ERR_BREAK(errlev); - - // Read in chunks (buffer size limitation) - size_t pos = 0; - bool success = true; - while (pos < len) { - // Read whichever is greater: number of bytes remaining, or buffer size - uint8_t read_len = ((len - pos) > I2CIP_MAXBUFFER) ? I2CIP_MAXBUFFER : (len - pos); - - // Don't stop the bus unless we've read everything - bool read_stop = (pos + read_len >= len); - - // Request bytes; How many have we received? - size_t recv = I2CIP_FQA_TO_WIRE(fqa)->requestFrom(I2CIP_FQA_SEG_DEVADR(fqa), read_len, (uint8_t)read_stop); - - // We didn't get all the bytes we expected - if (recv != read_len) { - success = false; - len = pos; - break; - } - - // Read in all the bytes - for (uint16_t i = 0; i < read_len; i++) { - dest[pos + i] = I2CIP_FQA_TO_WIRE(fqa)->read(); - if(nullterminate && dest[pos + i] == '\0') { - len = pos + i; - goto endloop0; - } - } - - // Advance the index by the amount of bytes read - pos += read_len; - } -endloop0: - - // Reset MUX bus if `reset` == true - if (resetbus) { - errlev = MUX::resetBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - // Did we read all the bytes we hoped to? - return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); -} - -i2cip_errorlevel_t Device::readByte(const i2cip_fqa_t& fqa, uint8_t& dest, bool resetbus) { - size_t len = 1; - return read(fqa, &dest, len, resetbus); -} - -i2cip_errorlevel_t Device::readWord(const i2cip_fqa_t& fqa, uint16_t& dest, bool resetbus) { - size_t len = 2; - uint8_t buff[2]; - i2cip_errorlevel_t errlev = read(fqa, buff, len, resetbus); - I2CIP_ERR_BREAK(errlev); - dest = ((uint16_t)buff[1] << 8) | (uint16_t)buff[0]; - return errlev; -} - -i2cip_errorlevel_t Device::readRegisterByte(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t& dest, bool resetbus) { - size_t len = 1; - return readRegister(fqa, reg, &dest, len, false, resetbus); -} - -i2cip_errorlevel_t Device::readRegisterByte(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t& dest, bool resetbus) { - size_t len = 1; - return readRegister(fqa, reg, &dest, len, false, resetbus); -} - -i2cip_errorlevel_t Device::readRegisterWord(const i2cip_fqa_t& fqa, const uint8_t& reg, uint16_t& dest, bool resetbus) { - size_t len = 2; - uint8_t buff[2]; - i2cip_errorlevel_t errlev = readRegister(fqa, reg, buff, len, false, resetbus); - I2CIP_ERR_BREAK(errlev); - dest = ((uint16_t)buff[1] << 8) | (uint16_t)buff[0]; - return errlev; -} - -i2cip_errorlevel_t Device::readRegisterWord(const i2cip_fqa_t& fqa, const uint16_t& reg, uint16_t& dest, bool resetbus) { - size_t len = 2; - uint8_t buff[2]; - i2cip_errorlevel_t errlev = readRegister(fqa, reg, buff, len, false, resetbus); - I2CIP_ERR_BREAK(errlev); - dest = ((uint16_t)buff[1] << 8) | (uint16_t)buff[0]; - return errlev; -} - -i2cip_errorlevel_t Device::readRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { - // Device alive? - i2cip_errorlevel_t errlev = ping(fqa, false); - I2CIP_ERR_BREAK(errlev); - - // Read in chunks (buffer size limitation) - size_t pos = 0; - bool success = true; - while (pos < len) { - // Read whichever is greater: number of bytes remaining, or buffer size - uint8_t read_len = ((len - pos) > I2CIP_MAXBUFFER) ? I2CIP_MAXBUFFER : (len - pos); - - // Don't stop the bus unless we've read everything - bool read_stop = (pos + read_len >= len); - - // Request bytes; How many have we received? - size_t recv = I2CIP_FQA_TO_WIRE(fqa)->requestFrom(I2CIP_FQA_SEG_DEVADR(fqa), read_len, reg, 1, (uint8_t)read_stop); - - // We didn't get all the bytes we expected - if (recv != read_len) { - success = false; - len = pos; - break; - } - - // Read in all the bytes - for (uint16_t i = 0; i < read_len; i++) { - dest[pos + i] = I2CIP_FQA_TO_WIRE(fqa)->read(); - if(nullterminate && dest[pos + i] == '\0') { - len = pos + i; - goto endloop1; - } - } - - // Advance the index by the amount of bytes read - pos += read_len; - } -endloop1: - - // Reset MUX bus if `reset` == true - if (resetbus) { - errlev = MUX::resetBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - // Did we read all the bytes we hoped to? - return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); -} - -i2cip_errorlevel_t Device::readRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { - // Device alive? - i2cip_errorlevel_t errlev = ping(fqa, false); - I2CIP_ERR_BREAK(errlev); - - // Read in chunks (buffer size limitation) - size_t pos = 0; - bool success = true; - while (pos < len) { - // Read whichever is greater: number of bytes remaining, or buffer size - uint8_t read_len = min(len - pos, I2CIP_MAXBUFFER); - - // Don't stop the bus unless we've read everything - bool read_stop = (pos + read_len >= len); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Reading bytes ")); - I2CIP_DEBUG_SERIAL.print(pos); - I2CIP_DEBUG_SERIAL.print(F(" - ")); - I2CIP_DEBUG_SERIAL.print(pos+read_len); - I2CIP_DEBUG_SERIAL.print(F(": '")); - DEBUG_DELAY(); - #endif - - // Request bytes; How many have we received? - size_t recv = I2CIP_FQA_TO_WIRE(fqa)->requestFrom(I2CIP_FQA_SEG_DEVADR(fqa), read_len, reg, 2, (uint8_t)read_stop); - - // We didn't get all the bytes we expected - if (recv != read_len) { - success = false; - } - - // Read in all the bytes received - for (uint16_t i = 0; i < recv; i++) { - dest[pos + i] = I2CIP_FQA_TO_WIRE(fqa)->read(); - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print((char)dest[pos + i]); - #endif - if(nullterminate && dest[pos + i] == '\0') { - len = pos + i; - goto endloop2; - } - } - - // Advance the index by the amount of bytes received - pos += recv; - } -endloop2: - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.println('\''); - DEBUG_DELAY(); - #endif - - // Reset MUX bus if `reset` == true - if (resetbus) { - errlev = MUX::resetBus(fqa); - I2CIP_ERR_BREAK(errlev); - } - - // Did we read all the bytes we hoped to? - return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); -} - -// NON-STATIC OBJECT-MEMBER FUNCTIONS (PUBLIC EXTERNAL API) - -i2cip_errorlevel_t Device::ping(bool resetbus) { return Device::ping(this->fqa, resetbus); } -i2cip_errorlevel_t Device::pingTimeout(bool setbus, bool resetbus, unsigned int timeout) { return Device::pingTimeout(this->fqa, setbus, resetbus, timeout); } -i2cip_errorlevel_t Device::writeByte(const uint8_t& value, bool setbus) { return Device::writeByte(this->fqa, value, setbus); } -i2cip_errorlevel_t Device::write(const uint8_t* buffer, size_t len, bool setbus) { return Device::write(this->fqa, buffer, len, setbus); } -i2cip_errorlevel_t Device::writeRegister(const uint8_t& reg, const uint8_t& value, bool setbus) { return Device::writeRegister(this->fqa, reg, value, setbus); } -i2cip_errorlevel_t Device::writeRegister(const uint16_t& reg, const uint8_t& value, bool setbus) { return Device::writeRegister(this->fqa, reg, value, setbus); } -i2cip_errorlevel_t Device::writeRegister(const uint8_t& reg, uint8_t* buffer, size_t len, bool setbus) { return Device::writeRegister(this->fqa, reg, buffer, len, setbus); } -i2cip_errorlevel_t Device::writeRegister(const uint16_t& reg, uint8_t* buffer, size_t len, bool setbus) { return Device::writeRegister(this->fqa, reg, buffer, len, setbus); } -i2cip_errorlevel_t Device::read(uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { return Device::read(this->fqa, dest, len, nullterminate, resetbus); } -i2cip_errorlevel_t Device::readByte(uint8_t& dest, bool resetbus) { return Device::readByte(this->fqa, dest, resetbus); } -i2cip_errorlevel_t Device::readWord(uint16_t& dest, bool resetbus) { return Device::readWord(this->fqa, dest, resetbus); } -i2cip_errorlevel_t Device::readRegister(const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { return Device::readRegister(this->fqa, reg, dest, len, nullterminate, resetbus); } -i2cip_errorlevel_t Device::readRegister(const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate, bool resetbus) { return Device::readRegister(this->fqa, reg, dest, len, nullterminate, resetbus); } -i2cip_errorlevel_t Device::readRegisterByte(const uint8_t& reg, uint8_t& dest, bool resetbus) { return Device::readRegisterByte(this->fqa, reg, dest, resetbus); } -i2cip_errorlevel_t Device::readRegisterByte(const uint16_t& reg, uint8_t& dest, bool resetbus) { return Device::readRegisterByte(this->fqa, reg, dest, resetbus); } -i2cip_errorlevel_t Device::readRegisterWord(const uint8_t& reg, uint16_t& dest, bool resetbus) { return Device::readRegisterWord(this->fqa, reg, dest, resetbus); } -i2cip_errorlevel_t Device::readRegisterWord(const uint16_t& reg, uint16_t& dest, bool resetbus) { return Device::readRegisterWord(this->fqa, reg, dest, resetbus); } - -DeviceGroup::DeviceGroup(i2cip_id_t key, factory_device_t factory) : key(key), factory(factory) { - for(uint8_t i = 0; i < I2CIP_DEVICES_PER_GROUP; i++) { - devices[i] = nullptr; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Constructed DeviceGroup('")); - I2CIP_DEBUG_SERIAL.print(this->key); - I2CIP_DEBUG_SERIAL.print(F("' @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)this->key, HEX); - I2CIP_DEBUG_SERIAL.print(F(", Device* (*)(fqa) @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)factory, HEX); - I2CIP_DEBUG_SERIAL.print(F(")\n")); - DEBUG_DELAY(); - #endif -} - -DeviceGroup::~DeviceGroup(void) { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("~DeviceGroup")); - // DEBUG_DELAY(); - // #endif - for(uint8_t i = 0; i < I2CIP_DEVICES_PER_GROUP; i++) { - if(devices[i] != nullptr) { - delete devices[i]; - } - } - -} - -bool DeviceGroup::add(Device& device) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("DeviceGroup (ID '")); - I2CIP_DEBUG_SERIAL.print(this->key); - I2CIP_DEBUG_SERIAL.print(F("' @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)this->key, HEX); - I2CIP_DEBUG_SERIAL.print(F(") Add Device (ID '")); - I2CIP_DEBUG_SERIAL.print(device.getID()); - I2CIP_DEBUG_SERIAL.print(F("' @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)device.getID(), HEX); - I2CIP_DEBUG_SERIAL.print(")\n"); - DEBUG_DELAY(); - #endif - if((const char*)device.getID() != (const char*)this->key && strcmp(device.getID(), this->key) != 0) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F(": Failed\n")); - DEBUG_DELAY(); - #endif - return false; - } - - if(this->contains(&device)) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F(": NOP\n")); - DEBUG_DELAY(); - #endif - return true; // Already added - } - - unsigned int n = 0; - while(this->devices[n] != nullptr) { n++; if(n > I2CIP_DEVICES_PER_GROUP) return false;} - - // Append new devices - this->devices[n] = &device; - this->numdevices = (n + 1); - return true; -} - -bool DeviceGroup::addGroup(Device* devices[], uint8_t numdevices) { - unsigned int n = 0; - while(this->devices[n] != nullptr) { n++; if(n + numdevices > I2CIP_DEVICES_PER_GROUP) return false; } - - // Append new devices - for(unsigned int i = 0; i < numdevices && (n + i) < I2CIP_DEVICES_PER_GROUP; i++) { - this->devices[n+i] = devices[i]; - this->numdevices++; - } - return true; -} - -void DeviceGroup::remove(Device* device) { - bool swap = false; - for(int i = 0; i < this->numdevices-1; i++) { - if(swap) { - this->devices[i - 1] = this->devices[i]; - } - if(this->devices[i] == nullptr) break; - if(this->devices[i]->getFQA() == device->getFQA()) { - this->devices[i] = nullptr; - this->numdevices--; - swap = true; - } - } -} - -bool DeviceGroup::contains(Device* device) const { - if(device == nullptr || ((const char*)device->getID() != (const char*)this->key && strcmp(device->getID(), this->key) != 0)) return false; - return this->contains(device->getFQA()); -} - -bool DeviceGroup::contains(const i2cip_fqa_t& fqa) const { - for(int i = 0; i < this->numdevices; i++) { - if(this->devices[i] == nullptr) break; - if(this->devices[i]->getFQA() == fqa) return true; - } - return false; -} - -Device* DeviceGroup::operator[](const i2cip_fqa_t& fqa) const { - for(int i = 0; i < this->numdevices; i++) { - if(this->devices[i] == nullptr) break; - if((i2cip_fqa_t)this->devices[i]->getFQA() == (i2cip_fqa_t)fqa) return this->devices[i]; - } - return nullptr; -} - -Device* DeviceGroup::operator()(const i2cip_fqa_t& fqa) { - if(this->contains(fqa)) { - return (*this)[fqa]; - } - - Device* device = this->factory(fqa); - - if(device != nullptr) { - bool b = this->add(*device); - - if(!b) { - delete device; - return nullptr; - } - } - - return device; -} - -// DeviceGroup& DeviceGroup::operator=(const DeviceGroup& rhs) { -// for(unsigned char i = 0; i < I2CIP_DEVICES_PER_GROUP; i++) this->devices[i] = rhs.devices[i]; -// this->numdevices = rhs.numdevices; -// this->factory = rhs.factory; -// this->key = rhs.key; -// return *this; -// } \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/device.h b/software/microcontroller/lib/I2CIP/device.h deleted file mode 100644 index 4797aaf0..00000000 --- a/software/microcontroller/lib/I2CIP/device.h +++ /dev/null @@ -1,398 +0,0 @@ -#ifndef I2CIP_DEVICE_H_ -#define I2CIP_DEVICE_H_ - -#include -#include - -/** - * Shifts and masks a number's bits. - * @param data Data to shift/mask - * @param lsb LSB position - * @param bits Number of bits to keep - **/ -#define READ_BITS(data, lsb, bits) (((data) >> (lsb)) & ((1 << (bits)) - 1)) - -/** - * Overwrites some bits in existing data. - * @param existing Existing data - * @param data New data - * @param lsb Position to insert (LSB) - * @param bits Number of bits to overwrite - **/ -#define OVERWRITE_BITS(existing, data, lsb, bits) (((existing) & ~(((1 << (bits)) - 1) << (lsb))) | (((data) & ((1 << (bits)) - 1)) << (lsb))) - -#define I2CIP_DEVICES_PER_GROUP ((size_t)4) -#define I2CIP_ID_SIZE ((size_t)10) - -namespace ControlSystemsOS { - class Sensor; -} - -namespace I2CIP { - - class Module; - - // typedef enum { I2CIP_ITYPE_NULL = 0b00, I2CIP_ITYPE_INPUT = 0b01, I2CIP_ITYPE_OUTPUT = 0b10, I2CIP_ITYPE_IO = 0b11 } i2cip_itype_t; - - // Barebones template-less abstract classes expose voidptr hooks for the device to be used as an input or output - - class InputGetter { - protected: - static const char failptr_get = '\a'; - - ControlSystemsOS::Sensor* sensor = nullptr; - public: - virtual ~InputGetter() = 0; - virtual i2cip_errorlevel_t get(const void* args = nullptr) { return I2CIP_ERR_HARD; } // Unimplemented; delete this device - i2cip_errorlevel_t failGet(void) { return this->get(&failptr_get); } - - ControlSystemsOS::Sensor* getSensor(void) const { return this->sensor; } - void setSensor(ControlSystemsOS::Sensor* sensor) { this->sensor = sensor; } - }; - - class OutputSetter { - protected: - static const char failptr_set = '\a'; - public: - virtual ~OutputSetter() = 0; - virtual i2cip_errorlevel_t set(const void* value = nullptr, const void* args = nullptr) { return I2CIP_ERR_HARD; } // Unimplemented; delete this device - i2cip_errorlevel_t reset(const void* args = nullptr) { return this->set(&failptr_set, args); } - i2cip_errorlevel_t failSet(const void* value) { return this->set(value, &failptr_set); } - i2cip_errorlevel_t failSet(void) { return this->set(&failptr_set, &failptr_set); } - }; - - class Device { - protected: - const i2cip_fqa_t fqa; - i2cip_id_t id; - - // // Set by public API, deleted on deconstruction - InputGetter* input = nullptr; - OutputSetter* output = nullptr; - - Device(const i2cip_fqa_t& fqa, i2cip_id_t id); - - /** - * Attempt to communicate with a device. Always sets the bus. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | { resetbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param resetbus Should the bus be reset? (Default: `true`) - * @return Hardware failure: Device lost; no ACK (check MUX). Software failure: Failed to switch MUX bus. - */ - static i2cip_errorlevel_t ping(const i2cip_fqa_t& fqa, bool resetbus = true); - - /** - * Attempt to communicate with a device repeatedly until timeout. Always sets the bus. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | { until ACK or timeout: DEV ADDR (7) | ACK? | } { failed? : | MUX ADDR (7) | ACK? | }{ resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | - * @param fqa FQA of the device - * @param setbus Should the bus be set? (Default: `true`, set false if checking EEPROM write!) - * @param resetbus Should the bus be reset? (Default: `true`, set false if checking EEPROM write!) - * @param timeout Attempt duration (ms) - * @return Hardware failure: Device unreachable, module check. Software failure: Failed to switch MUX bus - */ - static i2cip_errorlevel_t pingTimeout(const i2cip_fqa_t& fqa, bool setbus = true, bool resetbus = true, unsigned int timeout = 100); - - /** - * Write one byte to a device. - * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | DATA BYTE (8) | ACK? | { setbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param value Byte to be written - * @param setbus Should the MUX be set and reset? (Default: `true`) - * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus - */ - static i2cip_errorlevel_t writeByte(const i2cip_fqa_t& fqa, const uint8_t& value, bool setbus = true); - - /** - * Write data to a device. - * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | DATA BYTE (8 * len) | ACK? | { setbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param buffer Bytes to be sent - * @param len Number of bytes (Default: `1`) - * @param setbus Should the MUX be set and reset? (Default: `true`) - * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus - */ - static i2cip_errorlevel_t write(const i2cip_fqa_t& fqa, const uint8_t* buffer, size_t len = 1, bool setbus = true); - - /** - * Write one byte to a device's register. Effectively adds ONE prefix byte. - * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | REG ADDR (16) | DATA BYTE (8) | ACK? | { setbus? : MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param reg Register address - * @param value Byte to be written - * @param setbus Should the MUX be reset? (Default: `true`) - * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus - */ - static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, const uint8_t& value, bool setbus = true); - - /** - * Write one byte to a device's register (16-bit register address). Effectively adds TWO prefix bytes. - * | { setbus? : MUX ADDR (7) | MUX CONFIG (8) | ACK? | } DEV ADDR (7) | REG ADDR (16) | DATA BYTE (8) | ACK? | { setbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param reg Register address - * @param value Byte to be written - * @param setbus Should the MUX be reset? (Default: `true`) - * @return Hardware failure: Device and/or module lost. Software failure: Failed to write and/or failed to switch MUX bus - */ - static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, const uint8_t& value, bool setbus = true); - - static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); - - static i2cip_errorlevel_t writeRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); - - /** - * Request and read in data from a device. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | READ BYTES (8*len) | - * resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | - * @param fqa FQA of the device - * @param dest Bytes to read into - * @param len Number of bytes to read (Default: `1`) - * @param setbus Should the MUX be reset? (Default: `true`) - */ - static i2cip_errorlevel_t read(const i2cip_fqa_t& fqa, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); - - /** - * Read one byte of data from the device. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | READ BYTES (8) | - * resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | - * @param fqa FQA of the device - * @param dest Byte to read into - * @param setbus Should the MUX be reset? (Default: `true`) - */ - static i2cip_errorlevel_t readByte(const i2cip_fqa_t& fqa, uint8_t& dest, bool resetbus = true); - - /** - * Read one byte of data from the device. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | READ BYTES (16) | - * resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | - * @param fqa FQA of the device - * @param dest Word to read into - * @param setbus Should the MUX be reset? (Default: `true`) - */ - static i2cip_errorlevel_t readWord(const i2cip_fqa_t& fqa, uint16_t& dest, bool resetbus = true); - - static i2cip_errorlevel_t readRegister(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); - - static i2cip_errorlevel_t readRegister(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); - - - /** - * Read one byte of data from the device. Effectively adds a prefix byte. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (8) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param dest Bytes to read into - * @param setbus Should the MUX be reset? (Default: `true`) - */ - static i2cip_errorlevel_t readRegisterByte(const i2cip_fqa_t& fqa, const uint8_t& reg, uint8_t& dest, bool resetbus = true); - - /** - * Read one byte of data from the device. Effectively adds TWO prefix bytes. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (16) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param dest Bytes to read into - * @param setbus Should the MUX be reset? (Default: `true`) - **/ - static i2cip_errorlevel_t readRegisterByte(const i2cip_fqa_t& fqa, const uint16_t& reg, uint8_t& dest, bool resetbus = true); - - /** - * Read two bytes of data from the device. Effectively adds one prefix bytes. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (16) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param dest Bytes to read into - * @param setbus Should the MUX be reset? (Default: `true`) - **/ - static i2cip_errorlevel_t readRegisterWord(const i2cip_fqa_t& fqa, const uint8_t& reg, uint16_t& dest, bool resetbus = true); - - /** - * Read two bytes of data from the device. Effectively adds TWO prefix bytes. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | DEV ADDR (7) | ACK? | DEV ADDR (7) | REG ADDR (16) | READ BYTES (8*len) | { resetbus? : | MUX ADDR (7) | MUX RESET (8) | ACK? | } - * @param fqa FQA of the device - * @param dest Bytes to read into - * @param setbus Should the MUX be reset? (Default: `true`) - **/ - static i2cip_errorlevel_t readRegisterWord(const i2cip_fqa_t& fqa, const uint16_t& reg, uint16_t& dest, bool resetbus = true); - - public: - virtual ~Device() = 0; - - void setInput(InputGetter* input); - void setOutput(OutputSetter* output); - - void removeInput(void); - void removeOutput(void); - - InputGetter* getInput(void) const; - OutputSetter* getOutput(void) const; - - i2cip_errorlevel_t get(const void* args = nullptr); - i2cip_errorlevel_t set(const void* value = nullptr, const void* args = nullptr); - - const i2cip_fqa_t& getFQA(void) const; - const i2cip_id_t& getID(void) const; - - i2cip_errorlevel_t ping(bool resetbus = true); - i2cip_errorlevel_t pingTimeout(bool setbus = true, bool resetbus = true, unsigned int timeout = 100); - i2cip_errorlevel_t writeByte(const uint8_t& value, bool setbus = true); - i2cip_errorlevel_t write(const uint8_t* buffer, size_t len = 1, bool setbus = true); - i2cip_errorlevel_t writeRegister(const uint8_t& reg, const uint8_t& value, bool setbus = true); - i2cip_errorlevel_t writeRegister(const uint16_t& reg, const uint8_t& value, bool setbus = true); - i2cip_errorlevel_t writeRegister(const uint8_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); - i2cip_errorlevel_t writeRegister(const uint16_t& reg, uint8_t* buffer, size_t len = 1, bool setbus = true); - i2cip_errorlevel_t read(uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); - i2cip_errorlevel_t readByte(uint8_t& dest, bool resetbus = true); - i2cip_errorlevel_t readWord(uint16_t& dest, bool resetbus = true); - i2cip_errorlevel_t readRegister(const uint8_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); - i2cip_errorlevel_t readRegister(const uint16_t& reg, uint8_t* dest, size_t& len, bool nullterminate = true, bool resetbus = true); - i2cip_errorlevel_t readRegisterByte(const uint8_t& reg, uint8_t& dest, bool resetbus = true); - i2cip_errorlevel_t readRegisterByte(const uint16_t& reg, uint8_t& dest, bool resetbus = true); - i2cip_errorlevel_t readRegisterWord(const uint8_t& reg, uint16_t& dest, bool resetbus = true); - i2cip_errorlevel_t readRegisterWord(const uint16_t& reg, uint16_t& dest, bool resetbus = true); - - inline operator i2cip_fqa_t() const { return this->fqa; } - }; - - typedef Device* (* factory_device_t)(const i2cip_fqa_t& fqa); - - class DeviceGroup { - friend class Module; - - protected: - bool add(Device& device); - bool addGroup(Device* devices[], uint8_t numdevices); - void remove(Device* device); - - public: - i2cip_id_t key; - uint8_t numdevices = 0; - Device* devices[I2CIP_DEVICES_PER_GROUP] = { nullptr }; - - factory_device_t factory; - - DeviceGroup(i2cip_id_t key, factory_device_t factory); - - ~DeviceGroup(); - - bool contains(Device* device) const; - bool contains(const i2cip_fqa_t& fqa) const; - - Device* operator[](const i2cip_fqa_t& fqa) const; - - // DeviceGroup& operator=(const DeviceGroup& rhs); - - Device* operator()(const i2cip_fqa_t& fqa); - }; - - /** - * An I2CIP peripheral used for input/state "getting". - * @param G type used for "get" variable - * @param A type used for "get" arguments - **/ - template class InputInterface : public InputGetter { - private: - G cache; // Last RECIEVED value - A argsA; // Last passed arguments - - bool argsAset = false; - protected: - void setCache(G value); - void setArgsA(A args); - - /** - * Gets the default arguments used for the "get" operation. - * A constant reference. - * To be implemented by the child class. - */ - virtual const A& getDefaultA(void) const = 0; - public: - InputInterface(Device* device); - virtual ~InputInterface() = 0; - - i2cip_errorlevel_t get(const void* args = nullptr) override; - - /** - * Gets the last recieved value. - */ - G getCache(void) const; - - /** - * Sets the cache to the default "zero" value. - * To be implemented by the child class. - */ - virtual void clearCache(void); - - /** - * Gets the arguments used for the last "get" operation. - */ - A getArgsA(void) const; - - /** - * Gets the input device's state. - **/ - virtual i2cip_errorlevel_t get(G& dest, const A& args) { return I2CIP_ERR_HARD; } // Unimplemented; Disable this device - }; - - /** - * An I2CIP peripheral used for output/state "setting". - * @param S type used for "set" value - * @param B type used for "set" arguments - **/ - template class OutputInterface : public OutputSetter { - private: - S value; // Last SET value (not PASSED value) - B argsB; // Last passed arguments - - bool argsBset = false; - protected: - void setValue(S value); - void setArgsB(B args); - - /** - * Gets the default arguments used for the "set" operation. - * To be implemented by the child class. - */ - virtual const B& getDefaultB(void) const = 0; - public: - OutputInterface(Device* device); - - virtual ~OutputInterface() = 0; - - i2cip_errorlevel_t set(const void* value = nullptr, const void* args = nullptr) override; - - /** - * Gets the arguments used for the last "set" operation. - */ - B getArgsB(void) const; - - /** - * Gets the last set value. - */ - S getValue(void) const; - - /** - * Gets the default "zero"/off-state value. - * To be implemented by the child class. - */ - virtual void resetFailsafe(void); - - /** - * Sets the output device's state. - **/ - virtual i2cip_errorlevel_t set(const S& value, const B& args) { return I2CIP_ERR_HARD; } // Unimplemented; Disable this device - }; - - /** - * An I2CIP peripheral used for input/state "getting" as well as output/state "setting". - * @param G type used for "get" variable - * @param A type used for "get" arguments - * @param S type used for "set" value - * @param B type used for "set" arguments - **/ - template class IOInterface : public InputInterface, public OutputInterface { - public: - IOInterface(Device* device); - virtual ~IOInterface() = 0; - }; -}; - -#include - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/device.tpp b/software/microcontroller/lib/I2CIP/device.tpp deleted file mode 100644 index 113eb39c..00000000 --- a/software/microcontroller/lib/I2CIP/device.tpp +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef I2CIP_DEVICE_H_ -#error __FILE__ should only be included AFTER -// #include -#endif - -#ifdef I2CIP_DEVICE_H_ - -#ifndef I2CIP_DEVICE_T_ -#define I2CIP_DEVICE_T_ - -#include - -using I2CIP::i2cip_errorlevel_t; -using I2CIP::InputGetter; -using I2CIP::OutputSetter; -using I2CIP::InputInterface; -using I2CIP::OutputInterface; -using I2CIP::IOInterface; - -template InputInterface::InputInterface(Device* device) { device->setInput(this); } - -template InputInterface::~InputInterface() { } - -template G InputInterface::getCache(void) const { return this->cache; } - -template void InputInterface::setCache(G value) { this->cache = value; } - -template void InputInterface::clearCache(void) { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("InputInterface::clearCache() Not Implemented; Nothing Done\n")); - // DEBUG_DELAY(); - // #endif -} - -template void InputInterface::setArgsA(A args) { this->argsA = args; } - -template A InputInterface::getArgsA(void) const { return this->argsA; } - -template i2cip_errorlevel_t InputInterface::get(const void* args) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("InputInterface::get(")); - if(args == &InputGetter::failptr_get) I2CIP_DEBUG_SERIAL.print(F("fail")); - else if(args == nullptr) I2CIP_DEBUG_SERIAL.print(F("null")); - else { - I2CIP_DEBUG_SERIAL.print(*(A* const)args); - I2CIP_DEBUG_SERIAL.print(F(" @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)args, HEX); - } - I2CIP_DEBUG_SERIAL.print(F(")\n")); - DEBUG_DELAY(); - #endif - - if (args == &InputGetter::failptr_get) this->clearCache(); - G temp = this->cache; - - if(!this->argsAset) { this->argsA = this->getDefaultA(); this->argsAset = true; } - A arg = (args == &InputGetter::failptr_get) ? this->getDefaultA() : ((args == nullptr) ? this->getArgsA() : *(A* const)args); - - i2cip_errorlevel_t errlev = this->get(temp, arg); - - // If successful, update last cache - if(errlev == I2CIP::i2cip_errorlevel_t::I2CIP_ERR_NONE) { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F(" -> ")); - // I2CIP_DEBUG_SERIAL.print(temp); - // I2CIP_DEBUG_SERIAL.print(F(" @0x")); - // I2CIP_DEBUG_SERIAL.print((uint16_t)temp, HEX); - // I2CIP_DEBUG_SERIAL.print(F(", setting cache\n")); - // DEBUG_DELAY(); - // #endif - // this->clearCache(); - this->cache = temp; this->argsA = arg; - } - return errlev; -} - -template OutputInterface::OutputInterface(Device* device) { device->setOutput(this); } - -template OutputInterface::~OutputInterface() { } - -template void OutputInterface::setValue(S value) { this->value = value; } - -template void OutputInterface::resetFailsafe(void) { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("OutputInterface::resetFailsafe() Not Implemented; Nothing Done\n")); - // DEBUG_DELAY(); - // #endif -} - -template S OutputInterface::getValue(void) const { return this->value; } - -template void OutputInterface::setArgsB(B args) { this->argsB = args; } - -template B OutputInterface::getArgsB(void) const { return this->argsB; } - -template i2cip_errorlevel_t OutputInterface::set(const void* value, const void* args) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("OutputInterface::set(")); - if(value == &OutputSetter::failptr_set) I2CIP_DEBUG_SERIAL.print(F("fail")); - else if(value == nullptr) I2CIP_DEBUG_SERIAL.print(F("null")); - else { - I2CIP_DEBUG_SERIAL.print(*(S* const)value); - I2CIP_DEBUG_SERIAL.print(F(" @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)value, HEX); - } - I2CIP_DEBUG_SERIAL.print(F(", ")); - if(args == &OutputSetter::failptr_set) I2CIP_DEBUG_SERIAL.print(F("fail")); - else if(args == nullptr) I2CIP_DEBUG_SERIAL.print(F("null")); - else { - I2CIP_DEBUG_SERIAL.print(*(B* const)args); - I2CIP_DEBUG_SERIAL.print(F(" @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)args, HEX); - } - I2CIP_DEBUG_SERIAL.print(F(")\n")); - DEBUG_DELAY(); - #endif - - // If fail, reset to failsafe value - if (value == &OutputSetter::failptr_set) this->resetFailsafe(); - // 1. If `set` value is not given, repeat last action - S val = ((value == nullptr || value == &OutputSetter::failptr_set) ? this->getValue() : *(S* const)value); - - if(!this->argsBset) { this->argsB = this->getDefaultB(); this->argsBset = true; } - - // 2. If `set` args are not given, use last args - B arg = (args == &OutputSetter::failptr_set) ? this->getDefaultB() : ((args == nullptr || value == nullptr) ? this->getArgsB() : *(B* const)args); - - // 3. Attempt `set` - i2cip_errorlevel_t errlev = this->set(val, arg); - - // 4. If successful, update cached `value` and `args` - if(errlev == I2CIP::i2cip_errorlevel_t::I2CIP_ERR_NONE) { this->value = val; this->argsB = arg; }; - - return errlev; -} - -template IOInterface::IOInterface(Device* device) : InputInterface(device), OutputInterface(device) { } - -template IOInterface::~IOInterface() { } - -#endif -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/eeprom.cc b/software/microcontroller/lib/I2CIP/eeprom.cc deleted file mode 100644 index 7624d46b..00000000 --- a/software/microcontroller/lib/I2CIP/eeprom.cc +++ /dev/null @@ -1,368 +0,0 @@ -#include - -#include -#include -#include - -bool I2CIP::EEPROM::_id_set; -char I2CIP::EEPROM::_id[I2CIP_ID_SIZE]; -bool I2CIP::EEPROM::_failsafe_set; -char I2CIP::EEPROM::_failsafe[I2CIP_EEPROM_SIZE]; -uint16_t I2CIP::EEPROM::_failsafe_b; - -using namespace I2CIP; - -// Handles ID pointer assignment too -// NEVER returns nullptr, unless out of memory -Device* EEPROM::eepromFactory(const i2cip_fqa_t& fqa, const i2cip_id_t& id) { - if(!EEPROM::_id_set) { - uint8_t idlen = strlen_P(i2cip_eeprom_id_progmem); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("PROGMEM ID EEPROM Loaded: char[")); - I2CIP_DEBUG_SERIAL.print(idlen+1); - I2CIP_DEBUG_SERIAL.print(F("] = {'")); - #endif - - // Read in PROGMEM - for (uint8_t k = 0; k < idlen; k++) { - char c = pgm_read_byte_near(i2cip_eeprom_id_progmem + k); - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_SERIAL.print(c); - #endif - EEPROM::_id[k] = c; - } - - EEPROM::_id[idlen] = '\0'; - EEPROM::_id_set = true; - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print("'} @0x"); - I2CIP_DEBUG_SERIAL.print((uint16_t)(&(EEPROM::_id[0])), HEX); - DEBUG_SERIAL.print('\n'); - DEBUG_DELAY(); - #endif - } - - return (Device*)(new EEPROM(fqa, id)); -} - -Device* EEPROM::eepromFactory(const i2cip_fqa_t& fqa) { return eepromFactory(fqa, EEPROM::_id); } - -EEPROM::EEPROM(const i2cip_fqa_t& fqa, const i2cip_id_t& id) : Device(fqa, &(EEPROM::_id[0])), IOInterface((Device*)this) { - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("EEPROM Constructed (ID '")); - I2CIP_DEBUG_SERIAL.print(this->id); - I2CIP_DEBUG_SERIAL.print(F("' @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)&(this->id[0]), HEX); - I2CIP_DEBUG_SERIAL.print(F("; FQA ")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(F(":")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(F(":")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(F(":")); - I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(fqa), HEX); - I2CIP_DEBUG_SERIAL.print(F(")\n")); - DEBUG_DELAY(); - #endif -} - -EEPROM::~EEPROM() { - // #ifdef I2CIP_DEBUG_SERIAL - // DEBUG_DELAY(); - // I2CIP_DEBUG_SERIAL.print(F("EEPROM Destructed (ID '")); - // I2CIP_DEBUG_SERIAL.print(this->id); - // I2CIP_DEBUG_SERIAL.print(F("' @0x")); - // I2CIP_DEBUG_SERIAL.print((uint16_t)&(this->id[0]), HEX); - // I2CIP_DEBUG_SERIAL.print(F("; FQA ")); - // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_I2CBUS(this->fqa), HEX); - // I2CIP_DEBUG_SERIAL.print(F(":")); - // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MODULE(this->fqa), HEX); - // I2CIP_DEBUG_SERIAL.print(F(":")); - // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_MUXBUS(this->fqa), HEX); - // I2CIP_DEBUG_SERIAL.print(F(":")); - // I2CIP_DEBUG_SERIAL.print(I2CIP_FQA_SEG_DEVADR(this->fqa), HEX); - // I2CIP_DEBUG_SERIAL.print(F(")\n")); - // DEBUG_DELAY(); - // #endif - - // Cleanup - if(this->getValue() != nullptr && this->getValue() != _failsafe) delete this->getValue(); -} - -// EEPROM::EEPROM(const uint8_t& wire, const uint8_t& module, const uint8_t& addr) : EEPROM(I2CIP_FQA_CREATE(wire, module, I2CIP_MUX_BUS_DEFAULT, addr)) { } - -i2cip_errorlevel_t EEPROM::readContents(uint8_t* dest, size_t& num_read, size_t max_read) { - size_t bytes_read = max_read; - i2cip_errorlevel_t errlev = Device::readRegister(fqa, (uint16_t)0, dest, bytes_read, true, false); - num_read = bytes_read; - return errlev; -} - -i2cip_errorlevel_t EEPROM::clearContents(bool setbus, uint16_t numbytes) { - i2cip_errorlevel_t errlev = pingTimeout(setbus, false); - I2CIP_ERR_BREAK(errlev); - - uint8_t zeroes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - - for (uint16_t bytes_written = 0; bytes_written < numbytes; bytes_written+=8) { - const uint8_t pagelen = min(numbytes - bytes_written, 8); - - errlev = writeRegister(bytes_written, zeroes, pagelen, false); - if(errlev == I2CIP_ERR_SOFT) { - // Actual failed write - return errlev; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Cleared EEPROM bytes ")); - I2CIP_DEBUG_SERIAL.print(bytes_written); - I2CIP_DEBUG_SERIAL.print(F(" - ")); - I2CIP_DEBUG_SERIAL.println((bytes_written + pagelen - 1)); - DEBUG_DELAY(); - #endif - - // Note: Timeout ping before each byte write to await completion of last write cycle - errlev = pingTimeout(false, false); - I2CIP_ERR_BREAK(errlev); - } - - return errlev; -} - -i2cip_errorlevel_t EEPROM::overwriteContents(const char* contents, bool clear, bool setbus) { - for(size_t i = 0; i < I2CIP_EEPROM_SIZE; i++) { - if(contents[i] == '\0') { - return overwriteContents((uint8_t*)contents, i, clear, setbus); - } - } - return I2CIP_ERR_SOFT; -} - -i2cip_errorlevel_t EEPROM::overwriteContents(uint8_t* buffer, size_t len, bool clear, bool setbus) { - i2cip_errorlevel_t errlev = I2CIP_ERR_NONE; - if(clear) { - errlev = clearContents(setbus, len); - } else if(setbus) { - errlev = MUX::setBus(this->fqa); - } - I2CIP_ERR_BREAK(errlev); - - for (uint16_t bytes_written = 0; bytes_written < len; bytes_written+=8) { - const uint8_t pagelen = min(len - bytes_written, 8); - errlev = writeRegister(bytes_written, buffer+bytes_written, pagelen, false); - if(errlev == I2CIP_ERR_SOFT) { - // Actual failed write - return errlev; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Bytes ")); - I2CIP_DEBUG_SERIAL.print(bytes_written); - I2CIP_DEBUG_SERIAL.print(F(" - ")); - I2CIP_DEBUG_SERIAL.print(bytes_written + pagelen); - I2CIP_DEBUG_SERIAL.print(F(" written '")); - for(int i = 0; i < pagelen; i++) { - I2CIP_DEBUG_SERIAL.print((char)((buffer+bytes_written)[i])); - } - I2CIP_DEBUG_SERIAL.print(F("'... ")); - DEBUG_DELAY(); - #endif - - // Note: Timeout ping before each byte write to await completion of last write cycle - errlev = pingTimeout(false, false); - I2CIP_ERR_BREAK(errlev); - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.println("... ACK'd"); - DEBUG_DELAY(); - #endif - - if(buffer[bytes_written] == '\0') { - // Null terminator - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.println("Overwrite stopped - null terminator"); - DEBUG_DELAY(); - #endif - break; - } - } - - return errlev; -} - -i2cip_errorlevel_t EEPROM::get(char*& dest, const uint16_t& args) { - // 0. Check args - if(args > I2CIP_EEPROM_SIZE) { - return I2CIP_ERR_SOFT; - } - - // 1. Read register (until null terminator or max bytes, arg-dependant) into a local buffer - size_t len = args == 0 ? I2CIP_EEPROM_SIZE-1 : args; - uint8_t buffer[len]; - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("EEPROM Get (up to ")); - I2CIP_DEBUG_SERIAL.print(len); - I2CIP_DEBUG_SERIAL.print(F(" bytes)\n")); - DEBUG_DELAY(); - #endif - - i2cip_errorlevel_t errlev = readRegister((uint16_t)0, buffer, len); - I2CIP_ERR_BREAK(errlev); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(len); - I2CIP_DEBUG_SERIAL.print(F("+'\\0' bytes read from EEPROM\n")); - DEBUG_DELAY(); - #endif - - // if((uint16_t)len != args) return I2CIP_ERR_SOFT; - - // 2. Copy local buffer to heap buffer (null-terminated) - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Reading to static heap buffer @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)(&this->readBuffer[0]), HEX); - I2CIP_DEBUG_SERIAL.print(F(" (")); - I2CIP_DEBUG_SERIAL.print(len+1); - I2CIP_DEBUG_SERIAL.print(F(" bytes) '")); - #endif - - for(size_t i = 0; i < len; i++) { - this->readBuffer[i] = (char)buffer[i]; - } - this->readBuffer[len] = '\0'; - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(this->readBuffer); - I2CIP_DEBUG_SERIAL.print("'\n"); - DEBUG_DELAY(); - #endif - - if(errlev == I2CIP_ERR_NONE) { - dest = &this->readBuffer[0]; - } - return errlev; -} - -i2cip_errorlevel_t EEPROM::set(const char * const& value, const uint16_t& args) { - if(args > I2CIP_EEPROM_SIZE) { - return I2CIP_ERR_SOFT; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("EEPROM Set (")); - I2CIP_DEBUG_SERIAL.print(args); - I2CIP_DEBUG_SERIAL.print(F(" bytes @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)value, HEX); - I2CIP_DEBUG_SERIAL.print(F(") '")); - I2CIP_DEBUG_SERIAL.print(value); - I2CIP_DEBUG_SERIAL.print("'...\n"); - #endif - - // Write register - size_t len = args; - i2cip_errorlevel_t errlev = writeRegister((uint16_t)0, (uint8_t*)value, len); - I2CIP_ERR_BREAK(errlev); - - // Pre-caching Cleanup - if(this->getValue() != value && this->getValue() != nullptr && this->getValue()) { delete this->getValue(); } - - // if((uint16_t)len != args) return I2CIP_ERR_SOFT; - return errlev; -} - -// Args: len (2 bytes) -// int EEPROM::read(char*& dest, uint8_t argc, uint8_t[] argv) { -// size_t len = 0; -// if(argc == 0 || argv == nullptr) { -// this->readContents((uint8_t*&)dest, len); -// } else { -// this->readContents((uint8_t*&)dest, len, argc > 1 ? ((argv[1] << 8) & argv[0]) : argv[0]); - -// argv[0] = (len & 0xFF); -// if(argc > 1) argv[1] = (len >> 8); -// } - -// return (len > 0 ? 0 : 1); -// } - -// G - Getter type: char* (null-terminated; writable heap) -void EEPROM::clearCache(void) { - this->setCache(nullptr); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("EEPROM Cache Cleared\n")); - DEBUG_DELAY(); - #endif -} - -// A - Getter argument type: uint16_t (max bytes to read) -const uint16_t& EEPROM::getDefaultA(void) const { - return i2cip_eeprom_capacity; -} - -// S - Setter type: const char* (null-terminated; immutable) -void EEPROM::resetFailsafe(void) { - if(_failsafe_set && this->getValue() == _failsafe) return; // Already set - if(this->getValue() != nullptr && this->getValue() != _failsafe) delete this->getValue(); - - // Load from PROGMEM - if(!_failsafe_set) { - uint8_t len = strlen_P(i2cip_eeprom_default); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("Loading Failsafe PROGMEM Static Heap @0x")); - I2CIP_DEBUG_SERIAL.print((uint16_t)(&_failsafe[0]), HEX); - I2CIP_DEBUG_SERIAL.print(F(" (")); - I2CIP_DEBUG_SERIAL.print(len+1); - I2CIP_DEBUG_SERIAL.print(F(" bytes) '")); - #endif - - // Read in PROGMEM - for (uint8_t k = 0; k < len; k++) { - char c = pgm_read_byte_near(i2cip_eeprom_default + k); - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(c); - #endif - _failsafe[k] = c; - } - - _failsafe[len] = '\0'; - - _failsafe_b = len; - _failsafe_set = true; - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print("'\n"); - DEBUG_DELAY(); - #endif - } - - - this->setValue(_failsafe); - this->setArgsB(_failsafe_b); - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("EEPROM Value Reset\n")); - DEBUG_DELAY(); - #endif -} - -// B - Setter argument type: uint16_t (max bytes to write) -const uint16_t& EEPROM::getDefaultB(void) const { - return i2cip_eeprom_capacity; -} \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/eeprom.h b/software/microcontroller/lib/I2CIP/eeprom.h deleted file mode 100644 index 85856099..00000000 --- a/software/microcontroller/lib/I2CIP/eeprom.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef I2CIP_EEPROM_H_ -#define I2CIP_EEPROM_H_ - -#include -#include - -#define I2CIP_EEPROM_SIZE 100 // EEPROM size in bytes -#define I2CIP_EEPROM_ADDR 0x50 // SPRT EEPROM address -#define I2CIP_EEPROM_TIMEOUT 100 // How long to wait for a write to complete (ms) - -#define I2CIP_EEPROM_ID "24LC32" -#define I2CIP_EEPROM_DEFAULT "[{\"" I2CIP_EEPROM_ID "\":[80],\"SHT31\":[68]}]" - -// Future-Proofing ;) -// namespace ControlSystemsOS { -// class Linker; -// } - -namespace I2CIP { - - class Device; - template class IOInterface; - - const char i2cip_eeprom_default[] PROGMEM = {I2CIP_EEPROM_DEFAULT}; - const uint16_t i2cip_eeprom_capacity = I2CIP_EEPROM_SIZE; - const char i2cip_eeprom_id_progmem[] PROGMEM = {I2CIP_EEPROM_ID}; - - /** - * 24LC32 EEPROM - * - * This class is a wrapper for on- and off-module EEPROM as an I2CIP device and a high-level null-terminated UTF-8 C-string I/O peripheral. - * NOTE: This object handles all C-string memory management, including allocation and deallocation. All pass-by-ref(!) pointer arguments are reassigned. Similar array-valued interfaces should follow the same pattern. - * - * G - Getter type: char* (null-terminated; writable heap) - * A - Getter argument type: uint16_t (max bytes to read) - * S - Setter type: const char* (null-terminated; immutable) - * B - Setter argument type: uint16_t (max bytes to write) - */ - class EEPROM : public Device, public IOInterface { - // friend Device* I2CIP::eepromFactory(const i2cip_fqa_t& fqa); - // friend class Module; - // friend class ControlSystemsOS::Linker; // Future-Proofing ;) - - EEPROM(const i2cip_fqa_t& fqa, const i2cip_id_t& id); - private: - static bool _id_set; - static char _id[]; // to be loaded from progmem - - static bool _failsafe_set; - static char _failsafe[]; // to be loaded from progmem - static uint16_t _failsafe_b; - - char readBuffer[I2CIP_EEPROM_SIZE+1] = { '\0' }; - public: - static Device* eepromFactory(const i2cip_fqa_t& fqa, const i2cip_id_t& id); - static Device* eepromFactory(const i2cip_fqa_t& fqa); - - ~EEPROM(); - - i2cip_errorlevel_t readContents(uint8_t* dest, size_t& num_read, size_t max_read = I2CIP_EEPROM_SIZE); - - i2cip_errorlevel_t writeByte(const uint16_t& bytenum, const uint8_t& value, bool setbus = true); - - i2cip_errorlevel_t clearContents(bool setbus = true, uint16_t numbytes = I2CIP_EEPROM_SIZE); - - i2cip_errorlevel_t overwriteContents(const char* contents, bool clear = true, bool setbus = true); - - i2cip_errorlevel_t overwriteContents(uint8_t* buffer, size_t len, bool clear = true, bool setbus = true); - - /** - * Read a section from EEPROM. - * @param dest Destination heap (pointer reassigned, not overwritten) - * @param args Number of bytes to read - **/ - i2cip_errorlevel_t get(char*& dest, const uint16_t& args) override; - - /** - * Write to a section of EEPROM. - * @param value Value to write (null-terminated) - * @param args Number of bytes to write - **/ - i2cip_errorlevel_t set(const char * const& value, const uint16_t& args) override; - - void clearCache(void) override; - const uint16_t& getDefaultA(void) const override; - void resetFailsafe(void) override; - const uint16_t& getDefaultB(void) const override; - - static const char* getStaticIDBuffer() { return EEPROM::_id_set ? &(EEPROM::_id[0]) : nullptr; } - }; -} - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/fqa.cc b/software/microcontroller/lib/I2CIP/fqa.cc deleted file mode 100644 index e0c343da..00000000 --- a/software/microcontroller/lib/I2CIP/fqa.cc +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#include - -TwoWire* const I2CIP::wires[I2CIP_NUM_WIRES] = { &Wire }; - -// Has wire N been wires[N].begin() yet? -bool wiresBegun[I2CIP_NUM_WIRES] = { false }; - -i2cip_fqa_t I2CIP::createFQA(const uint8_t& wire, const uint8_t& mux, const uint8_t& bus, const uint8_t& addr) { - if (( wire < I2CIP_FQA_I2CBUS_MAX ) && - ( mux < I2CIP_FQA_MODULE_MAX ) && - ( bus < I2CIP_FQA_MUXBUS_MAX ) && - ( addr < I2CIP_FQA_DEVADR_MAX ) - ) { - return I2CIP_FQA_CREATE(wire, mux, bus, addr); - } - return (i2cip_fqa_t)0; -} - -void I2CIP::beginWire(const uint8_t& wire) { - if(!wiresBegun[wire]) { - wires[wire]->begin(); - wiresBegun[wire] = true; - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("INIT I2C WIRE ")); - I2CIP_DEBUG_SERIAL.println(wire); - DEBUG_DELAY(); - #endif - } -} \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/fqa.h b/software/microcontroller/lib/I2CIP/fqa.h deleted file mode 100644 index 2ef830ba..00000000 --- a/software/microcontroller/lib/I2CIP/fqa.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef I2CIP_FQA_H_ -#define I2CIP_FQA_H_ - -#include -#include - -// -------------------------------- -// FQA: Fully Qualified Addressing -// -------------------------------- - -// Two useful typedefs -typedef uint16_t i2cip_fqa_t; -typedef const char* i2cip_id_t; - -// Address segments: Least Significant Bit positions, lengths, and maximum values -#define I2CIP_FQA_I2CBUS_LSB 13 -#define I2CIP_FQA_I2CBUS_LEN 3 -#define I2CIP_FQA_I2CBUS_MAX ((1 << I2CIP_FQA_I2CBUS_LEN) - 1) -#define I2CIP_FQA_MODULE_LSB 10 -#define I2CIP_FQA_MODULE_LEN 3 -#define I2CIP_FQA_MODULE_MAX ((1 << I2CIP_FQA_MODULE_LEN) - 1) -#define I2CIP_FQA_MUXBUS_LSB 7 -#define I2CIP_FQA_MUXBUS_LEN 3 -#define I2CIP_FQA_MUXBUS_MAX ((1 << I2CIP_FQA_MUXBUS_LEN) - 1) -#define I2CIP_FQA_DEVADR_LSB 0 -#define I2CIP_FQA_DEVADR_LEN 7 -#define I2CIP_FQA_DEVADR_MAX ((1 << I2CIP_FQA_DEVADR_LEN) - 1) - -#define I2CIP_FQA_CREATE(wire, module, bus, addr) (i2cip_fqa_t)((wire << (16 - I2CIP_FQA_I2CBUS_LEN)) | (module << (16 - I2CIP_FQA_I2CBUS_LEN - I2CIP_FQA_MODULE_LEN)) | (bus << (16 - I2CIP_FQA_I2CBUS_LEN - I2CIP_FQA_MODULE_LEN - I2CIP_FQA_MUXBUS_LEN)) | addr) - -/** - * Segment extraction. Shifts all bits so LSB is at index 0. Returns as uint8_t. - * @param fqa FQA of interest - * @param lsb Right-most (least significant) bit position - * @param len Length of the segment - */ -#define I2CIP_FQA_SEG(fqa, lsb, len) ((uint8_t)((fqa >> lsb) & (0xFFFF >> (16 - len)))) - -// Shorthands -#define I2CIP_FQA_SEG_DEVADR(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_DEVADR_LSB, I2CIP_FQA_DEVADR_LEN) // Extracts the device address segment from an FQA -#define I2CIP_FQA_SEG_MUXBUS(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_MUXBUS_LSB, I2CIP_FQA_MUXBUS_LEN) // Extracts the MUX bus number segment from an FQA -#define I2CIP_FQA_SEG_MODULE(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_MODULE_LSB, I2CIP_FQA_MODULE_LEN) // Extracts the MUX number segment from an FQA -#define I2CIP_FQA_SEG_I2CBUS(fqa) I2CIP_FQA_SEG(fqa, I2CIP_FQA_I2CBUS_LSB, I2CIP_FQA_I2CBUS_LEN) // Extracts the I2C bus number segment from an FQA - -// I2C Wire Implementation -#define I2CIP_MAXBUFFER 32 // I2C buffer size -#define I2CIP_NUM_WIRES 1 // Number of I2C wires - TODO: autodetect and populate `wires[]` based on hardware spec macros - -extern TwoWire Wire; - -#define I2CIP_FQA_TO_WIRE(fqa) (I2CIP::wires[I2CIP_FQA_SEG_I2CBUS(fqa)]) - -#define I2CIP_ERR_BREAK(errlev) if((errlev) != I2CIP::I2CIP_ERR_NONE) { return (errlev); } - -namespace I2CIP { - /** - * Errorlevels for I2CIP communication. - */ - typedef enum { - I2CIP_ERR_NONE = 0x0, // No error - I2CIP_ERR_SOFT = 0x1, // Communications error, device still reachable - I2CIP_ERR_HARD = 0x2, // Device unreachable - } i2cip_errorlevel_t; - - /** - * Create an FQA from segments. Validates. - * @param wire I2C bus number - * @param mux MUX number - * @param bus MUX bus number - * @param addr Device address - * @return A valid FQA, or 0 if any segment has an invalid value. - */ - i2cip_fqa_t createFQA(const uint8_t& wire, const uint8_t& mux, const uint8_t& bus, const uint8_t& addr); - - extern TwoWire* const wires[I2CIP_NUM_WIRES]; - - /** - * Initialize an I2C interface (if it has not already been initialized) - * @param wire - */ - void beginWire(const uint8_t& wire); -}; - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/hashtable.h b/software/microcontroller/lib/I2CIP/hashtable.h deleted file mode 100644 index c6c1ce8e..00000000 --- a/software/microcontroller/lib/I2CIP/hashtable.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef I2CIP_HASHTABLE_H_ -#define I2CIP_HASHTABLE_H_ - -#include -#include - -#include - -#include - -// Constants -#define HASHTABLE_OFFSET 7 -#define HASHTABLE_SLOTS 4 // Number of unique ID slots to include in the hashtable (for optimization purposes) - -// Hash table entry -template class HashTableEntry { - public: - HashTableEntry(const char* key, T value, HashTableEntry* last); - ~HashTableEntry(); - - const char* key; // Pointer to the IMMUTABLE (copied) key - de/allocated on de/construction - T value; // REFERENCE to the value - passed in - HashTableEntry* next; // Pointer to the previous entry - passed in -}; - -// TODO: Class 'HashTableEntry < DeviceGroup & >' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s). -// TODO: Class 'HashTableEntry < DeviceGroup & >' does not have a operator= which is recommended since it has dynamic memory/resource allocation(s). - -// A hash table. Yup. -template class HashTable { - public: - // Slots are static, entries are dynamic - HashTableEntry* hashtable[HASHTABLE_SLOTS] = { nullptr }; // FIXED POINTER to the entries - de/allocated on de/construction - - HashTable(); - ~HashTable(); - - /** - * Put {key: value} in the hash table. - * @param key String key - * @param value Pointer to value - * @param overwrite Overwrite existing value if found? Default: `true` - * @return Pointer to the new entry - */ - HashTableEntry* set(const char* key, T value, bool overwrite = true); - - /** - * Look at the index for that key, down the chain until either key=key or next = nullptr - * @param key to look for - * @return Pointer if found, nullptr otherwise - */ - HashTableEntry* get(const char* key); - - /** - * Index operator by key. - * @param key - * @return Pointer to entry - */ - HashTableEntry* operator[](const char* key); - - bool remove(const char* key); -}; - -#include - -#endif \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/hashtable.tpp b/software/microcontroller/lib/I2CIP/hashtable.tpp deleted file mode 100644 index 6b27ccf8..00000000 --- a/software/microcontroller/lib/I2CIP/hashtable.tpp +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef I2CIP_HASHTABLE_H_ -#error __FILE__ should only be included AFTER -#endif - -#ifdef I2CIP_HASHTABLE_H_ - -#ifndef I2CIP_HASHTABLE_T_ -#define I2CIP_HASHTABLE_T_ - -inline static uint8_t hash(const char* s) { - unsigned index; - for (index = 0; *s != '\0'; s++) { - index = *s + HASHTABLE_OFFSET * index; - } - return index % HASHTABLE_SLOTS; -} - -// HASH TABLE ENTRY - -template HashTableEntry::HashTableEntry(const char* key, T value, HashTableEntry* last) : key(key), value(value), next(last) { } - -template HashTableEntry::~HashTableEntry() { - // Free our key then trigger the next entry - delete this->key; - delete this->next; -} - -// HASH TABLE - -template HashTable::HashTable() { - for(int i = 0; i < HASHTABLE_SLOTS; i++) { - hashtable[i] = nullptr; - } -} - -template HashTable::~HashTable() { - // Free all allocated entries (and their keys) recursively; slots are static - for (uint8_t i = 0; i < HASHTABLE_SLOTS; i++) { - delete(this->hashtable[i]); - } -} - -template HashTableEntry* HashTable::operator[](const char* key) { - return get(key); -} - -// Public methods - -template HashTableEntry* HashTable::set(const char* key, T value, bool overwrite) { - HashTableEntry* head = get(key); - - // Match found? - if (head != nullptr) { - if (overwrite) head->value = value; - return head; - } - // No match, allocate new; point "next" to the top entry - uint8_t index = hash(key); - head = new HashTableEntry(key, value, hashtable[index]); - - // If allocation was successful: - if (head != nullptr && head->key != nullptr) { - // Point the top of the hashtable to the new entry - hashtable[index] = head; - } - #ifdef I2CIP_DEBUG_SERIAL - else { I2CIP_DEBUG_SERIAL.print(F("HashTable(x) ENOMEM\n")); } - #endif - - return head; -} - -template HashTableEntry* HashTable::get(const char* key) { - HashTableEntry* np; - for (np = hashtable[hash(key)]; np != nullptr; np = np->next) { - if (strcmp(key, np->key) == 0) { - return np; /* found */ - } - } - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("HashTable[''] ENOENT\n")); - #endif - return nullptr; /* not found */ -} - -template bool HashTable::remove(const char* key) { - // Find ptr.next.key == key - uint8_t index = hash(key); - HashTableEntry* ptr = hashtable[index]; - - // Check top-level - if (strcmp(key, ptr->key) == 0) { - // Found; point the slot to next - hashtable[index] = ptr->next; - ptr->next = nullptr; - delete(ptr); - return true; - } - - for (; ptr->next != nullptr; ptr = ptr->next) { - if (strcmp(key, ptr->next->key) == 0) { - // Found; point the preceding entry to the subsequent one - HashTableEntry* entry = ptr->next; - ptr->next = ptr->next->next; - entry->next = nullptr; - delete(entry); - return true; - } - } - // Not found - return false; -} - -#endif -#endif diff --git a/software/microcontroller/lib/I2CIP/library.properties b/software/microcontroller/lib/I2CIP/library.properties deleted file mode 100644 index b280ca6b..00000000 --- a/software/microcontroller/lib/I2CIP/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=I2CIP -version=1.0.0 -author=Jayden Lefebvre -maintainer=Jayden Lefebvre -sentence=A library of protocols for interfacing with I2C devices on a modular switched network. -paragraph=See README for compatible hardware specifications. State management for plug-and-play compatibility. Tested on Arduino Nano. -category=Communication -url=https://github.com/PeaPodTechnologies/I2CIP/ -architectures=avr -includes=I2CIP.h \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/mux.cc b/software/microcontroller/lib/I2CIP/mux.cc deleted file mode 100644 index 9c0a7e6f..00000000 --- a/software/microcontroller/lib/I2CIP/mux.cc +++ /dev/null @@ -1,105 +0,0 @@ -#include - -#include -#include - -namespace I2CIP { - - namespace MUX { - bool pingMUX(const uint8_t& wire, const uint8_t& module) { - beginWire(wire); - wires[wire]->beginTransmission(I2CIP_MODULE_TO_MUXADDR(module)); - uint8_t code = wires[wire]->endTransmission(); - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("MUX Ping: Code 0x")); - I2CIP_DEBUG_SERIAL.print(code, HEX); - I2CIP_DEBUG_SERIAL.print(F("\n")); - DEBUG_DELAY(); - #endif - return (code == 0); - } - - bool pingMUX(const i2cip_fqa_t& fqa) { - beginWire(I2CIP_FQA_SEG_I2CBUS(fqa)); - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_MODULE_TO_MUXADDR(I2CIP_FQA_SEG_MODULE(fqa))); - uint8_t code = I2CIP_FQA_TO_WIRE(fqa)->endTransmission(); - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("MUX Ping: Code 0x")); - I2CIP_DEBUG_SERIAL.print(code, HEX); - I2CIP_DEBUG_SERIAL.print(F("\n")); - DEBUG_DELAY(); - #endif - return (code == 0); - } - - i2cip_errorlevel_t setBus(const i2cip_fqa_t& fqa) { - // Note: no need to ping MUX, we'll see in real time what the result is - beginWire(I2CIP_FQA_SEG_I2CBUS(fqa)); - - // Was the bus switched successfully? - bool success = true; - - // Begin transmission - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_MODULE_TO_MUXADDR(I2CIP_FQA_SEG_MODULE(fqa))); - - // Write the bus switch instruction - uint8_t instruction = I2CIP_MUX_BUS_TO_INSTR(I2CIP_FQA_SEG_MUXBUS(fqa)); - if (I2CIP_FQA_TO_WIRE(fqa)->write(&instruction, 1) != 1) { - success = false; - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("MUX Write Failed\n")); - #endif - } - - // End transmission - uint8_t r = I2CIP_FQA_TO_WIRE(fqa)->endTransmission(); - if (r != 0) { - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("MUX Transmission Failed 0x")); - I2CIP_DEBUG_SERIAL.print(r, HEX); - I2CIP_DEBUG_SERIAL.print('\n'); - DEBUG_DELAY(); - #endif - return I2CIP_ERR_HARD; - } - - #ifdef I2CIP_DEBUG_SERIAL - I2CIP_DEBUG_SERIAL.print(F("MUX Bus Set\n")); - DEBUG_DELAY(); - #endif - - return (success ? I2CIP_ERR_NONE : I2CIP_ERR_SOFT); - } - - i2cip_errorlevel_t resetBus(const i2cip_fqa_t& fqa) { - // Note: no need to ping MUX, we'll see in real time what the result is - beginWire(I2CIP_FQA_SEG_I2CBUS(fqa)); - - // Begin transmission - I2CIP_FQA_TO_WIRE(fqa)->beginTransmission(I2CIP_MODULE_TO_MUXADDR(I2CIP_FQA_SEG_MODULE(fqa))); - - // Write the "inactive" bus switch instruction - const uint8_t instruction = I2CIP_MUX_INSTR_RST; - if (I2CIP_FQA_TO_WIRE(fqa)->write(&instruction, 1) != 1) { - return I2CIP_ERR_SOFT; - } - - // End transmission - if (I2CIP_FQA_TO_WIRE(fqa)->endTransmission() != 0) { - return I2CIP_ERR_HARD; - } - - #ifdef I2CIP_DEBUG_SERIAL - DEBUG_DELAY(); - I2CIP_DEBUG_SERIAL.print(F("MUX Bus Reset\n")); - DEBUG_DELAY(); - #endif - - return I2CIP_ERR_NONE; - } - }; -}; \ No newline at end of file diff --git a/software/microcontroller/lib/I2CIP/mux.h b/software/microcontroller/lib/I2CIP/mux.h deleted file mode 100644 index 5caf6283..00000000 --- a/software/microcontroller/lib/I2CIP/mux.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef I2CIP_MUX_H_ -#define I2CIP_MUX_H_ - -#include - -// ---------------------- -// MUX: I2C Multiplexers -// ---------------------- - -#define I2CIP_MUX_ADDR_MIN 0x70 // The lowest device address -#define I2CIP_MUX_ADDR_MAX 0x77 // The highest device address -#define I2CIP_MUX_COUNT (I2CIP_MUX_ADDR_MAX - I2CIP_MUX_ADDR_MIN + 1) -#define I2CIP_MUX_BUS_MIN 0x00 // The lowest bus number -#define I2CIP_MUX_BUS_MAX 0x07 // The highest bus number -#define I2CIP_MUX_BUS_COUNT (I2CIP_MUX_BUS_MAX - I2CIP_MUX_BUS_MIN + 1) -#define I2CIP_MUX_BUS_DEFAULT 0x00 // The default bus for integrated on-module interfaces. -#define I2CIP_MUX_INSTR_RST 0x00 // Disable all busses - -/** - * Converts a bus number to a MUX instruction. - * @param bus Bus number (0-7) - */ -#define I2CIP_MUX_BUS_TO_INSTR(bus) (uint8_t)(1 << bus) - -/** - * Converts a module number to its MUX address. - * @param num MUX number (0-7) - */ -#define I2CIP_MODULE_TO_MUXADDR(module) (module + I2CIP_MUX_ADDR_MIN) - -namespace I2CIP { - namespace MUX { - /** - * Pings the MUX. Begins the Wire. - * | MUX ADDR (7) | ACK? | - * @param fqa FQA of a device this MUX is in front of. - * @return ACK? - */ - bool pingMUX(const i2cip_fqa_t& fqa); - - /** - * Pings the MUX. Begins the Wire. - * | MUX ADDR (7) | ACK? | - * @param fqa FQA of a device this MUX is in front of. - * @return ACK? - */ - bool pingMUX(const uint8_t& wire, const uint8_t& module); - - /** - * Sets the MUX bus. - * | MUX ADDR (7) | MUX CONFIG (8) | ACK? | - * @param fqa FQA of a device that is on the target Subnet. - * @return Hardware failure: No ACK; Module lost. Software failure: Failed to write to MUX. - */ - i2cip_errorlevel_t setBus(const i2cip_fqa_t& fqa); - - /** - * Reset the MUX to the "inactive" bus. - * | MUX ADDR (7) | MUX RESET (8) | ACK? | - * @param fqa FQA of a device that this MUX is in front of - * @return Hardware failure: No ACK; Module lost. Software failure: Failed to write to MUX. - */ - i2cip_errorlevel_t resetBus(const i2cip_fqa_t& fqa); - }; -}; - -#endif \ No newline at end of file diff --git a/software/microcontroller/library.properties b/software/microcontroller/library.properties deleted file mode 100644 index 4e8a1eeb..00000000 --- a/software/microcontroller/library.properties +++ /dev/null @@ -1,11 +0,0 @@ -name=ControlSystemsOS -version=0.2.2 -author=Jayden Lefebvre -maintainer=Jayden Lefebvre -sentence=A robotics library for interfacing with a dynamic set of plug-and-play low-level I2C modules through a high-level control systems API. -paragraph=Currently supports a variety of I2C middleware devices ("Interfaces", i.e. 16-pin GPIO expander, 12-bit ADC, autonomous PWM) for communicating with sensors and actuators. Intra-module linking info for device detection is read from on-module EEPROM. Tested on Arduino Nano. -category=Device Control -url=https://github.com/PeaPodTechnologies/ControlSystemsOS/ -architectures=avr -depends=I2CIP (>=1.0.0), FiniteStateMachine (>=0.1.0), ArduinoJson (>=6.19.4) -includes=ControlSystemsOS.h diff --git a/software/microcontroller/test/config.h b/software/microcontroller/test/config.h deleted file mode 100644 index c8b11bce..00000000 --- a/software/microcontroller/test/config.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef I2CIP_TESTS_TEST_H_ -#define I2CIP_TESTS_TEST_H_ - -// TESTING PARAMETERS - -#define I2CIP_TEST_BUFFERSIZE 100 // Need to limit this, or else crash; I think Unity takes up a lot of stack space - -#endif \ No newline at end of file diff --git a/software/microcontroller/test/test_0_blink/test_0_blink.cc b/software/microcontroller/test/test_0_blink/test_0_blink.cc deleted file mode 100644 index e22285cc..00000000 --- a/software/microcontroller/test/test_0_blink/test_0_blink.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -void test_led_builtin_pin_number(void) { - TEST_ASSERT_EQUAL(13, LED_BUILTIN); -} - -void test_led_state_high(void) { - digitalWrite(LED_BUILTIN, HIGH); - TEST_ASSERT_EQUAL(HIGH, digitalRead(LED_BUILTIN)); -} - -void test_led_state_low(void) { - digitalWrite(LED_BUILTIN, LOW); - TEST_ASSERT_EQUAL(LOW, digitalRead(LED_BUILTIN)); -} - -void setup() { - delay(2000); - - UNITY_BEGIN(); - RUN_TEST(test_led_builtin_pin_number); - - pinMode(LED_BUILTIN, OUTPUT); -} - -uint8_t i = 0; - -void loop() { - if (i < 3) { - RUN_TEST(test_led_state_high); - delay(500); - RUN_TEST(test_led_state_low); - delay(500); - i++; - } else { - UNITY_END(); - } -} \ No newline at end of file diff --git a/software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc b/software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc deleted file mode 100644 index f4230117..00000000 --- a/software/microcontroller/test/test_1_eeprom/test_1_eeprom.cc +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include - -#include -#include "../config.h" - -#define CSOS_TEST_EEPROM_BYTE '[' // This should be the first character of ANY valid SPRT EEPROM - -i2cip_fqa_t eeprom_fqa = I2CIP::createFQA(WIRENUM, MUXNUM, CSOS_MUX_BUS_DEFAULT, CSOS_EEPROM_ADDR); -char buffer[I2CIP_TEST_BUFFERSIZE] = { '\0' }; -uint16_t bufferlen = 0; - -// ALL MACROS - -void test_eeprom_ping(void) { - char msg[30]; - sprintf(msg, "Device unreachable (%01X.%01X.%01X.%02X)", CSOS_FQA_SEG_I2CBUS(eeprom_fqa), CSOS_FQA_SEG_MUXNUM(eeprom_fqa), CSOS_FQA_SEG_MUXBUS(eeprom_fqa), CSOS_FQA_SEG_DEVADR(eeprom_fqa)); - I2CIP::i2cip_errorlevel_t result = I2CIP::Device::ping(eeprom_fqa); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, msg); -} - -void test_eeprom_read_byte(void) { - uint8_t c = '\0'; - I2CIP::i2cip_errorlevel_t result = I2CIP::Device::readRegisterByte(eeprom_fqa, (uint16_t)0, c); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Read Byte"); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_TEST_EEPROM_BYTE, c, "EEPROM Read Byte (Match)"); -} - -void test_eeprom_write_byte(void) { - I2CIP::i2cip_errorlevel_t result = I2CIP::Device::writeRegister(eeprom_fqa, (uint16_t)0, CSOS_TEST_EEPROM_BYTE); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Write Byte"); -} - -void test_eeprom_read_contents(void) { - I2CIP::i2cip_errorlevel_t result = I2CIP::EEPROM::readContents(eeprom_fqa, (uint8_t*)buffer, bufferlen, CSOS_TEST_BUFFERSIZE); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Read Contents"); - TEST_ASSERT_NOT_EQUAL_MESSAGE(0, bufferlen, "EEPROM Read Contents (Empty)"); -} - -void test_eeprom_overwrite_contents(void) { - I2CIP::i2cip_errorlevel_t result = I2CIP::EEPROM::overwriteContents(eeprom_fqa, (uint8_t*)buffer, bufferlen); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "EEPROM Overwrite Contents"); -} - -void setup() { - delay(2000); - - UNITY_BEGIN(); - - RUN_TEST(test_eeprom_ping); - RUN_TEST(test_eeprom_read_byte); - // RUN_TEST(test_eeprom_write_byte); - RUN_TEST(test_eeprom_read_contents); - // RUN_TEST(test_eeprom_overwrite_contents); - - UNITY_END(); -} - -void loop() {w - -} \ No newline at end of file diff --git a/software/microcontroller/test/test_2_json/test_2_json.cc b/software/microcontroller/test/test_2_json/test_2_json.cc deleted file mode 100644 index 0e5b2452..00000000 --- a/software/microcontroller/test/test_2_json/test_2_json.cc +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#include - -#include -#include "../config.h" - -// GLOBALS -i2cip_fqa_t eeprom_fqa = I2CIP::createFQA(WIRENUM, MUXNUM, CSOS_MUX_BUS_DEFAULT, CSOS_EEPROM_ADDR); -char buffer[I2CIP_TEST_BUFFERSIZE] = { '\0' }; -StaticJsonDocument eeprom_json; // EEPROM JSON doc -uint8_t totaldevices = 0; // Number of devices in EEPROM -i2cip_fqa_t* devices; // Array of EEPROM device FQAs -uint8_t d = 0; // Loop variable - -/** - * Read and deserialize the EEPROM. - **/ -void test_sprt_json_deser(void) { - // READ EEPROM - uint16_t size = 0; - I2CIP::i2cip_errorlevel_t result = I2CIP::EEPROM::readContents(eeprom_fqa, (uint8_t*)buffer, size, CSOS_TEST_BUFFERSIZE); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, result, "Failed to read EEPROM"); - TEST_ASSERT_NOT_EQUAL_MESSAGE(0, size, "EEPROM empty!"); - - // DESERIALIZE - DeserializationError jsonerr = deserializeJson(eeprom_json, buffer); - - TEST_ASSERT_TRUE_MESSAGE(jsonerr.code() == DeserializationError::Code::Ok, buffer); - TEST_ASSERT_NOT_EQUAL_MESSAGE(0, eeprom_json.memoryUsage(), buffer); - TEST_ASSERT_FALSE_MESSAGE(eeprom_json.overflowed(), buffer); -} - -/** - * Parse the JSON, count devices and busses. - **/ -void test_sprt_json_valid(void) { - JsonArray arr = eeprom_json.as(); - - uint8_t buscount = 0; - for (JsonObject bus : arr) { - // Count reachable devices in each device group - uint8_t devicecount = 0; - for (JsonPair device : bus) { - buscount++; - - // Device addresses - JsonArray addresses = device.value().as(); - - for (JsonVariant address : addresses) { - devicecount++; - } - } - - // Add the number of devices on this bus to the tally - totaldevices += devicecount; - } - - TEST_ASSERT_NOT_EQUAL(0, buscount); - - TEST_ASSERT_NOT_EQUAL(0, totaldevices); -} - -/** - * Attempt to reach a device. - **/ -void test_device_reachable(void) { - char msg[30]; - sprintf(msg, "Device unreachable (%01X.%01X.%01X.%02X)", CSOS_FQA_SEG_I2CBUS(devices[d]), CSOS_FQA_SEG_MUXNUM(devices[d]), CSOS_FQA_SEG_MUXBUS(devices[d]), CSOS_FQA_SEG_DEVADR(devices[d])); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP::I2CIP_ERR_NONE, I2CIP::Device::ping(devices[d]), msg); -} - -void setup() { - delay(2000); - - UNITY_BEGIN(); - - RUN_TEST(test_sprt_json_deser); - - RUN_TEST(test_sprt_json_valid); - - devices = new i2cip_fqa_t[totaldevices]; - - JsonArray arr = eeprom_json.as(); - - uint8_t busnum = 0, i = 0; - for (JsonObject bus : arr) { - for (JsonPair device : bus) { - - // Device addresses - JsonArray addresses = device.value().as(); - - for (JsonVariant address : addresses) { - devices[i] = I2CIP::createFQA(WIRENUM, MUXNUM, busnum, address.as()); - i++; - } - - busnum++; - } - } -} - -void loop() { - if(d >= totaldevices) { - UNITY_END(); - delete devices; - } - RUN_TEST(test_device_reachable); - d++; -} \ No newline at end of file diff --git a/software/microcontroller/test/test_3_csos/test_3_csos.cc b/software/microcontroller/test/test_3_csos/test_3_csos.cc deleted file mode 100644 index a7c22356..00000000 --- a/software/microcontroller/test/test_3_csos/test_3_csos.cc +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include "../config.h" - -#include -#include - -using namespace ControlSystemsOS; - -CSOSModule* m; // to be initialized in setup() - -void test_csos_map(void) { } -void test_csos_build(void) { - #ifdef DEBUG_SERIAL - DEBUG_SERIAL.println(F("==== [ BUILD ] ====")); - DEBUG_DELAY(); - #endif - - i2cip_errorlevel_t errlev = ControlSystemsOS::update(true); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "Build failed!"); - - // TODO: Test Module Contents! (Against what?) -} - -void test_csos_update(void) { - #ifdef DEBUG_SERIAL - DEBUG_SERIAL.println(F("==== [ UPDATE ] ====")); - DEBUG_DELAY(); - #endif - - i2cip_errorlevel_t errlev = ControlSystemsOS::update(false); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "Update failed!"); -} - -void setup(void) { - Serial.begin(115200); - - delay(2000); - - UNITY_BEGIN(); - - RUN_TEST(test_csos_build); - - delay(1000); -} - -uint8_t count = 1; -void loop(void) { - - RUN_TEST(test_csos_update); - - if(count > 10) { - UNITY_END(); - while(true); - } - - delay(1000); - count++; -} From 818a6f9e7cd4cfca44e877cee345c998d5151920 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 29 Aug 2025 18:36:23 -0400 Subject: [PATCH 37/85] Import Main Script --- .gitmodules | 3 + software/microcontroller/platformio.ini | 48 ++-- software/microcontroller/src/main.cc | 283 ++++++++++++++++++++++++ software/microcontroller/src/peapod.cc | 119 ++++++++++ software/microcontroller/src/peapod.h | 62 ++++++ 5 files changed, 490 insertions(+), 25 deletions(-) create mode 100644 .gitmodules create mode 100644 software/microcontroller/src/main.cc create mode 100644 software/microcontroller/src/peapod.cc create mode 100644 software/microcontroller/src/peapod.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f1306b15 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "software/microcontroller/lib/I2CIP"] + path = software/microcontroller/lib/I2CIP + url = https://github.com/PeaPodTechnologies/I2CIP diff --git a/software/microcontroller/platformio.ini b/software/microcontroller/platformio.ini index f5e15593..d6c586f3 100644 --- a/software/microcontroller/platformio.ini +++ b/software/microcontroller/platformio.ini @@ -9,40 +9,38 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = peapod +default_envs = feather -[env:peapod] +[env:nano] platform = atmelavr -board = nanoatmega328new framework = arduino -upload_protocol = custom -upload_command = sudo avrdude -p $BOARD_MCU -C avrdude_gpio.conf -c peapod -v -U flash:w:$SOURCE:i -test_port = /dev/ttyS0 -test_speed = 115200 +board = nanoatmega328new test_build_src = true -monitor_port = /dev/ttyS0 monitor_speed = 115200 -lib_deps = - bblanchon/ArduinoJson@^6.19.4 +lib_deps = bblanchon/ArduinoJson@^7.2.1 +lib_extra_dirs = lib/I2CIP/lib -[env:direct] -platform = atmelavr -board = nanoatmega328new +[env:feather] +platform = espressif32 framework = arduino -test_port = /dev/ttyS0 -test_speed = 115200 -test_build_src = true -monitor_port = /dev/ttyS0 +board = adafruit_feather_esp32_v2 monitor_speed = 115200 -lib_deps = - bblanchon/ArduinoJson@^6.19.4 +test_build_src = true +test_speed = 115200 +lib_deps = bblanchon/ArduinoJson@^7.2.1 +monitor_filters = esp32_exception_decoder +lib_extra_dirs = lib/I2CIP/lib -[env:autousb] +[env:gpionano] platform = atmelavr -board = nanoatmega328new framework = arduino -test_speed = 115200 -test_build_src = true +board = nanoatmega328new +upload_protocol = custom +upload_command = sudo avrdude -p $BOARD_MCU -C avrdude_gpio.conf -c peapod -v -U flash:w:$SOURCE:i monitor_speed = 115200 -lib_deps = - bblanchon/ArduinoJson@^6.19.4 \ No newline at end of file +monitor_port = /dev/ttyS0 +test_build_src = true +test_speed = 115200 +test_port = /dev/ttyS0 +lib_deps = bblanchon/ArduinoJson@^7.2.1 +lib_extra_dirs = lib/I2CIP/lib \ No newline at end of file diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc new file mode 100644 index 00000000..c55614dc --- /dev/null +++ b/software/microcontroller/src/main.cc @@ -0,0 +1,283 @@ +#ifndef UNIT_TEST +#define UNIT_TEST 1 +#define IS_MAIN 1 + +#include + +#include + +#include + +#define HEARTBEAT_DELAY 5000 +#define LCD_REFRESH_MAX 1000 +#define RGB_REFRESH_MAX 1 +#define EPSILON_TEMPERATURE 0.5f +#define EPSILON_HUMIDITY 2.0f // 0.11f + +// PeaPod Stuff +#define PEAPOD_PIN_TOGGLE 33 // Multipurpose; Interpreted input from rotary button (Debounced; Toggle) +#define PEAPOD_PIN_OUT 32 // Multipurpose; Direct input from rotary button +#define PEAPOD_PIN_PWM1 15 // Multipurpose; Interpreted output from rotary knob (0-360 -> 0-255) +#define PEAPOD_PIN_PWM2 14 // Multipurpose; Interpreted output from rotary knob (0-360 -> 0-255) +#define DURATION_WATERING 2000 // 10 seconds every... +#define PERIOD_WATERING 10000 // ...30 minutes +#define DURATION_LIGHTING (TWENTYFOURHRS_MILLIS*1/2) // 12:12 hrs +#define PIN_WATERING PEAPOD_PIN_OUT + +using namespace PeaPod; +using namespace I2CIP; + +i2cip_fqa_t fqa_sht45 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SHT45_ADDRESS); +// i2cip_fqa_t fqa_pca9685 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_PCA9685_ADDRESS); +i2cip_fqa_t fqa_lcd = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_JHD1313_ADDRESS); +i2cip_fqa_t fqa_rotary = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SEESAW_ADDRESS); +i2cip_fqa_t fqa_nunchuck = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_NUNCHUCK_ADDRESS); + +void setup(void) { + // 0. Builtin LED Pinmode; Serial Begin + + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); + while(!Serial) { digitalWrite(LED_BUILTIN, HIGH); delay(100); digitalWrite(LED_BUILTIN, LOW); delay(100); } + + // Instantiate module + modules[PEAPOD_MODULENUM] = new PeaPodModule(); + + // Load Devices + modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, DebugJsonBreakpoints); + // modules[PEAPOD_MODULENUM]->operator()(fqa_pca9685, false, _i2cip_args_io_default, DebugJsonBreakpoints); + modules[PEAPOD_MODULENUM]->operator()(fqa_sht45, true, _i2cip_args_io_default, DebugJsonBreakpoints); + modules[PEAPOD_MODULENUM]->operator()(fqa_lcd, true, _i2cip_args_io_default, DebugJsonBreakpoints); + modules[PEAPOD_MODULENUM]->operator()(fqa_rotary, true, _i2cip_args_io_default, DebugJsonBreakpoints); +} + +// LOOP GLOBALS +unsigned long last = 0; +unsigned long lastHeartbeat = 0; +uint32_t fps = 0; // Something other than zero +bool revision = false; + +// bool temphum = false; +state_sht45_t temphum = {NAN, NAN}; +int32_t rotary_zero1 = 0; +int32_t rotary_zero2 = 0; +unsigned long last_lcd = LCD_REFRESH_MAX; unsigned long last_rgb = RGB_REFRESH_MAX; // Fixes first-frame bug +bool do_lcd = true; +float nunchuck_sum = 0.0f; + +// PeaPod Stuff +uint8_t peapod_pwm1 = 0; // Pin 15 +uint8_t peapod_pwm2 = 0; // Pin 14 +bool peapod_toggle = false; // Pin 33 +bool peapod_out = false; // Pin 32 +bool flag_debounce = false; // Used to debounce for toggles + +void loop(void) { + last = millis(); + #ifdef FSM_STATE_H_ + FSM::Chronos.set(last); // Update chronograph and do event/interval conditionals & callbacks(?) + #endif + + while(Serial.available() > 0) { // With baud 115200, this should not block + DebugJson::update(Serial, I2CIP::commandRouter); + } + + if(millis() - lastHeartbeat >= HEARTBEAT_DELAY) { + DebugJson::heartbeat(millis(), Serial); + DebugJson::revision(I2CIP_REVISION, Serial); + DebugJson::telemetry(millis(), fps, "fps", Serial); + lastHeartbeat = millis(); + } + + // 0. Prep Args: Onboard 7SEG (Default to Snake Mode) + i2cip_ht16k33_mode_t seg_mode = SEG_SNAKE; + i2cip_ht16k33_data_t seg_data = { .h = fps }; + i2cip_args_io_t args_7seg = { .a = nullptr, .s = &seg_data, .b = &seg_mode }; + + // 1. MUX & EEPROM: Ping and load devices + errlev[PEAPOD_MODULENUM] = modules[PEAPOD_MODULENUM]->operator()(); // First-Time EEPROM Self-Registration and Discovery; Ping MUX && EEPROM + if(errlev[PEAPOD_MODULENUM] == I2CIP_ERR_NONE) { + + // 2. DebugJson Heartbeat & Display FPS + DebugJson::revision(0, Serial); // sends revision + seg_mode = SEG_UINT; + + // 3. Prep Args: LCD + String msg = String(fps) + "Hz I2CIP\n"; // Further append will be on second line + i2cip_jhd1313_args_t rgb = JHD1313::randomRGBLCD(); + i2cip_args_io_t args_lcd = { .a = nullptr, .s = nullptr, .b = &rgb }; + + // Prep Args: 7SEG + // seg_data.f = NAN; // Will produce "NUL.L" on display instead of temperature + // args_7seg.s = &seg_data.f; + + // SHT45 - Average and print to LCD + I2CIP::DeviceGroup* dg_sht45 = modules[PEAPOD_MODULENUM]->operator[]("SHT45"); + if(dg_sht45 != nullptr && dg_sht45->getNumDevices() > 0) { + // AVERAGES + state_sht45_t th = {0.0f, 0.0f}; uint8_t c = 0; + for(uint8_t i = 0; i < dg_sht45->getNumDevices(); i++) { + SHT45* d = (SHT45*)(dg_sht45->getDevice(i)); + if(d == nullptr) continue; + + i2cip_errorlevel_t errlev_sht45 = modules[PEAPOD_MODULENUM]->operator()(d->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); + if(errlev_sht45 != I2CIP_ERR_NONE) continue; + + th.temperature += d->getCache().temperature; + th.humidity += d->getCache().humidity; + c++; + + DebugJson::telemetry(d->getLastRX(), d->getCache().temperature, "temperature"); + DebugJson::telemetry(d->getLastRX(), d->getCache().humidity, "humidity"); + } + + if(c > 0) { + + th.temperature /= c; th.humidity /= c; + + msg += '[' + String(th.temperature, 1) + "C, " + String(th.humidity, 1) + "%]"; // The double-space might cut off the % but who cares + seg_data.f = th.temperature; + seg_mode = SEG_1F; + + } else { + // LCD Default + msg += "SHT45 EIO :("; + args_lcd.s = &msg; + do_lcd = true; + } + + // If messages match and neither temperature nor humidity have changed (and are not NaN), skip LCD + if(!isnan(temphum.temperature) && !isnan(temphum.humidity) && (abs(th.temperature - temphum.temperature) < EPSILON_TEMPERATURE && abs(th.humidity - temphum.humidity) < EPSILON_HUMIDITY) && ((long int)millis() - last_lcd < LCD_REFRESH_MAX)) { + // MAIN_DEBUG_SERIAL.println(F("[I2CIP | LCD SKIP; RGB ONLY]")); + do_lcd = false; + } else { + temphum.temperature = th.temperature; temphum.humidity = th.humidity; + args_lcd.s = &msg; + do_lcd = true; + } + } else { + // LCD Default + msg += "SHT45 ENOENT :("; + args_lcd.s = &msg; + do_lcd = true; + } + + // Nunchuck + I2CIP::DeviceGroup* dg_nunchuck = modules[PEAPOD_MODULENUM]->operator[]("NUNCHUCK"); + if(dg_nunchuck != nullptr && dg_nunchuck->getNumDevices() > 0) { + Nunchuck* d = (Nunchuck*)(dg_nunchuck->getDevice(0)); // Use first device + if(d != nullptr && d->getInput() != nullptr) { + + i2cip_errorlevel_t errlev_nunchuck = modules[PEAPOD_MODULENUM]->operator()(d->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); + + if(errlev_nunchuck != I2CIP_ERR_NONE) { + nunchuck_sum += ((float)(d->getCache().x) - 127.f) / 255.f; // += cache.y; + + DebugJson::telemetry(d->getLastRX(), d->getCache().x, "joy_x"); + DebugJson::telemetry(d->getLastRX(), d->getCache().y, "joy_y"); + DebugJson::telemetry(d->getLastRX(), d->getCache().c, "button_c"); + DebugJson::telemetry(d->getLastRX(), d->getCache().z, "button_z"); + } else { + nunchuck_sum = 0.0f; + } + } else { + nunchuck_sum = 0.0f; + } + } else { + nunchuck_sum = 0.0f; + } + + I2CIP::DeviceGroup* dg_rotary = modules[PEAPOD_MODULENUM]->operator[]("SEESAW"); + if(dg_rotary != nullptr && dg_rotary->getNumDevices() > 0) { + RotaryEncoder* rotary = (RotaryEncoder*)(dg_rotary->getDevice(0)); // Use first device + if(rotary != nullptr) { + i2cip_errorlevel_t errlev_rotary = modules[PEAPOD_MODULENUM]->operator()(rotary->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); + if(errlev_rotary == I2CIP_ERR_NONE) { + i2cip_rotaryencoder_t cache = rotary->getCache(); + + // PeaPod Stuff + if(cache.button == HIGH) { // Debounce to pulse ONCE on button press + if(!flag_debounce){ + // Invert and set debounce + peapod_toggle = !peapod_toggle; + flag_debounce = true; + } + peapod_out = true; + } else { + // Reset debounce + flag_debounce = false; + peapod_out = false; + } + uint32_t angle_ticks = 0; + + // Toggle also selects PWM control target + if(peapod_toggle) { + if(cache.button) rotary_zero1 = cache.encoder; // Set zero-point + angle_ticks = -(cache.encoder - rotary_zero1); // Invert rotation + peapod_pwm1 = (uint8_t)((float)(Seesaw::_encoderDegrees(angle_ticks)) / 360 * 255); // 0-3840 (256 ticks) -> 0-255 + } else { + if(cache.button) rotary_zero2 = cache.encoder; // Set zero-point + angle_ticks = -(cache.encoder - rotary_zero2); // Invert rotation + peapod_pwm2 = (uint8_t)((float)(Seesaw::_encoderDegrees(angle_ticks)) / 360 * 255); // 0-3840 (256 ticks) -> 0-255 + } + uint32_t position = Seesaw::_encoderDegrees(angle_ticks, 9999); // 27 * 360 = 9720; Maximum revolutions on 4 digit display + + if(args_lcd.s != nullptr) { + if(peapod_toggle) { + *((String*)args_lcd.s) += " B"; + } else { + *((String*)args_lcd.s) += " R"; + } + } + + // DebugJson::telemetry(rotary->getLastRX(), cache.encoder, "encoder"); + DebugJson::telemetry(rotary->getLastRX(), position, "position"); + DebugJson::telemetry(rotary->getLastRX(), cache.button, "button"); + // DebugJson::telemetry(rotary->getLastRX(), ((float)peapod_pwm1 / 255), "peapod_pwm1"); + // DebugJson::telemetry(rotary->getLastRX(), ((float)peapod_pwm2 / 255), "peapod_pwm2"); + // DebugJson::telemetry(rotary->getLastRX(), peapod_toggle, "peapod_toggle"); + // DebugJson::telemetry(rotary->getLastRX(), peapod_out, "peapod_out"); + + // Overwrite 7Seg Args + seg_mode = SEG_UINT; + seg_data.h = position + ((unsigned)nunchuck_sum); // Now h is the active member + } + } + + } + // else { + // rotary_zero = 0; + // } + + if(do_lcd || (long int)millis() - last_rgb > RGB_REFRESH_MAX) { + // LCD: On-Module SHT45 Status Display + i2cip_errorlevel_t errlev_lcd = modules[PEAPOD_MODULENUM]->operator()(fqa_lcd, true, args_lcd, DebugJsonOut); + if(errlev_lcd == I2CIP_ERR_NONE && fps != 0) { + if(args_lcd.s != nullptr) last_lcd = millis(); + last_rgb = millis(); + } + } + } else { + seg_mode = SEG_SNAKE; + } + // 7SEG: Off-Module (MCU Featherwing/Shield) Multi-Status Display: Rotary, else SHT45, else Snake + i2cip_errorlevel_t errlev_7seg = modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, args_7seg, DebugJsonOut); + + // PeaPod Stuff + controlPin(peapod_toggle); + controlPin(peapod_out); + + controlPWM(peapod_pwm1); + controlPWM(peapod_pwm2); + + #ifdef CYCLE_DELAY + delay(CYCLE_DELAY); + #endif + + // DEBUG PRINT: CYCLE COUNT, FPS, and ERRLEV + unsigned long delta = millis() - last; + fps += 1000.f / max(1.f, (float)delta); + fps /= 2; +} + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/peapod.cc b/software/microcontroller/src/peapod.cc new file mode 100644 index 00000000..ddaf156e --- /dev/null +++ b/software/microcontroller/src/peapod.cc @@ -0,0 +1,119 @@ +#include + +bool PeaPod::pinModeSet[255] = { false }; + +using namespace PeaPod; +using namespace I2CIP; + +PeaPodModule::PeaPodModule() : JsonModule(PEAPOD_WIRENUM, PEAPOD_MODULENUM) { } + +DeviceGroup* PeaPodModule::deviceGroupFactory(const i2cip_id_t& id) { + DeviceGroup* dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + return dg; +} + +void PeaPodModule::handleCommand(JsonObject command, Print& out) { + JsonDocument doc; + doc["timestamp"] = millis(); + + i2cip_fqa_t fqa = command["fqa"].as(); + + Device** dptr = I2CIP::devicetree[fqa]; + if(dptr != nullptr && *dptr != nullptr) { + + Device* d = *dptr; + + DeviceGroup* dg = this->operator[](d->getID()); + + if(dg != nullptr && dg->handler != nullptr) { + i2cip_args_io_t args = _i2cip_args_io_default; + + JsonVariant argsG = command["g"]; + JsonVariant argsA = command["a"]; + JsonVariant argsS = command["s"]; + JsonVariant argsB = command["b"]; + + dg->handler(args, argsA, argsS, argsB); + + // DebugJson::StringWriter sw; + // i2cip_errorlevel_t errlev = this->operator()(d, true, args, sw); + unsigned long start = millis(); + String msg = String(d->getID()) + ' ' + fqaToString(fqa) + ' '; + bool spacer = false; + + i2cip_errorlevel_t errlev = MUX::setBus(fqa); + if(errlev == I2CIP_ERR_NONE) { + if(d->getOutput() != nullptr && !argsS.isNull()) { + errlev = d->set(args.s, args.b); + + // Print output cache + msg += "OUTSET "; + msg += d->getOutput()->valueToString(); + spacer = true; + } + if(errlev == I2CIP_ERR_NONE && d->getInput() != nullptr && !argsG.isNull()) { + errlev = d->get(args.a); + + // Print input cache + if(spacer) msg += "; "; + else spacer = true; + msg += "INPGET "; + msg += d->getInput()->printCache(); + + if(errlev == I2CIP_ERR_NONE) { + DebugJson::telemetryJsonString(d->getInput()->getLastRX(), d->getInput()->cacheToString()); + } + } + if(errlev == I2CIP_ERR_NONE && argsG.isNull() && argsS.isNull()) { + errlev = d->pingTimeout(false, true); + msg = "PING"; + } + } + + dg->cleanup(args); + + msg += " DELTA "; + msg += String(millis() - start); + msg += "ms"; + + msg += errlev == I2CIP_ERR_NONE ? " OK" : (errlev == I2CIP_ERR_SOFT ? " EINVAL" : " EIO"); + + + // TODO: Print to out with sw, errlev, timestamp + doc["type"] = "info"; + doc["id"] = d->getID(); + doc["fqa"] = d->getFQA(); + doc["errlev"] = errlev; + // doc["msg"] = sw.operator String(); + doc["msg"] = msg; + } else { + doc["type"] = "error"; + doc["msg"] = "LIBRARY ENOENT"; + doc["errlev"] = I2CIP_ERR_SOFT; + } + } else { + doc["type"] = "error"; + doc["msg"] = "DEVICE ENOENT"; + doc["errlev"] = I2CIP_ERR_SOFT; + } + + DebugJson::jsonPrintln(doc, out); +} + +void PeaPodModule::handleConfig(JsonObject config, Print& out) { + // TODO: Implement - something with EEPROM? or FSM? +} \ No newline at end of file diff --git a/software/microcontroller/src/peapod.h b/software/microcontroller/src/peapod.h new file mode 100644 index 00000000..65462616 --- /dev/null +++ b/software/microcontroller/src/peapod.h @@ -0,0 +1,62 @@ +#ifndef PEAPOD_H_ +#define PEAPOD_H_ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define PEAPOD_WIRENUM 0 +#define PEAPOD_MODULENUM 0 + +namespace PeaPod { + class PeaPodModule : public I2CIP::JsonModule { + private: + protected: + I2CIP::DeviceGroup* deviceGroupFactory(const i2cip_id_t& id) override; + public: + PeaPodModule(); + + void handleCommand(JsonObject command, Print& out) override; + + void handleConfig(JsonObject config, Print& out) override; + }; + + extern bool pinModeSet[255]; + + template void controlPin(const bool& s) { + if(!pinModeSet[P]) { pinMode(P, OUTPUT); } + if(s) { + digitalWrite(P, HIGH); + } else { + digitalWrite(P, LOW); + } + } + template void controlPWM(const uint8_t& v) { + if(!pinModeSet[P]) { pinMode(P, OUTPUT); } + analogWrite(P, v); + } + + template void controlPin(bool _, const bool& s) { controlPin

    (s); } + + template void controlPWM(bool _, const FSM::Number& v) { + if(v.isFloating) { + // 0.0 - 1.0 + controlPWM

    (min(255, max(0, (int)((double)v * 255.f)))); + } else { + // 0 - 255 + controlPWM

    (min(255, max(0, (int)(v)))); + } + } +} + +#endif \ No newline at end of file From 4c7ba5ecd75ee12d2f0f67ec6238e605131a6326 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 29 Aug 2025 22:13:06 -0400 Subject: [PATCH 38/85] Revamped Main --- software/microcontroller/src/main.cc | 305 ++++++++----------------- software/microcontroller/src/peapod.cc | 2 + software/microcontroller/src/peapod.h | 3 +- 3 files changed, 100 insertions(+), 210 deletions(-) diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index c55614dc..7abbe5bf 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -4,34 +4,33 @@ #include -#include - #include +#include + #define HEARTBEAT_DELAY 5000 -#define LCD_REFRESH_MAX 1000 -#define RGB_REFRESH_MAX 1 -#define EPSILON_TEMPERATURE 0.5f -#define EPSILON_HUMIDITY 2.0f // 0.11f - -// PeaPod Stuff -#define PEAPOD_PIN_TOGGLE 33 // Multipurpose; Interpreted input from rotary button (Debounced; Toggle) -#define PEAPOD_PIN_OUT 32 // Multipurpose; Direct input from rotary button -#define PEAPOD_PIN_PWM1 15 // Multipurpose; Interpreted output from rotary knob (0-360 -> 0-255) -#define PEAPOD_PIN_PWM2 14 // Multipurpose; Interpreted output from rotary knob (0-360 -> 0-255) -#define DURATION_WATERING 2000 // 10 seconds every... -#define PERIOD_WATERING 10000 // ...30 minutes -#define DURATION_LIGHTING (TWENTYFOURHRS_MILLIS*1/2) // 12:12 hrs -#define PIN_WATERING PEAPOD_PIN_OUT + +void callback_heartbeat(bool _, const FSM::fsm_timestamp_t& __); +void callback_module(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); +void callback_sht45(bool _, const FSM::fsm_timestamp_t& __); +void callback_temperature(bool _, const FSM::Number& v); +void callback_humidity(bool _, const FSM::Number& v); using namespace PeaPod; using namespace I2CIP; +// SETUP GLOBALS + i2cip_fqa_t fqa_sht45 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SHT45_ADDRESS); // i2cip_fqa_t fqa_pca9685 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_PCA9685_ADDRESS); -i2cip_fqa_t fqa_lcd = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_JHD1313_ADDRESS); -i2cip_fqa_t fqa_rotary = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SEESAW_ADDRESS); -i2cip_fqa_t fqa_nunchuck = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_NUNCHUCK_ADDRESS); +// i2cip_fqa_t fqa_lcd = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_JHD1313_ADDRESS); +// i2cip_fqa_t fqa_rotary = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SEESAW_ADDRESS); +// i2cip_fqa_t fqa_nunchuck = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_NUNCHUCK_ADDRESS); + +FSM::Variable cycle(FSM::Number(0, false, false), "cycle"); +FSM::Variable temperature = FSM::Variable(FSM::Number(), "air-temperature"); +FSM::Variable humidity = FSM::Variable(FSM::Number(), "air-humidity"); +const FSM::Number notanumber = FSM::Number(); void setup(void) { // 0. Builtin LED Pinmode; Serial Begin @@ -40,78 +39,86 @@ void setup(void) { Serial.begin(115200); while(!Serial) { digitalWrite(LED_BUILTIN, HIGH); delay(100); digitalWrite(LED_BUILTIN, LOW); delay(100); } - // Instantiate module - modules[PEAPOD_MODULENUM] = new PeaPodModule(); + // Instantiate module ahead of time + callback_module(); // Load Devices modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, DebugJsonBreakpoints); // modules[PEAPOD_MODULENUM]->operator()(fqa_pca9685, false, _i2cip_args_io_default, DebugJsonBreakpoints); modules[PEAPOD_MODULENUM]->operator()(fqa_sht45, true, _i2cip_args_io_default, DebugJsonBreakpoints); - modules[PEAPOD_MODULENUM]->operator()(fqa_lcd, true, _i2cip_args_io_default, DebugJsonBreakpoints); - modules[PEAPOD_MODULENUM]->operator()(fqa_rotary, true, _i2cip_args_io_default, DebugJsonBreakpoints); + // modules[PEAPOD_MODULENUM]->operator()(fqa_lcd, true, _i2cip_args_io_default, DebugJsonBreakpoints); + // modules[PEAPOD_MODULENUM]->operator()(fqa_rotary, true, _i2cip_args_io_default, DebugJsonBreakpoints); + + humidity.addConditional(FSM::CMP_NEQ, notanumber, callback_humidity); + temperature.addConditional(FSM::CMP_NEQ, notanumber, callback_temperature); + + FSM::Chronos.addInterval(HEARTBEAT_DELAY, callback_heartbeat); + FSM::Chronos.addInterval(100, callback_module); + FSM::Chronos.addInterval(250, callback_sht45); + } // LOOP GLOBALS + unsigned long last = 0; -unsigned long lastHeartbeat = 0; -uint32_t fps = 0; // Something other than zero -bool revision = false; - -// bool temphum = false; -state_sht45_t temphum = {NAN, NAN}; -int32_t rotary_zero1 = 0; -int32_t rotary_zero2 = 0; -unsigned long last_lcd = LCD_REFRESH_MAX; unsigned long last_rgb = RGB_REFRESH_MAX; // Fixes first-frame bug -bool do_lcd = true; -float nunchuck_sum = 0.0f; - -// PeaPod Stuff -uint8_t peapod_pwm1 = 0; // Pin 15 -uint8_t peapod_pwm2 = 0; // Pin 14 -bool peapod_toggle = false; // Pin 33 -bool peapod_out = false; // Pin 32 -bool flag_debounce = false; // Used to debounce for toggles +uint32_t fps = 0; void loop(void) { + + // I2CIP_DEBUG_SERIAL.println(I2CIP::devicetree.toString()); + // I2CIP_DEBUG_SERIAL.println(modules[PEAPOD_MODULENUM]->toString()); + + cycle.set(cycle.get() + FSM::Number(1, false, false)); + last = millis(); - #ifdef FSM_STATE_H_ - FSM::Chronos.set(last); // Update chronograph and do event/interval conditionals & callbacks(?) - #endif + FSM::Chronos.set(last); // Update chronograph and do event/interval conditionals & callbacks(?) while(Serial.available() > 0) { // With baud 115200, this should not block DebugJson::update(Serial, I2CIP::commandRouter); } - if(millis() - lastHeartbeat >= HEARTBEAT_DELAY) { - DebugJson::heartbeat(millis(), Serial); - DebugJson::revision(I2CIP_REVISION, Serial); - DebugJson::telemetry(millis(), fps, "fps", Serial); - lastHeartbeat = millis(); + #ifdef CYCLE_DELAY + delay(CYCLE_DELAY); + #endif + + unsigned long delta = millis() - last; + fps += 1000.f / max(1.f, (float)delta); + fps /= 2; +} + +// HELPER FUNCTIONS + +void callback_heartbeat(bool _, const FSM::fsm_timestamp_t& __) { + DebugJson::heartbeat(millis(), Serial); + DebugJson::revision(I2CIP_REVISION, Serial); + DebugJson::telemetry(millis(), fps, "fps", Serial); + DebugJson::telemetry(millis(), (unsigned)cycle.get(), "cycle", Serial); +} + +void callback_module(bool _, const FSM::fsm_timestamp_t& __) { + if(I2CIP::MUX::pingMUX(PEAPOD_WIRENUM, PEAPOD_MODULENUM)) { + if(I2CIP::modules[PEAPOD_MODULENUM] == nullptr) { + I2CIP::modules[PEAPOD_MODULENUM] = new PeaPodModule(); + + // First Module - Add HT16K33 + I2CIP::modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, NullStream); + } + + I2CIP::errlev[PEAPOD_MODULENUM] = I2CIP::modules[PEAPOD_MODULENUM]->operator()(); + } else { + I2CIP::errlev[PEAPOD_MODULENUM] = I2CIP_ERR_HARD; } - // 0. Prep Args: Onboard 7SEG (Default to Snake Mode) - i2cip_ht16k33_mode_t seg_mode = SEG_SNAKE; - i2cip_ht16k33_data_t seg_data = { .h = fps }; - i2cip_args_io_t args_7seg = { .a = nullptr, .s = &seg_data, .b = &seg_mode }; - - // 1. MUX & EEPROM: Ping and load devices - errlev[PEAPOD_MODULENUM] = modules[PEAPOD_MODULENUM]->operator()(); // First-Time EEPROM Self-Registration and Discovery; Ping MUX && EEPROM - if(errlev[PEAPOD_MODULENUM] == I2CIP_ERR_NONE) { - - // 2. DebugJson Heartbeat & Display FPS - DebugJson::revision(0, Serial); // sends revision - seg_mode = SEG_UINT; - - // 3. Prep Args: LCD - String msg = String(fps) + "Hz I2CIP\n"; // Further append will be on second line - i2cip_jhd1313_args_t rgb = JHD1313::randomRGBLCD(); - i2cip_args_io_t args_lcd = { .a = nullptr, .s = nullptr, .b = &rgb }; - - // Prep Args: 7SEG - // seg_data.f = NAN; // Will produce "NUL.L" on display instead of temperature - // args_7seg.s = &seg_data.f; - - // SHT45 - Average and print to LCD + for(uint8_t m = 0; m < I2CIP_MUX_COUNT; m++) { + if(I2CIP::modules[m] != nullptr && I2CIP::errlev[m] == I2CIP_ERR_HARD) { + delete I2CIP::modules[m]; + I2CIP::modules[m] = nullptr; + } + } +} + +void callback_sht45(bool _, const FSM::fsm_timestamp_t& __) { + if(I2CIP::errlev[PEAPOD_MODULENUM] == I2CIP_ERR_NONE) { I2CIP::DeviceGroup* dg_sht45 = modules[PEAPOD_MODULENUM]->operator[]("SHT45"); if(dg_sht45 != nullptr && dg_sht45->getNumDevices() > 0) { // AVERAGES @@ -127,157 +134,37 @@ void loop(void) { th.humidity += d->getCache().humidity; c++; - DebugJson::telemetry(d->getLastRX(), d->getCache().temperature, "temperature"); - DebugJson::telemetry(d->getLastRX(), d->getCache().humidity, "humidity"); + // DebugJson::telemetry(d->getLastRX(), d->getCache().temperature, "air-temperature"); + // DebugJson::telemetry(d->getLastRX(), d->getCache().humidity, "air-humidity"); } if(c > 0) { th.temperature /= c; th.humidity /= c; - msg += '[' + String(th.temperature, 1) + "C, " + String(th.humidity, 1) + "%]"; // The double-space might cut off the % but who cares - seg_data.f = th.temperature; - seg_mode = SEG_1F; + // seg_data.f = th.temperature; + // seg_mode = SEG_1F; - } else { - // LCD Default - msg += "SHT45 EIO :("; - args_lcd.s = &msg; - do_lcd = true; - } - - // If messages match and neither temperature nor humidity have changed (and are not NaN), skip LCD - if(!isnan(temphum.temperature) && !isnan(temphum.humidity) && (abs(th.temperature - temphum.temperature) < EPSILON_TEMPERATURE && abs(th.humidity - temphum.humidity) < EPSILON_HUMIDITY) && ((long int)millis() - last_lcd < LCD_REFRESH_MAX)) { - // MAIN_DEBUG_SERIAL.println(F("[I2CIP | LCD SKIP; RGB ONLY]")); - do_lcd = false; - } else { - temphum.temperature = th.temperature; temphum.humidity = th.humidity; - args_lcd.s = &msg; - do_lcd = true; - } - } else { - // LCD Default - msg += "SHT45 ENOENT :("; - args_lcd.s = &msg; - do_lcd = true; - } - - // Nunchuck - I2CIP::DeviceGroup* dg_nunchuck = modules[PEAPOD_MODULENUM]->operator[]("NUNCHUCK"); - if(dg_nunchuck != nullptr && dg_nunchuck->getNumDevices() > 0) { - Nunchuck* d = (Nunchuck*)(dg_nunchuck->getDevice(0)); // Use first device - if(d != nullptr && d->getInput() != nullptr) { - - i2cip_errorlevel_t errlev_nunchuck = modules[PEAPOD_MODULENUM]->operator()(d->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); - - if(errlev_nunchuck != I2CIP_ERR_NONE) { - nunchuck_sum += ((float)(d->getCache().x) - 127.f) / 255.f; // += cache.y; - - DebugJson::telemetry(d->getLastRX(), d->getCache().x, "joy_x"); - DebugJson::telemetry(d->getLastRX(), d->getCache().y, "joy_y"); - DebugJson::telemetry(d->getLastRX(), d->getCache().c, "button_c"); - DebugJson::telemetry(d->getLastRX(), d->getCache().z, "button_z"); - } else { - nunchuck_sum = 0.0f; - } - } else { - nunchuck_sum = 0.0f; - } - } else { - nunchuck_sum = 0.0f; - } - - I2CIP::DeviceGroup* dg_rotary = modules[PEAPOD_MODULENUM]->operator[]("SEESAW"); - if(dg_rotary != nullptr && dg_rotary->getNumDevices() > 0) { - RotaryEncoder* rotary = (RotaryEncoder*)(dg_rotary->getDevice(0)); // Use first device - if(rotary != nullptr) { - i2cip_errorlevel_t errlev_rotary = modules[PEAPOD_MODULENUM]->operator()(rotary->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); - if(errlev_rotary == I2CIP_ERR_NONE) { - i2cip_rotaryencoder_t cache = rotary->getCache(); - - // PeaPod Stuff - if(cache.button == HIGH) { // Debounce to pulse ONCE on button press - if(!flag_debounce){ - // Invert and set debounce - peapod_toggle = !peapod_toggle; - flag_debounce = true; - } - peapod_out = true; - } else { - // Reset debounce - flag_debounce = false; - peapod_out = false; - } - uint32_t angle_ticks = 0; - - // Toggle also selects PWM control target - if(peapod_toggle) { - if(cache.button) rotary_zero1 = cache.encoder; // Set zero-point - angle_ticks = -(cache.encoder - rotary_zero1); // Invert rotation - peapod_pwm1 = (uint8_t)((float)(Seesaw::_encoderDegrees(angle_ticks)) / 360 * 255); // 0-3840 (256 ticks) -> 0-255 - } else { - if(cache.button) rotary_zero2 = cache.encoder; // Set zero-point - angle_ticks = -(cache.encoder - rotary_zero2); // Invert rotation - peapod_pwm2 = (uint8_t)((float)(Seesaw::_encoderDegrees(angle_ticks)) / 360 * 255); // 0-3840 (256 ticks) -> 0-255 - } - uint32_t position = Seesaw::_encoderDegrees(angle_ticks, 9999); // 27 * 360 = 9720; Maximum revolutions on 4 digit display - - if(args_lcd.s != nullptr) { - if(peapod_toggle) { - *((String*)args_lcd.s) += " B"; - } else { - *((String*)args_lcd.s) += " R"; - } - } - - // DebugJson::telemetry(rotary->getLastRX(), cache.encoder, "encoder"); - DebugJson::telemetry(rotary->getLastRX(), position, "position"); - DebugJson::telemetry(rotary->getLastRX(), cache.button, "button"); - // DebugJson::telemetry(rotary->getLastRX(), ((float)peapod_pwm1 / 255), "peapod_pwm1"); - // DebugJson::telemetry(rotary->getLastRX(), ((float)peapod_pwm2 / 255), "peapod_pwm2"); - // DebugJson::telemetry(rotary->getLastRX(), peapod_toggle, "peapod_toggle"); - // DebugJson::telemetry(rotary->getLastRX(), peapod_out, "peapod_out"); - - // Overwrite 7Seg Args - seg_mode = SEG_UINT; - seg_data.h = position + ((unsigned)nunchuck_sum); // Now h is the active member - } - } + temperature.set(th.temperature); + humidity.set(th.humidity); - } - // else { - // rotary_zero = 0; - // } - - if(do_lcd || (long int)millis() - last_rgb > RGB_REFRESH_MAX) { - // LCD: On-Module SHT45 Status Display - i2cip_errorlevel_t errlev_lcd = modules[PEAPOD_MODULENUM]->operator()(fqa_lcd, true, args_lcd, DebugJsonOut); - if(errlev_lcd == I2CIP_ERR_NONE && fps != 0) { - if(args_lcd.s != nullptr) last_lcd = millis(); - last_rgb = millis(); } } - } else { - seg_mode = SEG_SNAKE; - } - // 7SEG: Off-Module (MCU Featherwing/Shield) Multi-Status Display: Rotary, else SHT45, else Snake - i2cip_errorlevel_t errlev_7seg = modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, args_7seg, DebugJsonOut); +} +} - // PeaPod Stuff - controlPin(peapod_toggle); - controlPin(peapod_out); +void callback_temperature(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, "air-temperature"); - controlPWM(peapod_pwm1); - controlPWM(peapod_pwm2); + i2cip_ht16k33_mode_t seg_mode = SEG_1F; + i2cip_ht16k33_data_t seg_data = { .f = (float)v }; + i2cip_args_io_t seg_args = { .a = nullptr, .s = &seg_data, .b = &seg_mode }; - #ifdef CYCLE_DELAY - delay(CYCLE_DELAY); - #endif + I2CIP::modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); +} - // DEBUG PRINT: CYCLE COUNT, FPS, and ERRLEV - unsigned long delta = millis() - last; - fps += 1000.f / max(1.f, (float)delta); - fps /= 2; +void callback_humidity(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, "air-humidity"); } #endif \ No newline at end of file diff --git a/software/microcontroller/src/peapod.cc b/software/microcontroller/src/peapod.cc index ddaf156e..ad01d589 100644 --- a/software/microcontroller/src/peapod.cc +++ b/software/microcontroller/src/peapod.cc @@ -1,5 +1,7 @@ #include +#include + bool PeaPod::pinModeSet[255] = { false }; using namespace PeaPod; diff --git a/software/microcontroller/src/peapod.h b/software/microcontroller/src/peapod.h index 65462616..0c9868b6 100644 --- a/software/microcontroller/src/peapod.h +++ b/software/microcontroller/src/peapod.h @@ -2,11 +2,12 @@ #define PEAPOD_H_ #include -#include #include #include +#include + #include #include #include From 92e1901edb4211d9fa709ca4b6293ae152aa6caa Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sun, 31 Aug 2025 16:55:03 -0400 Subject: [PATCH 39/85] Add I2CIP --- software/microcontroller/lib/I2CIP | 1 + 1 file changed, 1 insertion(+) create mode 160000 software/microcontroller/lib/I2CIP diff --git a/software/microcontroller/lib/I2CIP b/software/microcontroller/lib/I2CIP new file mode 160000 index 00000000..96061a63 --- /dev/null +++ b/software/microcontroller/lib/I2CIP @@ -0,0 +1 @@ +Subproject commit 96061a6343e2f65c24a94b311ba1a8c3bedf4241 From 974c952e44e6c874459942e8abd174686dc8283f Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 11 Sep 2025 18:18:50 -0400 Subject: [PATCH 40/85] Air Module w/ Tests --- software/microcontroller/lib/I2CIP | 2 +- software/microcontroller/src/air.cc | 5 + software/microcontroller/src/air.h | 32 +++ software/microcontroller/src/air.tpp | 80 +++++++ software/microcontroller/src/main.cc | 202 ++++++++++-------- software/microcontroller/src/peapod.cc | 58 ++++- software/microcontroller/src/peapod.h | 23 +- .../test/test_0_blink/test_0_blink.cc | 39 ++++ .../test_1_module_air/test_1_module_air.cc | 116 ++++++++++ 9 files changed, 457 insertions(+), 100 deletions(-) create mode 100644 software/microcontroller/src/air.cc create mode 100644 software/microcontroller/src/air.h create mode 100644 software/microcontroller/src/air.tpp create mode 100644 software/microcontroller/test/test_0_blink/test_0_blink.cc create mode 100644 software/microcontroller/test/test_1_module_air/test_1_module_air.cc diff --git a/software/microcontroller/lib/I2CIP b/software/microcontroller/lib/I2CIP index 96061a63..3d512d39 160000 --- a/software/microcontroller/lib/I2CIP +++ b/software/microcontroller/lib/I2CIP @@ -1 +1 @@ -Subproject commit 96061a6343e2f65c24a94b311ba1a8c3bedf4241 +Subproject commit 3d512d39929caa3ddd0c59e71394ad030d8045e1 diff --git a/software/microcontroller/src/air.cc b/software/microcontroller/src/air.cc new file mode 100644 index 00000000..2e57beb4 --- /dev/null +++ b/software/microcontroller/src/air.cc @@ -0,0 +1,5 @@ +#include + +FSM::Variable PeaPod::air_temperature = FSM::Variable(FSM::Number(), "air-temperature"); +FSM::Variable PeaPod::air_humidity = FSM::Variable(FSM::Number(), "air-humidity"); +FSM::Variable PeaPod::air_co2 = FSM::Variable(FSM::Number(), "air-ppm-carbondioxide"); \ No newline at end of file diff --git a/software/microcontroller/src/air.h b/software/microcontroller/src/air.h new file mode 100644 index 00000000..7c838a18 --- /dev/null +++ b/software/microcontroller/src/air.h @@ -0,0 +1,32 @@ +#ifndef PEAPOD_AIR_H_ +#define PEAPOD_AIR_H_ + +#include + +#define PEAPOD_SHT45_DELTA 250 +#define PEAPOD_K30_DELTA 3000 + +#define PEAPOD_MODULE_AIR_EEPROM_CONTENTS {"[{\"24LC32\":[80],\"SHT45\":[" STR(I2CIP_SHT45_ADDRESS) "]},{\"SHT45\":[" STR(I2CIP_SHT45_ADDRESS) "],\"K30\":[" STR(I2CIP_K30_ADDRESS) "]},{\"SHT45\":[" STR(I2CIP_SHT45_ADDRESS) "]}]"} + +namespace PeaPod { + extern FSM::Variable air_temperature; + extern FSM::Variable air_humidity; + extern FSM::Variable air_co2; + + class PeaPodModuleAir : public PeaPodModule { + public: + PeaPodModuleAir(bool chronoCallbacks = true) : PeaPodModule(PEAPOD_MODULENUM_AIR) { + if(chronoCallbacks) { + FSM::Chronos.addInterval(PEAPOD_SHT45_DELTA, PeaPodModuleAir::callback_sht45_mean); + FSM::Chronos.addInterval(PEAPOD_K30_DELTA, PeaPodModuleAir::callback_k30_mean); + } + } + + template static void callback_sht45_mean(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); + template static void callback_k30_mean(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); + }; +} + +#include + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/air.tpp b/software/microcontroller/src/air.tpp new file mode 100644 index 00000000..7a9f56e2 --- /dev/null +++ b/software/microcontroller/src/air.tpp @@ -0,0 +1,80 @@ +#ifndef PEAPOD_AIR_H_ +#error __FILE__ should only be included AFTER +#endif + +#ifdef PEAPOD_AIR_H_ + +#ifndef PEAPOD_AIR_T_ +#define PEAPOD_AIR_T_ + +template void PeaPod::PeaPodModuleAir::callback_sht45_mean(bool _, const FSM::fsm_timestamp_t& __) { + if(M < 0 || M > I2CIP_MUX_COUNT || I2CIP::modules[M] == nullptr) return; + + // If the module is not in an errored state, + if(I2CIP::errlev[M] == I2CIP_ERR_NONE) { + // Get SHT45 device group + I2CIP::DeviceGroup* dg_sht45 = I2CIP::modules[M]->operator[]("SHT45"); + + // If we have devices, + if(dg_sht45 != nullptr && dg_sht45->getNumDevices() > 0) { + + // Read from all devices and take a mean + state_sht45_t th = {0.0f, 0.0f}; uint8_t c = 0; + for(uint8_t i = 0; i < dg_sht45->getNumDevices(); i++) { + SHT45* d = (SHT45*)(dg_sht45->getDevice(i)); + if(d == nullptr) continue; + + i2cip_errorlevel_t errlev_sht45 = I2CIP::modules[M]->operator()(d->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); + if(errlev_sht45 != I2CIP_ERR_NONE) continue; + + th.temperature += d->getCache().temperature; + th.humidity += d->getCache().humidity; + c++; + } + + // If readings were taken, + if(c > 0) { + // Set variables according to averages + PeaPod::air_temperature.set(th.temperature / c); + PeaPod::air_humidity.set(th.humidity / c); + } + } + } +} + +template void PeaPod::PeaPodModuleAir::callback_k30_mean(bool _, const FSM::fsm_timestamp_t& __) { + if(M < 0 || M > I2CIP_MUX_COUNT || I2CIP::modules[M] == nullptr) return; + + // If the module is not in an errored state, + if(I2CIP::errlev[M] == I2CIP_ERR_NONE) { + // Get K30 device group + I2CIP::DeviceGroup* dg_k30 = I2CIP::modules[M]->operator[]("K30"); + + // If we have devices, + if(dg_k30 != nullptr && dg_k30->getNumDevices() > 0) { + + // Read from all devices and take a mean + uint16_t co2 = 0; uint8_t c = 0; + for(uint8_t i = 0; i < dg_k30->getNumDevices(); i++) { + K30* d = (K30*)(dg_k30->getDevice(i)); + if(d == nullptr) continue; + + i2cip_errorlevel_t errlev_k30 = I2CIP::modules[M]->operator()(d->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); + if(errlev_k30 != I2CIP_ERR_NONE) continue; + + co2 += d->getCache(); + c++; + } + + // If readings were taken, + if(c > 0) { + // Set variable according to average + PeaPod::air_co2.set(co2 / c); + } + } + } +} + +#endif + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index 7abbe5bf..cce61ff7 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -5,32 +5,41 @@ #include #include +#include #include -#define HEARTBEAT_DELAY 5000 +// #define SEVENSEG_TEMPERATURE 1 // Uncomment to enable 7-segment display for temperature +// #define SEVENSEG_HUMIDITY 1 // Uncomment to enable 7-segment display for humidity +#define SEVENSEG_CO2 1 // Uncomment to enable 7-segment display for CO2 + +#define PEAPOD_MODULENUM_PCA9685 0 +#define PEAPOD_MODULENUM_MCP23017 0 +#define PEAPOD_MODULENUM_SEVENSEG 0 -void callback_heartbeat(bool _, const FSM::fsm_timestamp_t& __); -void callback_module(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); -void callback_sht45(bool _, const FSM::fsm_timestamp_t& __); void callback_temperature(bool _, const FSM::Number& v); void callback_humidity(bool _, const FSM::Number& v); +void callback_co2(bool _, const FSM::Number& v); + +template void callback_mcp23017_digitalWrite(bool _, const bool& v); +template void callback_pca9685_analogWrite(bool _, const FSM::Number& v); +template void callback_pca9685_onOff(bool _, const bool& v); using namespace PeaPod; using namespace I2CIP; // SETUP GLOBALS -i2cip_fqa_t fqa_sht45 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SHT45_ADDRESS); -// i2cip_fqa_t fqa_pca9685 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_PCA9685_ADDRESS); +// i2cip_fqa_t fqa_sht45 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SHT45_ADDRESS); // i2cip_fqa_t fqa_lcd = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_JHD1313_ADDRESS); // i2cip_fqa_t fqa_rotary = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SEESAW_ADDRESS); // i2cip_fqa_t fqa_nunchuck = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_NUNCHUCK_ADDRESS); +i2cip_fqa_t fqa_gpio = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_MCP23017, 2, I2CIP_MCP23017_ADDRESS); +i2cip_fqa_t fqa_pwm = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_PCA9685, 1, I2CIP_PCA9685_ADDRESS); -FSM::Variable cycle(FSM::Number(0, false, false), "cycle"); -FSM::Variable temperature = FSM::Variable(FSM::Number(), "air-temperature"); -FSM::Variable humidity = FSM::Variable(FSM::Number(), "air-humidity"); -const FSM::Number notanumber = FSM::Number(); +FSM::Flag led_zero = FSM::Flag("led0"); +FSM::Flag led_one = FSM::Flag("led1"); +FSM::Flag led_pwm = FSM::Flag("led_pwm"); void setup(void) { // 0. Builtin LED Pinmode; Serial Begin @@ -38,133 +47,136 @@ void setup(void) { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); while(!Serial) { digitalWrite(LED_BUILTIN, HIGH); delay(100); digitalWrite(LED_BUILTIN, LOW); delay(100); } + + delay(100); + + DebugJson::revision(I2CIP_REVISION, Serial); + + delay(100); // Instantiate module ahead of time - callback_module(); + PeaPod::callback_module(); + + // // Print all devices + // delay(100); + // DEBUG_JSON(I2CIP::devicetree.toString()); + // DEBUG_JSON(modules[PEAPOD_MODULENUM]->toString()); + + delay(100); + + PeaPod::registerCallbacks(); + + delay(100); // Load Devices - modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, DebugJsonBreakpoints); + // modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, DebugJsonBreakpoints); // modules[PEAPOD_MODULENUM]->operator()(fqa_pca9685, false, _i2cip_args_io_default, DebugJsonBreakpoints); - modules[PEAPOD_MODULENUM]->operator()(fqa_sht45, true, _i2cip_args_io_default, DebugJsonBreakpoints); + // modules[PEAPOD_MODULENUM]->operator()(fqa_sht45, true, _i2cip_args_io_default, DebugJsonBreakpoints); // modules[PEAPOD_MODULENUM]->operator()(fqa_lcd, true, _i2cip_args_io_default, DebugJsonBreakpoints); // modules[PEAPOD_MODULENUM]->operator()(fqa_rotary, true, _i2cip_args_io_default, DebugJsonBreakpoints); - humidity.addConditional(FSM::CMP_NEQ, notanumber, callback_humidity); - temperature.addConditional(FSM::CMP_NEQ, notanumber, callback_temperature); + PeaPod::air_humidity.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_humidity); + PeaPod::air_temperature.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_temperature); + PeaPod::air_co2.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_co2); + + FSM::Chronos.addIntervalFlag(2000, 0, &led_zero, false); + FSM::Chronos.addIntervalFlag(2000, 1000, &led_zero, true); + led_zero.addLatchingConditional(true, false, callback_mcp23017_digitalWrite<0>); - FSM::Chronos.addInterval(HEARTBEAT_DELAY, callback_heartbeat); - FSM::Chronos.addInterval(100, callback_module); - FSM::Chronos.addInterval(250, callback_sht45); + FSM::Chronos.addIntervalFlag(10000, 0, &led_one, false); + FSM::Chronos.addIntervalFlag(10000, 5000, &led_one, true); + led_one.addLatchingConditional(true, false, callback_mcp23017_digitalWrite<2>); + FSM::Chronos.addIntervalFlag(5000, 0, &led_pwm, false); + FSM::Chronos.addIntervalFlag(5000, 2500, &led_pwm, true); + led_pwm.addLatchingConditional(true, false, callback_pca9685_onOff<0>); } // LOOP GLOBALS -unsigned long last = 0; -uint32_t fps = 0; - void loop(void) { // I2CIP_DEBUG_SERIAL.println(I2CIP::devicetree.toString()); // I2CIP_DEBUG_SERIAL.println(modules[PEAPOD_MODULENUM]->toString()); - cycle.set(cycle.get() + FSM::Number(1, false, false)); - - last = millis(); - FSM::Chronos.set(last); // Update chronograph and do event/interval conditionals & callbacks(?) - - while(Serial.available() > 0) { // With baud 115200, this should not block - DebugJson::update(Serial, I2CIP::commandRouter); - } - - #ifdef CYCLE_DELAY - delay(CYCLE_DELAY); - #endif - - unsigned long delta = millis() - last; - fps += 1000.f / max(1.f, (float)delta); - fps /= 2; + PeaPod::cycle.set(PeaPod::cycle.get()++); } // HELPER FUNCTIONS -void callback_heartbeat(bool _, const FSM::fsm_timestamp_t& __) { - DebugJson::heartbeat(millis(), Serial); - DebugJson::revision(I2CIP_REVISION, Serial); - DebugJson::telemetry(millis(), fps, "fps", Serial); - DebugJson::telemetry(millis(), (unsigned)cycle.get(), "cycle", Serial); -} - -void callback_module(bool _, const FSM::fsm_timestamp_t& __) { - if(I2CIP::MUX::pingMUX(PEAPOD_WIRENUM, PEAPOD_MODULENUM)) { - if(I2CIP::modules[PEAPOD_MODULENUM] == nullptr) { - I2CIP::modules[PEAPOD_MODULENUM] = new PeaPodModule(); - - // First Module - Add HT16K33 - I2CIP::modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, NullStream); - } +void callback_temperature(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, air_temperature.getKey()); - I2CIP::errlev[PEAPOD_MODULENUM] = I2CIP::modules[PEAPOD_MODULENUM]->operator()(); - } else { - I2CIP::errlev[PEAPOD_MODULENUM] = I2CIP_ERR_HARD; - } + #ifdef SEVENSEG_TEMPERATURE + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_1F; + i2cip_ht16k33_data_t seg_data = { .f = (float)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; - for(uint8_t m = 0; m < I2CIP_MUX_COUNT; m++) { - if(I2CIP::modules[m] != nullptr && I2CIP::errlev[m] == I2CIP_ERR_HARD) { - delete I2CIP::modules[m]; - I2CIP::modules[m] = nullptr; - } + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); } + #endif } -void callback_sht45(bool _, const FSM::fsm_timestamp_t& __) { - if(I2CIP::errlev[PEAPOD_MODULENUM] == I2CIP_ERR_NONE) { - I2CIP::DeviceGroup* dg_sht45 = modules[PEAPOD_MODULENUM]->operator[]("SHT45"); - if(dg_sht45 != nullptr && dg_sht45->getNumDevices() > 0) { - // AVERAGES - state_sht45_t th = {0.0f, 0.0f}; uint8_t c = 0; - for(uint8_t i = 0; i < dg_sht45->getNumDevices(); i++) { - SHT45* d = (SHT45*)(dg_sht45->getDevice(i)); - if(d == nullptr) continue; +void callback_humidity(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, air_humidity.getKey()); - i2cip_errorlevel_t errlev_sht45 = modules[PEAPOD_MODULENUM]->operator()(d->getFQA(), true, _i2cip_args_io_default, DebugJsonBreakpoints); - if(errlev_sht45 != I2CIP_ERR_NONE) continue; + #ifdef SEVENSEG_HUMIDITY + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_1F; + i2cip_ht16k33_data_t seg_data = { .f = (float)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; - th.temperature += d->getCache().temperature; - th.humidity += d->getCache().humidity; - c++; + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + } + #endif +} - // DebugJson::telemetry(d->getLastRX(), d->getCache().temperature, "air-temperature"); - // DebugJson::telemetry(d->getLastRX(), d->getCache().humidity, "air-humidity"); - } +void callback_co2(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (unsigned)v, air_co2.getKey()); - if(c > 0) { + #ifdef SEVENSEG_CO2 + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_UINT; + i2cip_ht16k33_data_t seg_data = { .h = (unsigned)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; - th.temperature /= c; th.humidity /= c; + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + } + #endif +} - // seg_data.f = th.temperature; - // seg_mode = SEG_1F; +template void callback_mcp23017_digitalWrite(bool _, const bool& v) { + if(I2CIP::errlev[PEAPOD_MODULENUM_MCP23017] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_MCP23017] != nullptr) { - temperature.set(th.temperature); - humidity.set(th.humidity); + i2cip_mcp23017_bitmask_t gpio_mask = 1 << P; + i2cip_mcp23017_t gpio_data = v ? (1 << P) : 0; + i2cip_args_io_t gpio_args = { .g = false, .a = nullptr, .s = &gpio_data, .b = &gpio_mask }; - } - } -} + modules[PEAPOD_MODULENUM_MCP23017]->operator()(fqa_gpio, true, gpio_args, DebugJsonBreakpoints); + } } -void callback_temperature(bool _, const FSM::Number& v) { - DebugJson::telemetry(millis(), (double)v, "air-temperature"); +template void callback_pca9685_analogWrite(bool _, const FSM::Number& v) { + if(I2CIP::errlev[PEAPOD_MODULENUM_PCA9685] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_PCA9685] != nullptr && P <= PCA9685_CH15) { - i2cip_ht16k33_mode_t seg_mode = SEG_1F; - i2cip_ht16k33_data_t seg_data = { .f = (float)v }; - i2cip_args_io_t seg_args = { .a = nullptr, .s = &seg_data, .b = &seg_mode }; + i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; + i2cip_pca9685_t data = (uint16_t)min(0, max(4096, (int)v)); + i2cip_args_io_t args = { .g = false, .a = nullptr, .s = &data, .b = &channel }; - I2CIP::modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + I2CIP::modules[PEAPOD_MODULENUM_PCA9685]->operator()(fqa_pwm, true, args, DebugJsonBreakpoints); + } } -void callback_humidity(bool _, const FSM::Number& v) { - DebugJson::telemetry(millis(), (double)v, "air-humidity"); +template void callback_pca9685_onOff(bool _, const bool& v) { + if(I2CIP::errlev[PEAPOD_MODULENUM_PCA9685] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_PCA9685] != nullptr && P <= PCA9685_CH15) { + + i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; + i2cip_pca9685_t data = v ? 4096 : 0; + i2cip_args_io_t args = { .g = false, .a = nullptr, .s = &data, .b = &channel }; + + I2CIP::modules[PEAPOD_MODULENUM_PCA9685]->operator()(fqa_pwm, true, args, DebugJsonBreakpoints); + } } #endif \ No newline at end of file diff --git a/software/microcontroller/src/peapod.cc b/software/microcontroller/src/peapod.cc index ad01d589..1a39d1c7 100644 --- a/software/microcontroller/src/peapod.cc +++ b/software/microcontroller/src/peapod.cc @@ -1,19 +1,22 @@ #include +#include #include +FSM::Variable PeaPod::cycle = FSM::Variable(FSM::Number(0, false, false), "cycle"); +FSM::Variable PeaPod::fps = FSM::Variable(FSM::Number(0, false, false), "fps"); bool PeaPod::pinModeSet[255] = { false }; using namespace PeaPod; using namespace I2CIP; -PeaPodModule::PeaPodModule() : JsonModule(PEAPOD_WIRENUM, PEAPOD_MODULENUM) { } - DeviceGroup* PeaPodModule::deviceGroupFactory(const i2cip_id_t& id) { DeviceGroup* dg = DeviceGroup::create(id); if(dg != nullptr) return dg; dg = DeviceGroup::create(id); if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; dg = DeviceGroup::create(id); if(dg != nullptr) return dg; dg = DeviceGroup::create(id); @@ -118,4 +121,55 @@ void PeaPodModule::handleCommand(JsonObject command, Print& out) { void PeaPodModule::handleConfig(JsonObject config, Print& out) { // TODO: Implement - something with EEPROM? or FSM? +} + +void PeaPod::registerCallbacks(void) { + cycle.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_cycle); + + FSM::Chronos.addInterval(PEAPOD_DELTA_HEARTBEAT, PeaPod::callback_heartbeat); + FSM::Chronos.addInterval(PEAPOD_DELTA_MODULECHECK, PeaPod::callback_module); +} + +unsigned long last = 0; +void PeaPod::callback_cycle(bool _, const FSM::Number& __) { + FSM::Chronos.set(millis()); + + unsigned long delta = millis() - last; + PeaPod::fps.set(((unsigned)PeaPod::fps.get() + 1000.f / max(1.f, (float)delta))/2); // Old FPS plus new FPS over two (moving average) + last = millis(); + + while(Serial.available() > 0) { // With baud 115200, this should not block + DebugJson::update(Serial, I2CIP::commandRouter); + } +} + +void PeaPod::callback_heartbeat(bool _, const FSM::fsm_timestamp_t& __) { + DebugJson::heartbeat(millis(), Serial); + DebugJson::revision(I2CIP_REVISION, Serial); + DebugJson::telemetry(millis(), (unsigned)PeaPod::fps.get(), "fps", Serial); + DebugJson::telemetry(millis(), (unsigned)PeaPod::cycle.get(), "cycle", Serial); +} + +template ::value, int>::type = 0> void PeaPod::callback_module(bool _, const FSM::fsm_timestamp_t& __) { + if(M < 0 || M > I2CIP_MUX_COUNT) return; + + if(I2CIP::MUX::pingMUX(PEAPOD_WIRENUM, M)) { + if(I2CIP::modules[M] == nullptr) { + I2CIP::modules[M] = new T(); + + // First Module - Add HT16K33 + if(M == 0) { + I2CIP::modules[0]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, NullStream); + } + } + + I2CIP::errlev[M] = I2CIP::modules[M]->operator()(); + } else { + I2CIP::errlev[M] = I2CIP_ERR_HARD; + } + + if(I2CIP::modules[M] != nullptr && I2CIP::errlev[M] == I2CIP_ERR_HARD) { + delete I2CIP::modules[M]; + I2CIP::modules[M] = nullptr; + } } \ No newline at end of file diff --git a/software/microcontroller/src/peapod.h b/software/microcontroller/src/peapod.h index 0c9868b6..a452bb60 100644 --- a/software/microcontroller/src/peapod.h +++ b/software/microcontroller/src/peapod.h @@ -15,9 +15,16 @@ #include #include #include +#include #define PEAPOD_WIRENUM 0 -#define PEAPOD_MODULENUM 0 + +#define PEAPOD_MODULENUM_AIR 0 +#define PEAPOD_MODULENUM_WATERING 1 +#define PEAPOD_MODULENUM_LIGHTING 2 + +#define PEAPOD_DELTA_HEARTBEAT 5000 +#define PEAPOD_DELTA_MODULECHECK 100 namespace PeaPod { class PeaPodModule : public I2CIP::JsonModule { @@ -25,13 +32,25 @@ namespace PeaPod { protected: I2CIP::DeviceGroup* deviceGroupFactory(const i2cip_id_t& id) override; public: - PeaPodModule(); + PeaPodModule(const uint8_t& mux) : JsonModule(PEAPOD_WIRENUM, mux) { } void handleCommand(JsonObject command, Print& out) override; void handleConfig(JsonObject config, Print& out) override; }; + // Global States + extern FSM::Variable cycle; + extern FSM::Variable fps; + + void registerCallbacks(void); + + // Callbacks + void callback_cycle(bool _, const FSM::Number& __); + void callback_heartbeat(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); + template ::value, int>::type = 0> void callback_module(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); + + // GPIO Helpers extern bool pinModeSet[255]; template void controlPin(const bool& s) { diff --git a/software/microcontroller/test/test_0_blink/test_0_blink.cc b/software/microcontroller/test/test_0_blink/test_0_blink.cc new file mode 100644 index 00000000..e22285cc --- /dev/null +++ b/software/microcontroller/test/test_0_blink/test_0_blink.cc @@ -0,0 +1,39 @@ +#include +#include + +void test_led_builtin_pin_number(void) { + TEST_ASSERT_EQUAL(13, LED_BUILTIN); +} + +void test_led_state_high(void) { + digitalWrite(LED_BUILTIN, HIGH); + TEST_ASSERT_EQUAL(HIGH, digitalRead(LED_BUILTIN)); +} + +void test_led_state_low(void) { + digitalWrite(LED_BUILTIN, LOW); + TEST_ASSERT_EQUAL(LOW, digitalRead(LED_BUILTIN)); +} + +void setup() { + delay(2000); + + UNITY_BEGIN(); + RUN_TEST(test_led_builtin_pin_number); + + pinMode(LED_BUILTIN, OUTPUT); +} + +uint8_t i = 0; + +void loop() { + if (i < 3) { + RUN_TEST(test_led_state_high); + delay(500); + RUN_TEST(test_led_state_low); + delay(500); + i++; + } else { + UNITY_END(); + } +} \ No newline at end of file diff --git a/software/microcontroller/test/test_1_module_air/test_1_module_air.cc b/software/microcontroller/test/test_1_module_air/test_1_module_air.cc new file mode 100644 index 00000000..d2d61a16 --- /dev/null +++ b/software/microcontroller/test/test_1_module_air/test_1_module_air.cc @@ -0,0 +1,116 @@ +#include +#include + +#include + +#include +#include + +using namespace I2CIP; + +bool run_temperature = false; +void callback_run_temperature() { run_temperature = true; } + +bool run_co2 = false; +void callback_run_co2() { run_co2 = true; } + +void test_eeprom_ping(void) { + EEPROM& eeprom = (I2CIP::modules[PEAPOD_MODULENUM_AIR]->operator EEPROM &()); + i2cip_fqa_t eeprom_fqa = eeprom.getFQA(); + + char msg[30]; + sprintf(msg, "Device unreachable (%01X.%01X.%01X.%02X)", I2CIP_FQA_SEG_I2CBUS(eeprom_fqa), I2CIP_FQA_SEG_MODULE(eeprom_fqa), I2CIP_FQA_SEG_MUXBUS(eeprom_fqa), I2CIP_FQA_SEG_DEVADR(eeprom_fqa)); + + i2cip_errorlevel_t result = eeprom.ping(true, true); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, msg); +} + +void test_eeprom_overwrite(void) { + EEPROM& eeprom = (I2CIP::modules[PEAPOD_MODULENUM_AIR]->operator EEPROM &()); + + i2cip_errorlevel_t result = I2CIP_ERR_NONE; + const char* msg = PEAPOD_MODULE_AIR_EEPROM_CONTENTS; + size_t len = strlen(msg); + + result = eeprom.getOutput()->set(&msg, &len); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, "EEPROM Output Setter (Default Value, Args)"); + + result = eeprom.getInput()->get(nullptr); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, "EEPROM Input Getter (Default Args)"); + + const char* cache = eeprom.getCache(); + TEST_ASSERT_EQUAL_STRING_MESSAGE(msg, cache, "EEPROM Cache (Match)"); +} + +void test_module_check(void) { + i2cip_errorlevel_t errlev = I2CIP::modules[PEAPOD_MODULENUM_AIR]->operator()(); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "Module Check"); +} + +void test_devicegroups(void) { + // SHT45 + DeviceGroup* dg_sht45 = I2CIP::modules[PEAPOD_MODULENUM_AIR]->operator[]("SHT45"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_sht45, "SHT45 Device Group (Exists)"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, dg_sht45->getNumDevices(), "SHT45 Device Group (Non-Empty)"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_sht45->getDevice(0), "SHT45 Device Group (Valid Device)"); + + // K30 + DeviceGroup* dg_k30 = I2CIP::modules[PEAPOD_MODULENUM_AIR]->operator[]("K30"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_k30, "K30 Device Group (Exists)"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, dg_k30->getNumDevices(), "K30 Device Group (Non-Empty)"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_k30->getDevice(0), "K30 Device Group (Valid Device)"); +} + +void test_sht45_mean(void) { + run_temperature = false; + PeaPod::PeaPodModuleAir::callback_sht45_mean(); + TEST_ASSERT_TRUE_MESSAGE(run_temperature, "SHT45 Temperature Callback Execution"); +} + +void test_k30_mean(void) { + run_co2 = false; + PeaPod::PeaPodModuleAir::callback_k30_mean(); + TEST_ASSERT_TRUE_MESSAGE(run_co2, "K30 Mean Callback Execution"); +} + +void test_sevenseg(void) { + i2cip_ht16k33_mode_t seg_mode = SEG_ASCII; + i2cip_ht16k33_data_t seg_data = { .h = ('T') + ('E' << 8) + ('S' << 16) + ('T' << 24) }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; + i2cip_errorlevel_t errlev = I2CIP::modules[PEAPOD_MODULENUM_AIR]->operator()(I2CIP::sevenSegmentFQA, true, seg_args); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "7-Segment Display"); +} + +void setup() { + Serial.begin(115200); + + I2CIP::modules[PEAPOD_MODULENUM_AIR] = new PeaPod::PeaPodModuleAir(false); + + PeaPod::air_temperature.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_run_temperature); + PeaPod::air_co2.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_run_co2); + + delay(2000); + + UNITY_BEGIN(); + + delay(1000); + RUN_TEST(test_eeprom_ping); + delay(1000); + RUN_TEST(test_eeprom_overwrite); + delay(1000); + RUN_TEST(test_module_check); + delay(1000); + RUN_TEST(test_devicegroups); + delay(1000); + RUN_TEST(test_sht45_mean); + delay(1000); + RUN_TEST(test_k30_mean); + delay(1000); + RUN_TEST(test_sevenseg); + + UNITY_END(); +} + +void loop() { + +} \ No newline at end of file From 7cb2864f8d1296bd601be371fd000c72615d27a6 Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 25 Sep 2025 14:04:14 -0400 Subject: [PATCH 41/85] Watering, Lighting Modules w/ Tests --- software/microcontroller/lib/I2CIP | 2 +- software/microcontroller/src/air.tpp | 2 + software/microcontroller/src/light.h | 22 ++++++ software/microcontroller/src/main.cc | 40 ++++++---- software/microcontroller/src/peapod.cc | 5 ++ software/microcontroller/src/water.h | 22 ++++++ .../test_2_module_water.cc | 77 +++++++++++++++++++ .../test_3_module_light.cc | 77 +++++++++++++++++++ 8 files changed, 233 insertions(+), 14 deletions(-) create mode 100644 software/microcontroller/src/light.h create mode 100644 software/microcontroller/src/water.h create mode 100644 software/microcontroller/test/test_2_module_water/test_2_module_water.cc create mode 100644 software/microcontroller/test/test_3_module_light/test_3_module_light.cc diff --git a/software/microcontroller/lib/I2CIP b/software/microcontroller/lib/I2CIP index 3d512d39..81bbae2c 160000 --- a/software/microcontroller/lib/I2CIP +++ b/software/microcontroller/lib/I2CIP @@ -1 +1 @@ -Subproject commit 3d512d39929caa3ddd0c59e71394ad030d8045e1 +Subproject commit 81bbae2cc347fdd3867caa7177aaa2afef6f5d6f diff --git a/software/microcontroller/src/air.tpp b/software/microcontroller/src/air.tpp index 7a9f56e2..11345fc2 100644 --- a/software/microcontroller/src/air.tpp +++ b/software/microcontroller/src/air.tpp @@ -7,6 +7,8 @@ #ifndef PEAPOD_AIR_T_ #define PEAPOD_AIR_T_ +#include + template void PeaPod::PeaPodModuleAir::callback_sht45_mean(bool _, const FSM::fsm_timestamp_t& __) { if(M < 0 || M > I2CIP_MUX_COUNT || I2CIP::modules[M] == nullptr) return; diff --git a/software/microcontroller/src/light.h b/software/microcontroller/src/light.h new file mode 100644 index 00000000..e24f6a80 --- /dev/null +++ b/software/microcontroller/src/light.h @@ -0,0 +1,22 @@ +#ifndef PEAPOD_LIGHT_H_ +#define PEAPOD_LIGHT_H_ + +#include + +#define PEAPOD_MODULE_LIGHTING_EEPROM_CONTENTS {"[{\"24LC32\":[80],\"PCA9685\":[" STR(I2CIP_PCA9685_ADDRESS) "]}]"} + +namespace PeaPod { + + class PeaPodModuleLighting : public PeaPodModule { + public: + PeaPodModuleLighting(bool chronoCallbacks = true) : PeaPodModule(PEAPOD_MODULENUM_LIGHTING) { + if(chronoCallbacks) { + // TODO + } + } + }; +} + +// #include + +#endif \ No newline at end of file diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index cce61ff7..e768dc86 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -6,6 +6,8 @@ #include #include +#include +#include #include @@ -13,8 +15,8 @@ // #define SEVENSEG_HUMIDITY 1 // Uncomment to enable 7-segment display for humidity #define SEVENSEG_CO2 1 // Uncomment to enable 7-segment display for CO2 -#define PEAPOD_MODULENUM_PCA9685 0 -#define PEAPOD_MODULENUM_MCP23017 0 +#define PEAPOD_MODULENUM_PCA9685 2 +#define PEAPOD_MODULENUM_MCP23017 1 #define PEAPOD_MODULENUM_SEVENSEG 0 void callback_temperature(bool _, const FSM::Number& v); @@ -25,6 +27,8 @@ template void callback_mcp23017_digitalWrite(bool _, const boo template void callback_pca9685_analogWrite(bool _, const FSM::Number& v); template void callback_pca9685_onOff(bool _, const bool& v); +void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __); + using namespace PeaPod; using namespace I2CIP; @@ -34,12 +38,12 @@ using namespace I2CIP; // i2cip_fqa_t fqa_lcd = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_JHD1313_ADDRESS); // i2cip_fqa_t fqa_rotary = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SEESAW_ADDRESS); // i2cip_fqa_t fqa_nunchuck = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_NUNCHUCK_ADDRESS); -i2cip_fqa_t fqa_gpio = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_MCP23017, 2, I2CIP_MCP23017_ADDRESS); -i2cip_fqa_t fqa_pwm = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_PCA9685, 1, I2CIP_PCA9685_ADDRESS); +i2cip_fqa_t fqa_gpio = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_MCP23017, 0, I2CIP_MCP23017_ADDRESS); +i2cip_fqa_t fqa_pwm = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_PCA9685, 0, I2CIP_PCA9685_ADDRESS); FSM::Flag led_zero = FSM::Flag("led0"); FSM::Flag led_one = FSM::Flag("led1"); -FSM::Flag led_pwm = FSM::Flag("led_pwm"); +FSM::Variable led_pwm = FSM::Variable(FSM::notanumber, "led_pwm"); void setup(void) { // 0. Builtin LED Pinmode; Serial Begin @@ -54,8 +58,10 @@ void setup(void) { delay(100); - // Instantiate module ahead of time + // Instantiate modules ahead of time PeaPod::callback_module(); + PeaPod::callback_module(); + PeaPod::callback_module(); // // Print all devices // delay(100); @@ -81,15 +87,14 @@ void setup(void) { FSM::Chronos.addIntervalFlag(2000, 0, &led_zero, false); FSM::Chronos.addIntervalFlag(2000, 1000, &led_zero, true); - led_zero.addLatchingConditional(true, false, callback_mcp23017_digitalWrite<0>); + led_zero.addLatchingConditional(true, false, callback_mcp23017_digitalWrite); FSM::Chronos.addIntervalFlag(10000, 0, &led_one, false); FSM::Chronos.addIntervalFlag(10000, 5000, &led_one, true); - led_one.addLatchingConditional(true, false, callback_mcp23017_digitalWrite<2>); + led_one.addLatchingConditional(true, false, callback_mcp23017_digitalWrite); - FSM::Chronos.addIntervalFlag(5000, 0, &led_pwm, false); - FSM::Chronos.addIntervalFlag(5000, 2500, &led_pwm, true); - led_pwm.addLatchingConditional(true, false, callback_pca9685_onOff<0>); + FSM::Chronos.addInterval(50, 0, &callback_pwm_cycle, false); + led_pwm.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_pca9685_analogWrite); } // LOOP GLOBALS @@ -99,7 +104,7 @@ void loop(void) { // I2CIP_DEBUG_SERIAL.println(I2CIP::devicetree.toString()); // I2CIP_DEBUG_SERIAL.println(modules[PEAPOD_MODULENUM]->toString()); - PeaPod::cycle.set(PeaPod::cycle.get()++); + PeaPod::cycle.set(PeaPod::cycle.get() + FSM::Number(1, false, false)); } // HELPER FUNCTIONS @@ -147,6 +152,7 @@ void callback_co2(bool _, const FSM::Number& v) { } template void callback_mcp23017_digitalWrite(bool _, const bool& v) { + if(P > PIN_B7) return; // Invalid Pin if(I2CIP::errlev[PEAPOD_MODULENUM_MCP23017] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_MCP23017] != nullptr) { i2cip_mcp23017_bitmask_t gpio_mask = 1 << P; @@ -158,10 +164,11 @@ template void callback_mcp23017_digitalWrite(bool _, const boo } template void callback_pca9685_analogWrite(bool _, const FSM::Number& v) { + if(P > PCA9685_CH15) return; // Invalid Channel if(I2CIP::errlev[PEAPOD_MODULENUM_PCA9685] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_PCA9685] != nullptr && P <= PCA9685_CH15) { i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; - i2cip_pca9685_t data = (uint16_t)min(0, max(4096, (int)v)); + i2cip_pca9685_t data = (uint16_t)max(0, min(4096, (int)v)); i2cip_args_io_t args = { .g = false, .a = nullptr, .s = &data, .b = &channel }; I2CIP::modules[PEAPOD_MODULENUM_PCA9685]->operator()(fqa_pwm, true, args, DebugJsonBreakpoints); @@ -169,6 +176,7 @@ template void callback_pca9685_analogWrite(bool _, const FSM:: } template void callback_pca9685_onOff(bool _, const bool& v) { + if(P > PCA9685_CH15) return; // Invalid Channel if(I2CIP::errlev[PEAPOD_MODULENUM_PCA9685] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_PCA9685] != nullptr && P <= PCA9685_CH15) { i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; @@ -179,4 +187,10 @@ template void callback_pca9685_onOff(bool _, const bool& v) { } } +void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __) { + double pwm = ((cos((FSM::Chronos.get() % 5000) / 5000.0 * 2.0 * M_PI) + 1.0) * 2048.0); // 0 to 1 over 5s + + led_pwm.set(FSM::Number(pwm, true, false)); +} + #endif \ No newline at end of file diff --git a/software/microcontroller/src/peapod.cc b/software/microcontroller/src/peapod.cc index 1a39d1c7..6035c4b6 100644 --- a/software/microcontroller/src/peapod.cc +++ b/software/microcontroller/src/peapod.cc @@ -1,5 +1,8 @@ #include + #include +#include +#include #include @@ -128,6 +131,8 @@ void PeaPod::registerCallbacks(void) { FSM::Chronos.addInterval(PEAPOD_DELTA_HEARTBEAT, PeaPod::callback_heartbeat); FSM::Chronos.addInterval(PEAPOD_DELTA_MODULECHECK, PeaPod::callback_module); + FSM::Chronos.addInterval(PEAPOD_DELTA_MODULECHECK, PeaPod::callback_module); + FSM::Chronos.addInterval(PEAPOD_DELTA_MODULECHECK, PeaPod::callback_module); } unsigned long last = 0; diff --git a/software/microcontroller/src/water.h b/software/microcontroller/src/water.h new file mode 100644 index 00000000..43b14ae1 --- /dev/null +++ b/software/microcontroller/src/water.h @@ -0,0 +1,22 @@ +#ifndef PEAPOD_WATER_H_ +#define PEAPOD_WATER_H_ + +#include + +#define PEAPOD_MODULE_WATERING_EEPROM_CONTENTS {"[{\"24LC32\":[80],\"MCP23017\":[" STR(I2CIP_MCP23017_ADDRESS) "]}]"} + +namespace PeaPod { + + class PeaPodModuleWatering : public PeaPodModule { + public: + PeaPodModuleWatering(bool chronoCallbacks = true) : PeaPodModule(PEAPOD_MODULENUM_WATERING) { + if(chronoCallbacks) { + // TODO + } + } + }; +} + +// #include + +#endif \ No newline at end of file diff --git a/software/microcontroller/test/test_2_module_water/test_2_module_water.cc b/software/microcontroller/test/test_2_module_water/test_2_module_water.cc new file mode 100644 index 00000000..a121d968 --- /dev/null +++ b/software/microcontroller/test/test_2_module_water/test_2_module_water.cc @@ -0,0 +1,77 @@ +#include +#include + +#include + +#include +#include + +using namespace I2CIP; + +void test_eeprom_ping(void) { + EEPROM& eeprom = (I2CIP::modules[PEAPOD_MODULENUM_WATERING]->operator EEPROM &()); + i2cip_fqa_t eeprom_fqa = eeprom.getFQA(); + + char msg[30]; + sprintf(msg, "Device unreachable (%01X.%01X.%01X.%02X)", I2CIP_FQA_SEG_I2CBUS(eeprom_fqa), I2CIP_FQA_SEG_MODULE(eeprom_fqa), I2CIP_FQA_SEG_MUXBUS(eeprom_fqa), I2CIP_FQA_SEG_DEVADR(eeprom_fqa)); + + i2cip_errorlevel_t result = eeprom.ping(true, true); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, msg); +} + +void test_eeprom_overwrite(void) { + EEPROM& eeprom = (I2CIP::modules[PEAPOD_MODULENUM_WATERING]->operator EEPROM &()); + + i2cip_errorlevel_t result = I2CIP_ERR_NONE; + const char* msg = PEAPOD_MODULE_WATERING_EEPROM_CONTENTS; + size_t len = strlen(msg); + + result = eeprom.getOutput()->set(&msg, &len); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, "EEPROM Output Setter (Default Value, Args)"); + + result = eeprom.getInput()->get(nullptr); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, "EEPROM Input Getter (Default Args)"); + + const char* cache = eeprom.getCache(); + TEST_ASSERT_EQUAL_STRING_MESSAGE(msg, cache, "EEPROM Cache (Match)"); +} + +void test_module_check(void) { + i2cip_errorlevel_t errlev = I2CIP::modules[PEAPOD_MODULENUM_WATERING]->operator()(); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "Module Check"); +} + +void test_devicegroups(void) { + // MCP23017 + DeviceGroup* dg_gpio = I2CIP::modules[PEAPOD_MODULENUM_WATERING]->operator[]("MCP23017"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_gpio, "MCP23017 Device Group (Exists)"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, dg_gpio->getNumDevices(), "MCP23017 Device Group (Non-Empty)"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_gpio->getDevice(0), "MCP23017 Device Group (Valid Device)"); +} + +void setup() { + Serial.begin(115200); + + I2CIP::modules[PEAPOD_MODULENUM_WATERING] = new PeaPod::PeaPodModuleWatering(false); + + delay(2000); + + UNITY_BEGIN(); + + delay(1000); + RUN_TEST(test_eeprom_ping); + delay(1000); + RUN_TEST(test_eeprom_overwrite); + delay(1000); + RUN_TEST(test_module_check); + delay(1000); + RUN_TEST(test_devicegroups); + + // TODO: More tests + + UNITY_END(); +} + +void loop() { + +} \ No newline at end of file diff --git a/software/microcontroller/test/test_3_module_light/test_3_module_light.cc b/software/microcontroller/test/test_3_module_light/test_3_module_light.cc new file mode 100644 index 00000000..540b8ca9 --- /dev/null +++ b/software/microcontroller/test/test_3_module_light/test_3_module_light.cc @@ -0,0 +1,77 @@ +#include +#include + +#include + +#include +#include + +using namespace I2CIP; + +void test_eeprom_ping(void) { + EEPROM& eeprom = (I2CIP::modules[PEAPOD_MODULENUM_LIGHTING]->operator EEPROM &()); + i2cip_fqa_t eeprom_fqa = eeprom.getFQA(); + + char msg[30]; + sprintf(msg, "Device unreachable (%01X.%01X.%01X.%02X)", I2CIP_FQA_SEG_I2CBUS(eeprom_fqa), I2CIP_FQA_SEG_MODULE(eeprom_fqa), I2CIP_FQA_SEG_MUXBUS(eeprom_fqa), I2CIP_FQA_SEG_DEVADR(eeprom_fqa)); + + i2cip_errorlevel_t result = eeprom.ping(true, true); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, msg); +} + +void test_eeprom_overwrite(void) { + EEPROM& eeprom = (I2CIP::modules[PEAPOD_MODULENUM_LIGHTING]->operator EEPROM &()); + + i2cip_errorlevel_t result = I2CIP_ERR_NONE; + const char* msg = PEAPOD_MODULE_LIGHTING_EEPROM_CONTENTS; + size_t len = strlen(msg); + + result = eeprom.getOutput()->set(&msg, &len); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, "EEPROM Output Setter (Default Value, Args)"); + + result = eeprom.getInput()->get(nullptr); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, result, "EEPROM Input Getter (Default Args)"); + + const char* cache = eeprom.getCache(); + TEST_ASSERT_EQUAL_STRING_MESSAGE(msg, cache, "EEPROM Cache (Match)"); +} + +void test_module_check(void) { + i2cip_errorlevel_t errlev = I2CIP::modules[PEAPOD_MODULENUM_LIGHTING]->operator()(); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(I2CIP_ERR_NONE, errlev, "Module Check"); +} + +void test_devicegroups(void) { + // PCA9685 + DeviceGroup* dg_pwm = I2CIP::modules[PEAPOD_MODULENUM_LIGHTING]->operator[]("PCA9685"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_pwm, "PCA9685 Device Group (Exists)"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, dg_pwm->getNumDevices(), "PCA9685 Device Group (Non-Empty)"); + TEST_ASSERT_NOT_NULL_MESSAGE(dg_pwm->getDevice(0), "PCA9685 Device Group (Valid Device)"); +} + +void setup() { + Serial.begin(115200); + + I2CIP::modules[PEAPOD_MODULENUM_LIGHTING] = new PeaPod::PeaPodModuleLighting(false); + + delay(2000); + + UNITY_BEGIN(); + + delay(1000); + RUN_TEST(test_eeprom_ping); + delay(1000); + RUN_TEST(test_eeprom_overwrite); + delay(1000); + RUN_TEST(test_module_check); + delay(1000); + RUN_TEST(test_devicegroups); + + // TODO: More tests + + UNITY_END(); +} + +void loop() { + +} \ No newline at end of file From 093f84dd32af04c3d27c6171e5ba41fb321cee3e Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Fri, 26 Sep 2025 21:47:40 -0400 Subject: [PATCH 42/85] Added ADC --- software/microcontroller/lib/I2CIP | 2 +- software/microcontroller/src/main.cc | 46 ++++++++++++++++++++++++-- software/microcontroller/src/peapod.cc | 2 ++ software/microcontroller/src/peapod.h | 1 + 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/software/microcontroller/lib/I2CIP b/software/microcontroller/lib/I2CIP index 81bbae2c..68110b86 160000 --- a/software/microcontroller/lib/I2CIP +++ b/software/microcontroller/lib/I2CIP @@ -1 +1 @@ -Subproject commit 81bbae2cc347fdd3867caa7177aaa2afef6f5d6f +Subproject commit 68110b869f0914b8fd4082c7956257602a332d02 diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index e768dc86..2689b0db 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -13,11 +13,13 @@ // #define SEVENSEG_TEMPERATURE 1 // Uncomment to enable 7-segment display for temperature // #define SEVENSEG_HUMIDITY 1 // Uncomment to enable 7-segment display for humidity -#define SEVENSEG_CO2 1 // Uncomment to enable 7-segment display for CO2 +// #define SEVENSEG_CO2 1 // Uncomment to enable 7-segment display for CO2 +#define SEVENSEG_ADC 1 #define PEAPOD_MODULENUM_PCA9685 2 #define PEAPOD_MODULENUM_MCP23017 1 #define PEAPOD_MODULENUM_SEVENSEG 0 +#define PEAPOD_MODULENUM_ADS1115 2 void callback_temperature(bool _, const FSM::Number& v); void callback_humidity(bool _, const FSM::Number& v); @@ -26,8 +28,10 @@ void callback_co2(bool _, const FSM::Number& v); template void callback_mcp23017_digitalWrite(bool _, const bool& v); template void callback_pca9685_analogWrite(bool _, const FSM::Number& v); template void callback_pca9685_onOff(bool _, const bool& v); +template void callback_adc_read(bool _, const FSM::fsm_timestamp_t& __); void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __); +void callback_adc_voltage(bool _, const FSM::Number& v); using namespace PeaPod; using namespace I2CIP; @@ -40,10 +44,12 @@ using namespace I2CIP; // i2cip_fqa_t fqa_nunchuck = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_NUNCHUCK_ADDRESS); i2cip_fqa_t fqa_gpio = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_MCP23017, 0, I2CIP_MCP23017_ADDRESS); i2cip_fqa_t fqa_pwm = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_PCA9685, 0, I2CIP_PCA9685_ADDRESS); +i2cip_fqa_t fqa_adc = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_ADS1115, 0, I2CIP_ADS1115_ADDRESS); FSM::Flag led_zero = FSM::Flag("led0"); FSM::Flag led_one = FSM::Flag("led1"); FSM::Variable led_pwm = FSM::Variable(FSM::notanumber, "led_pwm"); +FSM::Variable adc_voltage = FSM::Variable(FSM::notanumber, "adc_volts"); void setup(void) { // 0. Builtin LED Pinmode; Serial Begin @@ -93,8 +99,11 @@ void setup(void) { FSM::Chronos.addIntervalFlag(10000, 5000, &led_one, true); led_one.addLatchingConditional(true, false, callback_mcp23017_digitalWrite); - FSM::Chronos.addInterval(50, 0, &callback_pwm_cycle, false); + FSM::Chronos.addInterval(50, 0, callback_pwm_cycle, false); led_pwm.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_pca9685_analogWrite); + + FSM::Chronos.addInterval(100, 0, callback_adc_read, false); + adc_voltage.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_adc_voltage); } // LOOP GLOBALS @@ -193,4 +202,37 @@ void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __) { led_pwm.set(FSM::Number(pwm, true, false)); } +template void callback_adc_read(bool _, const FSM::fsm_timestamp_t& __) { + if(C > ADS1115_CHANNEL_3) return; // Invalid Channel + if(I2CIP::errlev[PEAPOD_MODULENUM_ADS1115] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_ADS1115] != nullptr) { + + i2cip_ads1115_chsel_t channel = (i2cip_ads1115_chsel_t)C; + i2cip_args_io_t args = { .g = true, .a = nullptr, .s = nullptr, .b = &channel }; + + i2cip_errorlevel_t errlev = I2CIP::modules[PEAPOD_MODULENUM_ADS1115]->operator()(fqa_adc, true, args, DebugJsonBreakpoints); + + if(errlev == I2CIP_ERR_NONE){ + Device** dptr = I2CIP::devicetree[fqa_adc]; + if(dptr != nullptr) { + ADS1115* ads = (ADS1115*)(*dptr); + float voltage = ads->getCache(); + adc_voltage.set(FSM::Number((double)voltage, true, false)); + } + } + } +} +void callback_adc_voltage(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, adc_voltage.getKey()); + + #ifdef SEVENSEG_ADC + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_2F; + i2cip_ht16k33_data_t seg_data = { .f = (float)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; + + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + } + #endif +} + #endif \ No newline at end of file diff --git a/software/microcontroller/src/peapod.cc b/software/microcontroller/src/peapod.cc index 6035c4b6..9215e611 100644 --- a/software/microcontroller/src/peapod.cc +++ b/software/microcontroller/src/peapod.cc @@ -31,6 +31,8 @@ DeviceGroup* PeaPodModule::deviceGroupFactory(const i2cip_id_t& id) { dg = DeviceGroup::create(id); if(dg != nullptr) return dg; dg = DeviceGroup::create(id); + if(dg != nullptr) return dg; + dg = DeviceGroup::create(id); return dg; } diff --git a/software/microcontroller/src/peapod.h b/software/microcontroller/src/peapod.h index a452bb60..2cc05f4d 100644 --- a/software/microcontroller/src/peapod.h +++ b/software/microcontroller/src/peapod.h @@ -16,6 +16,7 @@ #include #include #include +#include #define PEAPOD_WIRENUM 0 From 3bb731002cfe3d398d226c1625760a30637d1f9e Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Wed, 1 Oct 2025 11:30:44 -0400 Subject: [PATCH 43/85] Minor Fixes --- software/microcontroller/lib/I2CIP | 2 +- software/microcontroller/src/air.h | 14 ++++++++++++-- software/microcontroller/src/main.cc | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/software/microcontroller/lib/I2CIP b/software/microcontroller/lib/I2CIP index 68110b86..b87b7fac 160000 --- a/software/microcontroller/lib/I2CIP +++ b/software/microcontroller/lib/I2CIP @@ -1 +1 @@ -Subproject commit 68110b869f0914b8fd4082c7956257602a332d02 +Subproject commit b87b7fac2540cdc00e31650f971de0f9955e2589 diff --git a/software/microcontroller/src/air.h b/software/microcontroller/src/air.h index 7c838a18..10de3d16 100644 --- a/software/microcontroller/src/air.h +++ b/software/microcontroller/src/air.h @@ -14,13 +14,23 @@ namespace PeaPod { extern FSM::Variable air_co2; class PeaPodModuleAir : public PeaPodModule { + private: + FSM::IntervalCallback* interval_sht45 = nullptr; + FSM::IntervalCallback* interval_k30 = nullptr; public: PeaPodModuleAir(bool chronoCallbacks = true) : PeaPodModule(PEAPOD_MODULENUM_AIR) { if(chronoCallbacks) { - FSM::Chronos.addInterval(PEAPOD_SHT45_DELTA, PeaPodModuleAir::callback_sht45_mean); - FSM::Chronos.addInterval(PEAPOD_K30_DELTA, PeaPodModuleAir::callback_k30_mean); + interval_sht45 = FSM::Chronos.addInterval(PEAPOD_SHT45_DELTA, PeaPodModuleAir::callback_sht45_mean); + interval_k30 = FSM::Chronos.addInterval(PEAPOD_K30_DELTA, PeaPodModuleAir::callback_k30_mean); } } + + ~PeaPodModuleAir() { + FSM::Chronos.removeInterval(interval_sht45); + FSM::Chronos.removeInterval(interval_k30); + interval_sht45 = nullptr; + interval_k30 = nullptr; + } template static void callback_sht45_mean(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); template static void callback_k30_mean(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index 2689b0db..2bf1320c 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -213,7 +213,7 @@ template void callback_adc_read(bool _, const FSM::fsm_timesta if(errlev == I2CIP_ERR_NONE){ Device** dptr = I2CIP::devicetree[fqa_adc]; - if(dptr != nullptr) { + if(dptr != nullptr && *dptr != nullptr) { ADS1115* ads = (ADS1115*)(*dptr); float voltage = ads->getCache(); adc_voltage.set(FSM::Number((double)voltage, true, false)); @@ -221,6 +221,7 @@ template void callback_adc_read(bool _, const FSM::fsm_timesta } } } + void callback_adc_voltage(bool _, const FSM::Number& v) { DebugJson::telemetry(millis(), (double)v, adc_voltage.getKey()); From c59763786abf5f66321872bab6324d7e1d927a5f Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Sat, 13 Dec 2025 22:17:31 -0500 Subject: [PATCH 44/85] Modularized --- software/microcontroller/src/air.cc | 44 +++++- software/microcontroller/src/air.h | 6 + software/microcontroller/src/light.cc | 19 +++ software/microcontroller/src/light.h | 66 ++++++++- software/microcontroller/src/light.tpp | 31 +++++ software/microcontroller/src/main.cc | 184 +------------------------ software/microcontroller/src/peapod.cc | 1 + software/microcontroller/src/peapod.h | 48 ++++++- software/microcontroller/src/water.h | 20 ++- 9 files changed, 232 insertions(+), 187 deletions(-) create mode 100644 software/microcontroller/src/light.cc create mode 100644 software/microcontroller/src/light.tpp diff --git a/software/microcontroller/src/air.cc b/software/microcontroller/src/air.cc index 2e57beb4..1601c2aa 100644 --- a/software/microcontroller/src/air.cc +++ b/software/microcontroller/src/air.cc @@ -2,4 +2,46 @@ FSM::Variable PeaPod::air_temperature = FSM::Variable(FSM::Number(), "air-temperature"); FSM::Variable PeaPod::air_humidity = FSM::Variable(FSM::Number(), "air-humidity"); -FSM::Variable PeaPod::air_co2 = FSM::Variable(FSM::Number(), "air-ppm-carbondioxide"); \ No newline at end of file +FSM::Variable PeaPod::air_co2 = FSM::Variable(FSM::Number(), "air-ppm-carbondioxide"); + +void PeaPod::Callbacks::callback_temperature(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, air_temperature.getKey()); + + #ifdef PEAPOD_SEVENSEG_TEMPERATURE + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_1F; + i2cip_ht16k33_data_t seg_data = { .f = (float)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; + + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + } + #endif +} + +void PeaPod::Callbacks::callback_humidity(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, air_humidity.getKey()); + + #ifdef PEAPOD_SEVENSEG_HUMIDITY + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_1F; + i2cip_ht16k33_data_t seg_data = { .f = (float)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; + + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + } + #endif +} + +void PeaPod::Callbacks::callback_co2(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (unsigned)v, air_co2.getKey()); + + #ifdef PEAPOD_SEVENSEG_CO2 + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_UINT; + i2cip_ht16k33_data_t seg_data = { .h = (unsigned)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; + + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + } + #endif +} \ No newline at end of file diff --git a/software/microcontroller/src/air.h b/software/microcontroller/src/air.h index 10de3d16..f2e7cca0 100644 --- a/software/microcontroller/src/air.h +++ b/software/microcontroller/src/air.h @@ -13,6 +13,12 @@ namespace PeaPod { extern FSM::Variable air_humidity; extern FSM::Variable air_co2; + namespace Callbacks { + void callback_temperature(bool _, const FSM::Number& v); + void callback_humidity(bool _, const FSM::Number& v); + void callback_co2(bool _, const FSM::Number& v); + } + class PeaPodModuleAir : public PeaPodModule { private: FSM::IntervalCallback* interval_sht45 = nullptr; diff --git a/software/microcontroller/src/light.cc b/software/microcontroller/src/light.cc new file mode 100644 index 00000000..129c5167 --- /dev/null +++ b/software/microcontroller/src/light.cc @@ -0,0 +1,19 @@ +#include + +FSM::Variable PeaPod::adc_voltage = FSM::Variable(FSM::notanumber, "adc_voltage"); +FSM::Variable PeaPod::pwm_lighting = FSM::Variable(FSM::notanumber, "pwm_lighting"); +FSM::Flag PeaPod::flag_lighting = FSM::Flag("flag_lighting"); + +void PeaPod::Callbacks::callback_adc_voltage(bool _, const FSM::Number& v) { + DebugJson::telemetry(millis(), (double)v, adc_voltage.getKey()); + + #ifdef PEAPOD_SEVENSEG_ADC + if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { + i2cip_ht16k33_mode_t seg_mode = SEG_2F; + i2cip_ht16k33_data_t seg_data = { .f = (float)v }; + i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; + + I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); + } + #endif +} \ No newline at end of file diff --git a/software/microcontroller/src/light.h b/software/microcontroller/src/light.h index e24f6a80..42c64ce8 100644 --- a/software/microcontroller/src/light.h +++ b/software/microcontroller/src/light.h @@ -5,18 +5,80 @@ #define PEAPOD_MODULE_LIGHTING_EEPROM_CONTENTS {"[{\"24LC32\":[80],\"PCA9685\":[" STR(I2CIP_PCA9685_ADDRESS) "]}]"} +#define PEAPOD_MODULE_LIGHTING_PWM_CHANNEL PCA9685_CH0 +#define PEAPOD_MODULE_LIGHTING_PWM_FQA I2CIP_FQA_CREATE(PEAPOD_WIRENUM, PEAPOD_MODULENUM_LIGHTING, 0, I2CIP_PCA9685_ADDRESS) + +#define PEAPOD_MODULE_LIGHTING_ADC_CHANNEL ADS1115_CHANNEL_0 +#define PEAPOD_MODULE_LIGHTING_ADC_FQA I2CIP_FQA_CREATE(PEAPOD_WIRENUM, PEAPOD_MODULENUM_LIGHTING, 0, I2CIP_ADS1115_ADDRESS) + +#define PEAPOD_MODULE_LIGHTING_ADC_DELTA 100 +#define PEAPOD_MODULE_LIGHTING_PWMCYCLE_DELTA 2500 +#define PEAPOD_MODULE_LIGHTING_DELTA 86400000 // 24 hours +#define PEAPOD_MODULE_LIGHTING_PHASE (PEAPOD_MODULE_LIGHTING_DELTA / 2) // 12 hours +#define PEAPOD_MODULE_LIGHTING_DUTY 0.5 // 50% Duty Cycle + namespace PeaPod { + extern FSM::Variable adc_voltage; + extern FSM::Variable pwm_lighting; + extern FSM::Flag flag_lighting; + + namespace Callbacks { + template void callback_adc_read(bool _, const FSM::fsm_timestamp_t& __); + void callback_adc_voltage(bool _, const FSM::Number& v); + } class PeaPodModuleLighting : public PeaPodModule { + FSM::IntervalCallback* interval_adc = nullptr; + FSM::IntervalCallback* interval_lighting_on = nullptr; + FSM::IntervalCallback* interval_lighting_off = nullptr; + FSM::IntervalCallback* interval_pwmcycle = nullptr; + + static void callback_lighting_modulate(bool _, const FSM::Number& pwm) { + if(flag_lighting.get()) { + Callbacks::callback_pca9685_analogWrite(true, pwm); + // Callbacks::callback_pca9685_analogWrite(true, FSM::Number(4096.0 * PEAPOD_MODULE_LIGHTING_DUTY)); + } else { + Callbacks::callback_pca9685_onOff(true, false); + } + } + + static void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __) { + double pwm = ((cos((FSM::Chronos.get() % PEAPOD_MODULE_LIGHTING_PWMCYCLE_DELTA) / ((double)PEAPOD_MODULE_LIGHTING_PWMCYCLE_DELTA) * 2.0 * M_PI) + 1.0) * 2048.0 * PEAPOD_MODULE_LIGHTING_DUTY); // 0 to PEAPOD_MODULE_LIGHTING_DUTY over PEAPOD_MODULE_LIGHTING_PWMCYCLE_DELTA milliseconds + + pwm_lighting.set(FSM::Number(pwm, true, false)); + } + public: PeaPodModuleLighting(bool chronoCallbacks = true) : PeaPodModule(PEAPOD_MODULENUM_LIGHTING) { if(chronoCallbacks) { - // TODO + interval_pwmcycle = FSM::Chronos.addInterval(50, 0, callback_pwm_cycle, false); + + // flag_lighting.addLatchingConditional(true, false, callback_lighting_modulate); + + pwm_lighting.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_lighting_modulate); + + interval_lighting_on = FSM::Chronos.addIntervalFlag(PEAPOD_MODULE_LIGHTING_DELTA, 0, &flag_lighting, false); + interval_lighting_off = FSM::Chronos.addIntervalFlag(PEAPOD_MODULE_LIGHTING_DELTA, PEAPOD_MODULE_LIGHTING_PHASE, &flag_lighting, true); + + // ADC + interval_adc = FSM::Chronos.addInterval(PEAPOD_MODULE_LIGHTING_ADC_DELTA, 0, Callbacks::callback_adc_read, false); + adc_voltage.addConditional(FSM::CMP_NEQ, FSM::notanumber, Callbacks::callback_adc_voltage); } } + + ~PeaPodModuleLighting() { + FSM::Chronos.removeInterval(interval_adc); + FSM::Chronos.removeInterval(interval_lighting_on); + FSM::Chronos.removeInterval(interval_lighting_off); + FSM::Chronos.removeInterval(interval_pwmcycle); + interval_adc = nullptr; + interval_lighting_on = nullptr; + interval_lighting_off = nullptr; + interval_pwmcycle = nullptr; + } }; } -// #include +#include #endif \ No newline at end of file diff --git a/software/microcontroller/src/light.tpp b/software/microcontroller/src/light.tpp new file mode 100644 index 00000000..f08ca858 --- /dev/null +++ b/software/microcontroller/src/light.tpp @@ -0,0 +1,31 @@ +#ifndef PEAPOD_LIGHT_H_ +#error __FILE__ should only be included AFTER +#endif + +#ifdef PEAPOD_LIGHT_H_ + +#ifndef PEAPOD_LIGHT_T_ +#define PEAPOD_LIGHT_T_ + +template void PeaPod::Callbacks::callback_adc_read(bool _, const FSM::fsm_timestamp_t& __) { + if(C > ADS1115_CHANNEL_3) return; // Invalid Channel + if(I2CIP::errlev[I2CIP_FQA_SEG_MODULE(F)] == I2CIP_ERR_NONE && I2CIP::modules[I2CIP_FQA_SEG_MODULE(F)] != nullptr) { + + i2cip_ads1115_chsel_t channel = (i2cip_ads1115_chsel_t)C; + i2cip_args_io_t args = { .g = true, .a = nullptr, .s = nullptr, .b = &channel }; + + i2cip_errorlevel_t errlev = I2CIP::modules[I2CIP_FQA_SEG_MODULE(F)]->operator()(F, true, args, DebugJsonBreakpoints); + + if(errlev == I2CIP_ERR_NONE){ + Device** dptr = I2CIP::devicetree[F]; + if(dptr != nullptr && *dptr != nullptr) { + ADS1115* ads = (ADS1115*)(*dptr); + float voltage = ads->getCache(); + adc_voltage.set(FSM::Number((double)voltage, true, false)); + } + } + } +} + +#endif +#endif \ No newline at end of file diff --git a/software/microcontroller/src/main.cc b/software/microcontroller/src/main.cc index 2bf1320c..64d53c26 100644 --- a/software/microcontroller/src/main.cc +++ b/software/microcontroller/src/main.cc @@ -11,48 +11,13 @@ #include -// #define SEVENSEG_TEMPERATURE 1 // Uncomment to enable 7-segment display for temperature -// #define SEVENSEG_HUMIDITY 1 // Uncomment to enable 7-segment display for humidity -// #define SEVENSEG_CO2 1 // Uncomment to enable 7-segment display for CO2 -#define SEVENSEG_ADC 1 - -#define PEAPOD_MODULENUM_PCA9685 2 -#define PEAPOD_MODULENUM_MCP23017 1 -#define PEAPOD_MODULENUM_SEVENSEG 0 -#define PEAPOD_MODULENUM_ADS1115 2 - -void callback_temperature(bool _, const FSM::Number& v); -void callback_humidity(bool _, const FSM::Number& v); -void callback_co2(bool _, const FSM::Number& v); - -template void callback_mcp23017_digitalWrite(bool _, const bool& v); -template void callback_pca9685_analogWrite(bool _, const FSM::Number& v); -template void callback_pca9685_onOff(bool _, const bool& v); -template void callback_adc_read(bool _, const FSM::fsm_timestamp_t& __); - -void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __); -void callback_adc_voltage(bool _, const FSM::Number& v); +// void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __); using namespace PeaPod; using namespace I2CIP; -// SETUP GLOBALS - -// i2cip_fqa_t fqa_sht45 = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SHT45_ADDRESS); -// i2cip_fqa_t fqa_lcd = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 1, I2CIP_JHD1313_ADDRESS); -// i2cip_fqa_t fqa_rotary = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_SEESAW_ADDRESS); -// i2cip_fqa_t fqa_nunchuck = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM, 0, I2CIP_NUNCHUCK_ADDRESS); -i2cip_fqa_t fqa_gpio = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_MCP23017, 0, I2CIP_MCP23017_ADDRESS); -i2cip_fqa_t fqa_pwm = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_PCA9685, 0, I2CIP_PCA9685_ADDRESS); -i2cip_fqa_t fqa_adc = createFQA(PEAPOD_WIRENUM, PEAPOD_MODULENUM_ADS1115, 0, I2CIP_ADS1115_ADDRESS); - -FSM::Flag led_zero = FSM::Flag("led0"); -FSM::Flag led_one = FSM::Flag("led1"); -FSM::Variable led_pwm = FSM::Variable(FSM::notanumber, "led_pwm"); -FSM::Variable adc_voltage = FSM::Variable(FSM::notanumber, "adc_volts"); - void setup(void) { - // 0. Builtin LED Pinmode; Serial Begin + // Builtin LED Pinmode; Serial Begin pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); @@ -79,31 +44,6 @@ void setup(void) { PeaPod::registerCallbacks(); delay(100); - - // Load Devices - // modules[PEAPOD_MODULENUM]->operator()(I2CIP::sevenSegmentFQA, true, _i2cip_args_io_default, DebugJsonBreakpoints); - // modules[PEAPOD_MODULENUM]->operator()(fqa_pca9685, false, _i2cip_args_io_default, DebugJsonBreakpoints); - // modules[PEAPOD_MODULENUM]->operator()(fqa_sht45, true, _i2cip_args_io_default, DebugJsonBreakpoints); - // modules[PEAPOD_MODULENUM]->operator()(fqa_lcd, true, _i2cip_args_io_default, DebugJsonBreakpoints); - // modules[PEAPOD_MODULENUM]->operator()(fqa_rotary, true, _i2cip_args_io_default, DebugJsonBreakpoints); - - PeaPod::air_humidity.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_humidity); - PeaPod::air_temperature.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_temperature); - PeaPod::air_co2.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_co2); - - FSM::Chronos.addIntervalFlag(2000, 0, &led_zero, false); - FSM::Chronos.addIntervalFlag(2000, 1000, &led_zero, true); - led_zero.addLatchingConditional(true, false, callback_mcp23017_digitalWrite); - - FSM::Chronos.addIntervalFlag(10000, 0, &led_one, false); - FSM::Chronos.addIntervalFlag(10000, 5000, &led_one, true); - led_one.addLatchingConditional(true, false, callback_mcp23017_digitalWrite); - - FSM::Chronos.addInterval(50, 0, callback_pwm_cycle, false); - led_pwm.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_pca9685_analogWrite); - - FSM::Chronos.addInterval(100, 0, callback_adc_read, false); - adc_voltage.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_adc_voltage); } // LOOP GLOBALS @@ -116,124 +56,4 @@ void loop(void) { PeaPod::cycle.set(PeaPod::cycle.get() + FSM::Number(1, false, false)); } -// HELPER FUNCTIONS - -void callback_temperature(bool _, const FSM::Number& v) { - DebugJson::telemetry(millis(), (double)v, air_temperature.getKey()); - - #ifdef SEVENSEG_TEMPERATURE - if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { - i2cip_ht16k33_mode_t seg_mode = SEG_1F; - i2cip_ht16k33_data_t seg_data = { .f = (float)v }; - i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; - - I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); - } - #endif -} - -void callback_humidity(bool _, const FSM::Number& v) { - DebugJson::telemetry(millis(), (double)v, air_humidity.getKey()); - - #ifdef SEVENSEG_HUMIDITY - if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { - i2cip_ht16k33_mode_t seg_mode = SEG_1F; - i2cip_ht16k33_data_t seg_data = { .f = (float)v }; - i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; - - I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); - } - #endif -} - -void callback_co2(bool _, const FSM::Number& v) { - DebugJson::telemetry(millis(), (unsigned)v, air_co2.getKey()); - - #ifdef SEVENSEG_CO2 - if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { - i2cip_ht16k33_mode_t seg_mode = SEG_UINT; - i2cip_ht16k33_data_t seg_data = { .h = (unsigned)v }; - i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; - - I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); - } - #endif -} - -template void callback_mcp23017_digitalWrite(bool _, const bool& v) { - if(P > PIN_B7) return; // Invalid Pin - if(I2CIP::errlev[PEAPOD_MODULENUM_MCP23017] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_MCP23017] != nullptr) { - - i2cip_mcp23017_bitmask_t gpio_mask = 1 << P; - i2cip_mcp23017_t gpio_data = v ? (1 << P) : 0; - i2cip_args_io_t gpio_args = { .g = false, .a = nullptr, .s = &gpio_data, .b = &gpio_mask }; - - modules[PEAPOD_MODULENUM_MCP23017]->operator()(fqa_gpio, true, gpio_args, DebugJsonBreakpoints); - } -} - -template void callback_pca9685_analogWrite(bool _, const FSM::Number& v) { - if(P > PCA9685_CH15) return; // Invalid Channel - if(I2CIP::errlev[PEAPOD_MODULENUM_PCA9685] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_PCA9685] != nullptr && P <= PCA9685_CH15) { - - i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; - i2cip_pca9685_t data = (uint16_t)max(0, min(4096, (int)v)); - i2cip_args_io_t args = { .g = false, .a = nullptr, .s = &data, .b = &channel }; - - I2CIP::modules[PEAPOD_MODULENUM_PCA9685]->operator()(fqa_pwm, true, args, DebugJsonBreakpoints); - } -} - -template void callback_pca9685_onOff(bool _, const bool& v) { - if(P > PCA9685_CH15) return; // Invalid Channel - if(I2CIP::errlev[PEAPOD_MODULENUM_PCA9685] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_PCA9685] != nullptr && P <= PCA9685_CH15) { - - i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; - i2cip_pca9685_t data = v ? 4096 : 0; - i2cip_args_io_t args = { .g = false, .a = nullptr, .s = &data, .b = &channel }; - - I2CIP::modules[PEAPOD_MODULENUM_PCA9685]->operator()(fqa_pwm, true, args, DebugJsonBreakpoints); - } -} - -void callback_pwm_cycle(bool _, const FSM::fsm_timestamp_t& __) { - double pwm = ((cos((FSM::Chronos.get() % 5000) / 5000.0 * 2.0 * M_PI) + 1.0) * 2048.0); // 0 to 1 over 5s - - led_pwm.set(FSM::Number(pwm, true, false)); -} - -template void callback_adc_read(bool _, const FSM::fsm_timestamp_t& __) { - if(C > ADS1115_CHANNEL_3) return; // Invalid Channel - if(I2CIP::errlev[PEAPOD_MODULENUM_ADS1115] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_ADS1115] != nullptr) { - - i2cip_ads1115_chsel_t channel = (i2cip_ads1115_chsel_t)C; - i2cip_args_io_t args = { .g = true, .a = nullptr, .s = nullptr, .b = &channel }; - - i2cip_errorlevel_t errlev = I2CIP::modules[PEAPOD_MODULENUM_ADS1115]->operator()(fqa_adc, true, args, DebugJsonBreakpoints); - - if(errlev == I2CIP_ERR_NONE){ - Device** dptr = I2CIP::devicetree[fqa_adc]; - if(dptr != nullptr && *dptr != nullptr) { - ADS1115* ads = (ADS1115*)(*dptr); - float voltage = ads->getCache(); - adc_voltage.set(FSM::Number((double)voltage, true, false)); - } - } - } -} - -void callback_adc_voltage(bool _, const FSM::Number& v) { - DebugJson::telemetry(millis(), (double)v, adc_voltage.getKey()); - - #ifdef SEVENSEG_ADC - if(I2CIP::errlev[PEAPOD_MODULENUM_SEVENSEG] == I2CIP_ERR_NONE && I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG] != nullptr) { - i2cip_ht16k33_mode_t seg_mode = SEG_2F; - i2cip_ht16k33_data_t seg_data = { .f = (float)v }; - i2cip_args_io_t seg_args = { .g = false, .a = nullptr, .s = &seg_data, .b = &seg_mode }; - - I2CIP::modules[PEAPOD_MODULENUM_SEVENSEG]->operator()(I2CIP::sevenSegmentFQA, true, seg_args, DebugJsonBreakpoints); - } - #endif -} - #endif \ No newline at end of file diff --git a/software/microcontroller/src/peapod.cc b/software/microcontroller/src/peapod.cc index 9215e611..080784e9 100644 --- a/software/microcontroller/src/peapod.cc +++ b/software/microcontroller/src/peapod.cc @@ -132,6 +132,7 @@ void PeaPod::registerCallbacks(void) { cycle.addConditional(FSM::CMP_NEQ, FSM::notanumber, callback_cycle); FSM::Chronos.addInterval(PEAPOD_DELTA_HEARTBEAT, PeaPod::callback_heartbeat); + FSM::Chronos.addInterval(PEAPOD_DELTA_MODULECHECK, PeaPod::callback_module); FSM::Chronos.addInterval(PEAPOD_DELTA_MODULECHECK, PeaPod::callback_module); FSM::Chronos.addInterval(PEAPOD_DELTA_MODULECHECK, PeaPod::callback_module); diff --git a/software/microcontroller/src/peapod.h b/software/microcontroller/src/peapod.h index 2cc05f4d..95c1b51d 100644 --- a/software/microcontroller/src/peapod.h +++ b/software/microcontroller/src/peapod.h @@ -18,6 +18,8 @@ #include #include +#include + #define PEAPOD_WIRENUM 0 #define PEAPOD_MODULENUM_AIR 0 @@ -27,6 +29,12 @@ #define PEAPOD_DELTA_HEARTBEAT 5000 #define PEAPOD_DELTA_MODULECHECK 100 +#define PEAPOD_MODULENUM_SEVENSEG 0 +// #define PEAPOD_SEVENSEG_TEMPERATURE 1 // Uncomment to enable 7-segment display for temperature +// #define PEAPOD_SEVENSEG_HUMIDITY 1 // Uncomment to enable 7-segment display for humidity +// #define PEAPOD_SEVENSEG_CO2 1 // Uncomment to enable 7-segment display for CO2 +#define PEAPOD_SEVENSEG_ADC 1 // Uncomment to enable 7-segment display for ADC voltage + namespace PeaPod { class PeaPodModule : public I2CIP::JsonModule { private: @@ -51,7 +59,45 @@ namespace PeaPod { void callback_heartbeat(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); template ::value, int>::type = 0> void callback_module(bool _ = true, const FSM::fsm_timestamp_t& __ = 0); - // GPIO Helpers + namespace Callbacks { + template void callback_mcp23017_digitalWrite(bool _, const bool& v) { + if(P > PIN_B7) return; // Invalid Pin + if(I2CIP::errlev[I2CIP_FQA_SEG_MODULE(F)] == I2CIP_ERR_NONE && I2CIP::modules[I2CIP_FQA_SEG_MODULE(F)] != nullptr) { + + i2cip_mcp23017_bitmask_t gpio_mask = 1 << P; + i2cip_mcp23017_t gpio_data = v ? (1 << P) : 0; + i2cip_args_io_t gpio_args = { .g = false, .a = nullptr, .s = &gpio_data, .b = &gpio_mask }; + + modules[I2CIP_FQA_SEG_MODULE(F)]->operator()(F, true, gpio_args, DebugJsonBreakpoints); + } + } + + template void callback_pca9685_analogWrite(bool _, const FSM::Number& v) { + if(P > PCA9685_CH15) return; // Invalid Channel + if(I2CIP::errlev[I2CIP_FQA_SEG_MODULE(F)] == I2CIP_ERR_NONE && I2CIP::modules[I2CIP_FQA_SEG_MODULE(F)] != nullptr && P <= PCA9685_CH15) { + + i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; + i2cip_pca9685_t data = (uint16_t)max(0, min(4096, (int)v)); + i2cip_args_io_t args = { .g = false, .a = nullptr, .s = &data, .b = &channel }; + + I2CIP::modules[I2CIP_FQA_SEG_MODULE(F)]->operator()(F, true, args, DebugJsonBreakpoints); + } + } + + template void callback_pca9685_onOff(bool _, const bool& v) { + if(P > PCA9685_CH15) return; // Invalid Channel + if(I2CIP::errlev[I2CIP_FQA_SEG_MODULE(F)] == I2CIP_ERR_NONE && I2CIP::modules[I2CIP_FQA_SEG_MODULE(F)] != nullptr && P <= PCA9685_CH15) { + + i2cip_pca9685_chsel_t channel = (i2cip_pca9685_chsel_t)P; + i2cip_pca9685_t data = v ? 4096 : 0; + i2cip_args_io_t args = { .g = false, .a = nullptr, .s = &data, .b = &channel }; + + I2CIP::modules[I2CIP_FQA_SEG_MODULE(F)]->operator()(F, true, args, DebugJsonBreakpoints); + } + } + } + + // Onboard GPIO Helpers extern bool pinModeSet[255]; template void controlPin(const bool& s) { diff --git a/software/microcontroller/src/water.h b/software/microcontroller/src/water.h index 43b14ae1..c2d43df4 100644 --- a/software/microcontroller/src/water.h +++ b/software/microcontroller/src/water.h @@ -4,16 +4,34 @@ #include #define PEAPOD_MODULE_WATERING_EEPROM_CONTENTS {"[{\"24LC32\":[80],\"MCP23017\":[" STR(I2CIP_MCP23017_ADDRESS) "]}]"} +#define PEAPOD_MODULE_WATERING_GPIO_FQA I2CIP_FQA_CREATE(PEAPOD_WIRENUM, PEAPOD_MODULENUM_WATERING, 0, I2CIP_MCP23017_ADDRESS) +#define PEAPOD_MODULE_WATERING_GPIO_PIN PIN_A0 +#define PEAPOD_MODULE_WATERING_DELTA 1800000 // 30 minutes +#define PEAPOD_MODULE_WATERING_PHASE 5000 // 5 seconds namespace PeaPod { class PeaPodModuleWatering : public PeaPodModule { + FSM::Flag flag_watering = FSM::Flag("flag_watering"); + FSM::IntervalCallback* interval_watering_on = nullptr; + FSM::IntervalCallback* interval_watering_off = nullptr; + public: PeaPodModuleWatering(bool chronoCallbacks = true) : PeaPodModule(PEAPOD_MODULENUM_WATERING) { if(chronoCallbacks) { - // TODO + interval_watering_on = FSM::Chronos.addIntervalFlag(PEAPOD_MODULE_WATERING_DELTA, 0, &flag_watering, false); + interval_watering_off = FSM::Chronos.addIntervalFlag(PEAPOD_MODULE_WATERING_DELTA, PEAPOD_MODULE_WATERING_PHASE, &flag_watering, true); + + flag_watering.addLatchingConditional(true, false, Callbacks::callback_mcp23017_digitalWrite); } } + + ~PeaPodModuleWatering() { + FSM::Chronos.removeInterval(interval_watering_on); + FSM::Chronos.removeInterval(interval_watering_off); + interval_watering_on = nullptr; + interval_watering_off = nullptr; + } }; } From 58e94777d3e0905df620aa05e5192b27426ac4fe Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Thu, 18 Dec 2025 22:06:24 -0500 Subject: [PATCH 45/85] Update workspace files and normalize line endings --- .gitattributes | 2 +- microcontroller.code-workspace | 9 +++++++++ peapod.code-workspace | 33 --------------------------------- software.code-workspace | 9 +++++++++ 4 files changed, 19 insertions(+), 34 deletions(-) create mode 100644 microcontroller.code-workspace delete mode 100644 peapod.code-workspace create mode 100644 software.code-workspace diff --git a/.gitattributes b/.gitattributes index dfe07704..0ffeae3f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ # Auto detect text files and perform LF normalization -* text=auto +* text=auto eol=lf \ No newline at end of file diff --git a/microcontroller.code-workspace b/microcontroller.code-workspace new file mode 100644 index 00000000..cc3f943e --- /dev/null +++ b/microcontroller.code-workspace @@ -0,0 +1,9 @@ +{ + "folders": [ + { + "name": "PeaPod Microcontroller Firmware", + "path": "software/microcontroller" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/peapod.code-workspace b/peapod.code-workspace deleted file mode 100644 index b4ef32a6..00000000 --- a/peapod.code-workspace +++ /dev/null @@ -1,33 +0,0 @@ -{ - "folders": [ - { - "name": "Peapod Development Folder", - "path": "software/" - } - ], - "extensions": { - "recommendations": [ - "davidanson.vscode-markdownlint", - "christian-kohler.npm-intellisense", - "2gua.rainbow-brackets", - "vscode-icons-team.vscode-icons", - "eamodio.gitlens", - "eg2.vscode-npm-script", - "esbenp.prettier-vscode" - ] - }, - "settings": { - "diffEditor.renderSideBySide": true, - "breadcrumbs.enabled": true, - "editor.fontSize": 14, - "editor.autoIndent": "full", - "editor.tabSize": 4, - "editor.detectIndentation": false, - "editor.formatOnSave": false, - "[typescript]": { - "editor.insertSpaces": false, - "editor.tabSize": 2, - "editor.defaultFormatter": "esbenp.prettier-vscode" - } - } -} \ No newline at end of file diff --git a/software.code-workspace b/software.code-workspace new file mode 100644 index 00000000..66312daa --- /dev/null +++ b/software.code-workspace @@ -0,0 +1,9 @@ +{ + "folders": [ + { + "name": "Peapod Software", + "path": "software/" + } + ], + "settings": {} +} \ No newline at end of file From 5055a2ec98be1e3f4a08e3e15a03d57a4719f93d Mon Sep 17 00:00:00 2001 From: Jayden Lefebvre Date: Tue, 6 Jan 2026 00:30:08 -0500 Subject: [PATCH 46/85] Import I2CIP.js --- software/.firebaserc | 5 + software/.gitignore | 54 +- software/.prettierrc | 19 +- software/api.esbuild.mjs | 40 + software/api.tsconfig.json | 22 + software/api/README.md | 5 + software/api/controller.ts | 376 + software/api/env.ts | 153 + software/api/errors.ts | 28 + software/api/firebase.ts | 30 + software/api/types.ts | 75 + software/api/ui.ts | 338 + software/api/utils.ts | 102 + software/{ => archive}/index.ts | 8 +- software/archive/src/controller.ts | 280 + software/{ => archive}/src/controlsystem.ts | 2 +- software/{ => archive}/src/env.ts | 0 software/{ => archive}/src/errors.ts | 0 software/{ => archive}/src/peapod.ts | 0 software/{ => archive}/src/publisher.ts | 0 software/{ => archive}/src/ui.ts | 0 software/archive/src/utils.ts | 131 + software/eslint.config.mjs | 45 + software/firebase.json | 26 + software/firestore.indexes.json | 4 + software/firestore.rules | 26 + software/functions/.gitignore | 10 + software/functions/package-lock.json | 9901 +++++++++++++++++ software/functions/package.json | 31 + .../src/device/listeners/devicelist.ts | 30 + .../functions/src/device/listeners/pubsub.ts | 75 + .../src/device/routes/registerDevice.ts | 109 + .../functions/src/device/routes/uploadFile.ts | 22 + software/functions/src/index.ts | 8 + .../functions/src/webapp/listeners/auth.ts | 32 + .../functions/src/webapp/routes/routes.ts | 21 + software/functions/tsconfig.dev.json | 5 + software/functions/tsconfig.json | 17 + software/microcontroller/src/light.h | 4 +- software/next.config.ts | 13 + software/next.tsconfig.json | 28 + software/package-lock.json | 6491 ----------- software/package.json | 78 +- software/postcss.config.mjs | 7 + software/public/file.svg | 1 + software/public/globe.svg | 1 + software/public/next.svg | 1 + software/public/vercel.svg | 1 + software/public/window.svg | 1 + software/server.ts | 393 + software/src/app/favicon.ico | Bin 0 -> 109804 bytes software/src/app/globals.css | 20 + software/src/app/layout.tsx | 44 + software/src/app/page.tsx | 22 + software/src/atoms/footer.tsx | 57 + software/src/atoms/message.tsx | 56 + software/src/contexts/devices.tsx | 73 + software/src/contexts/schedule.tsx | 0 software/src/contexts/socket.tsx | 134 + software/src/contexts/telemetry.tsx | 84 + software/src/controller.ts | 280 - software/src/devicetypes.ts | 130 + software/src/molecules/messages.tsx | 165 + software/src/molecules/serial.tsx | 141 + software/src/organisms/board.tsx | 128 + software/src/organisms/datagrid.tsx | 81 + software/src/organisms/descheduler.tsx | 120 + software/src/organisms/device.tsx | 428 + software/src/organisms/ping.tsx | 64 + software/src/organisms/telemetrychart.tsx | 55 + software/src/organisms/tree.tsx | 173 + software/src/organisms/unlinker.tsx | 161 + software/src/pages/dashboard.tsx | 268 + software/src/theme.ts | 39 + software/src/utils.ts | 152 +- software/tailwind.config.ts | 18 + software/tsconfig.json | 46 +- software/yarn.lock | 8045 ++++++++++++++ 78 files changed, 23084 insertions(+), 6949 deletions(-) create mode 100644 software/.firebaserc create mode 100644 software/api.esbuild.mjs create mode 100644 software/api.tsconfig.json create mode 100644 software/api/README.md create mode 100644 software/api/controller.ts create mode 100644 software/api/env.ts create mode 100644 software/api/errors.ts create mode 100644 software/api/firebase.ts create mode 100644 software/api/types.ts create mode 100644 software/api/ui.ts create mode 100644 software/api/utils.ts rename software/{ => archive}/index.ts (88%) create mode 100644 software/archive/src/controller.ts rename software/{ => archive}/src/controlsystem.ts (96%) rename software/{ => archive}/src/env.ts (100%) rename software/{ => archive}/src/errors.ts (100%) rename software/{ => archive}/src/peapod.ts (100%) rename software/{ => archive}/src/publisher.ts (100%) rename software/{ => archive}/src/ui.ts (100%) create mode 100644 software/archive/src/utils.ts create mode 100644 software/eslint.config.mjs create mode 100644 software/firebase.json create mode 100644 software/firestore.indexes.json create mode 100644 software/firestore.rules create mode 100644 software/functions/.gitignore create mode 100644 software/functions/package-lock.json create mode 100644 software/functions/package.json create mode 100644 software/functions/src/device/listeners/devicelist.ts create mode 100644 software/functions/src/device/listeners/pubsub.ts create mode 100644 software/functions/src/device/routes/registerDevice.ts create mode 100644 software/functions/src/device/routes/uploadFile.ts create mode 100644 software/functions/src/index.ts create mode 100644 software/functions/src/webapp/listeners/auth.ts create mode 100644 software/functions/src/webapp/routes/routes.ts create mode 100644 software/functions/tsconfig.dev.json create mode 100644 software/functions/tsconfig.json create mode 100644 software/next.config.ts create mode 100644 software/next.tsconfig.json delete mode 100644 software/package-lock.json create mode 100644 software/postcss.config.mjs create mode 100644 software/public/file.svg create mode 100644 software/public/globe.svg create mode 100644 software/public/next.svg create mode 100644 software/public/vercel.svg create mode 100644 software/public/window.svg create mode 100644 software/server.ts create mode 100644 software/src/app/favicon.ico create mode 100644 software/src/app/globals.css create mode 100644 software/src/app/layout.tsx create mode 100644 software/src/app/page.tsx create mode 100644 software/src/atoms/footer.tsx create mode 100644 software/src/atoms/message.tsx create mode 100644 software/src/contexts/devices.tsx create mode 100644 software/src/contexts/schedule.tsx create mode 100644 software/src/contexts/socket.tsx create mode 100644 software/src/contexts/telemetry.tsx delete mode 100644 software/src/controller.ts create mode 100644 software/src/devicetypes.ts create mode 100644 software/src/molecules/messages.tsx create mode 100644 software/src/molecules/serial.tsx create mode 100644 software/src/organisms/board.tsx create mode 100644 software/src/organisms/datagrid.tsx create mode 100644 software/src/organisms/descheduler.tsx create mode 100644 software/src/organisms/device.tsx create mode 100644 software/src/organisms/ping.tsx create mode 100644 software/src/organisms/telemetrychart.tsx create mode 100644 software/src/organisms/tree.tsx create mode 100644 software/src/organisms/unlinker.tsx create mode 100644 software/src/pages/dashboard.tsx create mode 100644 software/src/theme.ts create mode 100644 software/tailwind.config.ts create mode 100644 software/yarn.lock diff --git a/software/.firebaserc b/software/.firebaserc new file mode 100644 index 00000000..309c557d --- /dev/null +++ b/software/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "cloudponics-bc383" + } +} diff --git a/software/.gitignore b/software/.gitignore index beeee6ae..5bb69b75 100644 --- a/software/.gitignore +++ b/software/.gitignore @@ -15,4 +15,56 @@ logs/** *.elf *.hex *.eep -*.bin \ No newline at end of file +*.bin + +*.log + +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* +!.env.template + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +api/*.mjs +api/*.js + +logs/*.txt + +server.mjs \ No newline at end of file diff --git a/software/.prettierrc b/software/.prettierrc index 94ae9a39..d34c24c4 100644 --- a/software/.prettierrc +++ b/software/.prettierrc @@ -1,10 +1,11 @@ { - "trailingComma": "none", - "tabWidth": 2, - "useTabs": true, - "semi": true, - "singleQuote": true, - "arrowParens": "avoid", - "bracketSpacing": true, - "printWidth": 100 -} \ No newline at end of file + "semi": true, + "singleQuote": true, + "useTabs": false, + "tabWidth": 2, + "printWidth": 80, + "trailingComma": "es5", + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/software/api.esbuild.mjs b/software/api.esbuild.mjs new file mode 100644 index 00000000..41c18248 --- /dev/null +++ b/software/api.esbuild.mjs @@ -0,0 +1,40 @@ +import esbuild from 'esbuild'; +import { + esbuildPluginFilePathExtensions +} from 'esbuild-plugin-file-path-extensions'; + +console.log('Building API...'); + +const esbuildConfig = { + format: 'esm', + platform: 'node', + target: 'esnext', + packages: 'external', + bundle: true, + allowOverwrite: true, + keepNames: true, + outExtension: { '.js': '.mjs' }, + tsconfig: './api.tsconfig.json', + external: ['debug', 'ora', 'chalk', 'socket.io', 'blessed'], + plugins: [esbuildPluginFilePathExtensions({ + esm: true, + esmExtension: 'mjs', + filter: /^\..*\.(js|ts)$/ // '.*.js' or '.*.ts' + })] +}; + +// Build API +const build_api = esbuild.build({ + ...esbuildConfig, + entryPoints: ['./api/*.ts'], + outdir: './api/' +}); + +// Build server +const build_server = esbuild.build({ + ...esbuildConfig, + entryPoints: ['./server.ts'], + outdir: './' +}); + +await Promise.all([build_api, build_server]).catch((err) => { console.error(err); process.exit(1); } ); \ No newline at end of file diff --git a/software/api.tsconfig.json b/software/api.tsconfig.json new file mode 100644 index 00000000..410a342c --- /dev/null +++ b/software/api.tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["esnext"], + "strict": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "skipLibCheck": true, + "declaration": true, + "forceConsistentCasingInFileNames": true, + "removeComments": true, + "baseUrl": "./", + "paths": { + "serialport": ["./lib/serialport/packages/serialport/lib/index"], + "@serialport/*": ["./lib/serialport/packages/*/lib/index"], + "serialport/*": ["./lib/serialport/packages/serialport/lib/*"] + } + }, + "include": ["./api/**/*.ts"], + "exclude": ["./node_modules/**", "./src/**", "./lib/**"] +} diff --git a/software/api/README.md b/software/api/README.md new file mode 100644 index 00000000..8628d4bf --- /dev/null +++ b/software/api/README.md @@ -0,0 +1,5 @@ +# API + +- Node.JS Console i.e. limited-input devices +- WebSockets, SQL, File Logging, Firebase, etc. +- diff --git a/software/api/controller.ts b/software/api/controller.ts new file mode 100644 index 00000000..43d3fda3 --- /dev/null +++ b/software/api/controller.ts @@ -0,0 +1,376 @@ +import chalk from 'chalk'; +import { SerialPort, ReadlineParser } from 'serialport'; +import { ControllerTXError, DebugJsonSerialportError } from './errors'; +import { DebugJsonConsole as ui } from './ui'; +import { DebugJsonMessage, DebugJsonMessageTypes } from './types'; +import { updateMicrocontroller } from './utils'; + +// import { Gpio } from 'onoff'; + +// CONSTANTS + +/** + * Baud rate for serial communication. Must match that found in microcontroller code. + */ +const BAUDRATE = 115200; + +/** + * Microcontroller software revision matching this software. + */ +export const CONTROLLER_REVISION = 0; // Just zero for now + +/** + * Seconds to wait between messages before timing out. + */ +const SERIAL_TIMEOUT_SECONDS = 5; + +const RESET_INTERVAL = 10000; +const BATCH_INTERVAL = 100; + +/** + * GPIO pin attached to the reset grounding circuit + */ +// const RESET_PIN = 26; + +export function findSerialPort(path: string): Promise { + return SerialPort.list().then(ports => { + return ports.reduce((acc, port) => { + // console.log(JSON.stringify(port, null, 2)); + if(port && port['path'] && (port['path'] as string).toLowerCase().includes(path.toLowerCase())) { + acc.push(port.path as string); + } + return acc; + }, [] as string[]); + }); +} + +export function getSerialPorts(): Promise { + return SerialPort.list().then(ports => { + return ports.map(port => port.path as string); + }); +} + +// TYPES + +/** + * Base type for any controller. + */ +export type Controller = { + /** + * Establish communications with the Controller. + * @param onMessage Pipe all received messages. + * @throws If received message is invalid (JSON parsing fails). + */ + // start(onMessage: (msg: ControllerMessage) => void): Promise; + start(onMessages: (messages: DebugJsonMessage[]) => void): Promise; + + /** + * Write instructions to the Controller. + * @param instructions Instruction set. + */ + // write(instructions: ControllerInstructions): void; + write(instructions: DebugJsonMessage): void; + + /** + * Halt communications with the Controller. + */ + stop(): void; +}; + +/** + * Messages FROM the controller + */ +// export type ControllerMessage = +// | { +// type: 'info' | 'debug' | 'error'; +// data: string; +// } +// | { +// type: 'data'; +// data: { +// label: string; +// value: number; +// }; +// } +// | { +// type: 'revision'; +// data: number; +// }; + +/** + * Messages TO the controller + */ +// export type ControllerInstructions = { +// [key: string]: number; +// }; + +/** + * Simulated controller parameters + */ +export type SimulatorConfig = { + [key: string]: { + min: number; + max: number; + interval: number; + }; +}; + +// CLASSES + +/** + * Interface between this computer and the microcontroller. + */ +export class MicroController implements Controller { + serial: SerialPort; + parser: ReadlineParser; + #timedout: boolean = false; + #count: number = 0; + #batch: DebugJsonMessage[] = []; + private timeout?: NodeJS.Timeout; + private resetInterval?: NodeJS.Timeout; + #lastBatch = Date.now(); + // private resetpin: Gpio; + + constructor(readonly serialport: string, readonly passRevision: boolean = true) { + // Reset pin GPIO interface + // this.resetpin = new Gpio(RESET_PIN, 'out'); + + // Create the serial port interface + this.serial = new SerialPort({ + path: serialport, + baudRate: BAUDRATE, + autoOpen: false, + }); + + this.serial.on('error', async (err) => { + // ui.fail(`CONTROLLER SERIAL: ${err}`); + throw new DebugJsonSerialportError(`${err.name} - ${err.message}`); + }); + + // Create the newline parser + this.parser = this.serial.pipe( + new ReadlineParser({ + delimiter: '\n', + includeDelimiter: false, + }) + ); + } + + // Starts serial (newline parser) and resolves when RX revision is correct + start(onMessage: (messages: DebugJsonMessage[]) => void): Promise { + this.pauseTimeout(true); // Don't want it interrupting the start sequence + // Reset listeners + this.parser.removeAllListeners('data'); + + // Explicit promise construction so we can resolve only on valid comms AND revision check + return new Promise(async (res, rej) => { + + // Reset the microcontroller (opens the serial port) + this.reset().catch((err) => rej(err)).then(() => { + this.resetTimeout(rej); + this.resetInterval = setInterval(() => { + ui.info('CONTROLLER RESET INTERVAL'); + this.reset().catch((err) => rej(err)); + }, RESET_INTERVAL); + ui.start('CONTROLLER REVISION...'); + }); + + this.parser.on('error', async (_err) => { + await this.reset().catch(() => rej(new DebugJsonSerialportError(`${_err.name} - ${_err.message}`))); + }); + + // Set up the listener + this.parser.on('data', async (msgtxt) => { + this.resetTimeout(rej); + this.#count++; + + // Attempt to parse the raw text as a valid JSON object + let msg: DebugJsonMessage; + try { + msg = JSON.parse(msgtxt); + } catch (err) { + rej(err); + return; + } + + // Microcontroller-specific pre-handling + switch (msg.type) { + case 'revision': + // Software update + if (msg.data?.revision === CONTROLLER_REVISION) { + if(ui.spinning()) { + ui.succeed( + `CONTROLLER REVISION PASS! ${msg.data.revision}` + ); + } + res(); //Successful start sequence + } else { + ui.fail( + `CONTROLLER REVISION FAIL: ${msg.data?.revision ?? 'NULL'} != ${CONTROLLER_REVISION}` + ); + // Attempt to update the microcontroller, and then restart + this.stop(); + ui.start('Compiling latest microcontroller software and flashing...'); + await updateMicrocontroller(); + ui.succeed('Updated microcontroller software successfully!'); + } + if(this.passRevision === false) { break; } + // onMessage([msg]); // Uncomment to pass revision messages to the rest of the app (unbatched) + break; + default: + this.#batch.push(msg); + if(Date.now() - this.#lastBatch >= BATCH_INTERVAL) { + onMessage(this.#batch); + this.#batch = []; + this.#lastBatch = Date.now(); + } + break; + } + + + }); + }); + } + + /** + * Clear the serial timeout. + */ + private pauseTimeout(force: boolean = false): void { + if (this.timeout && (this.#timedout || force)) {clearTimeout(this.timeout);} + } + + /** + * Refresh (or start) the serial timeout. + */ + private resetTimeout(cb?: (err?: Error) => void, timeoutSeconds: number = SERIAL_TIMEOUT_SECONDS): void { + this.pauseTimeout(true); + this.timeout = setTimeout(() => { + ui.fail( + `CONTROLLER TIMEOUT: ${timeoutSeconds}s` + ); + this.#timedout = true; + this.reset().catch((err) => {if(cb) {cb(err);}}); + }, timeoutSeconds * 1000); + } + + write(msg: DebugJsonMessage): void { + ui.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); + this.serial.write(JSON.stringify(msg) + '\n', undefined, (err) => { + if (err) throw new ControllerTXError(JSON.stringify(msg)); + }); + } + + stop(): void { + this.pauseTimeout(); + if (this.serial.isOpen) this.serial.close(); + // Stop listening for data + // this.parser.removeAllListeners('data'); + } + + /** + * Resets the microcontroller by closing and re-opening serial. + */ + private reset(): Promise { + // Stop and reset + this.stop(); + // this.resetpin.writeSync(1); + + this.#count = 0; + + // Wait, then stop resetting + // await new Promise((r) => setTimeout(r, 1000)); + // this.resetpin.writeSync(0); + + // (Re-)open serial + return new Promise((reso, reje) => { + ui.start('CONTROLLER...'); + this.serial.open((err) => { + if (err) { + reje(err); + } else { + ui.succeed('CONTROLLER!'); + this.resetTimeout(reje); + reso(); + } + }); + }); + + // Restart timeout + } +} + +/** + * A simulated controller for generating random data. + */ +export class SimulatedController implements Controller { + private intervals: NodeJS.Timeout[] = []; + private readonly startDate: number = Date.now(); + private output?: (msg: DebugJsonMessage[]) => void; + + constructor(readonly parameters: SimulatorConfig) {} + + async start(onMessage: (msg: DebugJsonMessage[]) => void): Promise { + this.output = onMessage; + for (const label of Object.keys(this.parameters)) { + this.intervals.push( + setInterval(() => { + onMessage( + [this.generateData( + label, + this.parameters[label].min, + this.parameters[label].max + )] + ); + }, this.parameters[label].interval) + ); + } + } + write(instructions: DebugJsonMessage): void { + switch(instructions.type) { + case 'command': + if(instructions.data && Object.keys(instructions.data).includes('rebuild')) { + ui.info('SIMULATED CONTROLLER REBUILD TREE'); + if(this.output) { + this.output([ + this.generateTree() + ]); + } + } + break; + default: + ui.info(`SIMULATED CONTROLLER WRITE: ${JSON.stringify(instructions)}`); + break; + } + } + async stop(): Promise { + for (const interval of this.intervals) { + clearInterval(interval); + } + } + + /** + * Generate a single data point + * @param label Dataset label + * @param min Minimum value + * @param max Maximum value + */ + private generateData( + label: string, + min: number, + max: number + ): DebugJsonMessage { + const d = (Math.random() * (max - min) + min); + return { + type: 'event', + timestamp: Date.now() - this.startDate, + data: { + [label]: d, + }, + }; + } + + private generateTree() { + // modules: {devices: {id: string, fqa: number}[]}[] + return {type: 'tree' as DebugJsonMessageTypes, timestamp: Date.now() - this.startDate, data: Object.keys(this.parameters).reduce<{[key: string]: number[]}[]>((acc, label, idx) => { acc[0][label] = [idx]; return acc; }, [{}])}; + } +} diff --git a/software/api/env.ts b/software/api/env.ts new file mode 100644 index 00000000..66a133f4 --- /dev/null +++ b/software/api/env.ts @@ -0,0 +1,153 @@ +import dotenv from 'dotenv'; +import { existsSync } from 'fs'; +import { FirebaseOptions } from 'firebase/app'; +import { EnvFieldError } from './errors'; +import { DeviceFlowUIOptions } from '@peapodtech/firebasedeviceflow'; + +/** + * Env fields required for ALL modes. + */ +const ENV_FIELDS_PEAPOD = ['SERIALPORT']; + +/** + * Env fields required for Firebase functionality. + */ +const ENV_FIELDS_FIREBASE = [ + 'FIREBASE_APIKEY', + 'FIREBASE_AUTHDOMAIN', + 'FIREBASE_PROJECTID', + 'FIREBASE_STORAGEBUCKET', + 'FIREBASE_MESSAGINGSENDERID', + 'FIREBASE_APPID', + 'FIREBASE_MEASUREMENTID' +]; + +/** + * Env fields required for GCP IoT Core functionality. + */ +const ENV_FIELDS_IOT = [ + 'IOT_CLOUDREGION', + 'FIREBASE_PROJECTID', + 'IOT_REGISTRY', + 'IOT_JWT_EXPIRYMINS' +]; + +/** + * Env fields required for FirebaseDeviceFlow Auth functionality. + */ +const ENV_FIELDS_AUTH = [ + 'GOOGLE_SCOPES', + 'GOOGLE_CLIENTID', + 'GOOGLE_CLIENTSECRET', + 'GITHUB_SCOPES', + 'GITHUB_CLIENTID', + 'GITHUB_CLIENTSECRET' +]; + +export type PeaPodEnv = { + serialport: string; +}; + +/** + * Load a `.env`-style file to `process.env`. + * + * Throws an error if the file does not exist. + */ +function loadDotEnv(path: string = '.env'): void { + // Check for file + if (existsSync(path)) { + const config = dotenv.config({ path }); + if (config.error) { + throw config.error; + } + } else { + throw new Error('Environment variable file not found.'); + } +} + +/** + * Check which of the given fields are missing (undefined) in `process.env` + * @param fields List of fields to check + * @returns List of missing fields + */ +function getMissingEnvFields(fields: string[]): string[] { + return fields.filter(field => !Object.keys(process.env).includes(field)); +} + +/** + * Load config for the Firebase app and check for all fields. + * @returns Firebase app config object + */ +export function loadFirebaseEnv(): FirebaseOptions { + const missingFields = getMissingEnvFields(ENV_FIELDS_FIREBASE); + if (missingFields.length) { + throw new EnvFieldError('Firebase', missingFields); + } else { + return { + apiKey: process.env.FIREBASE_APIKEY, + authDomain: process.env.FIREBASE_AUTHDOMAIN, + databaseURL: process.env.FIREBASE_DATABASEURL, + projectId: process.env.FIREBASE_PROJECTID, + storageBucket: process.env.FIREBASE_STORAGEBUCKET, + messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, + appId: process.env.FIREBASE_APPID, + measurementId: process.env.FIREBASE_MEASUREMENTID + }; + } +} + +/** + * Load config for GCP IoT Core and check for all fields. + * @returns GCP IoT Core config object + */ +export function loadIoTEnv(): IoTConfig { + const missingFields = getMissingEnvFields(ENV_FIELDS_IOT); + if (missingFields.length) { + throw new EnvFieldError('GCP IoT Core', missingFields); + } else { + return { + cloudregion: process.env.IOT_CLOUDREGION!, + projectid: process.env.FIREBASE_PROJECTID!, + registryid: process.env.IOT_REGISTRY!, + jwtexpiryminutes: Number(process.env.IOT_JWT_EXPIRYMINS ?? '1440') + }; + } +} + +/** + * Load config for the Firebase app and check for all fields. + * @returns Firebase app config object + */ +export function loadAuthEnv(): DeviceFlowUIOptions { + const missingFields = getMissingEnvFields(ENV_FIELDS_AUTH); + if (missingFields.length) { + throw new EnvFieldError('Firebase Device Flow Auth', missingFields); + } else { + return { + Google: { + scopes: process.env.GOOGLE_SCOPES?.split(' '), + clientid: process.env.GOOGLE_CLIENTID, + clientsecret: process.env.GOOGLE_CLIENTSECRET + }, + GitHub: { + scopes: process.env.GITHUB_SCOPES?.split(' '), + clientid: process.env.GITHUB_CLIENTID, + clientsecret: process.env.GITHUB_CLIENTSECRET + } + }; + } +} + +export function loadPeaPodEnv(): PeaPodEnv { + const missingFields = getMissingEnvFields(ENV_FIELDS_PEAPOD); + if (missingFields.length) { + throw new EnvFieldError('PeaPod', missingFields); + } else { + return { + serialport: process.env.SERIALPORT! + }; + } +} + +// MAIN +loadDotEnv(); diff --git a/software/api/errors.ts b/software/api/errors.ts new file mode 100644 index 00000000..b56f39f9 --- /dev/null +++ b/software/api/errors.ts @@ -0,0 +1,28 @@ +export class ControllerTXError extends Error { + constructor(msg: any) { + super("DEBUGJSON RX FAIL: '" + JSON.stringify(msg) + "'"); + } +} + +// UNUSED +// export class SerialTimeoutError extends Error { +// constructor(timeoutSeconds?: number) { +// super(`Arduino serial communication timed out${ timeoutSeconds === undefined ? '' : ` after ${ timeoutSeconds } seconds` }.`); +// } +// }; + +export class EnvFieldError extends Error { + constructor(mode: string, missingFields: string[]) { + super( + `.env file is missing the following fields necessary for ${mode} functionality: ${missingFields.join( + ', ' + )}` + ); + } +} + +export class DebugJsonSerialportError extends Error { + constructor(err: string) { + super(`Serialport Error: ${err}`); + } +} \ No newline at end of file diff --git a/software/api/firebase.ts b/software/api/firebase.ts new file mode 100644 index 00000000..5cece4fe --- /dev/null +++ b/software/api/firebase.ts @@ -0,0 +1,30 @@ +// Imports: Firebase +import { initializeApp } from 'firebase/app'; +import { + getDatabase, + ref, + push, + // set +} from 'firebase/database'; +import { DebugJsonMessage } from './types'; + +export const pushDebugMessage = (message: DebugJsonMessage, t?: string) => { + const _r = ref(database, `messages/${t ?? 'default'}`); + const r = push(_r, message); + // set(r, message); +}; + +export const pushDebugMessages = (messages: DebugJsonMessage[], t?: string) => { + if(messages.length === 0) return; + const d = messages.reduce((l, msg) => { + const label = msg.t ?? (t ?? 'default'); + if(!l[label]) l[label] = []; + l[label].push(msg); + return l; + }, {}); + Object.keys(d).forEach(k => { + const _r = ref(getDatabase(), `messageBatches/${k}`); + const r = push(_r, d[k]); + // set(r, d[k]); + }); +}; \ No newline at end of file diff --git a/software/api/types.ts b/software/api/types.ts new file mode 100644 index 00000000..6bdf7f09 --- /dev/null +++ b/software/api/types.ts @@ -0,0 +1,75 @@ +export type DebugJsonMessageTypes = + | 'debug' + | 'info' + | 'warn' + | 'error' + | 'event' + | 'command' + | 'config' + | 'revision' + | 'tree'; + +export type DebugJsonMessage = { + type: DebugJsonMessageTypes; // * Required; Usually first in a stream + t?: string; // Additional message typing i.e. device IDs, "WARN", "BSOD", etc. + timestamp?: number; // ** Suggested; Milliseconds since t=revision sent (or program start) + msg?: string; + data?: { + [key: string]: boolean | number | number[] | string; // I.e. {"temperature": 25.0, "button0": true, "nav": "~/mydir/"} + // We'll handle floating-point precision at deserialization time, create a class DebugJsonNumber + }; + units?: { + [key: string]: string; // I.e. "temperature": "°C" + }; +}; + +export type DebugJsonInstruction = { + type: 'config' | 'command', + data: { + fqa?: number, + g?: boolean, + a?: boolean | number | string, + s?: boolean | number | string, + b?: boolean | number | string, + [key: string]: boolean | number | number[] | string } +}; + +export function parseType(type: DebugJsonMessageTypes): string { + switch (type) { + case 'event': + return 'Telemetry'; // 'event' is used bi-directionally; Controller-to-Host is telemetry (i.e. component states, program flow), Host-to-Controller is for state changes (non-component/ not command, setting component-state e.g. on/off; targeted/ not config, e.g. setting up the camera lighting component-state as part of the Controller API; instead: specific and programmatic (e.g. enabling/disabling specific component-states mid-program, tuning non-component-state control parameters) + default: + return type.toUpperCase().charAt(0) + type.slice(1); + } +} + +// JSON-friendly always +export function parseDatum(value: unknown): string { + if (typeof value === 'string') { + return value; + } else if (Array.isArray(value)) { + return '[' + value.map(parseDatum).join() + ']'; // Recurse + } else if (typeof value === 'number') { + return '' + value; // Let JS handle it + } else if (typeof value === 'boolean') { + return value === false ? 'false' : 'true'; + } else if (value === null) { + return 'null'; + } else { + return ''; + } +} + +export function parseTimestamp(ms: number): string { + // ms duration since program start + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + const msString = (ms % 1000).toString().padStart(3, '0'); + const secondsString = (seconds % 60).toString().padStart(2, '0'); + const minutesString = (minutes % 60).toString().padStart(2, '0'); + const hoursString = hours.toString().padStart(2, '0'); + + return `${hoursString}:${minutesString}:${secondsString}.${msString}`; +}; \ No newline at end of file diff --git a/software/api/ui.ts b/software/api/ui.ts new file mode 100644 index 00000000..86667c34 --- /dev/null +++ b/software/api/ui.ts @@ -0,0 +1,338 @@ +import { Ora, Spinner } from 'ora'; +import ora from 'ora'; +// import * as blessed from 'blessed'; + +const I2CIP_DEFAULT_SPINNER: Spinner = { + interval: 50, + frames: [ + '▁▁▁▁▁▁▁▁▁▁▁', + '█▁▁▁▁▁▁▁▁▁▁', + '██▁▁▁▁▁▁▁▁▁', + '███▁▁▁▁▁▁▁▁', + '████▁▁▁▁▁▁▁', + '█████▁▁▁▁▁▁', + '▁█████▁▁▁▁▁', + '▁▁█████▁▁▁▁', + '▁▁▁█████▁▁▁', + '▁▁▁▁█████▁▁', + '▁▁▁▁▁█████▁', + '▁▁▁▁▁▁█████', + '▁▁▁▁▁▁▁████', + '▁▁▁▁▁▁▁▁███', + '▁▁▁▁▁▁▁▁▁██', + '▁▁▁▁▁▁▁▁▁▁█', + ], +}; + +// class ConsoleStream implements NodeJS.WritableStream, NodeJS.EventEmitter { +// out: (message?: any, ...optionalParams: any[]) => void; +// constructor( +// readonly con: Console, +// readonly writable = true +// ) { +// // Capture console.log +// this.out = con.log; +// } +// // Writeable Stream +// write( +// buffer: Uint8Array | string, +// encodingOrCb?: BufferEncoding | ((err?: Error | null) => void), +// cb?: (err?: Error | null) => void +// ): boolean { +// if (typeof encodingOrCb === 'function') { +// this.out(buffer); +// encodingOrCb(); +// } else { +// if (encodingOrCb) this.out(Buffer.from(buffer as string, encodingOrCb)); +// else this.out(buffer); +// if (cb) cb(); +// } +// return true; +// } +// end( +// dataOrCb?: string | Uint8Array | (() => void), +// encodingOrCb?: BufferEncoding | (() => void), +// cb?: () => void +// ): this { +// if (typeof dataOrCb === 'function') { +// this.out('\n'); +// dataOrCb(); +// } else if (typeof encodingOrCb === 'function') { +// this.out(dataOrCb as string | Uint8Array); +// if ((dataOrCb as string | Uint8Array).toString().slice(-1) !== '\n') +// this.out('\n'); +// encodingOrCb(); +// } else { +// if (dataOrCb) this.out(dataOrCb); +// if (encodingOrCb) this.out('\n'); +// if (cb) cb(); +// } +// return this; +// } + +// // Readable (EventEmitter) - NOP FOR NOw +// on(eventName: string | symbol, listener: (...args: any[]) => void): this { +// return this; +// } +// once( +// eventName: string | symbol, +// listener: (...args: any[]) => void +// ): this { +// return this; +// } +// off(eventName: string | symbol, listener: (...args: any[]) => void): this { +// return this; +// } +// emit(eventName: string | symbol, ...args: any[]): boolean { +// return true; +// } +// // addListener, removeListener, removeAllListeners, setMaxListeners +// // listeners, rawListeners, eventNames +// addListener( +// eventName: string | symbol, +// listener: (...args: any[]) => void +// ): this { +// return this; +// } +// removeListener( +// eventName: string | symbol, +// listener: (...args: any[]) => void +// ): this { +// return this; +// } +// removeAllListeners(eventName?: string | symbol): this { +// return this; +// } +// setMaxListeners(n: number): this { +// return this; +// } +// getMaxListeners(): number { +// return 0; +// } +// listeners(eventName: string | symbol): Function[] { +// return []; +// } +// rawListeners(eventName: string | symbol): Function[] { +// return []; +// } +// eventNames(): (string | symbol)[] { +// return []; +// } +// listenerCount( +// eventName: string | symbol, +// listener?: Function | undefined +// ): number { +// return 0; +// } +// prependListener( +// eventName: string | symbol, +// listener: (...args: any[]) => void +// ): this { +// return this; +// } +// prependOnceListener( +// eventName: string | symbol, +// listener: (...args: any[]) => void +// ): this { +// return this; +// } +// } + +// class DebugJsonUI extends ConsoleStream { +class DebugJsonUI { + spinner: Ora = this.reset(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _log: (...args: any[]) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _err: (...args: any[]) => void; + constructor(con: Console = console) { + // super(con); + // Create and start the spinner + // this.spinner.start('Loading...'); + this._log = con.log; + this._err = con.error; + } + + reset(): Ora { + if(this.spinning()) { + this.spinner.stop(); + } else { + this.spinner = ora({ + // stream: this, + spinner: I2CIP_DEFAULT_SPINNER + }); + } + return this.spinner; + } + + spinning(): boolean { + return this.spinner?.isSpinning; + } + + /** + * Start the loading spinner. + * @param text Text to display. + * @param spinner Spinner to use. Defaults to the default spinner. + */ + start(text: string = '') { + // If it's already spinning, just change the text + if (this.spinning()) { + this.spinner.text = text; + } else { + // Otherwise, start a new one + this.reset(); + this.spinner.start(text); + } + } + + /** + * Fail the loading spinner. + * @param text Text to display. + */ + fail(text: string = '') { + // If it's spinning, change the text and fail + if (!this.spinning()) { + this.reset(); + } + this.spinner.fail(text); + } + + /** + * Succeed the loading spinner. + * @param text Text to display. + */ + succeed(text: string = '') { + // If it's spinning, change the text and succeed + if (!this.spinning()) { + this.reset(); + } + this.spinner.succeed(text); + } + + /** + * Complete the loading spinner with info (blue `i`) + * @param text Text to display. + */ + info(text: string = '') { + if (!this.spinning()) { + this.reset(); + } + this.spinner.info(text); + } + + /** + * If spinning: stop and clear the current spinner, log some text, then restart the spinner + * Else: Just log + * + * @param text Text to log + */ + log(...args: unknown[]) { + if (this.spinning()) { + const oldtext = this.spinner.text; + this.spinner.stop(); // stop spinner + this._log(...args); // log text + this.spinner.start(oldtext); // restart spinner + } else { + this._log(...args); // log text + } + } + + /** + * If spinning: stop and clear the current spinner, log some text, then restart the spinner + * Else: Just log + * + * @param text Text to log + */ + err(...args: unknown[]) { + if (this.spinning()) { + const oldtext = this.spinner.text; + this.spinner.stop(); // stop spinner + this._err(...args); // log text + this.spinner.start(oldtext); // restart spinner + } else { + this._err(...args); // log text + } + } +} + +export const DebugJsonConsole = new DebugJsonUI(); +export default DebugJsonConsole; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function _logRedirect(...message: any[]): void { + DebugJsonConsole.log(...message); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function _errRedirect(...message: any[]): void { + DebugJsonConsole.err(...message); +} + +// // Use yargs to get a flag "--spawn" +// const _IS_SPAWN = yargs(process.argv.slice(2)).boolean('spawn').argv; + +// // If spawn, spawn a new process +// if (_IS_SPAWN) { +// // Import the child_process module +// import { spawn } from 'child_process'; +// // Spawn a new process, with that console used as a stream for the UI +// const n = spawn('node', [], {}); +// // Make writablestream from n.stdout + +// } else { + +// } + +// If not spawn, this is the spawned process. Run the UI + +// Create a screen object. +// let screen = blessed.screen({ +// smartCSR: true +// }); + +// let box = blessed.box({ +// top: 'top', +// left: 'left', +// width: '50%', +// height: '50%', +// border: { +// type: 'line' +// }, +// style: { +// fg: 'white', +// bg: 'black', +// border: { +// fg: '#ffffff' +// } +// }, +// align: 'left' +// }); + +// screen.append(box); + +// export namespace Screen { +// export function render(): void { +// box.focus(); +// screen.render(); +// } +// export function hide(): void { +// box.hide(); +// } +// export function setData(data: DebugJsonMessage): void { +// let s = '[' + (data.t ?? parseType(data.type)) + ']'; +// if (data.timestamp) s += '@' + data.timestamp + 'ms'; +// if (data.data) { +// s += Object.entries(data.data).map(datum => { +// return ( +// datum[0] // label +// .split('-') // multiword delimiter; replace with space +// .map(word => word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase()) +// .join(' ') + +// ': ' + +// parseDatum(datum[1]) // value, to string +// ); +// }).join(', ') + ' }'; +// } +// box.setContent(s); +// } +// } diff --git a/software/api/utils.ts b/software/api/utils.ts new file mode 100644 index 00000000..96bbef50 --- /dev/null +++ b/software/api/utils.ts @@ -0,0 +1,102 @@ +import { spawn } from 'child_process'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; +import { hostname } from 'os'; +import { lookup } from 'dns'; + +const PATHSTEM_MICROCONTROLLER = './microcontroller/'; + +/** + * Compiles the microcontroller software, and flashes the binary to the chip. + */ +export function updateMicrocontroller(): Promise { + return new Promise((res, rej) => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute( + `${process.env.HOME}/.platformio/penv/bin/platformio run -d ${PATHSTEM_MICROCONTROLLER} -t upload`, + [1] + ) + .catch(err => { + writeFileSync('logs/updateMicrocontroller.log', err); + rej( + new Error( + 'Failed to update the microcontroller software. See logs/updateMicrocontroller.log' + ) + ); + }) + .then(log1 => { + if (log1) writeFileSync('logs/updateMicrocontroller.log', log1); + res(); + }); + }); +} + +/** + * General purpose command execution and logging. No `sudo` support. + */ +export function execute(command: string, failureCodes: number[] = []): Promise { + return new Promise((res, rej) => { + const args = command.split(' '); + const eprocess = spawn(args[0], args.slice(1)); + let log = '> ' + command + '\n'; + eprocess.stdout?.on('data', out => { + log += out; + }); + eprocess.stderr?.on('data', out => { + log += out; + }); + eprocess.on('error', error => { + log += error.message; + rej(log); + eprocess.kill(); + }); + eprocess.on('close', code => { + if (code) { + log += '> Process exited with code ' + code; + if (failureCodes.includes(code)) { + rej(log); + } + return; + } + // If no options, no codes, OR non-failure: + res(log); + }); + }); +} + +const PATHSTEM_IMAGES = '~/img/'; + +const dateFormat = (d: Date) => (`${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}_${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}`); + +type CameraCaptureOptions = { + width?: number; + height?: number; + // TODO: Add more options +}; + +// Returns a path to the JPEG image. +export function cameraCapture(options?: CameraCaptureOptions): Promise { + return new Promise((res, rej) => { + const p = `${PATHSTEM_IMAGES}-${dateFormat(new Date())}`; + execute(`libcamera-jpeg -o ${p}.jpg`) + .catch(err => { + rej(err); + }) + .then(() => { + res(`${p}.jpg`); + }); + }); +} + +// Helper: IPv4 Address Lookup (Fallback to 'localhost') +export const ipv4Lookup = async (): Promise => { + const h = hostname(); + if(!h) return 'localhost'; + return await new Promise((res) => { + lookup(h, { family: 4, all: true }, (err, addrs) => { + res((err || !addrs || !(addrs.length)) ? 'localhost' : addrs.find((a) => (a.address !== '127.0.0.1')).address); + }); + }); +}; \ No newline at end of file diff --git a/software/index.ts b/software/archive/index.ts similarity index 88% rename from software/index.ts rename to software/archive/index.ts index 5270ddbf..8f61c8de 100644 --- a/software/index.ts +++ b/software/archive/index.ts @@ -2,10 +2,10 @@ import chalk from 'chalk'; import * as inquirer from 'inquirer'; -import { checkInternet, sleep } from './src/utils'; -import { Spinner } from './src/ui'; -import { PublishingMode } from './src/publisher'; -import PeaPod from './src/peapod'; +import { checkInternet, sleep } from '../src/utils'; +import { Spinner } from '../src/ui'; +import { PublishingMode } from '../src/publisher'; +import PeaPod from '../src/peapod'; // MAIN diff --git a/software/archive/src/controller.ts b/software/archive/src/controller.ts new file mode 100644 index 00000000..0b6a6a74 --- /dev/null +++ b/software/archive/src/controller.ts @@ -0,0 +1,280 @@ +import chalk from 'chalk'; +import { ReadlineParser, SerialPort } from 'serialport'; +import { ControllerInstructionsError } from './errors'; +import { Spinner } from './ui'; +import { sleep, updateMicrocontroller } from './utils'; +// import { Gpio } from 'onoff'; + +// CONSTANTS + +/** + * Baud rate for serial communication. Must match that found in microcontroller code. + */ +const BAUDRATE = 115200; + +/** + * Microcontroller software revision matching this software. + */ +export const CONTROLLER_REVISION = 0; + +/** + * Seconds to wait between messages before timing out. + */ +const SERIAL_TIMEOUT_SECONDS = 10; + +/** + * GPIO pin attached to the reset grounding circuit + */ +// const RESET_PIN = 26; + +// TYPES + +/** + * Base type for any controller. + */ +export type Controller = { + /** + * Establish communications with the Controller. + * @param onMessage Pipe all received messages. + * @throws If received message is invalid (JSON parsing fails). + */ + start(onMessage: (msg: ControllerMessage) => void): Promise; + + /** + * Write instructions to the Controller. + * @param instructions Instruction set. + */ + write(instructions: ControllerInstructions): void; + + /** + * Halt communications with the Controller. + */ + stop(): void; +}; + +/** + * Messages FROM the controller + */ +export type ControllerMessage = + | { + type: 'info' | 'debug' | 'error'; + data: string; + } + | { + type: 'data'; + data: { + label: string; + value: number; + }; + } + | { + type: 'revision'; + data: number; + }; + +/** + * Messages TO the controller + */ +export type ControllerInstructions = { + [key: string]: number; +}; + +/** + * Simulated controller parameters + */ +export type SimulatorConfig = { + [key: string]: { + min: number; + max: number; + interval: number; + }; +}; + +// CLASSES + +/** + * Interface between this computer and the microcontroller. + */ +export default class MicroController implements Controller { + serial: SerialPort; + parser: ReadlineParser; + private timeout?: NodeJS.Timeout; + // private resetpin: Gpio; + + constructor(readonly serialport: string) { + // Reset pin GPIO interface + // this.resetpin = new Gpio(RESET_PIN, 'out'); + + // Create the serial port interface + this.serial = new SerialPort({ + path: serialport, + baudRate: BAUDRATE, + autoOpen: false + }); + + // Create the newline parser + this.parser = this.serial.pipe( + new ReadlineParser({ + delimiter: '\n', + includeDelimiter: false + }) + ); + } + + start(onMessage: (msg: ControllerMessage) => void): Promise { + this.clearTimeout(); + // Reset listeners + this.parser.removeAllListeners('data'); + + // Explicit promise construction so we can resolve only on valid comms AND revision check + return new Promise(async (res, rej) => { + // Reset the microcontroller (opens the serial port) + await this.reset(); + + Spinner.start('Awaiting microcontroller software revision number...'); + this.resetTimeout(); + + // Set up the listener + this.parser.on('data', async msgtxt => { + // Attempt to parse the raw text as a valid JSON object + let msg: ControllerMessage; + try { + msg = JSON.parse(msgtxt); + } catch (err) { + rej(err); + return; + } + + // Microcontroller-specific pre-handling + switch (msg.type) { + case 'revision': + // Software update + if (msg.data === CONTROLLER_REVISION) { + Spinner.succeed(`Microcontroller software up to date! Got ${msg.data}`); + res(); //Successful start sequence + } else { + Spinner.fail( + `Microcontroller software out of date! Got ${msg.data}, expected ${CONTROLLER_REVISION}.` + ); + // Attempt to update the microcontroller, and then restart + this.stop(); + Spinner.start('Compiling latest microcontroller software and flashing...'); + await updateMicrocontroller(); + Spinner.succeed('Updated microcontroller software successfully!'); + } + // Do not break + default: + onMessage(msg); + break; + } + this.resetTimeout(); + }); + }); + } + + /** + * Clear the serial timeout. + */ + private clearTimeout(): void { + if (this.timeout) clearTimeout(this.timeout); + } + + /** + * Refresh (or start) the serial timeout. + */ + private resetTimeout(timeoutSeconds: number = SERIAL_TIMEOUT_SECONDS): void { + this.clearTimeout(); + this.timeout = setTimeout(() => { + Spinner.fail( + `Microcontroller serial communications timed out after ${timeoutSeconds} seconds.` + ); + this.reset(); + }, timeoutSeconds * 1000); + } + + write(msg: ControllerInstructions): void { + Spinner.info(`[${chalk.yellow('WRITE')}] - ${JSON.stringify(msg)}`); + this.serial.write(JSON.stringify(msg) + '\n', undefined, err => { + if (err) throw new ControllerInstructionsError(JSON.stringify(msg)); + }); + } + + stop(): void { + this.clearTimeout(); + if (this.serial.isOpen) this.serial.close(); + // Stop listening for data + this.parser.removeAllListeners('data'); + } + + /** + * Resets the microcontroller by closing and re-opening serial. + */ + private async reset(): Promise { + // Stop and reset + this.stop(); + // this.resetpin.writeSync(1); + + // Wait, then stop resetting + // await sleep(1000); + // this.resetpin.writeSync(0); + + // (Re-)open serial + await new Promise((reso, reje) => { + Spinner.start('Establishing serial communications with the microcontroller...'); + this.serial.open(err => { + if (err) { + reje(err); + } else { + Spinner.succeed('Microcontroller serial communications established!'); + reso(); + } + }); + }); + + // Restart timeout + this.resetTimeout(); + } +} + +/** + * A simulated controller for generating random data. + */ +export class SimulatedController implements Controller { + private intervals: NodeJS.Timeout[] = []; + + constructor(readonly parameters: SimulatorConfig) {} + + async start(onMessage: (msg: ControllerMessage) => any): Promise { + for (const label of Object.keys(this.parameters)) { + this.intervals.push( + setInterval(() => { + onMessage( + this.generateData(label, this.parameters[label].min, this.parameters[label].max) + ); + }, this.parameters[label].interval) + ); + } + } + write() {} + async stop(): Promise { + for (const interval of this.intervals) { + clearInterval(interval); + } + } + + /** + * Generate a single data point + * @param label Dataset label + * @param min Minimum value + * @param max Maximum value + */ + private generateData(label: string, min: number, max: number): ControllerMessage { + return { + type: 'data', + data: { + label, + value: Math.random() * (max - min) + min + } + }; + } +} diff --git a/software/src/controlsystem.ts b/software/archive/src/controlsystem.ts similarity index 96% rename from software/src/controlsystem.ts rename to software/archive/src/controlsystem.ts index 3809f508..f35d1659 100644 --- a/software/src/controlsystem.ts +++ b/software/archive/src/controlsystem.ts @@ -55,7 +55,7 @@ export class PID extends ControlSystem { } const err = this.target - this.value; let derivative = 0; - if (this.valueLastSet != NaN || this.lasterr != NaN) { + if (Number.isNaN(this.valueLastSet) || Number.isNaN(this.lasterr)) { // Trapezoidal approximation this.integral += ((this.lasterr + err) / 2) * (Date.now() - this.valueLastSet); derivative = (err - this.lasterr) / (Date.now() - this.valueLastSet); diff --git a/software/src/env.ts b/software/archive/src/env.ts similarity index 100% rename from software/src/env.ts rename to software/archive/src/env.ts diff --git a/software/src/errors.ts b/software/archive/src/errors.ts similarity index 100% rename from software/src/errors.ts rename to software/archive/src/errors.ts diff --git a/software/src/peapod.ts b/software/archive/src/peapod.ts similarity index 100% rename from software/src/peapod.ts rename to software/archive/src/peapod.ts diff --git a/software/src/publisher.ts b/software/archive/src/publisher.ts similarity index 100% rename from software/src/publisher.ts rename to software/archive/src/publisher.ts diff --git a/software/src/ui.ts b/software/archive/src/ui.ts similarity index 100% rename from software/src/ui.ts rename to software/archive/src/ui.ts diff --git a/software/archive/src/utils.ts b/software/archive/src/utils.ts new file mode 100644 index 00000000..b87c08ff --- /dev/null +++ b/software/archive/src/utils.ts @@ -0,0 +1,131 @@ +import * as dns from 'dns'; +import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; +import axios from 'axios'; +import { spawn } from 'child_process'; + +const PATHSTEM_IMAGES = '~/img/'; + +const dateFormat = (d: Date) => (`${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}_${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}`); + +/** + * Checks the internet connectivity. + * @param timeout Timeout in milliseconds. Default: 5000 + * @param url The URL to ping. Default: 'www.google.com' + */ +export function checkInternet( + timeout: number = 5000, + url: string = 'www.google.com' +): Promise { + const resolver = new dns.Resolver({ timeout }); + return new Promise(ret => { + resolver.resolve(url, err => { + if (err) { + ret(false); + } else { + ret(true); + } + }); + }); +} + +/** + * Sleep a given number of milliseconds. + * + * Usage: `await sleep(x)` + * @param millis Number of milliseconds to sleep. + * @returns + */ +export function sleep(millis: number): Promise { + return new Promise(resolve => { + setTimeout(resolve, millis); + }); +} + +/** + * Fetch the Google Root CA certificate. + * @returns The very same certificate. + */ +export async function fetchServerCert(): Promise { + return String((await axios.get('https://pki.goog/roots.pem')).data); +} + +/** + * Compiles the microcontroller software, and flashes the binary to the chip. + */ +export function updateMicrocontroller(): Promise { + return new Promise((res, rej) => { + // Create log folder + if (!existsSync('logs/')) { + mkdirSync('logs/', { recursive: true }); + } + execute( + `${process.env.HOME}/.platformio/penv/bin/platformio run -d microcontroller/ -e peapod -t upload`, + [1] + ) + .catch(err => { + writeFileSync('logs/updateMicrocontroller.log', err); + rej( + new Error( + `Failed to update the microcontroller software. See logs/updateMicrocontroller.log` + ) + ); + }) + .then(log1 => { + if (log1) writeFileSync('logs/updateMicrocontroller.log', log1); + res(); + }); + }); +} + +/** + * General purpose command execution and logging. No `sudo` support. + */ +export function execute(command: string, failureCodes: number[] = []): Promise { + return new Promise((res, rej) => { + const args = command.split(' '); + const eprocess = spawn(args[0], args.slice(1)); + let log = '> ' + command + '\n'; + eprocess.stdout?.on('data', out => { + log += out; + }); + eprocess.stderr?.on('data', out => { + log += out; + }); + eprocess.on('error', error => { + log += error.message; + rej(log); + eprocess.kill(); + }); + eprocess.on('close', code => { + if (code) { + log += '> Process exited with code ' + code; + if (failureCodes.includes(code)) { + rej(log); + } + return; + } + // If no options, no codes, OR non-failure: + res(log); + }); + }); +} + +type CameraCaptureOptions = { + width?: number; + height?: number; + // TODO: Add more options +}; + +// Returns a path to the JPEG image. +export function cameraCapture(options?: CameraCaptureOptions): Promise { + return new Promise((res, rej) => { + const p = `${PATHSTEM_IMAGES}-${dateFormat(new Date())}`; + execute(`libcamera-jpeg -o ${p}.jpg`) + .catch(err => { + rej(err); + }) + .then(() => { + res(`${p}.jpg`); + }); + }); +} \ No newline at end of file diff --git a/software/eslint.config.mjs b/software/eslint.config.mjs new file mode 100644 index 00000000..6622e8f4 --- /dev/null +++ b/software/eslint.config.mjs @@ -0,0 +1,45 @@ +import globals from 'globals'; + +import { FlatCompat } from '@eslint/eslintrc'; +import typescriptEslint from '@typescript-eslint/eslint-plugin'; + + +const compat = new FlatCompat({ + // import.meta.dirname is available after Node.js v20.11.0 + baseDirectory: import.meta.dirname, + recommendedConfig: typescriptEslint.configs['recommended'], +}); + +console.log('Using eslint.config.mjs'); + +var eslintConfig = compat.config({ + extends: ['prettier', 'next', 'next/typescript'], + plugins: ['@typescript-eslint', 'react'], +}); + +eslintConfig.push({ files: [ + './*.{js,mjs,cjs,ts,jsx,tsx}', + './api/*.{js,mjs,cjs,ts,jsx,tsx}', + './api/**/*.{js,mjs,cjs,ts,jsx,tsx}', + './src/*.{js,mjs,cjs,ts,jsx,tsx}', + './src/**/*.{js,mjs,cjs,ts,jsx,tsx}' +]}) + +eslintConfig.push({ignores: ['node_modules', '.next', 'out', 'dist', 'build', 'coverage', 'public', 'lib']}); + +eslintConfig.push({ + languageOptions: { + globals: {...globals.browser, ...globals.node } + }, + // linterOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname}}, + rules: { + indent: ['error', 2], + 'linebreak-style': ['error', 'unix'], + quotes: ['error', 'single'], + semi: ['error', 'always'], + 'react/react-in-jsx-scope': 'off' + } +}); + +/** @type {import('eslint').Linter.Config[]} */ +export default eslintConfig; \ No newline at end of file diff --git a/software/firebase.json b/software/firebase.json new file mode 100644 index 00000000..9279f0f5 --- /dev/null +++ b/software/firebase.json @@ -0,0 +1,26 @@ +{ + "firestore": { + "database": "(default)", + "location": "nam5", + "rules": "firestore.rules", + "indexes": "firestore.indexes.json" + }, + "functions": [ + { + "source": "functions", + "codebase": "default", + "disallowLegacyRuntimeConfig": true, + "ignore": [ + "node_modules", + ".git", + "firebase-debug.log", + "firebase-debug.*.log", + "*.local" + ], + "predeploy": [ + "npm --prefix \"$RESOURCE_DIR\" run lint", + "npm --prefix \"$RESOURCE_DIR\" run build" + ] + } + ] +} diff --git a/software/firestore.indexes.json b/software/firestore.indexes.json new file mode 100644 index 00000000..2ddb5ce9 --- /dev/null +++ b/software/firestore.indexes.json @@ -0,0 +1,4 @@ +{ + "indexes": [], + "fieldOverrides": [] +} \ No newline at end of file diff --git a/software/firestore.rules b/software/firestore.rules new file mode 100644 index 00000000..6c4af405 --- /dev/null +++ b/software/firestore.rules @@ -0,0 +1,26 @@ +rules_version = '2'; +service cloud.firestore { + match /databases/{database}/documents { + match /projects/{projectId} { + allow read, write: if request.auth.uid in resource.data.owners; + } + match /projects/{projectId}/runs/{runId} { + allow read, write: if resource.data.owner == request.auth.uid; + } + match /projects/{projectId}/{subdoc=**} { + allow read, write: if request.auth.uid in get(/databases/$(database)/documents/projects/$(projectId)).data.owners; + } + match /users/{userId} { + allow read: if request.auth.uid == userId; + allow write: if false; //Cloud Functions only + } + match /users/{userId}/{subdoc=**} { + allow read: if request.auth.uid == userId; + allow write: if false; //Cloud Functions only + } + match /devices/{deviceId=**} { + allow read: if resource.data.owner == request.auth.uid; + allow write: if false; //Cloud Functions only + } + } +} \ No newline at end of file diff --git a/software/functions/.gitignore b/software/functions/.gitignore new file mode 100644 index 00000000..9be0f014 --- /dev/null +++ b/software/functions/.gitignore @@ -0,0 +1,10 @@ +# Compiled JavaScript files +lib/**/*.js +lib/**/*.js.map + +# TypeScript v1 declaration files +typings/ + +# Node.js dependency directory +node_modules/ +*.local \ No newline at end of file diff --git a/software/functions/package-lock.json b/software/functions/package-lock.json new file mode 100644 index 00000000..7f623665 --- /dev/null +++ b/software/functions/package-lock.json @@ -0,0 +1,9901 @@ +{ + "name": "functions", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "functions", + "dependencies": { + "firebase-admin": "^13.6.0", + "firebase-functions": "^7.0.0" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.12.0", + "@typescript-eslint/parser": "^5.12.0", + "eslint": "^8.9.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-import": "^2.25.4", + "firebase-functions-test": "^3.4.1", + "typescript": "^5.7.3" + }, + "engines": { + "node": "24" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "license": "MIT" + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/component": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.0.tgz", + "integrity": "sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.0.tgz", + "integrity": "sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.0.tgz", + "integrity": "sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/database": "1.1.0", + "@firebase/database-types": "1.0.16", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.16.tgz", + "integrity": "sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.13.0" + } + }, + "node_modules/@firebase/logger": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", + "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.13.0.tgz", + "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz", + "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.18.0.tgz", + "integrity": "sha512-r3ZwDMiz4nwW6R922Z1pwpePxyRwE5GdevYX63hRmAQUkUQJcBH/79EnQPDv5cOv1mFBgevdNWQfi3tie3dHrQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "<4.1.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^4.4.1", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.45", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.45.tgz", + "integrity": "sha512-qJcFVfCa5jxBFSuv7S5WYbA8XdeCPmhnaVVfX/2Y6L8WYg8sk3XY2+6W0zH+3mq1Cz+YC7Ki66HfqX6IHAwnkg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", + "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", + "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/request": { + "version": "2.48.13", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz", + "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.5" + } + }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "optional": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT", + "optional": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/transform": "30.2.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", + "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001761", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0", + "peer": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", + "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "optional": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/firebase-admin": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.6.0.tgz", + "integrity": "sha512-GdPA/t0+Cq8p1JnjFRBmxRxAGvF/kl2yfdhALl38PrRp325YxyQ5aNaHui0XmaKcKiGRFIJ/EgBNWFoDP0onjw==", + "license": "Apache-2.0", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "fast-deep-equal": "^3.1.1", + "google-auth-library": "^9.14.2", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" + } + }, + "node_modules/firebase-functions": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-7.0.2.tgz", + "integrity": "sha512-AFAZOn2iMp6X6lvCzEI92/J/JfT2ZAGSHKAfop4btWZdIqeHU40gFwVBGPl53bnqEpuv5VYKirmkxGuPu4OHpA==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.5", + "@types/express": "^4.17.21", + "cors": "^2.8.5", + "express": "^4.21.0", + "protobufjs": "^7.2.2" + }, + "bin": { + "firebase-functions": "lib/bin/firebase-functions.js" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "firebase-admin": "^11.10.0 || ^12.0.0 || ^13.0.0" + } + }, + "node_modules/firebase-functions-test": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/firebase-functions-test/-/firebase-functions-test-3.4.1.tgz", + "integrity": "sha512-qAq0oszrBGdf4bnCF6t4FoSgMsepeIXh0Pi/FhikSE6e+TvKKGpfrfUP/5pFjJZxFcLsweoau88KydCql4xSeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "^4.14.104", + "lodash": "^4.17.5", + "ts-deepmerge": "^2.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0", + "firebase-functions": ">=4.9.0", + "jest": ">=28.0.0" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "license": "MIT", + "optional": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", + "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/core": "30.2.0", + "@jest/types": "30.2.0", + "import-local": "^3.2.0", + "jest-cli": "30.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.2.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "p-limit": "^3.1.0", + "pretty-format": "30.2.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.2.0", + "@jest/types": "30.2.0", + "babel-jest": "30.2.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "jest-util": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "pretty-format": "30.2.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz", + "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==", + "license": "MIT", + "dependencies": { + "@types/express": "^4.17.20", + "@types/jsonwebtoken": "^9.0.4", + "debug": "^4.3.4", + "jose": "^4.15.4", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT", + "optional": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0", + "peer": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "license": "MIT", + "optional": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT", + "optional": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT", + "optional": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-deepmerge": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-2.0.7.tgz", + "integrity": "sha512-3phiGcxPSSR47RBubQxPoZ+pqXsEsozLo4G4AlSrsMKTFg9TA3l+3he5BqpUi9wiuDbaHWXH/amlzQ49uEdXtg==", + "dev": true, + "license": "ISC" + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT", + "optional": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/software/functions/package.json b/software/functions/package.json new file mode 100644 index 00000000..a0a54d7f --- /dev/null +++ b/software/functions/package.json @@ -0,0 +1,31 @@ +{ + "name": "functions", + "scripts": { + "lint": "eslint --ext .js,.ts .", + "build": "tsc", + "build:watch": "tsc --watch", + "serve": "npm run build && firebase emulators:start --only functions", + "shell": "npm run build && firebase functions:shell", + "start": "npm run shell", + "deploy": "firebase deploy --only functions", + "logs": "firebase functions:log" + }, + "engines": { + "node": "24" + }, + "main": "lib/index.js", + "dependencies": { + "firebase-admin": "^13.6.0", + "firebase-functions": "^7.0.0" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.12.0", + "@typescript-eslint/parser": "^5.12.0", + "eslint": "^8.9.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-import": "^2.25.4", + "firebase-functions-test": "^3.4.1", + "typescript": "^5.7.3" + }, + "private": true +} diff --git a/software/functions/src/device/listeners/devicelist.ts b/software/functions/src/device/listeners/devicelist.ts new file mode 100644 index 00000000..dab25039 --- /dev/null +++ b/software/functions/src/device/listeners/devicelist.ts @@ -0,0 +1,30 @@ +import * as functions from 'firebase-functions'; +import {firestore} from 'firebase-admin'; + +// Minimize Firestore reads by caching device IDs and metadata. + +type DeviceData = { + owner: string, + name: string, + latest: { + project: string, + run: string, + }, +} + +export const devices: {[key: string]: DeviceData} = {}; + +// Get initial set of registered devices +firestore().collection('devices').get().then((docs)=>{ + docs.docs.forEach((doc)=>{ + devices[doc.id] = {...(doc.data() as DeviceData)}; + }); +}); + +export const deviceCreationListener = functions.firestore.document('devices/{deviceID}').onCreate((doc)=>{ + devices[doc.id] = {...(doc.data() as DeviceData)}; +}); + +export const deviceDeletionListener = functions.firestore.document('devices/{deviceID}').onDelete((doc)=>{ + delete devices[doc.id]; +}); diff --git a/software/functions/src/device/listeners/pubsub.ts b/software/functions/src/device/listeners/pubsub.ts new file mode 100644 index 00000000..a8f19bcd --- /dev/null +++ b/software/functions/src/device/listeners/pubsub.ts @@ -0,0 +1,75 @@ +import * as functions from 'firebase-functions'; +import {firestore} from 'firebase-admin'; + +import {devices} from './devicelist'; + +// Message Attributes: +// - `deviceid`: ID of device as it appears in IoT registry AND Firestore document +// - `owner`: UID of user who registered the device + +type PubSubData = { + [key: string]: { + batch: { + timestamp: number, + value: number + }[] + } +} + +export const dataPubSubListener = functions.pubsub.topic('data').onPublish(async (message)=>{ + // try { + // let temp = message.json; + // } catch (e) { + // functions.logger.error('PubSub message was not JSON', e); + // return; + // } + + // TODO: Send STOP command (or something like that) on fatal errors + + const data = message.json.data as PubSubData; + const metadata = message.json.metadata as {project: string, run: string, owner: string}; + + if (!message.json || !metadata.owner || !metadata.project || !metadata.run) { + functions.logger.error(`Device published incomplete message.`, message); + return; + } + + if (!Object.keys(devices).includes(message.attributes.deviceId)) { + functions.logger.error(`Unregistered device '${message.attributes.deviceId}' attempted to publish data.`, devices); + return; + } + + if (devices[message.attributes.deviceId].owner != metadata.owner) { + functions.logger.error(`Decvice '${message.attributes.deviceId}' and`+ + ` owner '${metadata.owner}' do not match records.`, devices); + return; + } + + + const rundoc = await firestore().doc(`projects/${metadata.project}/runs/${metadata.run}`) + .get(); + + // Create run document + if (!rundoc.exists) { + rundoc.ref.create({ + owner: metadata.owner, + device: message.attributes.deviceId, + }); + } + + // New project? + if (devices[message.attributes.deviceId].latest.project != metadata.project || + devices[message.attributes.deviceId].latest.run != metadata.run) { + await firestore().doc(`devices/${message.attributes.deviceId}`).update({ + latest: { + project: metadata.project, + run: metadata.run, + }, + }); + } + + return Object.keys(data).map((label)=> + firestore() + .collection(`projects/${metadata.project}/runs/${metadata.run}/${label}/`) + .add({...data[label], timestamp: firestore.FieldValue.serverTimestamp()})); +}); diff --git a/software/functions/src/device/routes/registerDevice.ts b/software/functions/src/device/routes/registerDevice.ts new file mode 100644 index 00000000..22c6c006 --- /dev/null +++ b/software/functions/src/device/routes/registerDevice.ts @@ -0,0 +1,109 @@ +import * as functions from 'firebase-functions'; + +import * as iot from '@google-cloud/iot'; +import {v4 as uuid} from 'uuid'; +import {pki} from 'node-forge'; +import {firestore} from 'firebase-admin'; + +// Found at https://console.cloud.google.com/iot/registries +const gcpproject = 'cloudponics-bc383'; +const cloudregion = 'us-central1'; +const registryid = 'CloudPonics'; + +// Client SDKs +const iotClient = new iot.DeviceManagerClient(); + +type DeviceRegistrationData = { + name: string +} | null; + +export const registerDevice = functions.https.onCall(async (data: DeviceRegistrationData, context) => { + if (!context.auth || !context.auth.uid) { + throw new functions.https.HttpsError('unauthenticated', 'Not authenticated.'); + } + + const userdoc = await firestore().doc(`users/${context.auth.uid}`).get(); + + if (userdoc.get('devicecount') >= userdoc.get('devicequota')) { + throw new functions.https.HttpsError('resource-exhausted', 'Device quota exceeded.'); + } + + // Generate unique device identifier + const deviceid = 'peapod-'+uuid(); + + const {privateKey, publicKey} = pki.rsa.generateKeyPair(4096); + + // Build registry path + const registryPath = iotClient.registryPath(gcpproject, cloudregion, registryid); + + // Build IoT registry device creation request object + const request : iot.protos.google.cloud.iot.v1.ICreateDeviceRequest = { + parent: registryPath, + device: { + id: deviceid, + metadata: { + owner: context.auth?.uid, + }, + credentials: [ + { + publicKey: { + format: 'RSA_PEM', + key: pki.publicKeyToPem(publicKey), + }, + }, + ], + }, + }; + + // Create device + const [response] = await iotClient.createDevice(request); + + await firestore().doc('devices/'+response.id).create({ + owner: context.auth?.uid, + name: data?.name ?? 'PeaPod', + latest: { + project: '', + run: '', + }, + }); + + await firestore().doc('users/'+context.auth.uid).update({ + devicecount: firestore.FieldValue.increment(1), + }); + + return {id: response.id, name: response.name, privateKey: pki.privateKeyToPem(privateKey)}; +}); + +type DeviceUnregistrationData = { + deviceid: string +} + + +export const unregisterDevice = functions.https.onCall(async (data: DeviceUnregistrationData, context) => { + if (!context.auth || !context.auth.uid) { + throw new functions.https.HttpsError('unauthenticated', 'Not authenticated.'); + } + + const devicedoc = await firestore().doc(`devices/${data.deviceid}`).get(); + + if (!devicedoc.exists) { + throw new functions.https.HttpsError('invalid-argument', 'Device does not exist'); + } + + if (devicedoc.get('owner') != context.auth.uid) { + throw new functions.https.HttpsError('permission-denied', 'Permission denied'); + } + + await iotClient.deleteDevice({name: iotClient.devicePath( + gcpproject, + cloudregion, + registryid, + data.deviceid + )}); + + await devicedoc.ref.delete(); + + await firestore().doc('users/'+context.auth.uid).update({ + devicecount: firestore.FieldValue.increment(-1), + }); +}); diff --git a/software/functions/src/device/routes/uploadFile.ts b/software/functions/src/device/routes/uploadFile.ts new file mode 100644 index 00000000..44c88a92 --- /dev/null +++ b/software/functions/src/device/routes/uploadFile.ts @@ -0,0 +1,22 @@ +import * as functions from 'firebase-functions'; + +import {Storage} from '@google-cloud/storage'; + +type FileUploadData = { + deviceId: string, + filename: string, + project: string, + run: string, + file: string | Buffer +} + +const storage = new Storage(); + +export const uploadFile = functions.https.onCall(async (data: FileUploadData, context) => { + if (!context.auth) { + throw new functions.https.HttpsError('unauthenticated', 'Not authenticated.'); + } + + const bucket = storage.bucket(`projects/${data.project}/runs/${data.run}`).file(data.filename); + return bucket.save(data.file); +}); diff --git a/software/functions/src/index.ts b/software/functions/src/index.ts new file mode 100644 index 00000000..db18e102 --- /dev/null +++ b/software/functions/src/index.ts @@ -0,0 +1,8 @@ +import * as admin from 'firebase-admin'; +admin.initializeApp(); + +export {deviceCreationListener, deviceDeletionListener} from './device/listeners/devicelist'; +export * from './device/listeners/pubsub'; +export * from './device/routes/registerDevice'; +export * from './webapp/listeners/auth'; +export * from './webapp/routes/routes'; diff --git a/software/functions/src/webapp/listeners/auth.ts b/software/functions/src/webapp/listeners/auth.ts new file mode 100644 index 00000000..66b6fcab --- /dev/null +++ b/software/functions/src/webapp/listeners/auth.ts @@ -0,0 +1,32 @@ +import * as functions from 'firebase-functions'; +import {firestore} from 'firebase-admin'; + +export const newUser = functions.auth.user().onCreate((user)=>{ + return firestore().doc(`users/${user.uid}`).create({ + devicequota: 1, + devicecount: 0, + }); +}); + +/** + * On user account deletion, deletes user doc and removes access from projects and runs + */ +export const deleteUser = functions.auth.user().onDelete((user)=>{ + return [ + firestore().doc(`users/${user.uid}`).delete(), + firestore().collection('projects').where('owners', 'array-contains', user.uid).get().then((docs)=>{ + docs.docs.forEach((doc)=>{ + firestore().collection(`projects/${doc.id}/runs`).where('owner', '==', user.uid).get().then((rundocs)=>{ + rundocs.docs.forEach((rundoc)=>{ + rundoc.ref.update({ + owner: undefined, + }); + }); + }); + doc.ref.update({ + owners: firestore.FieldValue.arrayRemove(user.uid), + }); + }); + }), + ]; +}); diff --git a/software/functions/src/webapp/routes/routes.ts b/software/functions/src/webapp/routes/routes.ts new file mode 100644 index 00000000..935222f7 --- /dev/null +++ b/software/functions/src/webapp/routes/routes.ts @@ -0,0 +1,21 @@ +import * as functions from 'firebase-functions'; +import {firestore} from 'firebase-admin'; + +type GetDatasetsParams = { + project: string, + run: string +} + +export const getDatasets = functions.https.onCall(async (data: GetDatasetsParams, context)=>{ + if (!context.auth || !context.auth.uid) { + throw new functions.https.HttpsError('unauthenticated', 'Not authenticated.'); + } + + const rundoc = await firestore().doc(`projects/${data.project}/runs/${data.run}`).get(); + + if (rundoc.get('owner') != context.auth.uid) { + throw new functions.https.HttpsError('permission-denied', 'Permission denied'); + } + + return (await rundoc.ref.listCollections()).map((set)=>set.id); +}); diff --git a/software/functions/tsconfig.dev.json b/software/functions/tsconfig.dev.json new file mode 100644 index 00000000..7560eed4 --- /dev/null +++ b/software/functions/tsconfig.dev.json @@ -0,0 +1,5 @@ +{ + "include": [ + ".eslintrc.js" + ] +} diff --git a/software/functions/tsconfig.json b/software/functions/tsconfig.json new file mode 100644 index 00000000..57b915f3 --- /dev/null +++ b/software/functions/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "esModuleInterop": true, + "moduleResolution": "nodenext", + "noImplicitReturns": true, + "noUnusedLocals": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "compileOnSave": true, + "include": [ + "src" + ] +} diff --git a/software/microcontroller/src/light.h b/software/microcontroller/src/light.h index 42c64ce8..6ad151fd 100644 --- a/software/microcontroller/src/light.h +++ b/software/microcontroller/src/light.h @@ -3,7 +3,7 @@ #include -#define PEAPOD_MODULE_LIGHTING_EEPROM_CONTENTS {"[{\"24LC32\":[80],\"PCA9685\":[" STR(I2CIP_PCA9685_ADDRESS) "]}]"} +#define PEAPOD_MODULE_LIGHTING_EEPROM_CONTENTS {"[{\"24LC32\":[80],\"PCA9685\":[" STR(I2CIP_PCA9685_ADDRESS) "],\"ADS1115\":[" STR(I2CIP_ADS1115_ADDRESS) "]}]"} #define PEAPOD_MODULE_LIGHTING_PWM_CHANNEL PCA9685_CH0 #define PEAPOD_MODULE_LIGHTING_PWM_FQA I2CIP_FQA_CREATE(PEAPOD_WIRENUM, PEAPOD_MODULENUM_LIGHTING, 0, I2CIP_PCA9685_ADDRESS) @@ -14,7 +14,7 @@ #define PEAPOD_MODULE_LIGHTING_ADC_DELTA 100 #define PEAPOD_MODULE_LIGHTING_PWMCYCLE_DELTA 2500 #define PEAPOD_MODULE_LIGHTING_DELTA 86400000 // 24 hours -#define PEAPOD_MODULE_LIGHTING_PHASE (PEAPOD_MODULE_LIGHTING_DELTA / 2) // 12 hours +#define PEAPOD_MODULE_LIGHTING_PHASE 57600000 // 16 hours on #define PEAPOD_MODULE_LIGHTING_DUTY 0.5 // 50% Duty Cycle namespace PeaPod { diff --git a/software/next.config.ts b/software/next.config.ts new file mode 100644 index 00000000..69fb5490 --- /dev/null +++ b/software/next.config.ts @@ -0,0 +1,13 @@ +import type { NextConfig } from 'next'; + +console.log('Using next.config.ts with Node Version:', process.version); + +const nextConfig: NextConfig = { + /* config options here */ + typescript: { + tsconfigPath: './next.tsconfig.json', + }, + output: 'standalone', +}; + +export default nextConfig; diff --git a/software/next.tsconfig.json b/software/next.tsconfig.json new file mode 100644 index 00000000..2dc4b632 --- /dev/null +++ b/software/next.tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + }, + ], + "paths": { + "@/*": ["./src/*"], + "$/*": ["./api/*"] + }, + }, + "include": ["next-env.d.ts", "./src/**/*.ts", "./src/**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["./node_modules/**", "./api/**", "./lib/**", "serialport"] +} diff --git a/software/package-lock.json b/software/package-lock.json deleted file mode 100644 index 7795fb9c..00000000 --- a/software/package-lock.json +++ /dev/null @@ -1,6491 +0,0 @@ -{ - "name": "@peapodtech/peapodos", - "version": "0.1.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@peapodtech/peapodos", - "version": "0.1.1", - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.8", - "@peapodtech/firebasedeviceflow": "^0.2.0", - "@types/yargs": "^16.0.0", - "blessed": "^0.1.81", - "chalk": "^4.1.0", - "dotenv": "^10.0.0", - "firebase": "^9.3.0", - "jsonwebtoken": "^8.5.1", - "mqtt": "^4.2.8", - "onoff": "^6.0.3", - "ora": "^5.3.0", - "pi-camera": "^1.6.0", - "rxjs": "^6.6.3", - "serialport": "^10.4.0", - "uuid": "^8.3.2", - "wrtc": "^0.4.7", - "yargs": "^16.2.0" - }, - "bin": { - "peapodos": "index.js" - }, - "devDependencies": { - "@serialport/parser-readline": "^9.0.1", - "@types/blessed": "^0.1.19", - "@types/jest": "^27.5.0", - "@types/jsonwebtoken": "^8.5.5", - "@types/node": "^17.0.31", - "@types/pi-camera": "^1.5.3", - "@types/project-name-generator": "^2.1.0", - "@types/serialport": "^8.0.2", - "@types/uuid": "^8.3.0", - "jest": "^27.5.1", - "ts-jest": "^27.1.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", - "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.4", - "@babel/parser": "^7.24.4", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", - "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", - "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", - "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@firebase/analytics": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.0.tgz", - "integrity": "sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/installations": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/analytics-compat": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz", - "integrity": "sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==", - "dependencies": { - "@firebase/analytics": "0.10.0", - "@firebase/analytics-types": "0.8.0", - "@firebase/component": "0.6.4", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/analytics-types": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.0.tgz", - "integrity": "sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==" - }, - "node_modules/@firebase/app": { - "version": "0.9.13", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.13.tgz", - "integrity": "sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "idb": "7.1.1", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/app-check": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.0.tgz", - "integrity": "sha512-dRDnhkcaC2FspMiRK/Vbp+PfsOAEP6ZElGm9iGFJ9fDqHoPs0HOPn7dwpJ51lCFi1+2/7n5pRPGhqF/F03I97g==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/app-check-compat": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.7.tgz", - "integrity": "sha512-cW682AxsyP1G+Z0/P7pO/WT2CzYlNxoNe5QejVarW2o5ZxeWSSPAiVEwpEpQR/bUlUmdeWThYTMvBWaopdBsqw==", - "dependencies": { - "@firebase/app-check": "0.8.0", - "@firebase/app-check-types": "0.5.0", - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/app-check-interop-types": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", - "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" - }, - "node_modules/@firebase/app-check-types": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.0.tgz", - "integrity": "sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==" - }, - "node_modules/@firebase/app-compat": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.13.tgz", - "integrity": "sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==", - "dependencies": { - "@firebase/app": "0.9.13", - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/app-types": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", - "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" - }, - "node_modules/@firebase/auth": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.23.2.tgz", - "integrity": "sha512-dM9iJ0R6tI1JczuGSxXmQbXAgtYie0K4WvKcuyuSTCu9V8eEDiz4tfa1sO3txsfvwg7nOY3AjoCyMYEdqZ8hdg==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/auth-compat": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.4.2.tgz", - "integrity": "sha512-Q30e77DWXFmXEt5dg5JbqEDpjw9y3/PcP9LslDPR7fARmAOTIY9MM6HXzm9KC+dlrKH/+p6l8g9ifJiam9mc4A==", - "dependencies": { - "@firebase/auth": "0.23.2", - "@firebase/auth-types": "0.12.0", - "@firebase/component": "0.6.4", - "@firebase/util": "1.9.3", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/auth-compat/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@firebase/auth-compat/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/@firebase/auth-compat/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/@firebase/auth-compat/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/@firebase/auth-interop-types": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", - "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" - }, - "node_modules/@firebase/auth-types": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", - "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" - } - }, - "node_modules/@firebase/auth/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@firebase/auth/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/@firebase/auth/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/@firebase/auth/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/@firebase/component": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", - "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", - "dependencies": { - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/database": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.14.4.tgz", - "integrity": "sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==", - "dependencies": { - "@firebase/auth-interop-types": "0.2.1", - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "faye-websocket": "0.11.4", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/database-compat": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz", - "integrity": "sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/database": "0.14.4", - "@firebase/database-types": "0.10.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/database-types": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz", - "integrity": "sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==", - "dependencies": { - "@firebase/app-types": "0.9.0", - "@firebase/util": "1.9.3" - } - }, - "node_modules/@firebase/firestore": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.13.0.tgz", - "integrity": "sha512-NwcnU+madJXQ4fbLkGx1bWvL612IJN/qO6bZ6dlPmyf7QRyu5azUosijdAN675r+bOOJxMtP1Bv981bHBXAbUg==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "@firebase/webchannel-wrapper": "0.10.1", - "@grpc/grpc-js": "~1.7.0", - "@grpc/proto-loader": "^0.6.13", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=10.10.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/firestore-compat": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.12.tgz", - "integrity": "sha512-mazuNGAx5Kt9Nph0pm6ULJFp/+j7GSsx+Ncw1GrnKl+ft1CQ4q2LcUssXnjqkX2Ry0fNGqUzC1mfIUrk9bYtjQ==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/firestore": "3.13.0", - "@firebase/firestore-types": "2.5.1", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/firestore-types": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.5.1.tgz", - "integrity": "sha512-xG0CA6EMfYo8YeUxC8FeDzf6W3FX1cLlcAGBYV6Cku12sZRI81oWcu61RSKM66K6kUENP+78Qm8mvroBcm1whw==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" - } - }, - "node_modules/@firebase/firestore/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@firebase/firestore/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/@firebase/firestore/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/@firebase/firestore/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/@firebase/functions": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.10.0.tgz", - "integrity": "sha512-2U+fMNxTYhtwSpkkR6WbBcuNMOVaI7MaH3cZ6UAeNfj7AgEwHwMIFLPpC13YNZhno219F0lfxzTAA0N62ndWzA==", - "dependencies": { - "@firebase/app-check-interop-types": "0.3.0", - "@firebase/auth-interop-types": "0.2.1", - "@firebase/component": "0.6.4", - "@firebase/messaging-interop-types": "0.2.0", - "@firebase/util": "1.9.3", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/functions-compat": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.5.tgz", - "integrity": "sha512-uD4jwgwVqdWf6uc3NRKF8cSZ0JwGqSlyhPgackyUPe+GAtnERpS4+Vr66g0b3Gge0ezG4iyHo/EXW/Hjx7QhHw==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/functions": "0.10.0", - "@firebase/functions-types": "0.6.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/functions-types": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.0.tgz", - "integrity": "sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==" - }, - "node_modules/@firebase/functions/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@firebase/functions/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/@firebase/functions/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/@firebase/functions/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/@firebase/installations": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.4.tgz", - "integrity": "sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/util": "1.9.3", - "idb": "7.0.1", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/installations-compat": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.4.tgz", - "integrity": "sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/installations": "0.6.4", - "@firebase/installations-types": "0.5.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/installations-types": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.0.tgz", - "integrity": "sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==", - "peerDependencies": { - "@firebase/app-types": "0.x" - } - }, - "node_modules/@firebase/installations/node_modules/idb": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", - "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" - }, - "node_modules/@firebase/logger": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", - "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/messaging": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.4.tgz", - "integrity": "sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/installations": "0.6.4", - "@firebase/messaging-interop-types": "0.2.0", - "@firebase/util": "1.9.3", - "idb": "7.0.1", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/messaging-compat": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.4.tgz", - "integrity": "sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/messaging": "0.12.4", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/messaging-interop-types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz", - "integrity": "sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==" - }, - "node_modules/@firebase/messaging/node_modules/idb": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", - "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" - }, - "node_modules/@firebase/performance": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.4.tgz", - "integrity": "sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/installations": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/performance-compat": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.4.tgz", - "integrity": "sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/performance": "0.6.4", - "@firebase/performance-types": "0.2.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/performance-types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.0.tgz", - "integrity": "sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==" - }, - "node_modules/@firebase/remote-config": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.4.tgz", - "integrity": "sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/installations": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/remote-config-compat": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz", - "integrity": "sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/logger": "0.4.0", - "@firebase/remote-config": "0.4.4", - "@firebase/remote-config-types": "0.3.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/remote-config-types": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz", - "integrity": "sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==" - }, - "node_modules/@firebase/storage": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.11.2.tgz", - "integrity": "sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/util": "1.9.3", - "node-fetch": "2.6.7", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/storage-compat": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.2.tgz", - "integrity": "sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==", - "dependencies": { - "@firebase/component": "0.6.4", - "@firebase/storage": "0.11.2", - "@firebase/storage-types": "0.8.0", - "@firebase/util": "1.9.3", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/storage-types": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.0.tgz", - "integrity": "sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" - } - }, - "node_modules/@firebase/storage/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@firebase/storage/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/@firebase/storage/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/@firebase/storage/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/@firebase/util": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", - "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/webchannel-wrapper": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.1.tgz", - "integrity": "sha512-Dq5rYfEpdeel0bLVN+nfD1VWmzCkK+pJbSjIawGE+RY4+NIJqhbUDDQjvV0NUK84fMfwxvtFoCtEe70HfZjFcw==" - }, - "node_modules/@grpc/grpc-js": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", - "integrity": "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==", - "dependencies": { - "@grpc/proto-loader": "^0.7.0", - "@types/node": ">=12.12.47" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - } - }, - "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { - "version": "0.7.12", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz", - "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.4", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@grpc/grpc-js/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@grpc/grpc-js/node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "node_modules/@grpc/grpc-js/node_modules/protobufjs": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", - "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@grpc/grpc-js/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@grpc/grpc-js/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.6.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.13.tgz", - "integrity": "sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==", - "dependencies": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^6.11.3", - "yargs": "^16.2.0" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "dev": true, - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dev": true, - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dev": true, - "dependencies": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@peapodtech/firebasedeviceflow": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@peapodtech/firebasedeviceflow/-/firebasedeviceflow-0.2.0.tgz", - "integrity": "sha512-IOkEZt2P7hcMZX7a7URSvtOu4K3Bw8QhA2REsLHqBLywHkslm1dXx0PCZY12Dk2dBdMdY9iUWAfJtjANPWs5og==", - "dependencies": { - "@types/inquirer": "^7.3.1", - "axios": "^0.21.1", - "chalk": "^4.1.0", - "firebase": "^9.4.0", - "inquirer": "^7.3.3", - "ora": "^5.2.0", - "typescript": "^4.1.3" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@serialport/binding-mock": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", - "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", - "dependencies": { - "@serialport/bindings-interface": "^1.2.1", - "debug": "^4.3.3" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@serialport/bindings-cpp": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.8.0.tgz", - "integrity": "sha512-OMQNJz5kJblbmZN5UgJXLwi2XNtVLxSKmq5VyWuXQVsUIJD4l9UGHnLPqM5LD9u3HPZgDI5w7iYN7gxkQNZJUw==", - "hasInstallScript": true, - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "@serialport/parser-readline": "^10.2.1", - "debug": "^4.3.2", - "node-addon-api": "^5.0.0", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=12.17.0 <13.0 || >=14.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.5.0.tgz", - "integrity": "sha512-/uR/yT3jmrcwnl2FJU/2ySvwgo5+XpksDUR4NF/nwTS5i3CcuKS+FKi/tLzy1k8F+rCx5JzpiK+koqPqOUWArA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.5.0.tgz", - "integrity": "sha512-0aXJknodcl94W9zSjvU+sLdXiyEG2rqjQmvBWZCr8wJZjWEtv3RgrnYiWq4i2OTOyC8C/oPK8ZjpBjQptRsoJQ==", - "dependencies": { - "@serialport/parser-delimiter": "10.5.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/bindings-interface": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", - "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", - "engines": { - "node": "^12.22 || ^14.13 || >=16" - } - }, - "node_modules/@serialport/parser-byte-length": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.5.0.tgz", - "integrity": "sha512-eHhr4lHKboq1OagyaXAqkemQ1XyoqbLQC8XJbvccm95o476TmEdW5d7AElwZV28kWprPW68ZXdGF2VXCkJgS2w==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-cctalk": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.5.0.tgz", - "integrity": "sha512-Iwsdr03xmCKAiibLSr7b3w6ZUTBNiS+PwbDQXdKU/clutXjuoex83XvsOtYVcNZmwJlVNhAUbkG+FJzWwIa4DA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-delimiter": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-9.2.4.tgz", - "integrity": "sha512-4nvTAoYAgkxFiXrkI+3CA49Yd43CODjeszh89EK+I9c8wOZ+etZduRCzINYPiy26g7zO+GRAb9FoPCsY+sYcbQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-inter-byte-timeout": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.5.0.tgz", - "integrity": "sha512-WPvVlSx98HmmUF9jjK6y9mMp3Wnv6JQA0cUxLeZBgS74TibOuYG3fuUxUWGJALgAXotOYMxfXSezJ/vSnQrkhQ==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-packet-length": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.5.0.tgz", - "integrity": "sha512-jkpC/8w4/gUBRa2Teyn7URv1D7T//0lGj27/4u9AojpDVXsR6dtdcTG7b7dNirXDlOrSLvvN7aS5/GNaRlEByw==", - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@serialport/parser-readline": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-9.2.4.tgz", - "integrity": "sha512-Z1/qrZTQUVhNSJP1hd9YfDvq0o7d87rNwAjjRKbVpa7Qi51tG5BnKt43IV3NFMyBlVcRe0rnIb3tJu57E0SOwg==", - "dev": true, - "dependencies": { - "@serialport/parser-delimiter": "9.2.4" - }, - "engines": { - "node": ">=10.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-ready": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.5.0.tgz", - "integrity": "sha512-QIf65LTvUoxqWWHBpgYOL+soldLIIyD1bwuWelukem2yDZVWwEjR288cLQ558BgYxH4U+jLAQahhqoyN1I7BaA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.5.0.tgz", - "integrity": "sha512-9jnr9+PCxRoLjtGs7uxwsFqvho+rxuJlW6ZWSB7oqfzshEZWXtTJgJRgac/RuLft4hRlrmRz5XU40i3uoL4HKw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-slip-encoder": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.5.0.tgz", - "integrity": "sha512-wP8m+uXQdkWSa//3n+VvfjLthlabwd9NiG6kegf0fYweLWio8j4pJRL7t9eTh2Lbc7zdxuO0r8ducFzO0m8CQw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-spacepacket": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.5.0.tgz", - "integrity": "sha512-BEZ/HAEMwOd8xfuJSeI/823IR/jtnThovh7ils90rXD4DPL1ZmrP4abAIEktwe42RobZjIPfA4PaVfyO0Fjfhg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/stream": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.5.0.tgz", - "integrity": "sha512-gbcUdvq9Kyv2HsnywS7QjnEB28g+6OGB5Z8TLP7X+UPpoMIWoUsoQIq5Kt0ZTgMoWn3JGM2lqwTsSHF+1qhniA==", - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "debug": "^4.3.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/blessed": { - "version": "0.1.25", - "resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.25.tgz", - "integrity": "sha512-kQsjBgtsbJLmG6CJA+Z6Nujj+tq1fcSE3UIowbDvzQI4wWmoTV7djUDhSo5lDjgwpIN0oRvks0SA5mMdKE5eFg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", - "dependencies": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "27.5.2", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", - "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", - "dev": true, - "dependencies": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - } - }, - "node_modules/@types/jsonwebtoken": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", - "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" - }, - "node_modules/@types/pi-camera": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/pi-camera/-/pi-camera-1.5.5.tgz", - "integrity": "sha512-H6DI1FKHkKwNwvSELUTxQQ+OMMwjSZ1w6UaG/sQ3dcXFwmJXrQvh+h7AQxXZgcu1NYy/4X4vej9wtHB7sH6yGQ==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, - "node_modules/@types/project-name-generator": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@types/project-name-generator/-/project-name-generator-2.1.3.tgz", - "integrity": "sha512-uomLFTmq6/eyLlGLTw1KPcdAMes8WPqcPtoZIA3FaJ6fJR3snXqY3TmIzsKNa2b4WV5ZaN5brh2xgGkWKLGPoQ==", - "dev": true - }, - "node_modules/@types/serialport": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/@types/serialport/-/serialport-8.0.5.tgz", - "integrity": "sha512-Lw082WIy46fYLnOzyf+8QN/vZaR3d9ol9WNyEGHsKJJ1pmZogFbloHGbnXyNcxfV9aTbgviWU8jktrIjRheYFQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/through": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", - "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, - "node_modules/babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dev": true, - "dependencies": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/blessed": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", - "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", - "bin": { - "blessed": "bin/tput.js" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001606", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001606.tgz", - "integrity": "sha512-LPbwnW4vfpJId225pwjZJOgX1m9sGfbw/RKJvw/t0QhYOOaTXHvkjVGFGPpvwEzufrjvTlsULnVTxdy4/6cqkg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", - "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", - "dependencies": { - "leven": "^2.1.0", - "minimist": "^1.1.0" - } - }, - "node_modules/commist/node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "engines": { - "node": ">=10" - } - }, - "node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.729", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz", - "integrity": "sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/epoll": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/epoll/-/epoll-4.0.2.tgz", - "integrity": "sha512-4KtBBSBYAfyta2/4TIFT4P7pZdDjU6UPvdzWcF4Fy6Q300V08Wdj/82ovAZBUDra6TTjsKsfy12Qxuwc3ob+Qw==", - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.17.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/firebase": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.23.0.tgz", - "integrity": "sha512-/4lUVY0lUvBDIaeY1q6dUYhS8Sd18Qb9CgWkPZICUo9IXpJNCEagfNZXBBFCkMTTN5L5gx2Hjr27y21a9NzUcA==", - "dependencies": { - "@firebase/analytics": "0.10.0", - "@firebase/analytics-compat": "0.2.6", - "@firebase/app": "0.9.13", - "@firebase/app-check": "0.8.0", - "@firebase/app-check-compat": "0.3.7", - "@firebase/app-compat": "0.2.13", - "@firebase/app-types": "0.9.0", - "@firebase/auth": "0.23.2", - "@firebase/auth-compat": "0.4.2", - "@firebase/database": "0.14.4", - "@firebase/database-compat": "0.3.4", - "@firebase/firestore": "3.13.0", - "@firebase/firestore-compat": "0.3.12", - "@firebase/functions": "0.10.0", - "@firebase/functions-compat": "0.3.5", - "@firebase/installations": "0.6.4", - "@firebase/installations-compat": "0.2.4", - "@firebase/messaging": "0.12.4", - "@firebase/messaging-compat": "0.2.4", - "@firebase/performance": "0.6.4", - "@firebase/performance-compat": "0.2.4", - "@firebase/remote-config": "0.4.4", - "@firebase/remote-config-compat": "0.2.4", - "@firebase/storage": "0.11.2", - "@firebase/storage-compat": "0.3.2", - "@firebase/util": "1.9.3" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/help-me": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", - "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", - "dependencies": { - "glob": "^7.1.6", - "readable-stream": "^3.6.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/idb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dev": true, - "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dev": true, - "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dev": true, - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dev": true, - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dev": true, - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mqtt": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.8.tgz", - "integrity": "sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw==", - "dependencies": { - "commist": "^1.0.0", - "concat-stream": "^2.0.0", - "debug": "^4.1.1", - "duplexify": "^4.1.1", - "help-me": "^3.0.0", - "inherits": "^2.0.3", - "lru-cache": "^6.0.0", - "minimist": "^1.2.5", - "mqtt-packet": "^6.8.0", - "number-allocator": "^1.0.9", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "reinterval": "^1.1.0", - "rfdc": "^1.3.0", - "split2": "^3.1.0", - "ws": "^7.5.5", - "xtend": "^4.0.2" - }, - "bin": { - "mqtt": "bin/mqtt.js", - "mqtt_pub": "bin/pub.js", - "mqtt_sub": "bin/sub.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/mqtt-packet": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", - "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", - "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/mqtt/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mqtt/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/number-allocator": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", - "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", - "dependencies": { - "debug": "^4.3.1", - "js-sdsl": "4.3.0" - } - }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", - "dev": true - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/onoff": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/onoff/-/onoff-6.0.3.tgz", - "integrity": "sha512-xtVlwRDzswYM69bzzIui/qzu7QHsFnjsQiCV1iYVA/HXt5xdc9utc97SYAlXzK8wAhIN7+H7MaVqh2vpfdKk9A==", - "dependencies": { - "epoll": "^4.0.1", - "lodash.debounce": "^4.0.8" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/pi-camera": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/pi-camera/-/pi-camera-1.7.0.tgz", - "integrity": "sha512-96LdjJeh/1sZKYdBugURUnhoTdIUu5u8oDbVW2EFtxiGpz6Vs4nATmQHY4jibopZNfDhH9He9j0iDmS6w0L2/Q==", - "engines": { - "node": ">=6.10.0" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/protobufjs": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", - "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/reinterval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", - "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/serialport": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.5.0.tgz", - "integrity": "sha512-7OYLDsu5i6bbv3lU81pGy076xe0JwpK6b49G6RjNvGibstUqQkI+I3/X491yBGtf4gaqUdOgoU1/5KZ/XxL4dw==", - "dependencies": { - "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "10.8.0", - "@serialport/parser-byte-length": "10.5.0", - "@serialport/parser-cctalk": "10.5.0", - "@serialport/parser-delimiter": "10.5.0", - "@serialport/parser-inter-byte-timeout": "10.5.0", - "@serialport/parser-packet-length": "10.5.0", - "@serialport/parser-readline": "10.5.0", - "@serialport/parser-ready": "10.5.0", - "@serialport/parser-regex": "10.5.0", - "@serialport/parser-slip-encoder": "10.5.0", - "@serialport/parser-spacepacket": "10.5.0", - "@serialport/stream": "10.5.0", - "debug": "^4.3.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/serialport/node_modules/@serialport/parser-delimiter": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.5.0.tgz", - "integrity": "sha512-/uR/yT3jmrcwnl2FJU/2ySvwgo5+XpksDUR4NF/nwTS5i3CcuKS+FKi/tLzy1k8F+rCx5JzpiK+koqPqOUWArA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/serialport/node_modules/@serialport/parser-readline": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.5.0.tgz", - "integrity": "sha512-0aXJknodcl94W9zSjvU+sLdXiyEG2rqjQmvBWZCr8wJZjWEtv3RgrnYiWq4i2OTOyC8C/oPK8ZjpBjQptRsoJQ==", - "dependencies": { - "@serialport/parser-delimiter": "10.5.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-jest": { - "version": "27.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", - "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^27.0.0", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "20.x" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@types/jest": "^27.0.0", - "babel-jest": ">=27.0.0 <28", - "jest": "^27.0.0", - "typescript": ">=3.8 <5.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@types/jest": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/wrtc": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz", - "integrity": "sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g==", - "bundleDependencies": [ - "node-pre-gyp" - ], - "hasInstallScript": true, - "dependencies": { - "node-pre-gyp": "^0.13.0" - }, - "engines": { - "node": "^8.11.2 || >=10.0.0" - }, - "optionalDependencies": { - "domexception": "^1.0.1" - } - }, - "node_modules/wrtc/node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "deprecated": "Use your platform's native DOMException instead", - "optional": true, - "dependencies": { - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/wrtc/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - } - } -} diff --git a/software/package.json b/software/package.json index f66944a3..118ddf6a 100644 --- a/software/package.json +++ b/software/package.json @@ -7,12 +7,14 @@ "lib": "lib" }, "scripts": { - "test": "npm run build && jest", - "dryrun": "npm run build && node . simulate offline", - "offline": "npm run build && node . offline", - "simulate": "npm run build && node . simulate", - "build": "npm i && tsc && clear", - "start": "node ." + "dev": "yarn buildapi && yarn start", + "devsim": "yarn buildapi && yarn startsim", + "build": "yarn upgrade && next build", + "buildapi": "rm -f ./api/*.mjs ./server.mjs && yarn install && node ./api.esbuild.mjs", + "start": "node ./server.mjs", + "startsim": "node ./server.mjs --simulator", + "lint": "next lint --fix", + "deploy_functions": "firebase deploy --only functions" }, "repository": { "type": "git", @@ -30,36 +32,76 @@ }, "homepage": "https://github.com/PeaPodTechnologies/PeaPod#readme", "dependencies": { + "@emotion/cache": "^11.14.0", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@inquirer/prompts": "^8.1.0", "@mapbox/node-pre-gyp": "^1.0.8", + "@mui/icons-material": "^7.3.1", + "@mui/material": "^7.3.1", + "@mui/material-nextjs": "^7.3.0", + "@mui/x-charts": "^8.10.0", + "@mui/x-data-grid": "^8.10.1", "@peapodtech/firebasedeviceflow": "^0.2.0", + "@tailwindcss/postcss": "^4.1.11", "@types/yargs": "^16.0.0", + "@xyflow/react": "^12.8.4", + "autoprefixer": "^10.4.21", "blessed": "^0.1.81", - "chalk": "^4.1.0", - "dotenv": "^10.0.0", - "firebase": "^9.3.0", + "chalk": "^5.4.1", + "debug": "^4.4.0", + "dotenv": "^17.2.3", + "firebase": "^12.7.0", + "jest": "^29.7.0", "jsonwebtoken": "^8.5.1", "mqtt": "^4.2.8", + "next": "15.1.3", "onoff": "^6.0.3", "ora": "^5.3.0", "pi-camera": "^1.6.0", + "postcss": "^8.5.6", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rxjs": "^6.6.3", "serialport": "^10.4.0", - "uuid": "^8.3.2", - "wrtc": "^0.4.7", - "yargs": "^16.2.0" + "socket.io-client": "^4.8.1", + "tailwindcss": "^4.1.11", + "ts-jest": "^29.2.5", + "uuid": "^11.0.3", + "yargs": "^17.7.2" }, "devDependencies": { + "@eslint/eslintrc": "^3", + "@eslint/js": "^9.17.0", + "@serialport/binding-mock": "^10.2.2", + "@serialport/bindings-cpp": "^13.0.1", "@serialport/parser-readline": "^9.0.1", - "@types/blessed": "^0.1.19", - "@types/jest": "^27.5.0", + "@types/blessed": "^0.1.25", + "@types/debug": "^4.1.12", + "@types/jest": "^29.5.14", "@types/jsonwebtoken": "^8.5.5", - "@types/node": "^17.0.31", + "@types/node": "^20", "@types/pi-camera": "^1.5.3", "@types/project-name-generator": "^2.1.0", + "@types/react": "^19", + "@types/react-dom": "^19", "@types/serialport": "^8.0.2", - "@types/uuid": "^8.3.0", - "jest": "^27.5.1", - "ts-jest": "^27.1.3" + "@types/uuid": "^10.0.0", + "@types/yargs": "^17.0.33", + "@typescript-eslint/eslint-plugin": "^8.19.1", + "@typescript-eslint/parser": "^8.0.0", + "esbuild": "^0.24.2", + "esbuild-plugin-file-path-extensions": "^2.1.4", + "eslint": "^9.17.0", + "eslint-config-next": "15.1.3", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-react": "^7.37.3", + "globals": "^15.14.0", + "ora": "^8.1.1", + "prettier": "3.4.2", + "socket.io": "^4.8.1", + "typescript": "^5", + "typescript-eslint": "^8.19.0" }, "publishConfig": { "access": "public" diff --git a/software/postcss.config.mjs b/software/postcss.config.mjs new file mode 100644 index 00000000..f598fedd --- /dev/null +++ b/software/postcss.config.mjs @@ -0,0 +1,7 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; + diff --git a/software/public/file.svg b/software/public/file.svg new file mode 100644 index 00000000..004145cd --- /dev/null +++ b/software/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/software/public/globe.svg b/software/public/globe.svg new file mode 100644 index 00000000..567f17b0 --- /dev/null +++ b/software/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/software/public/next.svg b/software/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/software/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/software/public/vercel.svg b/software/public/vercel.svg new file mode 100644 index 00000000..77053960 --- /dev/null +++ b/software/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/software/public/window.svg b/software/public/window.svg new file mode 100644 index 00000000..b2b2a44f --- /dev/null +++ b/software/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/software/server.ts b/software/server.ts new file mode 100644 index 00000000..b65bcb7e --- /dev/null +++ b/software/server.ts @@ -0,0 +1,393 @@ +import { hostname } from 'os'; +import { lookup } from 'dns'; + +import { createServer } from 'http'; +import { Server } from 'socket.io'; + +import next from 'next'; +import nextConfig from './next.config'; + +import { findSerialPort, MicroController, CONTROLLER_REVISION, Controller, SimulatedController, SimulatorConfig } from './api/controller'; +// import { pushDebugMessage, pushDebugMessages } from './api/firebase'; +import ui, { _logRedirect, _errRedirect } from './api/ui'; +import {DebugJsonSerialportError} from './api/errors'; +import { appendFileSync } from 'fs'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { getApp, initializeApp } from 'firebase/app'; +import { getAuth } from 'firebase/auth'; +import { DeviceFlowUI } from '@peapodtech/firebasedeviceflow'; +import { DebugJsonInstruction } from './api/types'; +import checkbox from '@inquirer/checkbox'; +import { ipv4Lookup } from './api/utils'; +import { pushDebugMessages } from './api/firebase'; + +enum PublishingMode { + FIREBASE = 'Firebase (CloudPonics)', + LOCAL = 'Local (Filesystem)' +} + +// Handle Command-Line Arguments +const argv = yargs(hideBin(process.argv)) + .option('simulator', { + alias: 's', + type: 'boolean', + describe: 'Run against the simulated controller' + }) + .option('port', { + alias: 'p', + type: 'number', + default: 3001, + describe: 'HTTP port' + }) + .option('host', { + alias: 'h', + type: 'string', + default: 'localhost', + describe: 'Hostname to bind' + }) + .option('serialport', { + alias: 'd', + type: 'string', + describe: 'SerialPort device path (overrides auto-detection)', + default: process.env.SERIALPORT || undefined + }) + .help().parse(); + +type Linker = { + instruction: DebugJsonInstruction, + key: string, + cast: 'string' | 'number' | 'boolean', + eval: string +}; + +// Redirect console.log/.error calls to ui.log/.err +console.log = _logRedirect; +console.error = _errRedirect; + +// Options: SerialPort Message Logging +// const _msg_print_delta = 1; +let _msg_print_count = 0; + +const schedule: { [key: string]: NodeJS.Timeout } = {}; // { [key: instruction string]: NodeJS.Timeout} - Scheduled tasks +const linker: { + [key: string]: Linker[] +} = {}; +// var linker = {encoder: [{key: 's', cast: 'number', eval: 'value * (-15)', instruction: {type: 'command', data: {fqa: 8183, b: 3}}}]}; // { [key: `telemetry key`]: { `instruction`: object, `key`: string, `cast`: 'string' | 'number' | 'boolean' }[] } - On `telemetry key`, send {...`instruction`, [`key`]: telemetry value as `cast`} + +const linkerTyper = (value, cast) => { + if(cast === 'string') return String(value); + if(cast === 'number') return Number(value); + if(cast === 'boolean') return Boolean(value); + return undefined; +}; + +const linkerChecker = (value, cast) => { + if(cast === 'string') return typeof value === 'string'; + if(cast === 'number') return typeof value === 'number' && !isNaN(value); + if(cast === 'boolean') return typeof value === 'boolean'; + return false; +}; + +const linkerEvaluator = (value, evalStr) => { + if(!evalStr || typeof evalStr !== 'string') return value; + try { + return eval(evalStr.replace(/value/g, value)); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (err) { + return undefined; + } +}; + +const SIMULATOR_CONFIG: SimulatorConfig = { + 'temperature': { min: 20, max: 30, interval: 1000 }, + 'humidity': { min: 30, max: 70, interval: 1500 }, +}; + +const findController = (simulator?: boolean): Promise => { + if(simulator) return Promise.resolve(new SimulatedController(SIMULATOR_CONFIG)); + ui.start('SerialPort: Scanning...'); + findSerialPort('usbserial').then((ports) => { + if(ports.length === 0) { throw new DebugJsonSerialportError('No SerialPorts Found!'); } + + ui.succeed(`SerialPorts[${ports.length}]`); + ports.forEach((ser, i) => { + console.info(`SerialPort[${i}]: ${ser}`); + return new MicroController(ser); + }); + }); +}; + +// ===== MAIN ===== +(async () => { + // 1. IPv4 Lookup + ui.start('IPv4: Lookup...'); + const host = await ipv4Lookup(); + ui.succeed(`IPv4: ${host}`); + + const hostname = host || argv.host || 'localhost'; + const port = argv.port || 3001; + + // 2. Next.JS App and HTTP Server + ui.start('Next.JS: Preparing...'); + const app = next({ dev: (process.env.NODE_ENV !== 'production'), hostname, port, conf: nextConfig }); + const handler = app.getRequestHandler(); + const server = createServer((req, res) => { + handler(req, res); + }); + await app.prepare(); + ui.succeed('Next.JS: Ready!'); + + await new Promise(r => server.listen(port, host, () => { + ui.info(`HTTP Server Listening: http://${host}:${port}/`); + r(); + })); + + // 3. WebSockets + const io = new Server(server, { + cors: { + origin: '*', + }, + }); + + // 4. Select & Prepare Publishing Modes + const pms = await checkbox({ + message: 'Select publishing modes:', + choices: Object.entries(PublishingMode).map(pm => ({ + // Object.entries casts `PublishingMode` values to `string`. This recasts them to PublishingMode enum values (TypeScript magic) + value: PublishingMode[pm[0] as keyof typeof PublishingMode], + name: pm[1] + })), + }); + + // 4A. Firebase w/ Auth + if(pms.includes(PublishingMode.FIREBASE)) { + const firebaseApp = initializeApp({ + apiKey: process.env.FIREBASE_APIKEY, + authDomain: process.env.FIREBASE_AUTHDOMAIN, + projectId: process.env.FIREBASE_PROJECTID, + storageBucket: process.env.FIREBASE_STORAGEBUCKET, + messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID, + appId: process.env.FIREBASE_APPID, + measurementId: process.env.FIREBASE_MEASUREMENTID + }); + + const auth = getAuth(firebaseApp); + + const deviceFlowUI = new DeviceFlowUI(firebaseApp, { + Google : { + clientid: process.env.GOOGLE_CLIENTID, + clientsecret: process.env.GOOGLE_CLIENTSECRET, + scopes: process.env.GOOGLE_SCOPES?.split(' ') + }, + GitHub : { + clientid: process.env.GITHUB_CLIENTID, + clientsecret: process.env.GITHUB_CLIENTSECRET, + scopes: process.env.GITHUB_SCOPES?.split(' ') + } + }); + + await deviceFlowUI.signIn().then((user) => { + ui.succeed(`Firebase Auth: Signed in as ${user.email}!`); + }); + } + + // 5. SerialPort DebugJson Controller + const controller = await findController(argv.simulator || false); + controller.start((messages) => { + + // Message Handling + messages.forEach((msg) => { + + // 1. Emit to WebSocket Clients + io.emit('microcontroller', msg); + + // 2. (aka 4B.) Local Filesystem Logging + if(pms.includes(PublishingMode.LOCAL)) appendFileSync(`logs/${argv.simulator ? 'simulator_' : ''}${(new Date()).toISOString().split('T')[0]}.txt`, JSON.stringify(msg) + '\n'); + + // LINKER HANDLER + if(msg.data) { + Object.keys(linker).forEach((key) => { + linker[key].forEach((item) => { + if(msg.data[key] !== undefined) { + const evalStr = item.eval; + const cast = item.cast; + const value = linkerTyper(linkerEvaluator(msg.data[key], evalStr), cast); + if(!linkerChecker(value, cast)) { + ui.fail(`LINKER ERROR: Invalid Type ${cast} for ${key} -> ${value} (${typeof value})`); + return; // Next key + } + const instruction = { ...item.instruction, data: { ...item.instruction.data, [item.key]: value } }; + ui.info(`LINKER: ${key}: ${msg.data[key]} -> '${evalStr}' as ${cast} = ${value} -> ${JSON.stringify(instruction)}`); + try { + controller.write(instruction); + } catch (err) { + ui.fail(`LINKER ERROR: ${err}`); + io.emit('server', {type: 'error', msg: `Linker TX Error: ${err}`}); + return; // Next key + } + } + }); + }); + } + }); + + // 3. (aka 4Aii.) Push to Firebase Realtime Database + if(pms.includes(PublishingMode.FIREBASE)) pushDebugMessages(messages, 'microcontroller'); + + // if (_msg_print_count % _msg_print_delta === 0) { + ui.info(`CONTROLLER JSON[${_msg_print_count}]: ${JSON.stringify(messages[0])}`); + // } + _msg_print_count++; + + // const timestamp = messages.reduce((max, msg) => ((msg['timestamp'] ? (msg['timestamp'] > max ? msg['timestamp'] : max) : max)), 0); + // if(timestamp) microcontroller.write({ + // type: 'command', + // data: { + // 'fqa': 8183, + // 's': Math.floor(timestamp / 1000), + // 'b': 2 + // } + // }); + + }).catch((err) => { + io.emit('server', {type: 'error', msg: `Lost Controller: ${err}`}); + ui.fail(err); + // io.off('connection', handleSocketConnection); + // microcontroller.reset(); + }).then(() => { + ui.succeed('I2CIP.js Ready!'); + + // 6. Socket.IO Connection Handler + io.on('connection', (socket) => { + ui.log('Socket.IO ++'); + + setTimeout(() => { + ui.info(`Socket.IO: ${socket.id}`); + socket.emit('json', { type: 'info', msg: 'Server Start', _socket: 'server' }); // Open subsocket 'server' + socket.emit('json', { type: 'revision', msg: 'Microcontroller Revision Match', data: CONTROLLER_REVISION, _socket: 'microcontroller' }); // Open subsocket 'microcontroller' + }, 1000); + + socket.on('disconnect', () => { + ui.log('Socket.IO --'); + }); + + // SERIAL INPUT SOCKET HANDLERS + + socket.on('serialinput', (data: DebugJsonInstruction, callback: (error?: {error: string}) => void) => { + ui.info(`CONTROLLER INPUT: ${JSON.stringify(data)}`); + if(!data || !data.type || !data.data) { + ui.fail('CONTROLLER INPUT ERROR: Invalid Data'); + socket.emit('server', {type: 'error', msg: 'Controller Input Error: Invalid Data'}); + callback({error: 'Invalid Serial Data'}); + return; + } + try { + controller.write({type: data.type, data: data.data}); + } catch (err) { + ui.fail(`CONTROLLER INPUT ERROR: ${err}`); + + socket.emit('server', {type: 'error', msg: `Controller TX Error: ${err}`}); + callback({error: `Controller TX Error: ${err}`}); + return; + } + callback(); + }); + + // SCHEDULER SOCKET HANDLERS + + socket.on('scheduler-post', (data: { interval: number, instruction: DebugJsonInstruction }, callback: (error?: {error: string}) => void) => { + ui.info(`SCHEDULER POST: ${JSON.stringify(data)}`); + if(!data || !data.interval || !data.instruction || !data.instruction.type || !data.instruction.data) { + ui.fail('SCHEDULER POST ERROR: Invalid Data'); + socket.emit('server', {type: 'error', msg: 'Scheduler Post Error: Invalid Data'}); + callback({error: 'Invalid Scheduler Data'}); + return; + } + const schedulerLabel = JSON.stringify(data.instruction); + if(schedule[schedulerLabel]) { + ui.info(`CONTROLLER INPUT SCHEDULE CLEARED: ${schedulerLabel}`); + clearInterval(schedule[schedulerLabel]); + delete schedule[schedulerLabel]; + } + if(typeof data.interval === 'number' && data.interval >= 100) { + ui.info(`CONTROLLER INPUT SCHEDULE: ${schedulerLabel} @${data.interval}ms`); + schedule[schedulerLabel] = setInterval(() => { + ui.info(`CONTROLLER INPUT SCHEDULE: ${schedulerLabel} @${data.interval}ms`); + try { + controller.write({type: data.instruction.type, data: data.instruction.data}); + } catch (err) { + ui.fail(`CONTROLLER INPUT ERROR: ${err}`); + socket.emit('server', {type: 'error', msg: `Controller TX Error: ${err}`}); + clearInterval(schedule[schedulerLabel]); + delete schedule[schedulerLabel]; + + callback({error: `Controller TX Error: ${err}`}); + return; + } + }, data.interval); + callback(); + } + }); + + socket.on('scheduler-get', (data: unknown, callback: (keys: string[]) => void) => { + ui.info('SCHEDULER GET'); + callback(Object.keys(schedule)); + }); + + socket.on('scheduler-clear', (key: string, callback: (error?: {error: string}) => void) => { + ui.info(`SCHEDULER CLEAR: ${key}`); + if(!schedule[key]) { + ui.fail(`SCHEDULER CLEAR ERROR: No such key "${key}"`); + socket.emit('server', {type: 'error', msg: `Scheduler Clear Error: No such key "${key}"`}); + callback({error: `No such key "${key}"`}); + return; + } + clearInterval(schedule[key]); + delete schedule[key]; + callback(); + }); + + // LINKER SOCKET HANDLERS + + socket.on('linker-post', (data: Linker & {label: string}, callback: (error?: {error: string}) => void) => { + ui.info(`LINKER POST: ${JSON.stringify(data)}`); + if(!data || !data.label || !data.key || !data.cast || !data.instruction || !data.eval || !data.instruction.type || !data.instruction.data) { + ui.fail('LINKER POST ERROR: Invalid Data'); + socket.emit('server', {type: 'error', msg: 'Linker Post Error: Invalid Data'}); + callback({error: 'Invalid Linker Data'}); + return; + } + if(!linker[data.label]) linker[data.label] = []; + linker[data.label].push({ + instruction: { + type: data.instruction.type, + data: { + ...data.instruction.data, + [data.key]: undefined + } + }, + key: data.key, + eval: data.eval, + cast: data.cast + }); + callback(); + }); + + socket.on('linker-get', (data: unknown, callback: (linker: {[key: string]: Linker[]}) => void) => { + ui.info('LINKER GET'); + callback(linker); + }); + + socket.on('linker-clear', (label: string, instruction: DebugJsonInstruction, callback: (error?: {error: string}) => void) => { + ui.info(`LINKER CLEAR: ${label} ${JSON.stringify(instruction)}`); + if(linker[label]) { + linker[label] = linker[label].filter((item) => { + return !(item.instruction.type === instruction.type && JSON.stringify(item.instruction.data) === JSON.stringify(instruction.data)); + }); + } + callback(); + }); + }); + }); +})(); diff --git a/software/src/app/favicon.ico b/software/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..89dfd8936dbae4dc23b2436034dcde4df059f346 GIT binary patch literal 109804 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 uYBagm<$3uo2^bEr2&ygT!JH~;5Ffz!iJ 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 { + return ( + + +

    + + {children} + +
    +