Compare commits

..

22 Commits

Author SHA1 Message Date
Siwat Sirichai fb61fa85f3 2.10.0, full support for Digital Input/Output cards 2024-11-04 00:26:20 +07:00
Siwat Sirichai a22c8ef55c card not installed handling 2024-11-03 22:32:36 +07:00
Siwat Sirichai 268218f546 input (not working) 2024-11-03 21:51:35 +07:00
Siwat Sirichai 09aee8d5f8 digital output card 2024-11-03 13:33:10 +07:00
Siwat Sirichai 85f789e216 enable default static IP if there are no ip in fram 2024-10-11 22:56:31 +07:00
Siwat Sirichai 5bce34bc5e fix networking stack crash 2024-10-10 19:07:23 +07:00
Siwat Sirichai d276e6d255 initial network config 2024-10-08 23:32:37 +07:00
Siwat Sirichai 8ea85254a0 add uptime feature 2024-10-07 15:08:12 +07:00
Siwat Sirichai 520fd76b91 Update ESPMegaCommon.hpp 2024-06-28 22:17:13 +07:00
Siwat Sirichai c2e45490b0 energy load error handling 2024-06-28 22:16:44 +07:00
Siwat Sirichai 8aeb6ab30a improve debounce 2024-06-21 23:11:08 +07:00
Siwat Sirichai 4ea2122f6e allow input preload and state toggling 2024-06-21 21:34:51 +07:00
Siwat Sirichai b778fb6a02 Update DigitalInputCard.cpp 2024-06-21 21:02:11 +07:00
Siwat Sirichai 221d8e59f5 Update ESPMegaCommon.hpp 2024-06-21 20:57:53 +07:00
Siwat Sirichai d0ce4bf391 change debouce logic 2024-06-21 16:35:26 +07:00
Siwat Sirichai b62ef700ed working timezone config 2024-05-22 00:06:12 +07:00
Siwat Sirichai b30cfde0b0 working ntp without offset 2024-05-21 23:50:21 +07:00
Siwat Sirichai 64d6c4fab3 Update ESPMegaCommon.hpp 2024-05-20 01:29:36 +07:00
Siwat Sirichai 2430b43f77 info request 2024-05-20 01:25:25 +07:00
Siwat Sirichai 3e7d3041f2 mqtt summary request 2024-05-20 00:53:09 +07:00
Siwat Sirichai ea49576b20 global state request 2024-05-20 00:44:18 +07:00
Siwat Sirichai 6b67591cc1 Update ESPMegaCommon.hpp 2024-05-20 00:13:43 +07:00
16 changed files with 876 additions and 478 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.DS_Store .DS_Store
/ESPMegaPRO-OS-SDK

View File

@ -84,6 +84,9 @@ void CurrentTransformerCard::saveEnergy(){
} }
void CurrentTransformerCard::loadEnergy(){ void CurrentTransformerCard::loadEnergy(){
this->fram->read(this->framAddress, (uint8_t*)&this->energy, sizeof(this->energy)); this->fram->read(this->framAddress, (uint8_t*)&this->energy, sizeof(this->energy));
if (this->energy < 0 || isnan(this->energy)) {
this->energy = 0;
}
} }
void CurrentTransformerCard::setEnergyAutoSave(bool autoSave){ void CurrentTransformerCard::setEnergyAutoSave(bool autoSave){

View File

@ -57,12 +57,16 @@ bool DigitalInputCard::begin()
{ {
this->inputBankA = PCF8574(this->address_a); this->inputBankA = PCF8574(this->address_a);
this->inputBankB = PCF8574(this->address_b); this->inputBankB = PCF8574(this->address_b);
if (!this->inputBankA.begin()) { if (!this->inputBankA.begin())
{
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Failed to install input bank A"); ESP_LOGE("DigitalInputCard", "Input Card ERROR: Failed to install input bank A");
this->initOk = false;
return false; return false;
} }
if (!this->inputBankB.begin()) { if (!this->inputBankB.begin())
{
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Failed to install input bank B"); ESP_LOGE("DigitalInputCard", "Input Card ERROR: Failed to install input bank B");
this->initOk = false;
return false; return false;
} }
// Set the debounce time for all pins to 50ms // Set the debounce time for all pins to 50ms
@ -77,6 +81,7 @@ bool DigitalInputCard::begin()
this->pinMap[i] = i; this->pinMap[i] = i;
this->virtualPinMap[i] = i; this->virtualPinMap[i] = i;
} }
this->initOk = true;
return true; return true;
} }
@ -100,6 +105,11 @@ bool DigitalInputCard::digitalRead(uint8_t pin)
*/ */
bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh) bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh)
{ {
if(!this->initOk)
{
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Card not initialized");
return false;
}
pin = pinMap[pin]; pin = pinMap[pin];
// First check if the pin is in bank A or B // First check if the pin is in bank A or B
if (pin >= 0 && pin <= 7) if (pin >= 0 && pin <= 7)
@ -118,7 +128,7 @@ bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh)
// Extract the bit from the buffer // Extract the bit from the buffer
return ((inputBufferB >> (15 - pin)) & 1); return ((inputBufferB >> (15 - pin)) & 1);
} }
return 255; return false;
} }
/** /**
@ -134,26 +144,82 @@ void DigitalInputCard::handlePinChange(int pin, uint8_t &currentBuffer, uint8_t
{ {
// Get the index of the pin in the pin map // Get the index of the pin in the pin map
uint8_t virtualPin = virtualPinMap[pin]; uint8_t virtualPin = virtualPinMap[pin];
// Handle Bank A if (pin < 8)
if (((previousBuffer >> (7 - pin)) & 1) != ((currentBuffer >> (7 - pin)) & 1))
{ {
if (millis() - lastDebounceTime[pin] > debounceTime[pin]) // Handle Bank A
if (((previousBuffer >> (7 - pin)) & 1) != ((currentBuffer >> (7 - pin)) & 1))
{ {
lastDebounceTime[pin] = millis(); // When the pin first change, store the time
previousBuffer ^= (-((currentBuffer >> (7 - pin)) & 1) ^ previousBuffer) & (1UL << (7 - pin)); if (!pinChanged[virtualPin])
for(const auto& callback : callbacks) {
callback.second(virtualPin, ((currentBuffer >> (7 - pin)) & 1)); ESP_LOGD("DigitalInputCard", "Pin %d changed", virtualPin);
pinChanged[virtualPin] = true;
lastDebounceTime[virtualPin] = millis();
}
else
{
ESP_LOGD("DigitalInputCard", "Pin %d (%d>%d) debounce time: %d", virtualPin, ((previousBuffer >> (7 - pin)) & 1), ((currentBuffer >> (7 - pin)) & 1), millis() - lastDebounceTime[virtualPin]);
// If the pin was already changed, check if the debounce time has passed
if ((millis() - lastDebounceTime[virtualPin]) > debounceTime[virtualPin])
{
ESP_LOGD("DigitalInputCard", "Pin %d triggered", virtualPin);
// Call the callback function
for (auto const &callback : callbacks)
{
callback.second(virtualPin, ((currentBuffer >> (7 - pin)) & 1));
}
// Store the previous buffer at the specified pin (bitwise operation)
// new value : (currentBuffer >> (7 - pin)) & 1)
previousBuffer = (previousBuffer & ~(1 << (7 - pin))) | (((currentBuffer >> (7 - pin)) & 1) << (7 - pin));
// Reset the pin changed flag
pinChanged[virtualPin] = false;
}
}
}
else
{
// Pin bounce back to previous state, reset the debounce time
lastDebounceTime[virtualPin] = millis();
pinChanged[virtualPin] = false;
} }
} }
// Handle Bank B else
if (((previousBuffer >> (15 - pin)) & 1) != ((currentBuffer >> (15 - pin)) & 1))
{ {
if (millis() - lastDebounceTime[pin] > debounceTime[pin]) // Handle Bank B
if (((previousBuffer >> (15 - pin)) & 1) != ((currentBuffer >> (15 - pin)) & 1))
{ {
lastDebounceTime[pin] = millis(); // When the pin first change, store the time
previousBuffer ^= (-((currentBuffer >> (15 - pin)) & 1) ^ previousBuffer) & (1UL << (15 - pin)); if (!pinChanged[virtualPin])
for (const auto& callback : callbacks) {
callback.second(virtualPin, ((currentBuffer >> (15 - pin)) & 1)); ESP_LOGD("DigitalInputCard", "Pin %d changed", virtualPin);
pinChanged[virtualPin] = true;
lastDebounceTime[virtualPin] = millis();
}
else
{
ESP_LOGD("DigitalInputCard", "Pin %d (%d>%d) debounce time: %d", virtualPin, ((previousBuffer >> (15 - pin)) & 1), ((currentBuffer >> (15 - pin)) & 1), millis() - lastDebounceTime[virtualPin]);
// If the pin was already changed, check if the debounce time has passed
if ((millis() - lastDebounceTime[virtualPin]) > debounceTime[virtualPin])
{
ESP_LOGD("DigitalInputCard", "Pin %d triggered", virtualPin);
// Call the callback function
for (auto const &callback : callbacks)
{
callback.second(virtualPin, ((currentBuffer >> (15 - pin)) & 1));
}
// Store the previous buffer at the specified pin (bitwise operation)
// new value : (currentBuffer >> (15 - pin)) & 1)
previousBuffer = (previousBuffer & ~(1 << (15 - pin))) | (((currentBuffer >> (15 - pin)) & 1) << (15 - pin));
// Reset the pin changed flag
pinChanged[virtualPin] = false;
}
}
}
else
{
// Pin bounce back to previous state, reset the debounce time
lastDebounceTime[virtualPin] = millis();
pinChanged[virtualPin] = false;
} }
} }
} }
@ -233,6 +299,11 @@ uint8_t DigitalInputCard::registerCallback(std::function<void(uint8_t, bool)> ca
*/ */
void DigitalInputCard::refreshInputBankA() void DigitalInputCard::refreshInputBankA()
{ {
if(!this->initOk)
{
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Card not initialized");
return;
}
inputBufferA = inputBankA.read8(); inputBufferA = inputBankA.read8();
} }
@ -241,6 +312,11 @@ void DigitalInputCard::refreshInputBankA()
*/ */
void DigitalInputCard::refreshInputBankB() void DigitalInputCard::refreshInputBankB()
{ {
if(!this->initOk)
{
ESP_LOGE("DigitalInputCard", "Input Card ERROR: Card not initialized");
return;
}
inputBufferB = inputBankB.read8(); inputBufferB = inputBankB.read8();
} }
@ -300,3 +376,26 @@ uint8_t DigitalInputCard::getType()
{ {
return CARD_TYPE_DIGITAL_INPUT; return CARD_TYPE_DIGITAL_INPUT;
} }
/**
* @brief Preload the previous input buffer and the input buffer
*
* @note This function is useful if you want to preload the input buffers with a run-time value
*/
void DigitalInputCard::preloadInputBuffer()
{
refreshInputBankA();
refreshInputBankB();
previousInputBufferA = inputBufferA;
previousInputBufferB = inputBufferB;
}
/**
* @brief Get the status of the card
*
* @return True if the card is initialized, false otherwise
*/
bool DigitalInputCard::getStatus()
{
return this->initOk;
}

View File

@ -41,9 +41,14 @@ class DigitalInputCard : public ExpansionCard {
void unregisterCallback(uint8_t handler); void unregisterCallback(uint8_t handler);
// Load a new pin map // Load a new pin map
void loadPinMap(uint8_t pinMap[16]); void loadPinMap(uint8_t pinMap[16]);
// Preload previousInputBuffer and inputBuffer
void preloadInputBuffer();
// Status of card
bool getStatus();
// Get type of card // Get type of card
uint8_t getType(); uint8_t getType();
private: private:
bool initOk = false;
PCF8574 inputBankA; PCF8574 inputBankA;
PCF8574 inputBankB; PCF8574 inputBankB;
uint8_t address_a; uint8_t address_a;
@ -54,6 +59,7 @@ class DigitalInputCard : public ExpansionCard {
uint8_t previousInputBufferB; uint8_t previousInputBufferB;
uint32_t debounceTime[16]; uint32_t debounceTime[16];
uint32_t lastDebounceTime[16]; uint32_t lastDebounceTime[16];
bool pinChanged[16];
// A map of the physical pin to the virtual pin // A map of the physical pin to the virtual pin
uint8_t pinMap[16]; uint8_t pinMap[16];
// A map of the virtual pin to the physical pin // A map of the virtual pin to the physical pin

View File

@ -17,6 +17,9 @@
*/ */
bool DigitalInputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) { bool DigitalInputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
this->card = (DigitalInputCard *)card; this->card = (DigitalInputCard *)card;
if(!this->card->getStatus()) {
return false;
}
this->card_id = card_id; this->card_id = card_id;
this->mqtt = mqtt; this->mqtt = mqtt;
this->base_topic = base_topic; this->base_topic = base_topic;

View File

@ -32,7 +32,7 @@ DigitalOutputCard::DigitalOutputCard(uint8_t address) : change_callbacks(){
* @param bit4 The position of the fifth switch on the dip switch * @param bit4 The position of the fifth switch on the dip switch
*/ */
DigitalOutputCard::DigitalOutputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4) : DigitalOutputCard::DigitalOutputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4) :
DigitalOutputCard(0x20+bit0+bit1*2+bit2*4+bit3*8+bit4*16) DigitalOutputCard(0x40+bit0+bit1*2+bit2*4+bit3*8+bit4*16)
{ {
@ -307,3 +307,12 @@ void DigitalOutputCard::saveStateToFRAM() {
uint16_t packed = packStates(); uint16_t packed = packStates();
this->fram->write16(framAddress, packed); this->fram->write16(framAddress, packed);
} }
/**
* @brief Toggle the state of the specified pin
*
* @param pin The pin to toggle
*/
void DigitalOutputCard::toggleState(uint8_t pin) {
this->setState(pin, !this->state_buffer[pin]);
}

View File

@ -58,6 +58,8 @@ public:
void setValue(uint8_t pin, uint16_t value); void setValue(uint8_t pin, uint16_t value);
// Get the state of the specified pin // Get the state of the specified pin
bool getState(uint8_t pin); bool getState(uint8_t pin);
// Toggle the state of the specified pin
void toggleState(uint8_t pin);
// Get the pwm value of the specified pin // Get the pwm value of the specified pin
uint16_t getValue(uint8_t pin); uint16_t getValue(uint8_t pin);
// Register a callback function that will be called when the state of a pin changes // Register a callback function that will be called when the state of a pin changes

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
#define SDK_VESRION "2.6.1" #define SDK_VESRION "2.10.0"

View File

@ -56,6 +56,22 @@ void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length)
{ {
callback.second(topic_without_base, payload_buffer); callback.second(topic_without_base, payload_buffer);
} }
// Check for global state request
if (!strcmp(topic_without_base,"requeststate")) {
for (int i = 0; i < 255; i++)
{
if (components[i] != NULL)
{
components[i]->publishReport();
}
}
return;
}
// Check for global summary request
if (!strcmp(topic_without_base,"requestinfo")) {
this->publishSystemSummary();
return;
}
// Call the respective card's mqtt callback // Call the respective card's mqtt callback
// Note that after the base topic, there should be the card id // Note that after the base topic, there should be the card id
// /base_topic/card_id/... // /base_topic/card_id/...
@ -128,6 +144,12 @@ void ESPMegaIoT::registerCard(uint8_t card_id)
{ {
return; return;
} }
// Check if the physical card is installed
if (cards[card_id] == NULL)
{
ESP_LOGE("ESPMegaIoT", "Registering card %d failed: Card not installed", card_id);
return;
}
// Get the card type // Get the card type
uint8_t card_type = cards[card_id]->getType(); uint8_t card_type = cards[card_id]->getType();
// Create the respective IoT component // Create the respective IoT component
@ -379,6 +401,18 @@ void ESPMegaIoT::publish(const char *topic, const char *payload)
mqtt.publish(topic, payload); mqtt.publish(topic, payload);
} }
/**
* @brief Publish a message to a topic
*
* @param topic The topic to publish to
* @param payload The payload to publish
* @param length The length of the payload
*/
void ESPMegaIoT::publish(const char *topic, const char *payload, unsigned int length)
{
mqtt.publish(topic, (const uint8_t *)payload, length);
}
/** /**
* @brief Register a callback for MQTT messages * @brief Register a callback for MQTT messages
* *
@ -402,7 +436,7 @@ void ESPMegaIoT::unregisterMqttCallback(uint16_t handler)
} }
/** /**
* @brief Subscribe to all components's topics * @brief Subscribe to all components's topics and all other topics
*/ */
void ESPMegaIoT::mqttSubscribe() void ESPMegaIoT::mqttSubscribe()
{ {
@ -422,6 +456,9 @@ void ESPMegaIoT::mqttSubscribe()
mqtt.loop(); mqtt.loop();
} }
} }
// Global topics
this->subscribeRelative("requeststate");
this->subscribeRelative("requestinfo");
} }
/** /**
@ -517,6 +554,21 @@ void ESPMegaIoT::publishRelative(const char *topic, const char *payload)
mqtt.loop(); mqtt.loop();
} }
/**
* @brief Publish a message relative to the base topic
*
* @param topic The topic to publish to
* @param payload The payload to publish
* @param length The length of the payload
*/
void ESPMegaIoT::publishRelative(const char *topic, const char *payload, unsigned int length)
{
char absolute_topic[100];
sprintf(absolute_topic, "%s/%s", this->mqtt_config.base_topic, topic);
mqtt.publish(absolute_topic, (const uint8_t *)payload, length);
mqtt.loop();
}
/** /**
* @brief Subscribe to a topic relative to the base topic * @brief Subscribe to a topic relative to the base topic
* *
@ -579,6 +631,22 @@ void ESPMegaIoT::loadNetworkConfig()
network_config.wifiUseAuth = fram->read8(IOT_FRAM_ADDRESS + 54); network_config.wifiUseAuth = fram->read8(IOT_FRAM_ADDRESS + 54);
fram->read(IOT_FRAM_ADDRESS + 55, (uint8_t *)network_config.ssid, 32); fram->read(IOT_FRAM_ADDRESS + 55, (uint8_t *)network_config.ssid, 32);
fram->read(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32); fram->read(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32);
// If ip,gateway,subnet,dns1,dns2 is 0, the device is not configured
// set to default values
// ip: 192.168.0.99
// gateway: 192.168.0.1
// subnet: 255.255.255.0
// dns1: 1.1.1.1
// dns2: 9.9.9.9
if ((uint32_t)network_config.ip == 0 && (uint32_t)network_config.gateway == 0 && (uint32_t)network_config.subnet == 0 && (uint32_t)network_config.dns1 == 0 && (uint32_t)network_config.dns2 == 0)
{
network_config.ip = IPAddress(192, 168, 0, 99);
network_config.gateway = IPAddress(192, 168, 0, 1);
network_config.subnet = IPAddress(255, 255, 255, 0);
network_config.dns1 = IPAddress(1, 1, 1, 1);
network_config.useStaticIp = true;
}
} }
/** /**
@ -834,3 +902,30 @@ String ESPMegaIoT::getMac()
else else
return this->getETHMac(); return this->getETHMac();
} }
/**
* @brief Publish a json object containing the system summary
*/
void ESPMegaIoT::publishSystemSummary() {
char payload[1024];
StaticJsonDocument<1024> doc;
JsonObject root = doc.to<JsonObject>();
root["ip"] = this->getIp().toString();
root["firmware"] = SW_VERSION;
root["sdk_version"] = SDK_VESRION;
root["board_model"] = BOARD_MODEL;
JsonArray cards = root.createNestedArray("cards");
for (int i = 0; i < 255; i++)
{
if (components[i] != NULL)
{
JsonObject card = cards.createNestedObject();
card["id"] = i;
card["type"] = components[i]->getType();
}
}
serializeJson(doc, payload);
ESP_LOGD("ESPMegaIoT", "Publishing system summary: %s", payload);
mqtt.loop();
this->publishRelative("info", payload, strlen(payload));
}

View File

@ -16,6 +16,8 @@
#include <WiFi.h> #include <WiFi.h>
#include <FRAM.h> #include <FRAM.h>
#include <map> #include <map>
#include <ArduinoJson.h>
#include <ESPMegaCommon.hpp>
// MQTT Connection Parameters // MQTT Connection Parameters
#define TCP_TIMEOUT_SEC 5 #define TCP_TIMEOUT_SEC 5
@ -81,6 +83,7 @@ public:
void publishCard(uint8_t card_id); void publishCard(uint8_t card_id);
// Publish topic appended with base topic // Publish topic appended with base topic
void publishRelative(const char *topic, const char *payload); void publishRelative(const char *topic, const char *payload);
void publishRelative(const char *topic, const char *payload, unsigned int length);
// Subscribe topic appended with base topic // Subscribe topic appended with base topic
void subscribeRelative(const char *topic); void subscribeRelative(const char *topic);
void subscribe(const char *topic); void subscribe(const char *topic);
@ -105,6 +108,7 @@ public:
bool mqttConnected(); bool mqttConnected();
void disconnectFromMqtt(); void disconnectFromMqtt();
void publish(const char *topic, const char *payload); void publish(const char *topic, const char *payload);
void publish(const char *topic, const char *payload, unsigned int length);
uint16_t registerMqttCallback(std::function<void(char *, char *)> callback); uint16_t registerMqttCallback(std::function<void(char *, char *)> callback);
void unregisterMqttCallback(uint16_t handler); void unregisterMqttCallback(uint16_t handler);
uint16_t registerRelativeMqttCallback(std::function<void(char *, char *)> callback); uint16_t registerRelativeMqttCallback(std::function<void(char *, char *)> callback);
@ -115,6 +119,7 @@ public:
void bindEthernetInterface(ETHClass *ethernetIface); void bindEthernetInterface(ETHClass *ethernetIface);
bool networkConnected(); bool networkConnected();
void bindFRAM(FRAM *fram); void bindFRAM(FRAM *fram);
void publishSystemSummary();
IoTComponent* getComponent(uint8_t card_id); IoTComponent* getComponent(uint8_t card_id);
IPAddress getETHIp(); IPAddress getETHIp();

View File

@ -1,4 +1,5 @@
#include <ESPMegaProOS.hpp> #include <ESPMegaProOS.hpp>
#include "esp_sntp.h"
// Reserve FRAM address 0 - 1000 for ESPMegaPRO Internal Use // Reserve FRAM address 0 - 1000 for ESPMegaPRO Internal Use
// (34 Bytes) Address 0-33 for Built-in Digital Output Card // (34 Bytes) Address 0-33 for Built-in Digital Output Card
@ -11,6 +12,7 @@
*/ */
ESPMegaPRO::ESPMegaPRO() ESPMegaPRO::ESPMegaPRO()
{ {
} }
/** /**
@ -102,6 +104,13 @@ void ESPMegaPRO::loop()
if (iotEnabled) if (iotEnabled)
{ {
iot->loop(); iot->loop();
static int64_t lastNTPUpdate = (esp_timer_get_time() / 1000) - NTP_UPDATE_INTERVAL_MS + NTP_INITIAL_SYNC_DELAY_MS;
if ((esp_timer_get_time() / 1000) - lastNTPUpdate > NTP_UPDATE_INTERVAL_MS)
{
ESP_LOGV("ESPMegaPRO", "Updating time from NTP");
lastNTPUpdate = esp_timer_get_time() / 1000;
this->updateTimeFromNTP();
}
} }
if (internalDisplayEnabled) if (internalDisplayEnabled)
{ {
@ -153,19 +162,35 @@ bool ESPMegaPRO::installCard(uint8_t slot, ExpansionCard *card)
bool ESPMegaPRO::updateTimeFromNTP() bool ESPMegaPRO::updateTimeFromNTP()
{ {
struct tm timeinfo; struct tm timeinfo;
if (getLocalTime(&timeinfo)) uint32_t start = esp_timer_get_time() / 1000;
time_t now;
time(&now);
localtime_r(&now, &timeinfo);
if (!(timeinfo.tm_year > (2016 - 1900)))
{ {
rtctime_t rtctime = this->getTime(); ESP_LOGI("ESPMegaPRO", "NTP is not ready yet!");
if (rtctime.hours != timeinfo.tm_hour || rtctime.minutes != timeinfo.tm_min || return false;
rtctime.seconds != timeinfo.tm_sec || rtctime.day != timeinfo.tm_mday ||
rtctime.month != timeinfo.tm_mon + 1 || rtctime.year != timeinfo.tm_year + 1900)
{
this->setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
}
return true;
} }
return false; rtctime_t rtctime = this->getTime();
if (rtctime.hours != timeinfo.tm_hour || rtctime.minutes != timeinfo.tm_min ||
rtctime.seconds != timeinfo.tm_sec || rtctime.day != timeinfo.tm_mday ||
rtctime.month != timeinfo.tm_mon + 1 || rtctime.year != timeinfo.tm_year + 1900)
{
this->setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
}
ESP_LOGV("ESPMegaPRO", "Time updated from NTP: %s", asctime(&timeinfo));
return true;
}
/**
* @brief Sets the timezone for the internal RTC.
*
* @note This function takes POSIX timezone strings (e.g. "EST5EDT,M3.2.0,M11.1.0").
*/
void ESPMegaPRO::setTimezone(const char* offset)
{
setenv("TZ", offset, 1);
} }
/** /**
@ -222,6 +247,10 @@ void ESPMegaPRO::enableIotModule()
this->iot->bindFRAM(&fram); this->iot->bindFRAM(&fram);
this->iot->intr_begin(cards); this->iot->intr_begin(cards);
iotEnabled = true; iotEnabled = true;
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, this->iot->getMqttConfig()->mqtt_server);
sntp_setservername(1, "pool.ntp.org");
sntp_init();
} }
/** /**

View File

@ -24,6 +24,11 @@
#define PWM_BANK_ADDRESS 0x5F #define PWM_BANK_ADDRESS 0x5F
#define RTC_ADDRESS 0x68 #define RTC_ADDRESS 0x68
// Constants
#define NTP_TIMEOUT_MS 5000
#define NTP_UPDATE_INTERVAL_MS 60000
#define NTP_INITIAL_SYNC_DELAY_MS 15000
/** /**
* @brief The ESPMegaPRO class is the main class for the ESPMegaPRO library. * @brief The ESPMegaPRO class is the main class for the ESPMegaPRO library.
* *
@ -47,6 +52,7 @@ class ESPMegaPRO {
void enableIotModule(); void enableIotModule();
void enableInternalDisplay(HardwareSerial *serial); void enableInternalDisplay(HardwareSerial *serial);
void enableWebServer(uint16_t port); void enableWebServer(uint16_t port);
void setTimezone(const char* offset);
rtctime_t getTime(); rtctime_t getTime();
void dumpFRAMtoSerial(uint16_t start, uint16_t end); void dumpFRAMtoSerial(uint16_t start, uint16_t end);
void dumpFRAMtoSerialASCII(uint16_t start, uint16_t end); void dumpFRAMtoSerialASCII(uint16_t start, uint16_t end);

View File

@ -482,6 +482,7 @@ void ESPMegaWebServer::getDeviceInfoHandler(AsyncWebServerRequest *request) {
doc["software_version"] = SW_VERSION; doc["software_version"] = SW_VERSION;
doc["sdk_version"] = SDK_VESRION; doc["sdk_version"] = SDK_VESRION;
doc["idf_version"] = IDF_VER; doc["idf_version"] = IDF_VER;
doc["uptime"] = esp_timer_get_time() / 1000000; // Uptime in seconds
char buffer[512]; char buffer[512];
serializeJson(doc, buffer); serializeJson(doc, buffer);
request->send(200, "application/json", buffer); request->send(200, "application/json", buffer);

View File

@ -14,6 +14,10 @@
MAC Address MAC Address
<span style="float: right" id="mac_address">Loading ...</span> <span style="float: right" id="mac_address">Loading ...</span>
</p> </p>
<p style="text-align: left">
Uptime
<span style="float: right" id="uptime">Loading ...</span>
</p>
<p style="text-align: left"> <p style="text-align: left">
Model Model
<span style="float: right" id="model">Loading ...</span> <span style="float: right" id="model">Loading ...</span>
@ -60,7 +64,7 @@
<b>SIWAT SYSTEM 2023</b> <b>SIWAT SYSTEM 2023</b>
</form> </form>
<script> <script>
window.onload =function () { window.onload = function () {
fetch("/get_device_info") fetch("/get_device_info")
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
@ -75,6 +79,19 @@
document.getElementById("api_server").innerHTML = data.mqtt_server + ":" + data.mqtt_port; document.getElementById("api_server").innerHTML = data.mqtt_server + ":" + data.mqtt_port;
document.getElementById("api_endpoint").innerHTML = data.base_topic; document.getElementById("api_endpoint").innerHTML = data.base_topic;
document.getElementById("centrally_managed").innerHTML = data.mqtt_connected; document.getElementById("centrally_managed").innerHTML = data.mqtt_connected;
var uptime = data.uptime;
var uptime_string = "";
// Uptime is in seconds, convert it to X days, HH:MM:SS
var days = Math.floor(uptime / (24 * 3600));
uptime -= days * 24 * 3600;
var hours = Math.floor(uptime / 3600);
uptime -= hours * 3600;
var minutes = Math.floor(uptime / 60);
uptime -= minutes * 60;
var seconds = uptime;
if (days > 0) uptime_string += days + " days, ";
uptime_string += hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + seconds.toString().padStart(2, "0");
document.getElementById("uptime").innerHTML = uptime_string;
}).catch(error => { }).catch(error => {
console.error(error); console.error(error);
}); });

View File

@ -51,449 +51,517 @@ const char ota_html[] PROGMEM = {
0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79,
0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69,
0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74,
0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20,
0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6d, 0x6f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x75,
0x64, 0x65, 0x6c, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69,
0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e,
0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20,
0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74,
0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c,
0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x46, 0x57, 0x20, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x4d,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x6f, 0x64, 0x65, 0x6c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73,
0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66,
0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22,
0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x66, 0x77, 0x5f, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0x3e,
0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c,
0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f,
0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79,
0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69,
0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x46, 0x57, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61,
0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f,
0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69,
0x64, 0x3d, 0x22, 0x66, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e,
0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20,
0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20,
0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d,
0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22,
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x53, 0x44, 0x4b, 0x20, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d,
0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68,
0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x73, 0x64, 0x6b, 0x5f, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64,
0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61,
0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a,
0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22,
0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20,
0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x53, 0x44, 0x4b, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x0d, 0x49, 0x44, 0x46, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73,
0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a,
0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22,
0x73, 0x64, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22,
0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e,
0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74,
0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c,
0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x49, 0x44, 0x46, 0x20, 0x56, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x41, 0x50, 0x49, 0x20, 0x53, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70,
0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c,
0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20,
0x20, 0x69, 0x64, 0x3d, 0x22, 0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x64, 0x3d, 0x22, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76,
0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e,
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20,
0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65,
0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65,
0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x41, 0x50,
0x49, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c,
0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69,
0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x61, 0x70, 0x69,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x3e, 0x4c, 0x6f, 0x61,
0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70,
0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d,
0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d,
0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a,
0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x41, 0x50, 0x49, 0x20, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
0x74, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e,
0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61,
0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64,
0x3d, 0x22, 0x61, 0x70, 0x69, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a,
0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70,
0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74,
0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74,
0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x43, 0x65, 0x6e, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x41, 0x50, 0x49, 0x20,
0x72, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x0d, 0x0a, 0x20, 0x20,
0x64, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c,
0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69,
0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x61, 0x70, 0x69,
0x3d, 0x22, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x3e, 0x4c,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f,
0x64, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70,
0x61, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x20, 0x73, 0x74, 0x79, 0x6c,
0x0a, 0x20, 0x20, 0x3c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67,
0x79, 0x70, 0x65, 0x3d, 0x22, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x22, 0x3e, 0x0d, 0x0a, 0x20,
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x20, 0x20, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79,
0x22, 0x20, 0x6f, 0x6e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x77, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x0d, 0x0a, 0x20, 0x20,
0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x20, 0x20, 0x3c, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x73, 0x74, 0x79, 0x6c,
0x6f, 0x6e, 0x2e, 0x68, 0x72, 0x65, 0x66, 0x3d, 0x27, 0x63, 0x6f, 0x6e, 0x65, 0x3d, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69,
0x66, 0x69, 0x67, 0x27, 0x22, 0x3e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x68, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x63, 0x65, 0x6e,
0x67, 0x73, 0x3c, 0x2f, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x3c, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x62, 0x72, 0x20, 0x2f, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x65, 0x64, 0x22, 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
0x0a, 0x20, 0x20, 0x3c, 0x68, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x3e, 0x0d, 0x0a,
0x68, 0x33, 0x3e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x53, 0x6f, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x62,
0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22,
0x67, 0x65, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73,
0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x20, 0x6f, 0x6e, 0x63,
0x66, 0x69, 0x6c, 0x65, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x68, 0x72,
0x66, 0x69, 0x6c, 0x65, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x65, 0x66, 0x3d, 0x27, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x27, 0x22,
0x67, 0x65, 0x3d, 0x22, 0x73, 0x75, 0x62, 0x28, 0x74, 0x68, 0x69, 0x73, 0x3e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x3c, 0x2f, 0x62,
0x29, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x64, 0x69, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e,
0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68,
0x20, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3d, 0x22, 0x2e, 0x62, 0x69, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x33, 0x3e, 0x55, 0x70,
0x6e, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x6c, 0x61, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
0x62, 0x65, 0x6c, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x65, 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x3c, 0x2f, 0x68,
0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x20, 0x66, 0x6f, 0x72, 0x3d, 0x33, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74,
0x22, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x3e, 0x43, 0x68, 0x6f, 0x6f, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x22,
0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x75, 0x70, 0x64, 0x61, 0x74,
0x61, 0x62, 0x65, 0x6c, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x65, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x22,
0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x73, 0x75, 0x20, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x22, 0x73,
0x62, 0x6d, 0x69, 0x74, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x75, 0x62, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x22, 0x20, 0x73, 0x74,
0x22, 0x62, 0x74, 0x6e, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
0x22, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x22, 0x20, 0x2f, 0x3e, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x61, 0x63, 0x63, 0x65,
0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x70, 0x74, 0x3d, 0x22, 0x2e, 0x62, 0x69, 0x6e, 0x22, 0x20, 0x2f, 0x3e,
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x69,
0x22, 0x70, 0x72, 0x67, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x64, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75,
0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x74, 0x22, 0x20, 0x66, 0x6f, 0x72, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x65,
0x20, 0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x70, 0x22, 0x3e, 0x43, 0x68, 0x6f, 0x6f, 0x73, 0x65, 0x20, 0x66, 0x69, 0x6c,
0x72, 0x67, 0x62, 0x61, 0x72, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x65, 0x2e, 0x2e, 0x2e, 0x3c, 0x2f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3e,
0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x61, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74,
0x72, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x22,
0x20, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x74, 0x6e, 0x22,
0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x50, 0x72, 0x6f, 0x67,
0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x33, 0x3e, 0x44, 0x65, 0x76, 0x72, 0x61, 0x6d, 0x22, 0x20, 0x2f, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f,
0x69, 0x63, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x3c, 0x3e, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
0x2f, 0x68, 0x33, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x70, 0x72, 0x67, 0x22,
0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x62, 0x75, 0x74, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c,
0x74, 0x6f, 0x6e, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x72, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x64, 0x69,
0x62, 0x74, 0x6e, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x70, 0x72, 0x67, 0x62, 0x61, 0x72,
0x52, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x6c, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76,
0x69, 0x63, 0x6b, 0x3d, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x61, 0x72, 0x22, 0x3e, 0x3c, 0x2f,
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x68, 0x72, 0x65, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x69,
0x66, 0x3d, 0x27, 0x2f, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x27, 0x22, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x62, 0x72, 0x20, 0x2f, 0x3e,
0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x62, 0x3e, 0x53, 0x49, 0x0d, 0x0a, 0x20, 0x20, 0x3c, 0x68, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20,
0x57, 0x41, 0x54, 0x20, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x20, 0x32, 0x3c, 0x68, 0x33, 0x3e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x43,
0x30, 0x32, 0x33, 0x3c, 0x2f, 0x62, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0x0d,
0x6f, 0x72, 0x6d, 0x3e, 0x0d, 0x0a, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x0a, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79,
0x74, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x70, 0x65, 0x3d, 0x22, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20,
0x2e, 0x6f, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x74, 0x6e, 0x22, 0x20,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x52, 0x65, 0x62, 0x6f, 0x6f,
0x20, 0x20, 0x20, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x28, 0x22, 0x2f, 0x74, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22,
0x67, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74,
0x6e, 0x66, 0x6f, 0x22, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x6e, 0x2e, 0x68, 0x72, 0x65, 0x66, 0x3d, 0x27, 0x2f, 0x72,
0x20, 0x2e, 0x74, 0x68, 0x65, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x27, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a,
0x6e, 0x73, 0x65, 0x20, 0x3d, 0x3e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x20, 0x20, 0x3c, 0x62, 0x3e, 0x53, 0x49, 0x57, 0x41, 0x54, 0x20, 0x53,
0x6e, 0x73, 0x65, 0x2e, 0x6a, 0x73, 0x6f, 0x6e, 0x28, 0x29, 0x29, 0x0d, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x20, 0x32, 0x30, 0x32, 0x33, 0x3c, 0x2f,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x74, 0x68, 0x65, 0x6e, 0x62, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x0d,
0x28, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x3e, 0x20, 0x7b, 0x0d, 0x0a, 0x0a, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f, 0x6e, 0x6c, 0x6f,
0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x64, 0x61, 0x74, 0x61, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x66, 0x65, 0x74, 0x63, 0x68, 0x28, 0x22, 0x2f, 0x67, 0x65, 0x74, 0x5f,
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22,
0x22, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x2e, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x74, 0x68,
0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x65, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20,
0x64, 0x61, 0x74, 0x61, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x3d, 0x3e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e,
0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x28, 0x29, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x20, 0x20, 0x20, 0x2e, 0x74, 0x68, 0x65, 0x6e, 0x28, 0x64, 0x61, 0x74,
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x61, 0x20, 0x3d, 0x3e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x22, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e,
0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x6c, 0x6f, 0x67, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0d, 0x0a,
0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x69, 0x70, 0x5f, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
0x79, 0x49, 0x64, 0x28, 0x22, 0x6d, 0x61, 0x63, 0x5f, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72,
0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e,
0x6d, 0x61, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x6d,
0x6f, 0x64, 0x65, 0x6c, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72,
0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e,
0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
0x79, 0x49, 0x64, 0x28, 0x22, 0x66, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48,
0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x73,
0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67,
0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49,
0x64, 0x28, 0x22, 0x73, 0x64, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54,
0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x73, 0x64,
0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x69, 0x64, 0x66, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x68, 0x6f, 0x73,
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x2e, 0x69, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65,
0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61,
0x61, 0x74, 0x61, 0x2e, 0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x0d, 0x0a,
0x69, 0x6f, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75,
0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x69, 0x70, 0x5f,
0x64, 0x28, 0x22, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x29, 0x2e, 0x69, 0x6e,
0x72, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d,
0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71, 0x74,
0x74, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, 0x20, 0x22,
0x3a, 0x22, 0x20, 0x2b, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71,
0x74, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x61, 0x70, 0x69, 0x5f, 0x65,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e,
0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61,
0x74, 0x61, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x74, 0x61, 0x2e, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x63, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
0x22, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x6d, 0x22, 0x6d, 0x61, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c,
0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x61, 0x63, 0x5f,
0x61, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x63, 0x74, 0x65, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
0x20, 0x7d, 0x29, 0x2e, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x65, 0x72, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
0x72, 0x6f, 0x72, 0x20, 0x3d, 0x3e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x6d, 0x6f, 0x64, 0x65, 0x6c,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c,
0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6f, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x6f, 0x64, 0x65,
0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x6c, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x75, 0x62, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x66, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22,
0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20,
0x65, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x73, 0x6f, 0x66, 0x74, 0x77,
0x65, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x28, 0x22, 0x5c, 0x5c, 0x22, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3b,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x73,
0x64, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x29,
0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d,
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x73, 0x64, 0x6b, 0x5f, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
0x79, 0x49, 0x64, 0x28, 0x22, 0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72,
0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e,
0x69, 0x64, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x61,
0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x29, 0x2e,
0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20,
0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, 0x20, 0x22, 0x3a, 0x22, 0x20, 0x2b,
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x70,
0x6f, 0x72, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67,
0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49,
0x64, 0x28, 0x22, 0x61, 0x70, 0x69, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48,
0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x62,
0x61, 0x73, 0x65, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, 0x75,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x63, 0x65, 0x6e,
0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x0d, 0x0a, 0x20, 0x65, 0x64, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54,
0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x22, 0x20, 0x2b, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6d, 0x71,
0x20, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x5b, 0x66, 0x69, 0x74, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64,
0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76,
0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x61, 0x72, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x3d, 0x20,
0x0d, 0x0a, 0x20, 0x20, 0x24, 0x28, 0x22, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x3b,
0x29, 0x2e, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x72, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x72,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x3b, 0x0d, 0x0a, 0x20,
0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x70,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x73,
0x72, 0x6d, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x22, 0x23, 0x75, 0x70, 0x6c, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x76,
0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x29, 0x5b, 0x30, 0x65, 0x72, 0x74, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x58, 0x20,
0x5d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x79, 0x73, 0x2c, 0x20, 0x48, 0x48, 0x3a, 0x4d, 0x4d, 0x3a,
0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x46, 0x53, 0x53, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x4d,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x75, 0x70,
0x61, 0x78, 0x28, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x2f, 0x20, 0x28, 0x32, 0x34, 0x20, 0x2a,
0x75, 0x72, 0x6c, 0x3a, 0x20, 0x22, 0x2f, 0x6f, 0x74, 0x61, 0x5f, 0x75, 0x20, 0x33, 0x36, 0x30, 0x30, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x22, 0x50, 0x4f, 0x20, 0x2d, 0x3d, 0x20, 0x64, 0x61, 0x79, 0x73, 0x20, 0x2a, 0x20, 0x32,
0x53, 0x54, 0x22, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x34, 0x20, 0x2a, 0x20, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x0d, 0x0a, 0x20,
0x64, 0x61, 0x74, 0x61, 0x3a, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x68,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6f, 0x75, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e,
0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x20, 0x2f, 0x20, 0x33, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x61, 0x74, 0x61, 0x3a, 0x20, 0x66, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d,
0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x20, 0x2d, 0x3d, 0x20, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x20, 0x2a,
0x20, 0x78, 0x68, 0x72, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x20, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6d, 0x69, 0x6e, 0x75,
0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x78, 0x68, 0x72, 0x74, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x66,
0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x20,
0x77, 0x2e, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x2f, 0x20, 0x36, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x75, 0x65, 0x73, 0x74, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x2d,
0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x68, 0x72, 0x2e, 0x75, 0x70, 0x6c, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x20, 0x2a, 0x20,
0x6f, 0x61, 0x64, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x36, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x20, 0x3d, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x3b, 0x0d, 0x0a,
0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x64, 0x61, 0x79, 0x73, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x20, 0x75, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x74, 0x29, 0x20, 0x7b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2b, 0x3d, 0x20, 0x64, 0x61, 0x79, 0x73, 0x20, 0x2b, 0x20, 0x22, 0x20,
0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x76, 0x74, 0x2e, 0x6c, 0x65, 0x64, 0x61, 0x79, 0x73, 0x2c, 0x20, 0x22, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x6e, 0x67, 0x74, 0x68, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x62, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
0x6c, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x2b, 0x3d, 0x20, 0x68,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x6f, 0x75, 0x72, 0x73, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e,
0x20, 0x70, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x76, 0x74, 0x2e, 0x6c, 0x67, 0x28, 0x29, 0x2e, 0x70, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74,
0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x2f, 0x20, 0x65, 0x76, 0x74, 0x2e, 0x28, 0x32, 0x2c, 0x20, 0x22, 0x30, 0x22, 0x29, 0x20, 0x2b, 0x20, 0x22,
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x22, 0x20, 0x2b, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x2e,
0x20, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x70, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x28, 0x32, 0x2c, 0x20,
0x28, 0x70, 0x65, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x22, 0x30, 0x22, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x3a, 0x22, 0x20, 0x2b,
0x3c, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x74, 0x6f, 0x53,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x2e, 0x70, 0x61, 0x64, 0x53,
0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x70, 0x72, 0x67, 0x22, 0x29, 0x2e, 0x74, 0x61, 0x72, 0x74, 0x28, 0x32, 0x2c, 0x20, 0x22, 0x30, 0x22, 0x29,
0x68, 0x74, 0x6d, 0x6c, 0x28, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x69, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64,
0x6e, 0x67, 0x20, 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x20, 0x28, 0x22, 0x20, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45,
0x2b, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22,
0x28, 0x70, 0x65, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e,
0x2b, 0x20, 0x22, 0x25, 0x29, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x75, 0x70, 0x74,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0d,
0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x2e, 0x63, 0x61,
0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x74, 0x63, 0x68, 0x28, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x3e,
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f,
0x72, 0x28, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x7d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x20, 0x73, 0x75, 0x62, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66,
0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x6f, 0x62,
0x6a, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x73, 0x70, 0x6c, 0x69,
0x74, 0x28, 0x22, 0x5c, 0x5c, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67,
0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49,
0x64, 0x28, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75,
0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d,
0x4c, 0x20, 0x3d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
0x20, 0x20, 0x20, 0x22, 0x20, 0x2b, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x4e,
0x61, 0x6d, 0x65, 0x5b, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65,
0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x24, 0x28,
0x22, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x29, 0x2e, 0x73, 0x75, 0x62, 0x6d,
0x69, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x28, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x76, 0x61, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x3d, 0x20, 0x24,
0x28, 0x22, 0x23, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f,
0x72, 0x6d, 0x22, 0x29, 0x5b, 0x30, 0x5d, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d,
0x20, 0x6e, 0x65, 0x77, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74,
0x61, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x22,
0x2f, 0x6f, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22,
0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
0x65, 0x3a, 0x20, 0x22, 0x50, 0x4f, 0x53, 0x54, 0x22, 0x2c, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x20,
0x64, 0x61, 0x74, 0x61, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65,
0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44,
0x61, 0x74, 0x61, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x68, 0x72, 0x3a, 0x20,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76,
0x61, 0x72, 0x20, 0x78, 0x68, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77,
0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x58, 0x4d, 0x4c, 0x48,
0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x29,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78,
0x68, 0x72, 0x2e, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x61, 0x64,
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e,
0x65, 0x72, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73,
0x22, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28,
0x65, 0x76, 0x74, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28,
0x65, 0x76, 0x74, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x43, 0x6f,
0x6d, 0x70, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x7b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x70, 0x72, 0x67, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x65, 0x72, 0x20, 0x3d,
0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x22, 0x55, 0x70, 0x64, 0x20, 0x65, 0x76, 0x74, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20,
0x61, 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2f, 0x20, 0x65, 0x76, 0x74, 0x2e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x3b,
0x64, 0x2c, 0x20, 0x52, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x4d, 0x61, 0x74, 0x68,
0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x70, 0x65, 0x72, 0x20, 0x2a,
0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x30, 0x30, 0x29,
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23,
0x70, 0x72, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x22,
0x55, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x20, 0x2e,
0x20, 0x2e, 0x20, 0x28, 0x22, 0x20, 0x2b, 0x20, 0x4d, 0x61, 0x74, 0x68,
0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x70, 0x65, 0x72, 0x20, 0x2a,
0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x25, 0x29, 0x22,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24,
0x28, 0x22, 0x23, 0x70, 0x72, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d,
0x6c, 0x28, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x43, 0x6f,
0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x52, 0x65, 0x62,
0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x20, 0x2e, 0x20, 0x2e,
0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x62, 0x61, 0x72, 0x22, 0x29, 0x2e, 0x63, 0x73,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x62, 0x61, 0x73, 0x28, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x2c, 0x20, 0x4d,
0x72, 0x22, 0x29, 0x2e, 0x63, 0x73, 0x73, 0x28, 0x22, 0x77, 0x69, 0x64, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x70, 0x65,
0x74, 0x68, 0x22, 0x2c, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x6f, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x22,
0x75, 0x6e, 0x64, 0x28, 0x70, 0x65, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x30, 0x25, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x30, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x25, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20,
0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x61, 0x6c,
0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x78, 0x68, 0x72, 0x3b, 0x0d,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x20, 0x78, 0x68, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x64, 0x2c, 0x20, 0x73, 0x29, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63,
0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x22,
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x21, 0x22, 0x29, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28,
0x20, 0x62, 0x2c, 0x20, 0x63, 0x29, 0x20, 0x7b, 0x20, 0x7d, 0x2c, 0x0d, 0x64, 0x2c, 0x20, 0x73, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65,
0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x22, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,
0x74, 0x3e, 0x0d, 0x0a, 0x3c, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3e, 0x0d, 0x73, 0x21, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x0a, 0x20, 0x20, 0x68, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x72, 0x72, 0x6f, 0x72, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x63, 0x29,
0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x20, 0x7b, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x3c,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x3c, 0x73,
0x20, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x74, 0x79, 0x6c, 0x65, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x68, 0x72, 0x20,
0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c,
0x70, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23,
0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x2d, 0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65,
0x72, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3a, 0x20, 0x69, 0x6e, 0x73,
0x65, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72,
0x64, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30,
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69,
0x67, 0x68, 0x74, 0x3a, 0x20, 0x33, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x66, 0x69, 0x6c,
0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
0x74, 0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64,
0x69, 0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31,
0x30, 0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65,
0x3a, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d,
0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x66, 0x31, 0x66, 0x31,
0x66, 0x31, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72,
0x64, 0x65, 0x72, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x30, 0x20,
0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
0x0d, 0x0a, 0x20, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x7b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75,
0x6e, 0x64, 0x2d, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x75, 0x72,
0x6c, 0x28, 0x22, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x66,
0x73, 0x2e, 0x73, 0x69, 0x77, 0x61, 0x74, 0x73, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6f, 0x6e, 0x61, 0x5f,
0x62, 0x67, 0x2e, 0x70, 0x6e, 0x67, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e,
0x64, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x63, 0x6f, 0x76, 0x65,
0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74,
0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x3a, 0x20, 0x73, 0x61, 0x6e,
0x73, 0x2d, 0x73, 0x65, 0x72, 0x69, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a,
0x20, 0x31, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x37, 0x37, 0x37, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23,
0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20,
0x23, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x35, 0x45,
0x35, 0x45, 0x35, 0x45, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70,
0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20,
0x31, 0x70, 0x78, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x20, 0x23, 0x64,
0x64, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e,
0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x34, 0x34,
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78,
0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e,
0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69,
0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f,
0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x62,
0x61, 0x72, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x70, 0x72, 0x67, 0x62,
0x61, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61,
0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c,
0x6f, 0x72, 0x3a, 0x20, 0x23, 0x44, 0x39, 0x44, 0x39, 0x44, 0x39, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72,
0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x31, 0x30, 0x70,
0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20,
0x20, 0x23, 0x62, 0x61, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d,
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x32, 0x39, 0x43, 0x44, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x61, 0x61, 0x61,
0x31, 0x46, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x61, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
0x74, 0x68, 0x3a, 0x20, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x2e, 0x35,
0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20,
0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x30, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x3a,
0x20, 0x72, 0x67, 0x62, 0x61, 0x28, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x39, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74,
0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x35, 0x38, 0x70, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x79, 0x6c,
0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x65, 0x3a, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x74, 0x3b, 0x0d, 0x0a, 0x20,
0x69, 0x6e, 0x3a, 0x20, 0x37, 0x35, 0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x77, 0x69,
0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
0x69, 0x6e, 0x67, 0x3a, 0x20, 0x33, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x33,
0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69, 0x6e, 0x70, 0x75,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x74, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68,
0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x34, 0x34, 0x70,
0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64,
0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x34,
0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72,
0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x61, 0x75,
0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e,
0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x35, 0x70, 0x78,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
0x2e, 0x62, 0x74, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a,
0x23, 0x43, 0x41, 0x33, 0x44, 0x33, 0x44, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x23, 0x66, 0x31, 0x66, 0x31, 0x66, 0x31, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30,
0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69,
0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x6e, 0x67, 0x3a, 0x20, 0x30, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d,
0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x62, 0x6f,
0x63, 0x6f, 0x6e, 0x66, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61,
0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x69, 0x6d, 0x61,
0x23, 0x34, 0x31, 0x37, 0x64, 0x66, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x67, 0x65, 0x3a, 0x20, 0x75, 0x72, 0x6c, 0x28, 0x22, 0x68, 0x74, 0x74,
0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x66, 0x73, 0x2e, 0x73, 0x69, 0x77, 0x61,
0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x74, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x61, 0x72, 0x6f, 0x6e, 0x61, 0x5f, 0x62, 0x67, 0x2e, 0x70, 0x6e, 0x67,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63,
0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x73, 0x69, 0x7a, 0x65,
0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3a, 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c,
0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x79, 0x3a, 0x20, 0x73, 0x61, 0x6e, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x69,
0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74,
0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x34, 0x70, 0x78, 0x3b,
0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x20, 0x23, 0x37, 0x37, 0x37, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x69,
0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3e 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63,
0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x43, 0x43, 0x43, 0x43, 0x43,
0x43, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
0x72, 0x3a, 0x20, 0x23, 0x35, 0x45, 0x35, 0x45, 0x35, 0x45, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f,
0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6f,
0x6c, 0x69, 0x64, 0x20, 0x23, 0x64, 0x64, 0x64, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67,
0x68, 0x74, 0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67,
0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
0x0d, 0x0a, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72, 0x2c, 0x0d, 0x0a, 0x20,
0x20, 0x23, 0x70, 0x72, 0x67, 0x62, 0x61, 0x72, 0x20, 0x7b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75,
0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x44,
0x39, 0x44, 0x39, 0x44, 0x39, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75,
0x73, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x23, 0x62, 0x61, 0x72, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
0x20, 0x23, 0x32, 0x39, 0x43, 0x44, 0x31, 0x46, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x25,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
0x74, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x72, 0x67, 0x62, 0x61, 0x28,
0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35,
0x35, 0x2c, 0x20, 0x30, 0x2e, 0x39, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68,
0x3a, 0x20, 0x32, 0x35, 0x38, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x37, 0x35,
0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x33,
0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f,
0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a,
0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20,
0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d,
0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x62, 0x74, 0x6e, 0x20, 0x7b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x43, 0x41, 0x33, 0x44, 0x33,
0x44, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x7d, 0x0d,
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x7b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x34, 0x31, 0x37, 0x64, 0x66,
0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
0x3a, 0x20, 0x34, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69,
0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31, 0x30,
0x70, 0x78, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a,
0x20, 0x31, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65,
0x3e
, 0x00}; , 0x00};

View File

@ -1,7 +1,7 @@
/** /**
* @file main.cpp * @file main.cpp
* @brief Test firmware for the ESPMegaPRO OOP library * @brief Test firmware for the ESPMegaPRO OOP library
*/ */
#include <ESPMegaProOS.hpp> #include <ESPMegaProOS.hpp>
#include <InternalDisplay.hpp> #include <InternalDisplay.hpp>
#include <ETH.h> #include <ETH.h>
@ -12,22 +12,39 @@
#include <AnalogCard.hpp> #include <AnalogCard.hpp>
#include <SmartVariable.hpp> #include <SmartVariable.hpp>
#define INPUT_DEBOUNCE_TIME_MS 1000
// #define FRAM_DEBUG // #define FRAM_DEBUG
// #define MQTT_DEBUG // #define MQTT_DEBUG
#define WRITE_DEFAULT_NETCONF // #define RTC_DEBUG
//#define CLIMATE_CARD_ENABLE // #define WRITE_DEFAULT_NETCONF
// #define CLIMATE_CARD_ENABLE
#define MQTT_CARD_REGISTER #define MQTT_CARD_REGISTER
#define DISPLAY_ENABLE // #define DISPLAY_ENABLE
#define WEB_SERVER_ENABLE #define WEB_SERVER_ENABLE
#define LCD_OTA_ENABLE // #define LCD_OTA_ENABLE
//#define REMOTE_VARIABLE_ENABLE // #define REMOTE_VARIABLE_ENABLE
//#define CT_ENABLE // #define CT_ENABLE
#define SMART_VARIABLE_ENABLE // #define SMART_VARIABLE_ENABLE
#define EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
#define EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
// Demo PLC firmware using the ESPMegaPRO OOP library // Demo PLC firmware using the ESPMegaPRO OOP library
ESPMegaPRO espmega = ESPMegaPRO(); ESPMegaPRO espmega = ESPMegaPRO();
#ifdef EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
DigitalOutputCard externalDigitalOutputCard = DigitalOutputCard(1, 0, 1, 1, 0);
#endif
#ifdef EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
void handleExternalDigitalInput(uint8_t pin, uint8_t state)
{
Serial.printf("Digital Input External %d: %d\n", pin, state);
}
DigitalInputCard externalDigitalInputCard = DigitalInputCard(0, 0, 1, 0, 1, 1);
#endif
// Remote Variable // Remote Variable
#ifdef REMOTE_VARIABLE_ENABLE #ifdef REMOTE_VARIABLE_ENABLE
RemoteVariable testVar = RemoteVariable(); RemoteVariable testVar = RemoteVariable();
@ -115,6 +132,7 @@ void input_change_callback(uint8_t pin, uint8_t value)
Serial.print(pin); Serial.print(pin);
Serial.print(" "); Serial.print(" ");
Serial.println(value); Serial.println(value);
espmega.outputs.toggleState(pin);
} }
void mqtt_callback(char *topic, char *payload) void mqtt_callback(char *topic, char *payload)
@ -162,6 +180,14 @@ void setup()
{ {
ESP_LOGI("Initializer", "Starting ESPMegaPRO OOP demo"); ESP_LOGI("Initializer", "Starting ESPMegaPRO OOP demo");
espmega.begin(); espmega.begin();
#ifdef EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
espmega.installCard(6, &externalDigitalOutputCard);
#endif
#ifdef EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
espmega.installCard(7, &externalDigitalInputCard);
externalDigitalInputCard.registerCallback(handleExternalDigitalInput);
#endif
espmega.setTimezone("UTC-7");
ESP_LOGI("Initializer", "Enabling IOT module"); ESP_LOGI("Initializer", "Enabling IOT module");
espmega.enableIotModule(); espmega.enableIotModule();
ESP_LOGI("Initializer", "Enabling Ethernet"); ESP_LOGI("Initializer", "Enabling Ethernet");
@ -185,11 +211,29 @@ void setup()
ESP_LOGI("Initializer", "Connecting to MQTT"); ESP_LOGI("Initializer", "Connecting to MQTT");
espmega.iot->connectToMqtt(); espmega.iot->connectToMqtt();
espmega.iot->registerMqttCallback(mqtt_callback); espmega.iot->registerMqttCallback(mqtt_callback);
// Set debounce time
for (uint8_t i = 0; i < 16; i++)
{
espmega.inputs.setDebounceTime(i, INPUT_DEBOUNCE_TIME_MS);
}
// Set all pin values to 4095
for (uint8_t i = 0; i < 16; i++)
{
espmega.outputs.setValue(i, 4095);
}
#ifdef MQTT_CARD_REGISTER #ifdef MQTT_CARD_REGISTER
ESP_LOGI("Initializer", "Registering cards 0"); ESP_LOGI("Initializer", "Registering cards 0");
espmega.iot->registerCard(0); espmega.iot->registerCard(0);
ESP_LOGI("Initializer", "Registering cards 1"); ESP_LOGI("Initializer", "Registering cards 1");
espmega.iot->registerCard(1); espmega.iot->registerCard(1);
#ifdef EXTERNAL_DIGITAL_OUTPUT_CARD_ENABLE
ESP_LOGI("Initializer", "Registering cards 6");
espmega.iot->registerCard(6);
#endif
#ifdef EXTERNAL_DIGITAL_INPUT_CARD_ENABLE
ESP_LOGI("Initializer", "Registering cards 7");
espmega.iot->registerCard(7);
#endif
#endif #endif
ESP_LOGI("Initializer", "Registering Input change callback"); ESP_LOGI("Initializer", "Registering Input change callback");
espmega.inputs.registerCallback(input_change_callback); espmega.inputs.registerCallback(input_change_callback);
@ -225,7 +269,7 @@ void setup()
#endif #endif
#ifdef REMOTE_VARIABLE_ENABLE #ifdef REMOTE_VARIABLE_ENABLE
ESP_LOGI("Initializer", "Initializing testvar"); ESP_LOGI("Initializer", "Initializing testvar");
testVar.begin(32, "/xm/fan_speed", espmega.iot, true,"/pm/request_fan_speed"); testVar.begin(32, "/xm/fan_speed", espmega.iot, true, "/pm/request_fan_speed");
testVar.enableSetValue("/pm/request_switch_state"); testVar.enableSetValue("/pm/request_switch_state");
#endif #endif
#ifdef CT_ENABLE #ifdef CT_ENABLE
@ -236,6 +280,7 @@ void setup()
ct.bindFRAM(&espmega.fram, 7000); ct.bindFRAM(&espmega.fram, 7000);
ct.loadEnergy(); ct.loadEnergy();
ct.setEnergyAutoSave(true); ct.setEnergyAutoSave(true);
espmega.iot->registerCard(3);
#endif #endif
#ifdef SMART_VARIABLE_ENABLE #ifdef SMART_VARIABLE_ENABLE
ESP_LOGI("Initializer", "Initializing smart variable"); ESP_LOGI("Initializer", "Initializing smart variable");
@ -325,4 +370,13 @@ void loop()
smartVar.setValue(last_smartvar_state ? "true" : "false"); smartVar.setValue(last_smartvar_state ? "true" : "false");
} }
#endif #endif
#ifdef RTC_DEBUG
static uint32_t last_time_print = 0;
if (millis() - last_time_print >= 1000)
{
last_time_print = millis();
rtctime_t time = espmega.getTime();
Serial.printf("Time: %02d:%02d:%02d %02d/%02d/%04d\n", time.hours, time.minutes, time.seconds, time.day, time.month, time.year);
}
#endif
} }