move UART bridge -> tutorials

This commit is contained in:
Sergio R. Caprile authored and Sergio R. Caprile committed 2025-02-26 15:08:06 -03:00
commit aeda7e5520
66 files changed
+56 -40

No files matched your search

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mongoose-esp32-example)
@@ -0,0 +1,38 @@
THISDIR = $(realpath $(CURDIR))
ROOTDIR = $(realpath $(CURDIR)/../../../..)
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf
CMD ?= idf.py build
PORT ?= /dev/ttyUSB0
all: example
example:
true
build: Makefile $(wildcard main/*)
$(DOCKER) $(CMD)
flash:
flash: CMD = idf.py flash
flash: DA = --device $(PORT)
flash: build
.PHONY: build
dashboard.hex: build
esputil mkhex \
0x8000 build/partition_table/partition-table.bin \
0x1000 build/bootloader/bootloader.bin \
0x100000 build/mongoose-esp32-example.bin > $@
flash2: dashboard.hex
esputil -p $(PORT) -b 921600 -fp 0x220 flash dashboard.hex
esputil -p $(PORT) monitor
ESPTOOL ?= esptool.py
flash3:
cd build && $(ESPTOOL) --chip esp32 -p $(PORT) -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x100000 mongoose-esp32-example.bin
clean:
test -d build && $(DOCKER) rm -rf build sdkconfig || true
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/esp32/device-dashboard
@@ -0,0 +1,7 @@
idf_component_register(SRCS "main.c"
"wifi.c"
"net.c"
"packed_fs.c"
"mongoose.c")
component_compile_options(-DHTTP_URL="http://0.0.0.0:80")
component_compile_options(-DHTTPS_URL="https://0.0.0.0:443")
@@ -0,0 +1,272 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#include "net.h"
#define WIFI_SSID "YOUR_WIFI_NETWORK_NAME" // SET THIS!
#define WIFI_PASS "YOUR_WIFI_PASSWORD" // SET THIS!
void app_main(void) {
// Setup wifi. This function is implemented in wifi.c
// It blocks until connected to the configured WiFi network
void wifi_init(const char *ssid, const char *pass);
wifi_init(WIFI_SSID, WIFI_PASS);
// Connected to WiFi, now start HTTP server
struct mg_mgr mgr;
mg_log_set(MG_LL_DEBUG); // Set log level
mg_mgr_init(&mgr);
MG_INFO(("Mongoose version : v%s", MG_VERSION));
MG_INFO(("Listening on : %s", HTTP_URL));
#if MG_ENABLE_MBEDTLS
MG_INFO(("Listening on : %s", HTTPS_URL));
#endif
web_init(&mgr);
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
}
#if MG_OTA == MG_OTA_CUSTOM
enum {
MG_OTA_UNAVAILABLE = 0, // No OTA information is present
MG_OTA_FIRST_BOOT = 1, // Device booting the first time after the OTA
MG_OTA_UNCOMMITTED = 2, // Ditto, but marking us for the rollback
MG_OTA_COMMITTED = 3 // The firmware is good
};
enum { MG_FIRMWARE_CURRENT = 0, MG_FIRMWARE_PREVIOUS = 1 };
void *mg_flash_start(void) {
return NULL;
}
size_t mg_flash_size(void) {
return 0;
}
size_t mg_flash_sector_size(void) {
return 0;
}
size_t mg_flash_write_align(void) {
return 0;
}
int mg_flash_bank(void) {
return 0;
}
bool mg_flash_erase(void *location) {
(void) location;
return false;
}
bool mg_flash_swap_bank(void) {
return true;
}
bool mg_flash_write(void *addr, const void *buf, size_t len) {
(void) addr, (void) buf, (void) len;
return false;
}
void mg_device_reset(void) {
esp_restart();
}
#include "esp_app_format.h"
#include "esp_ota_ops.h"
static size_t s_size = 0; // Firmware size to flash. In-progress indicator
static bool rx_checked = false; // Whether firmware being received has been checked as valid
const esp_partition_t * update_partition = NULL; // The partition the update is being applied to
esp_ota_handle_t update_handle = 0; // Handle of the current update process
const esp_partition_t * get_partition_from_fw(int fw) {
if (MG_FIRMWARE_CURRENT == fw) {
return esp_ota_get_running_partition();
}
const esp_partition_t * p = esp_ota_get_last_invalid_partition();
if (NULL == p) {
p = esp_ota_get_next_update_partition(NULL);
}
return p;
}
// Returns true if the data buffer contains the header of a valid firmware
const size_t fw_size_required = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t);
bool check_fw_header(const void* buf, size_t len) {
// Ensure we have received enough data to do the check
if (len < fw_size_required) {
MG_ERROR(("Insufficient data to check firmware header - %d bytes, require %d bytes", len, fw_size_required));
return false;
}
// Check the magic word of the received firmare
esp_app_desc_t* rx_app_info = (esp_app_desc_t*)(((char*)buf) + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t));
if (ESP_APP_DESC_MAGIC_WORD != rx_app_info->magic_word) {
MG_ERROR(("Invalid app - invalid magic word"));
return false;
}
MG_INFO(("Received firmware: %s %s", rx_app_info->project_name, rx_app_info->version));
// Get the name/version of the running firmware
const esp_partition_t *current = esp_ota_get_running_partition();
esp_app_desc_t curr_app_info;
esp_err_t res = esp_ota_get_partition_description(current, &curr_app_info);
if (ESP_OK != res) {
MG_ERROR(("Unable to get name/version of running firmware"));
return false;
}
MG_INFO(("Running firmware: %s %s", curr_app_info.project_name, curr_app_info.version));
// Check the name of the new firmware is the same as the old firmware
if (memcmp(curr_app_info.project_name, rx_app_info->project_name, sizeof(curr_app_info.project_name)) != 0) {
MG_ERROR(("Firmware name is different. Wrong firmware uploaded! Current %s Uploaded %s", curr_app_info.project_name, rx_app_info->project_name));
return false;
}
// Check the version of the new firmware is different from the old firmware
if (memcmp(curr_app_info.version, rx_app_info->version, sizeof(curr_app_info.version)) == 0) {
MG_ERROR(("Firmware version is the same. Wrong firmware uploaded! Current %s, Uploaded %s", curr_app_info.version, rx_app_info->version));
//return false;
}
return true;
}
size_t mg_ota_size(int fw) {
const esp_partition_t *p = get_partition_from_fw(fw);
if (NULL == p) {
return 0;
}
return p->size;
}
bool mg_ota_begin(size_t new_firmware_size) {
rx_checked = false;
if (s_size) {
MG_ERROR(("OTA already in progress. Call mg_ota_end()"));
return false;
}
int partition_size = mg_ota_size(MG_FIRMWARE_PREVIOUS);
if (new_firmware_size > partition_size) {
MG_ERROR(("Firmware %lu bytes, max %lu", new_firmware_size, partition_size));
return false;
}
update_partition = esp_ota_get_next_update_partition(NULL);
if (NULL == update_partition) {
MG_ERROR(("esp_ota_get_next_update_partition returned NULL"));
return false;
}
esp_err_t res = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);
if (res != ESP_OK) {
esp_ota_abort(update_handle);
MG_ERROR(("esp_ota_begin failed (%s)", esp_err_to_name(res)));
return false;
}
s_size = new_firmware_size;
MG_INFO(("Starting OTA, firmware size %lu", s_size));
return true;
}
bool mg_ota_write(const void *buf, size_t len) {
if (s_size == 0) {
MG_ERROR(("OTA is not started, call mg_ota_begin()"));
return false;
}
if (!rx_checked) {
if (!check_fw_header(buf, len)) {
return false;
}
rx_checked = true;
}
esp_err_t res = esp_ota_write(update_handle, buf, len);
if (res != ESP_OK) {
esp_ota_abort(update_handle);
MG_ERROR(("esp_ota_write FAILED (%s)", esp_err_to_name(res)));
return false;
}
return true;
}
bool mg_ota_end(void) {
if (0 == s_size) {
MG_INFO(("Finishing OTA: fail"));
return false;
}
s_size = 0;
esp_err_t err = esp_ota_end(update_handle);
if (err != ESP_OK) {
MG_ERROR(("esp_ota_end failed (%s)!", esp_err_to_name(err)));
return false;
}
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
MG_ERROR(("esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err)));
return false;
}
return true;
}
bool mg_ota_commit(void) {
esp_ota_mark_app_valid_cancel_rollback();
return true;
}
bool mg_ota_rollback(void) {
if(esp_ota_check_rollback_is_possible()) {
esp_ota_mark_app_invalid_rollback_and_reboot();
return true;
}
MG_ERROR(("Rollback NOT possible"));
return false;
}
int mg_ota_status(int fw) {
const esp_partition_t *p = get_partition_from_fw(fw);
if (NULL == p) {
return 0;
}
int status = MG_OTA_UNAVAILABLE;
esp_ota_img_states_t img_state = ESP_OTA_IMG_UNDEFINED;
esp_err_t res = esp_ota_get_state_partition(p, &img_state);
if (ESP_OK == res) {
if (ESP_OTA_IMG_VALID == img_state) {
status = MG_OTA_COMMITTED;
} else if (ESP_OTA_IMG_UNDEFINED == img_state) {
status = MG_OTA_UNCOMMITTED;
} else if (ESP_OTA_IMG_INVALID == img_state) {
status = MG_OTA_UNAVAILABLE;
} else if (ESP_OTA_IMG_ABORTED == img_state) {
status = MG_OTA_UNAVAILABLE;
} else if (ESP_OTA_IMG_NEW == img_state) {
status = MG_OTA_FIRST_BOOT;
} else if (ESP_OTA_IMG_PENDING_VERIFY == img_state) {
status = MG_OTA_UNCOMMITTED;
}
}
return status;
}
uint32_t mg_ota_crc32(int fw) {
(void) fw;
return 0;
}
uint32_t mg_ota_timestamp(int fw) {
const esp_partition_t *p = get_partition_from_fw(fw);
esp_app_desc_t app;
esp_err_t res = esp_ota_get_partition_description(p, &app);
if (ESP_OK != res)
{
return 0;
}
struct tm datetime = {};
if (NULL == strptime(app.date, "%b %d %Y", &datetime))
{
return 0;
}
if (NULL == strptime(app.time, "%H:%M:%S", &datetime))
{
return 0;
}
return mktime(&datetime);
}
#endif
@@ -0,0 +1 @@
../../../../../mongoose.c
@@ -0,0 +1 @@
../../../../../mongoose.h
@@ -0,0 +1,5 @@
#define MG_ARCH MG_ARCH_ESP32
#define MG_ENABLE_PACKED_FS 1
#define MG_TLS MG_TLS_NONE // change to 'MG_TLS_MBED' to enable TLS
#define MG_OTA MG_OTA_CUSTOM
+1
View File
@@ -0,0 +1 @@
../../net.c
+1
View File
@@ -0,0 +1 @@
../../net.h
@@ -0,0 +1 @@
../../packed_fs.c
@@ -0,0 +1,104 @@
// Code taken from the ESP32 IDF WiFi station Example
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "mongoose.h"
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about
* two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT &&
event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < 10) {
esp_wifi_connect();
s_retry_num++;
MG_INFO(("retry to connect to the AP"));
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
MG_ERROR(("connect to the AP fail"));
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
MG_INFO(("IP ADDRESS:" IPSTR, IP2STR(&event->ip_info.ip)));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init(const char *ssid, const char *pass) {
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(
WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(
IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
wifi_config_t c = {.sta = {.threshold = {.authmode = WIFI_AUTH_WPA2_PSK},
.pmf_cfg = {.capable = true, .required = false}}};
snprintf((char *) c.sta.ssid, sizeof(c.sta.ssid), "%s", ssid);
snprintf((char *) c.sta.password, sizeof(c.sta.password), "%s", pass);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &c));
ESP_ERROR_CHECK(esp_wifi_start());
MG_DEBUG(("wifi_init_sta finished."));
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
MG_INFO(("connected to ap SSID:%s password:%s", ssid, pass));
} else if (bits & WIFI_FAIL_BIT) {
MG_ERROR(("Failed to connect to SSID:%s, password:%s", ssid, pass));
} else {
MG_ERROR(("UNEXPECTED EVENT"));
}
/* The event will not be processed after unregister */
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
vEventGroupDelete(s_wifi_event_group);
}
@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x6000
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
ota_0, app, ota_0, , 948K,
ota_1, app, ota_1, , 948K,
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, , 0x6000
3 otadata, data, ota, , 0x2000,
4 phy_init, data, phy, , 0x1000,
5 ota_0, app, ota_0, , 948K,
6 ota_1, app, ota_1, , 948K,
@@ -0,0 +1,5 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
#CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
@@ -1,5 +1,5 @@
THISDIR = $(realpath $(CURDIR))
ROOTDIR = $(realpath $(CURDIR)/../../..)
ROOTDIR = $(realpath $(CURDIR)/../../../../..)
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) docker.io/mdashnet/8266
CMD ?= make -C src defconfig app
PORT ?= /dev/ttyUSB0
@@ -1 +1 @@
../../../../../mongoose.c
../../../../../../../mongoose.c
@@ -1 +1 @@
../../../../../mongoose.h
../../../../../../../mongoose.h
+36
View File
@@ -0,0 +1,36 @@
PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c net.c packed_fs.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_LINES=1
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
OUT ?= -o $(PROG) # Build output
endif
all: $(PROG) # Default target. Build and run program
$(RUN) ./$(PROG) $(ARGS)
ifneq ($(OS),Windows_NT)
# Before embedding files, gzip them to save space
packed_fs.c: $(shell find web_root -type f) Makefile
rm -rf tmp/web_root && mkdir tmp && cp -r web_root tmp/
find tmp -type f | xargs -n1 gzip
$(CC) ../../../test/pack.c -o pack
cd tmp && ../pack `find web_root -type f` > ../$@
endif
$(PROG): $(SOURCES) # Build program from sources
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
clean: # Cleanup. Delete built program and all build artifacts
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM pack tmp
+14
View File
@@ -0,0 +1,14 @@
# A UART to network bridge
This example is a demonstration of how Mongoose Library could be integrated
into an embedded device and provide a UART-to-Network bridge capability:
- The device opens listening TCP and Websocket ports and waits for connections
- When a client connects, data is exchanged with the device's UART
- Everything the client sends, is sent to the UART
- Everything that is read from the UART, gets sent to the client
- Multiple clients are allowed
- Live UART console allows to talk to the UART from the web page
- Web UI is hardcoded into the binary and does not need a filesystem
See a detailed tutorial at https://mongoose.ws/tutorials/uart-bridge/
@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mongoose-esp32-example)
+32
View File
@@ -0,0 +1,32 @@
CWD = $(realpath $(CURDIR))
MNT = $(realpath $(CURDIR)/../../../..)
PORT ?= /dev/ttyUSB0
CMD ?= build
all: example
example:
true
build: Makefile $(wildcard main/*)
docker run --rm $(DA) -v $(MNT):$(MNT) -w $(CWD) espressif/idf idf.py $(CMD)
flash:
flash: CMD = flash
flash: DA = --device $(PORT)
flash: build
.PHONY: build
bridge.hex: build
esputil mkhex \
0x8000 build/partition_table/partition-table.bin \
0x1000 build/bootloader/bootloader.bin \
0x100000 build/mongoose-esp32-example.bin > $@
flash2: bridge.hex
esputil -p $(PORT) -b 921600 -fp 0x220 flash bridge.hex
esputil -p $(PORT) monitor
clean:
rm -rf build
@@ -0,0 +1,41 @@
# A UART to network bridge for ESP32
![](../../uart-bridge/screenshots/dashboard.png)
- See detailed tutorial at https://mongoose.ws/tutorials/esp32/uart-bridge/
## Flash pre-built firmware
You can flash a pre-built firmware to the ESP32 device using the following
instructions:
1. Connect your ESP32 device to the workstation. It should be accessible
via a serial port
2. Download and unzip ESP32 flashing tool from https://mongoose.ws/downloads/esputil.zip
3. Download a prebuilt firmware https://mongoose.ws/downloads/uart-bridge.hex into the unzipped directory
4. Start command prompt (or terminal on Mac/Linux). Run `cd
PATH/TO/esputil` to go into the unzipped `esputil/` directory. After that, run
the following command (change `COMPORT` to the board's serial port):
| OS | Command |
| ------- | ------- |
| Windows | <pre><code class="language-bash">.\windows\esputil -p COMPORT flash uart-bridge.hex</code></pre>|
| Linux | <pre><code class="language-bash">./linux/esputil -p COMPORT flash uart-bridge.hex</pre> |
| MacOS | <pre><code class="language-bash">./macos/esputil -p COMPORT flash uart-bridge.hex</code></pre> |
Next step is to monitor and follow the instructions.
```sh
esputil -p COMPORT monitor
```
Note: if monitor command shows constant restarts, the flash parameters
settings can be wrong. Reflash your device with `-fp ...` flash parameters
settings. For example, WROOM-32 based boards use `-fp 0x220`:
```sh
esputil -p COMPORT -fp 0x220 flash uart-bridge.hex
```
For more on possible options for flash parameters, see
https://github.com/cpq/esputil#flash-parameters
@@ -0,0 +1,8 @@
idf_component_register(SRCS "main.c"
"wifi.c"
"uart.c"
"cli.c"
"net.c"
"packed_fs.c"
"mongoose.c")
component_compile_options(-DUART_API_IMPLEMENTED=1)
@@ -0,0 +1,94 @@
#include "main.h"
static void cli_wifi(const char *ssid, const char *pass) {
if (wifi_init(ssid, pass)) {
mg_file_printf(&mg_fs_posix, WIFI_FILE, "{%m:%m,%m:%m}\n", mg_print_esc, 0,
"ssid", mg_print_esc, 0, ssid, mg_print_esc, 0, "pass",
mg_print_esc, 0, pass);
MG_INFO(("Reboot now"));
}
}
static void cli_ls(void) {
DIR *dirp = opendir(FS_ROOT);
struct dirent *dp;
if (dirp == NULL) {
MG_ERROR(("Cannot open FS: %d", errno));
} else {
while ((dp = readdir(dirp)) != NULL) {
/* Do not show current and parent dirs */
if (strcmp((const char *) dp->d_name, ".") == 0 ||
strcmp((const char *) dp->d_name, "..") == 0) {
continue;
} else {
printf("%s\n", dp->d_name);
}
}
closedir(dirp);
}
}
static void cli_cat(const char *fname) {
char path[MG_PATH_MAX];
snprintf(path, sizeof(path), "%s/%s", FS_ROOT, fname);
FILE *fp = fopen(path, "r");
if (fp != NULL) {
int ch;
while ((ch = fgetc(fp)) != EOF) putchar(ch);
fclose(fp);
}
}
static void cli_rm(const char *fname) {
char path[100];
snprintf(path, sizeof(path), "%s/%s", FS_ROOT, fname);
remove(path);
}
static struct mg_iobuf in;
void cli(uint8_t input_byte) {
if (input_byte == 0 || input_byte == 0xff) return;
if (in.len >= 128) in.len = 0;
mg_iobuf_add(&in, in.len, &input_byte, sizeof(input_byte));
if (input_byte == '\n') {
const char *arrow = "---";
char buf0[10], buf1[50], buf2[250];
in.buf[in.len] = '\0';
buf0[0] = buf1[0] = buf2[0] = '\0';
sscanf((char *) in.buf, "%9s %49s %249[^\r\n]", buf0, buf1, buf2);
printf("%s CLI command: '%s'\n", arrow, buf0);
if (strcmp(buf0, "reboot") == 0) {
esp_restart();
} else if (strcmp(buf0, "ls") == 0) {
cli_ls();
} else if (strcmp(buf0, "cat") == 0) {
cli_cat(buf1);
} else if (strcmp(buf0, "rm") == 0) {
cli_rm(buf1);
} else if (strcmp(buf0, "reboot") == 0) {
esp_restart();
} else if (strcmp(buf0, "ll") == 0) {
mg_log_set(atoi(buf1));
} else if (strcmp(buf0, "wifi") == 0) {
cli_wifi(buf1, buf2);
} else {
printf("%s %s\n", arrow, "Unknown command. Usage:");
printf("%s %s\n", arrow, " set NAME VALUE");
printf("%s %s\n", arrow, " rm FILENAME");
printf("%s %s\n", arrow, " cat FILENAME");
printf("%s %s\n", arrow, " ls");
printf("%s %s\n", arrow, " reboot");
printf("%s %s\n", arrow, " wifi WIFI_NET WIFI_PASS");
}
printf("%s %s\n", arrow, "CLI output end");
in.len = 0;
}
}
void cli_init(void) {
mg_iobuf_init(&in, 0, 32);
}
@@ -0,0 +1,50 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include "main.h"
const char *s_listening_url = "http://0.0.0.0:80";
struct mg_str config_read(void) {
return mg_file_read(&mg_fs_posix, FS_ROOT "/config.json");
}
void config_write(struct mg_str config) {
mg_file_write(&mg_fs_posix, FS_ROOT "/config.json", config.buf, config.len);
}
void app_main(void) {
// Mount filesystem
esp_vfs_spiffs_conf_t conf = {
.base_path = FS_ROOT, .max_files = 20, .format_if_mount_failed = true};
int res = esp_vfs_spiffs_register(&conf);
MG_INFO(("FS at %s initialised, status: %d", conf.base_path, res));
// Try to connect to wifi by using saved WiFi credentials
struct mg_str json = mg_file_read(&mg_fs_posix, WIFI_FILE);
if (json.buf != NULL) {
char *ssid = mg_json_get_str(json, "$.ssid");
char *pass = mg_json_get_str(json, "$.pass");
while (!wifi_init(ssid, pass)) (void) 0;
free(ssid);
free(pass);
free((void *) json.buf);
} else {
// If WiFi is not configured, run CLI until configured
MG_INFO(("WiFi is not configured, running CLI. Press enter"));
cli_init();
for (;;) {
uint8_t ch = getchar();
cli(ch);
usleep(10000);
}
}
// Connected to WiFi, now start HTTP server
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_log_set(MG_LL_DEBUG); // Set log level
MG_INFO(("Mongoose v%s on %s", MG_VERSION, s_listening_url));
mg_http_listen(&mgr, s_listening_url, uart_bridge_fn, NULL);
for (;;) mg_mgr_poll(&mgr, 10); // Infinite event loop
}
@@ -0,0 +1,18 @@
#pragma once
#include "mongoose.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_spiffs.h"
#include "freertos/FreeRTOS.h"
#define FS_ROOT "/spiffs"
#define WIFI_FILE FS_ROOT "/wifi.json"
#define UART_NO 1
void uart_bridge_fn(struct mg_connection *, int, void *);
int uart_read(void *buf, size_t len);
bool wifi_init(const char *ssid, const char *pass);
void cli(uint8_t ch);
void cli_init(void);
+1
View File
@@ -0,0 +1 @@
../../../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../../../mongoose.h
@@ -0,0 +1,3 @@
#define MG_ARCH MG_ARCH_ESP32
#define MG_ENABLE_PACKED_FS 1
+1
View File
@@ -0,0 +1 @@
../../net.c
+1
View File
@@ -0,0 +1 @@
../../packed_fs.c
@@ -0,0 +1,44 @@
#include "main.h"
int uart_close(int no) {
return uart_driver_delete(no);
}
int uart_open(int no, int rx, int tx, int cts, int rts, int baud) {
uart_config_t uart_config = {
.baud_rate = baud,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = cts > 0 && rts > 0 ? UART_HW_FLOWCTRL_CTS_RTS
: cts > 0 ? UART_HW_FLOWCTRL_CTS
: rts > 0 ? UART_HW_FLOWCTRL_RTS
: UART_HW_FLOWCTRL_DISABLE,
};
int e1 = uart_param_config(no, &uart_config);
int e2 = uart_set_pin(no, tx, rx, rts, cts);
int e3 =
uart_driver_install(no, UART_FIFO_LEN * 2, UART_FIFO_LEN * 2, 0, NULL, 0);
MG_INFO(("%d: %d/%d/%d, %d %d %d", no, rx, tx, baud, e1, e2, e3));
if (e1 != ESP_OK || e2 != ESP_OK || e3 != ESP_OK) return -1;
return no;
}
void uart_init(int tx, int rx, int baud) {
uart_open(UART_NO, rx, tx, -1, -1, baud);
}
int uart_read(void *buf, size_t len) {
size_t x = 0;
int no = UART_NO;
if (uart_get_buffered_data_len(no, &x) != ESP_OK || x == 0) return 0;
int n = uart_read_bytes(no, buf, len, 10 / portTICK_PERIOD_MS);
MG_DEBUG(("%d bytes: [%.*s]", n, n, (char *) buf));
return n;
}
int uart_write(const void *buf, int len) {
int no = UART_NO;
MG_DEBUG(("%d bytes: [%.*s]", len, len, (char *) buf));
return uart_write_bytes(no, (const char *) buf, len);
}
@@ -0,0 +1,108 @@
// Code taken from the ESP32 IDF WiFi station Example
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "mongoose.h"
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about
* two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT &&
event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < 3) {
esp_wifi_connect();
s_retry_num++;
MG_INFO(("retry to connect to the AP"));
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
MG_ERROR(("connect to the AP fail"));
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
MG_INFO(("IP ADDRESS: " IPSTR ". Go to:", IP2STR(&event->ip_info.ip)));
MG_INFO(("http://" IPSTR, IP2STR(&event->ip_info.ip)));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
bool wifi_init(const char *ssid, const char *pass) {
bool result = false;
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(
WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(
IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
wifi_config_t c = {.sta = {.threshold = {.authmode = WIFI_AUTH_WPA2_PSK},
.pmf_cfg = {.capable = true, .required = false}}};
snprintf((char *) c.sta.ssid, sizeof(c.sta.ssid), "%s", ssid);
snprintf((char *) c.sta.password, sizeof(c.sta.password), "%s", pass);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &c));
ESP_ERROR_CHECK(esp_wifi_start());
MG_DEBUG(("wifi_init_sta finished."));
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
MG_INFO(("connected to ap SSID:%s", ssid));
result = true;
} else if (bits & WIFI_FAIL_BIT) {
MG_ERROR(("Failed to connect to SSID:%s, password:%s", ssid, pass));
} else {
MG_ERROR(("UNEXPECTED EVENT"));
}
/* The event will not be processed after unregister */
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
vEventGroupDelete(s_wifi_event_group);
return result;
}
Whitespace-only changes.
@@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
storage, data, spiffs, 0x10000, 0x10000,
factory, app, factory, 0x100000, 1M,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x6000
3 phy_init data phy 0xf000 0x1000
4 storage data spiffs 0x10000 0x10000
5 factory app factory 0x100000 1M
@@ -0,0 +1,4 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
#CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
const char *s_listening_url = "http://0.0.0.0:8000";
void uart_bridge_fn(struct mg_connection *, int, void *);
int main(void) {
struct mg_mgr mgr;
mg_log_set(MG_LL_DEBUG);
mg_mgr_init(&mgr);
mg_http_listen(&mgr, s_listening_url, uart_bridge_fn, &mgr);
MG_INFO(("Listening on %s", s_listening_url));
while (mgr.conns != NULL) mg_mgr_poll(&mgr, 500);
mg_mgr_free(&mgr);
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../mongoose.h
+234
View File
@@ -0,0 +1,234 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#define DEFAULT_TCP "tcp://0.0.0.0:4001"
#define DEFAULT_WEBSOCKET "ws://0.0.0.0:4002"
#define DEFAULT_MQTT "mqtt://broker.hivemq.com:1883?tx=b/tx&rx=b/rx"
struct endpoint {
char *url;
bool enable;
struct mg_connection *c;
};
struct state {
struct endpoint tcp, websocket, mqtt;
int tx, rx, baud;
} s_state = {.tcp = {.enable = true},
.websocket = {.enable = true},
.mqtt = {.enable = false},
.tx = 5,
.rx = 4,
.baud = 115200};
void uart_init(int tx, int rx, int baud);
int uart_read(void *buf, size_t len);
void uart_write(const void *buf, size_t len);
struct mg_str config_read(void);
void config_write(struct mg_str config);
// Let users define their own UART API. If they don't, use a dummy one
#if defined(UART_API_IMPLEMENTED)
#else
void uart_init(int tx, int rx, int baud) {
// We use stdin/stdout as UART. Make stdin non-blocking
#if MG_ARCH != MG_ARCH_WIN32
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
#endif
(void) tx, (void) rx, (void) baud;
}
void uart_write(const void *buf, size_t len) {
fwrite(buf, 1, len, stdout); // Write to stdout
fflush(stdout);
}
int uart_read(void *buf, size_t len) {
#if MG_ARCH == MG_ARCH_WIN32
(void) buf, (void) len;
return 0;
#else
return read(0, buf, len); // Read from stdin
#endif
}
struct mg_str config_read(void) {
return mg_file_read(&mg_fs_posix, "config.json");
}
void config_write(struct mg_str config) {
mg_file_write(&mg_fs_posix, "config.json", config.buf, config.len);
}
#endif
// Event handler for a connected Websocket client
static void ws_fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
mg_ws_upgrade(c, ev_data, NULL);
} else if (ev == MG_EV_WS_OPEN) {
// c->is_hexdumping = 1;
c->data[0] = 'W'; // When WS handhake is done, mark us as WS client
} else if (ev == MG_EV_WS_MSG) {
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
uart_write(wm->data.buf, wm->data.len); // Send to UART
c->recv.len = 0; // Discard received data
} else if (ev == MG_EV_CLOSE) {
if (c->is_listening) s_state.websocket.c = NULL;
}
}
// Event handler for a connected TCP client
static void tcp_fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ACCEPT) {
// c->is_hexdumping = 1;
c->data[0] = 'T'; // When client is connected, mark us as TCP client
} else if (ev == MG_EV_READ) {
uart_write(c->recv.buf, c->recv.len); // Send to UART
c->recv.len = 0; // Discard received data
} else if (ev == MG_EV_CLOSE) {
if (c->is_listening) s_state.tcp.c = NULL;
}
(void) ev_data;
}
// Extract topic name from the MQTT address
static struct mg_str mqtt_topic(const char *name, const char *dflt) {
struct mg_str qs = mg_str(strchr(s_state.mqtt.url, '?'));
struct mg_str v = mg_http_var(qs, mg_str(name));
return v.buf == NULL ? mg_str(dflt) : v;
}
// Event handler for MQTT connection
static void mq_fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1;
} else if (ev == MG_EV_MQTT_OPEN) {
c->data[0] = 'M';
struct mg_mqtt_opts sub_opts;
memset(&sub_opts, 0, sizeof(sub_opts));
sub_opts.topic = mqtt_topic("rx", "b/rx");
sub_opts.qos = 1;
mg_mqtt_sub(c, &sub_opts); // Subscribe to RX topic
} else if (ev == MG_EV_MQTT_MSG) {
struct mg_mqtt_message *mm = ev_data; // MQTT message
uart_write(mm->data.buf, mm->data.len); // Send to UART
} else if (ev == MG_EV_CLOSE) {
s_state.mqtt.c = NULL;
}
}
// Software timer with a frequency close to the scheduling time slot
static void timer_fn(void *param) {
// Start listeners if they're stopped for any reason
struct mg_mgr *mgr = (struct mg_mgr *) param;
if (s_state.tcp.c == NULL && s_state.tcp.enable) {
s_state.tcp.c = mg_listen(mgr, s_state.tcp.url, tcp_fn, 0);
}
if (s_state.websocket.c == NULL && s_state.websocket.enable) {
s_state.websocket.c = mg_http_listen(mgr, s_state.websocket.url, ws_fn, 0);
}
if (s_state.mqtt.c == NULL && s_state.mqtt.enable) {
struct mg_mqtt_opts opts = {.clean = true};
s_state.mqtt.c = mg_mqtt_connect(mgr, s_state.mqtt.url, &opts, mq_fn, 0);
}
// Read UART
char buf[512];
int len = uart_read(buf, sizeof(buf));
if (len > 0) {
// Iterate over all connections. Send data to WS and TCP clients
for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
if (c->data[0] == 'W') mg_ws_send(c, buf, len, WEBSOCKET_OP_TEXT);
if (c->data[0] == 'T') mg_send(c, buf, len);
if (c->data[0] == 'M') {
struct mg_mqtt_opts pub_opts;
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = mqtt_topic("tx", "b/tx");
pub_opts.message = mg_str_n(buf, len);
pub_opts.qos = 1, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
}
}
}
}
static void update_string(struct mg_str json, const char *path, char **value) {
char *jval;
if ((jval = mg_json_get_str(json, path)) != NULL) {
free(*value);
*value = strdup(jval);
}
}
static void config_apply(struct mg_str s) {
MG_INFO(("Applying config: %.*s", (int) s.len, s.buf));
bool b;
if (mg_json_get_bool(s, "$.tcp.enable", &b)) s_state.tcp.enable = b;
if (mg_json_get_bool(s, "$.ws.enable", &b)) s_state.websocket.enable = b;
if (mg_json_get_bool(s, "$.mqtt.enable", &b)) s_state.mqtt.enable = b;
update_string(s, "$.tcp.url", &s_state.tcp.url);
update_string(s, "$.mqtt.url", &s_state.mqtt.url);
update_string(s, "$.ws.url", &s_state.websocket.url);
double v;
if (mg_json_get_num(s, "$.rx", &v)) s_state.rx = (int) v;
if (mg_json_get_num(s, "$.tx", &v)) s_state.tx = (int) v;
if (mg_json_get_num(s, "$.baud", &v)) s_state.baud = (int) v;
if (s_state.mqtt.c) s_state.mqtt.c->is_draining = 1;
if (s_state.tcp.c) s_state.tcp.c->is_draining = 1;
if (s_state.websocket.c) s_state.websocket.c->is_draining = 1;
}
// HTTP request handler function
void uart_bridge_fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_OPEN && c->is_listening) {
struct mg_str config = mg_file_read(&mg_fs_posix, "config.json");
if (config.buf != NULL) config_apply(config);
free(config.buf);
s_state.tcp.url = strdup(DEFAULT_TCP);
s_state.websocket.url = strdup(DEFAULT_WEBSOCKET);
s_state.mqtt.url = strdup(DEFAULT_MQTT);
mg_timer_add(c->mgr, 20, MG_TIMER_REPEAT, timer_fn, c->mgr);
uart_init(s_state.tx, s_state.rx, s_state.baud);
// mg_log_set(MG_LL_DEBUG); // Set log level
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/hi"), NULL)) {
mg_http_reply(c, 200, "", "hi\n"); // Testing endpoint
} else if (mg_match(hm->uri, mg_str("/api/config/set"), NULL)) {
config_apply(hm->body);
config_write(hm->body);
mg_http_reply(c, 200, "", "true\n");
} else if (mg_match(hm->uri, mg_str("/api/config/get"), NULL)) {
mg_http_reply(c, 200, "Content-Type: application/json\r\n",
"{%m:{%m:%m,%m:%s},%m:{%m:%m,%m:%s},%m:{%m:%m,%m:%s},"
"%m:%d,%m:%d,%m:%d}\n",
mg_print_esc, 0, "tcp", mg_print_esc, 0, "url",
mg_print_esc, 0, s_state.tcp.url, mg_print_esc, 0, "enable",
s_state.tcp.enable ? "true" : "false", mg_print_esc, 0,
"ws", mg_print_esc, 0, "url", mg_print_esc, 0,
s_state.websocket.url, mg_print_esc, 0, "enable",
s_state.websocket.enable ? "true" : "false", mg_print_esc,
0, "mqtt", mg_print_esc, 0, "url", mg_print_esc, 0,
s_state.mqtt.url, mg_print_esc, 0, "enable",
s_state.mqtt.enable ? "true" : "false", mg_print_esc, 0,
"rx", s_state.rx, mg_print_esc, 0, "tx", s_state.tx,
mg_print_esc, 0, "baud", s_state.baud);
} else {
struct mg_http_serve_opts opts;
memset(&opts, 0, sizeof(opts));
#if 1
opts.root_dir = "/web_root";
opts.fs = &mg_fs_packed;
#else
opts.root_dir = "web_root";
#endif
mg_http_serve_dir(c, ev_data, &opts);
}
}
}
+753
View File
@@ -0,0 +1,753 @@
#include <stddef.h>
#include <string.h>
#include <time.h>
#if defined(__cplusplus)
extern "C" {
#endif
const char *mg_unlist(size_t no);
const char *mg_unpack(const char *, size_t *, time_t *);
#if defined(__cplusplus)
}
#endif
static const unsigned char v1[] = {
31, 139, 8, 8, 6, 78, 191, 103, 0, 3, 105, 110, // .....N.g..in
100, 101, 120, 46, 104, 116, 109, 108, 0, 93, 144, 177, // dex.html.]..
78, 196, 48, 12, 134, 247, 123, 138, 144, 153, 180, 176, // N.0...{.....
49, 36, 149, 208, 245, 6, 38, 24, 64, 130, 49, 77, // 1$....&.@.1M
124, 23, 67, 154, 148, 196, 237, 169, 111, 79, 218, 30, // |.C.....oO..
226, 196, 100, 255, 246, 103, 203, 254, 229, 77, 251, 188, // ..d..g...M..
127, 253, 120, 57, 48, 71, 189, 111, 118, 114, 9, 204, // ..x90G.ovr..
235, 112, 82, 28, 2, 111, 118, 140, 73, 7, 218, 46, // .pR..ov.I...
73, 73, 9, 201, 67, 211, 194, 132, 6, 88, 171, 179, // II..C....X..
235, 162, 78, 86, 214, 91, 125, 99, 122, 32, 205, 140, // ..NV.[}cz ..
211, 41, 3, 41, 62, 210, 81, 60, 112, 86, 95, 55, // .).)>.Q<pV_7
29, 209, 32, 224, 123, 196, 73, 241, 119, 241, 246, 40, // .. .{.I.w..(
246, 177, 31, 52, 97, 231, 129, 51, 19, 3, 65, 40, // ...4a..3..A(
147, 79, 7, 5, 246, 4, 255, 102, 131, 238, 65, 241, // .O.....f..A.
9, 225, 60, 196, 68, 87, 248, 25, 45, 57, 101, 215, // ..<.DW..-9e.
211, 196, 42, 110, 25, 6, 36, 212, 94, 100, 163, 61, // ..*n..$.^d.=
168, 251, 234, 238, 111, 153, 199, 240, 197, 18, 120, 197, // ....o.....x.
51, 205, 30, 178, 3, 40, 219, 92, 130, 227, 165, 82, // 3....(.....R
153, 156, 47, 184, 172, 127, 45, 144, 93, 180, 115, 35, // ../...-.].s#
235, 53, 44, 58, 155, 132, 3, 49, 154, 135, 114, 85, // .5,:...1..rU
31, 237, 184, 188, 144, 147, 41, 66, 99, 168, 62, 51, // ......)Bc.>3
47, 244, 6, 21, 115, 235, 205, 228, 31, 137, 226, 92, // /...s.......
130, 117, 1, 0, 0, 0 // .u...
};
static const unsigned char v2[] = {
31, 139, 8, 8, 6, 78, 191, 103, 0, 3, 109, 97, // .....N.g..ma
105, 110, 46, 106, 115, 0, 181, 88, 109, 111, 219, 70, // in.js..Xmo.F
18, 254, 174, 95, 49, 80, 125, 37, 121, 165, 72, 43, // ..._1P}%y.H+
113, 225, 131, 44, 235, 144, 6, 45, 112, 135, 38, 233, // q..,...-p.&.
217, 42, 114, 128, 99, 192, 75, 114, 37, 50, 33, 119, // .*r.c.Kr%2!w
89, 238, 210, 148, 161, 242, 191, 119, 118, 185, 164, 72, // Y......wv..H
89, 113, 172, 6, 81, 2, 152, 162, 102, 102, 103, 158, // Yq..Q...ffg.
121, 217, 153, 177, 74, 65, 65, 200, 34, 9, 165, 117, // y...JAA."..u
49, 74, 178, 156, 23, 18, 182, 175, 57, 62, 48, 202, // 1J......9>0.
164, 11, 49, 254, 151, 89, 234, 66, 65, 89, 68, 11, // ..1..Y.BAYD.
23, 144, 225, 231, 213, 138, 134, 82, 63, 94, 75, 34, // .......R?^K"
169, 126, 186, 162, 171, 26, 86, 5, 207, 192, 242, 252, // .~....V.....
188, 160, 36, 148, 94, 150, 48, 239, 163, 64, 193, 163, // ..$.^.0..@..
144, 51, 33, 225, 13, 21, 130, 172, 41, 92, 66, 6, // .3!.....).B.
151, 11, 45, 248, 110, 46, 114, 194, 70, 128, 74, 60, // ..-.n.r.F.J<
164, 244, 114, 28, 242, 148, 23, 51, 56, 217, 102, 94, // ..r....38.f^
214, 80, 123, 37, 65, 157, 254, 13, 214, 119, 103, 103, // .P{%A....wgg
103, 22, 204, 240, 225, 229, 249, 75, 171, 190, 24, 47, // g......K.../
144, 173, 79, 24, 17, 73, 234, 209, 220, 87, 18, 23, // ..O..I...W..
119, 221, 169, 175, 242, 28, 79, 92, 149, 44, 148, 9, // w.....O..,..
103, 118, 94, 240, 92, 56, 176, 69, 230, 230, 247, 155, // gv^..8.E....
112, 181, 118, 65, 80, 249, 122, 181, 190, 69, 202, 214, // p.vAP.z..E..
44, 123, 43, 195, 124, 6, 219, 218, 133, 74, 52, 127, // ,{+.|....J4.
179, 63, 164, 84, 79, 181, 115, 177, 99, 55, 199, 11, // .?.TO.s.c7..
45, 195, 152, 40, 6, 130, 110, 110, 251, 244, 248, 135, // -..(..nn....
33, 126, 52, 106, 14, 109, 191, 13, 56, 86, 36, 21, // !~4j.m..8V$.
180, 207, 36, 55, 82, 147, 47, 55, 114, 64, 104, 89, // ..$7R./7r@hY
125, 170, 170, 81, 226, 253, 240, 120, 86, 166, 41, 82, // }..Q...xV.)R
117, 100, 5, 93, 21, 84, 196, 72, 98, 59, 232, 7, // ud.].T.Hb;..
124, 175, 62, 43, 42, 195, 216, 182, 124, 146, 39, 62, // |.>+*...|.'>
210, 173, 146, 181, 191, 166, 210, 114, 60, 25, 83, 102, // .......r<.Sf
23, 202, 97, 5, 122, 19, 17, 116, 250, 239, 26, 220, // ..a.z..t....
236, 194, 233, 159, 128, 140, 58, 146, 240, 132, 178, 192, // ......:.....
224, 185, 87, 231, 128, 173, 190, 192, 159, 127, 2, 42, // ..W........*
237, 101, 68, 29, 231, 123, 255, 156, 217, 31, 162, 31, // .eD..{......
28, 223, 81, 63, 220, 88, 22, 18, 223, 58, 55, 211, // ..Q?.X...:7.
219, 158, 180, 74, 145, 190, 167, 129, 224, 225, 39, 42, // ...J......'*
251, 190, 108, 220, 8, 224, 251, 96, 112, 4, 201, 161, // ..l....`p...
50, 164, 5, 40, 37, 92, 245, 10, 35, 59, 165, 25, // 2..(%...#;..
70, 52, 188, 191, 214, 82, 121, 74, 53, 231, 61, 41, // F4...RyJ5.=)
16, 15, 227, 146, 67, 162, 27, 18, 99, 141, 177, 203, // ....C...c...
198, 136, 241, 42, 225, 105, 219, 206, 78, 79, 95, 104, // ...*.i..NO_h
31, 180, 180, 41, 18, 86, 9, 139, 120, 229, 165, 60, // ...).V..x..<
36, 74, 154, 11, 24, 117, 168, 198, 37, 164, 158, 126, // $J...u..%..~
194, 48, 247, 10, 154, 167, 36, 68, 23, 198, 82, 230, // .0....$D..R.
104, 183, 85, 9, 107, 32, 72, 38, 24, 34, 10, 178, // h.U.k H&."..
75, 184, 59, 217, 106, 190, 218, 247, 79, 182, 169, 23, // K.;.j...O...
115, 33, 25, 201, 104, 61, 195, 247, 168, 80, 237, 87, // s!..h=...P.W
226, 174, 101, 69, 48, 140, 133, 120, 254, 90, 97, 62, // ..eE0..x.Za>
144, 90, 9, 148, 199, 104, 5, 136, 232, 181, 70, 116, // .Z...h....Ft
64, 130, 102, 113, 198, 115, 202, 218, 240, 232, 112, 128, // @.fq.s....p.
65, 184, 218, 178, 40, 105, 199, 5, 77, 216, 217, 149, // A...(i..M...
232, 94, 213, 3, 137, 89, 151, 250, 244, 126, 40, 116, // .^...Y...~(t
79, 93, 122, 239, 34, 137, 206, 229, 161, 244, 54, 179, // O]z.".....6.
236, 141, 226, 223, 120, 200, 132, 224, 218, 55, 91, 69, // ....x....7[E
58, 107, 121, 16, 48, 44, 23, 51, 80, 218, 213, 183, // :ky.0,.3P...
206, 103, 180, 9, 83, 46, 232, 97, 111, 99, 208, 165, // .g..S..aoc..
148, 20, 203, 36, 163, 188, 148, 54, 250, 160, 167, 6, // ...$...6....
126, 67, 46, 149, 134, 230, 231, 46, 116, 92, 152, 158, // ~C......t...
158, 158, 14, 53, 222, 97, 181, 75, 231, 62, 88, 38, // ...5.a.K.>X&
47, 7, 10, 154, 63, 157, 92, 91, 19, 212, 58, 27, // /...?..[..:.
186, 210, 107, 247, 29, 99, 178, 217, 54, 146, 134, 201, // ..k..c..6...
98, 216, 93, 208, 37, 104, 151, 82, 2, 171, 249, 97, // b.].%h.R...a
151, 124, 25, 105, 44, 69, 240, 3, 88, 31, 152, 213, // .|.i,E..X...
130, 173, 205, 219, 161, 157, 172, 64, 5, 130, 130, 90, // .......@...Z
157, 99, 239, 24, 12, 65, 83, 199, 218, 218, 85, 247, // .c...AS...U.
82, 29, 79, 138, 9, 235, 41, 117, 160, 42, 33, 55, // R.O...)u.*!7
158, 188, 243, 215, 129, 79, 70, 101, 204, 35, 188, 42, // .....OFe.#.*
126, 123, 119, 189, 180, 220, 167, 72, 99, 74, 240, 94, // ~{w....HcJ.^
83, 245, 221, 66, 127, 73, 44, 17, 147, 229, 67, 78, // S..B.I,...CN
45, 100, 38, 121, 158, 38, 77, 2, 251, 170, 240, 89, // -d&y.&M....Y
245, 147, 146, 2, 30, 61, 204, 224, 191, 215, 239, 222, // .....=......
122, 234, 70, 101, 235, 100, 245, 160, 42, 133, 243, 4, // z.Fe.d..*...
87, 221, 47, 165, 152, 153, 223, 127, 175, 80, 211, 225, // W./......P..
105, 15, 42, 170, 208, 133, 143, 7, 31, 123, 53, 87, // i.*......{5W
187, 231, 93, 240, 17, 35, 194, 35, 66, 36, 107, 102, // ..]..#.#B$kf
111, 92, 69, 227, 244, 238, 4, 214, 112, 218, 204, 85, // o.E.....p..U
63, 124, 145, 123, 123, 195, 110, 103, 251, 175, 241, 157, // ?|.{{.ng....
230, 174, 251, 130, 181, 19, 59, 63, 225, 55, 188, 47, // ......;?.7./
55, 51, 200, 73, 33, 232, 127, 152, 196, 68, 246, 36, // 73.I!....D.$
41, 176, 98, 122, 247, 36, 197, 82, 81, 15, 121, 175, // ).bz.$.RQ.y.
246, 120, 139, 35, 120, 127, 34, 101, 52, 228, 14, 240, // .x.#x."e4...
205, 243, 249, 151, 97, 254, 187, 46, 171, 141, 4, 5, // ....a.......
145, 109, 225, 101, 175, 194, 10, 107, 225, 12, 238, 212, // .m.e...k....
205, 239, 251, 167, 158, 254, 135, 69, 118, 79, 96, 125, // .......EvO`}
183, 39, 241, 189, 120, 44, 16, 203, 185, 107, 196, 97, // .'..x,...k.a
255, 112, 140, 180, 55, 216, 102, 60, 150, 167, 154, 143, // .p..7.f<....
86, 226, 190, 132, 71, 246, 253, 204, 72, 199, 110, 247, // V...G...H.n.
12, 220, 82, 70, 130, 148, 246, 37, 132, 49, 197, 82, // ..RF...%.1.R
17, 213, 142, 219, 165, 159, 237, 236, 155, 119, 64, 158, // .........w@.
182, 239, 111, 137, 83, 246, 245, 4, 218, 125, 251, 224, // ..o.S....}..
8, 145, 35, 85, 253, 100, 89, 176, 166, 149, 28, 205, // ..#U.dY.....
163, 228, 30, 203, 55, 134, 173, 106, 36, 153, 36, 9, // ....7..j$.$.
163, 133, 110, 19, 231, 241, 180, 237, 48, 51, 148, 153, // ..n.....03..
176, 73, 192, 165, 228, 217, 12, 78, 177, 143, 252, 253, // .I.....N....
213, 213, 18, 62, 148, 47, 206, 127, 57, 199, 155, 81, // ...>./..9..Q
86, 188, 248, 4, 65, 145, 68, 88, 135, 230, 126, 60, // V...A.DX..~<
213, 2, 176, 165, 109, 69, 71, 19, 134, 221, 241, 120, // ....mEG....x
113, 178, 125, 156, 236, 46, 168, 170, 238, 194, 11, 167, // q.}.........
158, 171, 54, 88, 243, 246, 212, 42, 120, 53, 110, 58, // ..6X...*x5n:
174, 161, 178, 41, 162, 147, 78, 206, 198, 109, 55, 54, // ...)..N..m76
143, 95, 54, 106, 53, 69, 175, 44, 116, 25, 66, 109, // ._6j5E.,t.Bm
94, 118, 20, 61, 254, 104, 178, 74, 233, 6, 155, 140, // ^v.=.h.J....
201, 20, 178, 135, 201, 180, 19, 131, 100, 41, 9, 104, // ........d).h
218, 18, 146, 40, 226, 204, 24, 188, 252, 63, 228, 9, // ...(.....?..
138, 212, 4, 61, 134, 132, 229, 165, 108, 209, 170, 146, // ...=....l...
72, 198, 51, 248, 145, 102, 23, 99, 208, 177, 118, 121, // H.3..f.c..vy
178, 85, 237, 143, 220, 212, 157, 51, 240, 85, 251, 88, // .U.....3.U.X
247, 106, 29, 103, 90, 20, 254, 170, 107, 69, 13, 126, // .j.gZ...kE.~
167, 187, 143, 202, 47, 190, 218, 130, 171, 175, 178, 160, // ..../.......
56, 210, 130, 171, 163, 44, 120, 120, 150, 5, 170, 146, // 8....,xx....
253, 93, 253, 85, 205, 59, 206, 130, 159, 52, 199, 158, // .].U.;...4..
13, 163, 253, 199, 3, 113, 249, 175, 65, 92, 190, 53, // .....q..A..5
41, 114, 76, 104, 62, 199, 167, 191, 98, 183, 156, 194, // )rLh>...b...
242, 245, 111, 186, 221, 254, 2, 44, 56, 91, 78, 12, // ..o....,8[N.
52, 103, 8, 13, 168, 83, 102, 48, 85, 109, 216, 63, // 4g...Sf0Um.?
46, 198, 61, 4, 90, 200, 250, 189, 59, 150, 194, 174, // ..=.Z...;...
121, 159, 58, 71, 6, 178, 190, 60, 122, 48, 238, 91, // y.:G...<z0.[
147, 165, 24, 1, 198, 108, 253, 195, 120, 97, 84, 151, // .....l..xaT.
216, 88, 32, 168, 170, 154, 5, 124, 211, 87, 210, 84, // .X ....|.W.T
184, 54, 183, 80, 189, 166, 10, 14, 84, 235, 42, 187, // .6.P....T.*.
58, 28, 26, 130, 61, 148, 62, 27, 150, 207, 199, 31, // :...=.>.....
7, 164, 103, 192, 127, 20, 220, 131, 81, 233, 56, 180, // ..g.....Q.8.
245, 197, 250, 77, 193, 70, 229, 14, 99, 173, 47, 189, // ...M.F..c./.
111, 4, 245, 21, 205, 184, 164, 240, 230, 127, 203, 229, // o...........
215, 2, 173, 108, 80, 87, 167, 130, 248, 56, 108, 77, // ...lPW...8lM
155, 241, 77, 209, 213, 154, 29, 198, 215, 116, 1, 95, // ..M......t._
66, 120, 88, 159, 204, 195, 222, 173, 154, 137, 181, 65, // BxX........A
252, 45, 194, 58, 83, 19, 127, 59, 210, 243, 123, 90, // .-.:S..;..{Z
104, 156, 93, 48, 98, 245, 112, 59, 39, 16, 227, 228, // h.]0b.p;'...
116, 57, 86, 195, 55, 54, 103, 85, 85, 121, 113, 114, // t9V.76gUUyqr
79, 179, 63, 112, 220, 201, 252, 8, 253, 35, 252, 118, // O.?p.....#.v
145, 32, 39, 97, 154, 224, 100, 224, 247, 220, 106, 102, // . 'a..d...jf
214, 185, 79, 22, 46, 136, 50, 16, 97, 145, 4, 84, // ..O...2.a..T
157, 28, 248, 114, 3, 132, 69, 144, 151, 65, 154, 136, // ...r..E..A..
184, 121, 87, 108, 230, 65, 225, 63, 165, 36, 22, 62, // .yWl.A.?.$.>
189, 73, 83, 45, 8, 78, 29, 80, 202, 36, 77, 228, // .IS-.N.P.$M.
195, 108, 199, 119, 2, 44, 4, 156, 255, 205, 86, 97, // .l.w.,....Va
183, 6, 128, 39, 203, 219, 99, 216, 134, 173, 144, 228, // ...'..c.....
249, 12, 94, 168, 251, 197, 128, 29, 116, 173, 71, 99, // ..^.....t.Gc
98, 176, 208, 91, 186, 61, 182, 148, 174, 112, 246, 155, // b..[.=...p..
170, 226, 107, 182, 118, 223, 157, 159, 159, 163, 16, 117, // ..k.v......u
57, 136, 6, 109, 109, 24, 78, 56, 144, 246, 107, 139, // 9..mm.N8..k.
183, 219, 61, 160, 76, 34, 75, 28, 191, 204, 218, 174, // ..=.L"K.....
93, 7, 170, 207, 254, 74, 176, 91, 156, 169, 109, 160, // ]....J.[..m.
164, 36, 213, 219, 192, 130, 70, 237, 50, 80, 125, 176, // .$....F.2P}.
141, 251, 49, 92, 237, 83, 119, 95, 52, 75, 148, 136, // ..1..Sw_4K..
221, 139, 186, 61, 122, 212, 15, 179, 71, 40, 97, 167, // ...=z...G(a.
232, 225, 29, 140, 253, 34, 32, 123, 158, 18, 156, 245, // ....." {....
84, 94, 180, 152, 53, 105, 161, 55, 58, 49, 79, 113, // T^..5i.7:1Oq
164, 188, 28, 163, 131, 213, 252, 11, 205, 94, 66, 101, // .........^Be
76, 19, 22, 56, 174, 11, 140, 120, 73, 11, 207, 243, // L..8...xI...
198, 79, 38, 122, 155, 228, 56, 66, 239, 165, 79, 55, // .O&z..8B..O7
191, 183, 233, 189, 75, 237, 110, 46, 82, 67, 246, 163, // ....K.n.RC..
81, 168, 205, 246, 121, 80, 98, 7, 252, 121, 167, 142, // Q...yPb..y..
59, 185, 24, 252, 225, 167, 190, 220, 110, 65, 112, 115, // ;.......nAps
235, 212, 11, 189, 47, 193, 32, 209, 226, 134, 40, 170, // ..../. ...(.
166, 217, 200, 143, 105, 178, 142, 149, 104, 213, 200, 232, // ....i...h...
184, 88, 165, 188, 154, 1, 41, 37, 111, 189, 119, 178, // .X....)%o.w.
109, 119, 169, 94, 70, 114, 187, 91, 80, 44, 32, 182, // mw.^Fr.[P, .
205, 153, 56, 31, 180, 118, 59, 38, 178, 117, 151, 61, // ..8..v;&.u.=
106, 14, 189, 187, 24, 169, 117, 130, 89, 191, 113, 150, // j.....u.Y.q.
114, 18, 117, 139, 172, 102, 133, 109, 199, 246, 171, 60, // r.u..f.m...<
199, 81, 34, 226, 97, 169, 86, 130, 158, 26, 220, 113, // .Q".a.V....q
154, 248, 11, 218, 252, 214, 171, 12, 23, 0, 0, 0 // ...........
};
static const unsigned char v3[] = {
31, 139, 8, 8, 6, 78, 191, 103, 0, 3, 112, 114, // .....N.g..pr
101, 97, 99, 116, 46, 109, 105, 110, 46, 106, 115, 0, // eact.min.js.
157, 91, 123, 119, 219, 182, 146, 255, 127, 63, 69, 164, // .[{w.....?E.
211, 163, 18, 43, 68, 177, 147, 182, 187, 75, 5, 213, // ...+D....K..
105, 29, 183, 238, 109, 234, 230, 38, 105, 123, 123, 85, // i...m..&i{{U
93, 30, 138, 132, 44, 214, 20, 169, 242, 97, 91, 53, // ]...,....a[5
245, 221, 247, 55, 3, 128, 164, 108, 185, 247, 236, 158, // ...7...l....
36, 34, 30, 131, 1, 48, 47, 204, 12, 144, 155, 176, // $"...0/.....
120, 166, 101, 38, 3, 89, 201, 92, 22, 178, 150, 169, // x.e&.Y......
186, 223, 203, 68, 205, 23, 50, 82, 47, 194, 40, 169, // ...D..2R/.(.
26, 125, 231, 205, 252, 178, 185, 106, 178, 102, 219, 124, // .}.....j.f.|
34, 154, 98, 187, 110, 174, 138, 36, 110, 242, 219, 178, // ".b.n..$n...
217, 100, 81, 147, 85, 183, 77, 146, 233, 121, 180, 94, // .dQ.U.M..y.^
52, 127, 230, 121, 243, 175, 188, 136, 155, 164, 210, 69, // 4..y.......E
248, 34, 153, 174, 234, 44, 170, 146, 60, 123, 86, 122, // ."...,..<{Vz
152, 72, 220, 175, 242, 194, 187, 193, 172, 193, 179, 36, // .H.........$
123, 150, 9, 61, 15, 22, 42, 195, 207, 180, 208, 85, // {..=..*....U
93, 100, 207, 244, 190, 29, 177, 242, 180, 184, 39, 216, // ]d........'.
76, 233, 201, 54, 44, 116, 86, 93, 230, 177, 158, 102, // L..6,tV]...f
163, 81, 54, 41, 244, 38, 191, 209, 103, 235, 36, 141, // .Q6).&..g.$.
1, 214, 13, 10, 61, 222, 141, 25, 216, 109, 105, 74, // ....=....miJ
243, 214, 52, 103, 32, 134, 215, 122, 55, 84, 170, 158, // ..4g ..z7T..
229, 42, 152, 215, 11, 127, 88, 232, 21, 215, 11, 83, // .*....X....S
79, 241, 195, 165, 105, 178, 242, 194, 226, 170, 222, 96, // O...i......`
230, 114, 146, 234, 236, 170, 90, 127, 249, 114, 52, 242, // .r....Z..r4.
210, 73, 68, 19, 99, 69, 234, 81, 255, 171, 153, 158, // .ID.cE.Q....
68, 97, 154, 118, 35, 229, 75, 225, 87, 66, 14, 221, // Da.v#.K.WB..
26, 49, 87, 181, 219, 234, 124, 245, 140, 118, 82, 167, // .1W...|..vR.
233, 64, 101, 147, 88, 175, 194, 58, 173, 222, 21, 249, // .@e.X..:....
182, 20, 237, 98, 31, 180, 223, 228, 73, 252, 236, 68, // ...b....I..D
41, 69, 75, 164, 117, 208, 74, 15, 97, 208, 34, 28, // )EK.u.J.a.".
45, 183, 160, 69, 202, 124, 165, 89, 122, 68, 218, 130, // -..E.|.YzD..
23, 150, 225, 134, 80, 32, 17, 45, 201, 215, 114, 75, // ....P .-..rK
72, 252, 74, 130, 70, 62, 0, 244, 202, 47, 100, 16, // H.J.F>.../d.
92, 251, 132, 1, 5, 247, 93, 250, 39, 248, 213, 174, // ......].'...
26, 251, 102, 105, 40, 70, 174, 109, 109, 10, 81, 158, // ..fi(F.mm.Q.
149, 85, 81, 71, 85, 94, 116, 64, 55, 220, 71, 52, // .UQGU^t@7.G4
31, 143, 3, 191, 222, 187, 21, 59, 106, 220, 100, 96, // .......;j.d`
52, 177, 153, 11, 94, 42, 100, 218, 173, 126, 77, 114, // 4...^*d..~Mr
225, 196, 165, 229, 68, 215, 31, 27, 73, 171, 214, 73, // ....D...I..I
57, 225, 237, 40, 236, 150, 42, 88, 74, 165, 239, 42, // 9..(..*XJ..*
213, 131, 189, 49, 176, 96, 180, 89, 80, 38, 90, 196, // ...1.`.YP&Z.
65, 48, 67, 47, 62, 146, 126, 240, 247, 122, 146, 100, // A0C/>.~..z.d
177, 190, 251, 145, 228, 114, 124, 42, 120, 11, 211, 86, // .....r|*x..V
158, 167, 217, 107, 205, 64, 70, 14, 166, 217, 120, 44, // ...k.@F...x,
44, 218, 129, 242, 2, 197, 157, 243, 108, 33, 28, 207, // ,.......l!..
9, 165, 118, 211, 113, 197, 82, 225, 136, 160, 232, 9, // ..v.q.R.....
21, 104, 61, 102, 218, 110, 3, 187, 86, 73, 100, 48, // .h=f.n..VId0
237, 230, 211, 60, 95, 59, 23, 85, 34, 163, 124, 84, // ...<_;.U".|T
52, 189, 209, 100, 25, 150, 90, 49, 151, 50, 117, 242, // 4..d..Z1.2u.
255, 220, 192, 253, 35, 132, 102, 47, 203, 66, 135, 215, // ....#.f/.B..
123, 187, 61, 90, 229, 190, 91, 244, 134, 22, 237, 13, // {.=Z..[.....
104, 76, 12, 49, 230, 175, 26, 156, 0, 115, 53, 217, // hL.1.....s5.
214, 37, 49, 120, 52, 26, 92, 161, 185, 24, 143, 155, // .%1x4.......
166, 24, 40, 150, 242, 101, 14, 4, 250, 189, 6, 23, // ..(..e......
138, 36, 187, 2, 140, 231, 21, 71, 123, 154, 38, 23, // .$.....G{.&.
222, 85, 79, 226, 175, 188, 206, 244, 232, 41, 99, 86, // .UO......)cV
149, 219, 169, 208, 40, 151, 121, 81, 121, 14, 222, 72, // ....(.yQy..H
69, 79, 20, 110, 240, 111, 249, 60, 115, 165, 189, 144, // EO.n.o.<s...
21, 217, 72, 141, 97, 27, 221, 27, 214, 242, 194, 233, // ..H.a.......
215, 212, 109, 178, 80, 94, 174, 60, 152, 50, 65, 72, // ..m.P^.<.2AH
232, 71, 75, 175, 86, 132, 242, 29, 237, 36, 32, 124, // .GK.V....$ |
94, 165, 74, 15, 6, 56, 23, 12, 165, 114, 250, 29, // ^.J..8...r..
159, 202, 119, 94, 13, 116, 149, 36, 232, 76, 26, 53, // ..w^.t.$.L.5
2, 81, 234, 73, 126, 155, 233, 226, 195, 207, 223, 158, // .Q.I~.......
167, 154, 108, 141, 52, 156, 161, 97, 235, 217, 188, 88, // ..l.4..a...X
88, 93, 148, 70, 184, 11, 8, 80, 46, 160, 208, 220, // X].F...P....
47, 228, 27, 47, 192, 76, 92, 211, 3, 85, 140, 70, // /../.L...U.F
59, 116, 11, 177, 239, 209, 237, 218, 59, 60, 30, 34, // ;t......;<."
89, 202, 149, 217, 100, 40, 99, 185, 147, 27, 121, 37, // Y...d(c...y%
175, 229, 157, 188, 80, 21, 113, 15, 34, 210, 52, 137, // ....P.q.".4.
60, 87, 23, 142, 184, 68, 117, 214, 30, 218, 95, 8, // <W...Du..._.
49, 11, 95, 103, 174, 47, 60, 16, 177, 13, 75, 206, // 1._g./<...K.
245, 60, 92, 40, 179, 94, 180, 100, 168, 129, 155, 195, // .<.(.^.d....
101, 158, 167, 58, 236, 105, 196, 102, 70, 48, 254, 16, // e..:.i.fF0..
182, 5, 12, 239, 181, 3, 56, 171, 55, 75, 93, 28, // ......8.7K].
182, 45, 147, 171, 36, 171, 250, 227, 183, 60, 47, 54, // .-..$....</6
96, 20, 128, 203, 194, 255, 170, 40, 194, 221, 36, 41, // `......(..$)
249, 235, 109, 4, 192, 214, 242, 222, 25, 25, 127, 179, // ..m.........
239, 129, 179, 85, 245, 55, 36, 16, 95, 158, 0, 112, // ...U.7$._..p
195, 90, 42, 55, 198, 234, 224, 11, 35, 106, 17, 51, // .Z*7....#j.3
207, 253, 141, 96, 83, 67, 53, 21, 112, 227, 146, 247, // ...`SC5.p...
188, 4, 139, 205, 150, 149, 183, 83, 23, 102, 207, 187, // .......S.f..
209, 136, 49, 40, 181, 163, 15, 213, 8, 189, 162, 58, // ..1(.......:
21, 4, 193, 41, 35, 11, 83, 157, 150, 250, 25, 145, // ...)#.S.....
58, 6, 133, 227, 215, 231, 211, 24, 164, 165, 185, 24, // :...........
95, 76, 74, 251, 215, 200, 238, 9, 202, 97, 51, 186, // _LJ......a3.
187, 99, 54, 236, 223, 65, 4, 54, 114, 167, 118, 77, // .c6..A.6r.vM
147, 30, 72, 129, 188, 82, 27, 35, 198, 49, 10, 56, // ..H..R.#.1.8
42, 48, 201, 142, 190, 3, 69, 2, 127, 215, 52, 222, // *0....E...4.
29, 152, 46, 36, 55, 142, 70, 119, 70, 183, 185, 230, // ...$7.FwF...
232, 45, 109, 99, 204, 212, 136, 154, 230, 10, 141, 194, // .-mc........
138, 241, 213, 204, 90, 229, 107, 160, 187, 86, 87, 71, // ....Z.k..VWG
207, 80, 179, 17, 103, 147, 8, 203, 53, 237, 142, 4, // .P..g...5...
142, 54, 135, 239, 108, 195, 246, 165, 84, 75, 111, 131, // .6..l...TKo.
133, 195, 132, 150, 234, 204, 108, 74, 94, 64, 124, 75, // ......lJ^@|K
33, 87, 144, 144, 124, 203, 136, 161, 86, 129, 177, 182, // !W..|...V...
71, 38, 11, 236, 100, 44, 209, 64, 41, 124, 61, 185, // G&..d,.@)|=.
9, 211, 90, 171, 225, 16, 104, 105, 255, 100, 12, 21, // ..Z...hi.d..
74, 101, 207, 101, 129, 1, 198, 152, 82, 221, 120, 59, // Je.e....R.x;
168, 151, 211, 8, 173, 174, 161, 16, 231, 211, 240, 249, // ............
243, 169, 48, 203, 39, 150, 2, 242, 47, 102, 238, 224, // ..0.'.../f..
8, 195, 104, 228, 74, 138, 5, 41, 110, 151, 118, 227, // ..h.J..)n.v.
85, 50, 196, 41, 37, 228, 79, 30, 193, 72, 150, 42, // U2.)%.O..H.*
65, 71, 196, 29, 251, 23, 70, 21, 239, 250, 170, 248, // AG....F.....
209, 187, 35, 200, 187, 249, 120, 220, 126, 122, 182, 96, // ..#...x.~z.`
105, 108, 129, 209, 126, 24, 4, 86, 237, 10, 104, 170, // il..~..V..h.
195, 131, 163, 2, 42, 216, 58, 115, 94, 84, 36, 122, // ....*.:s^T$z
30, 153, 24, 156, 194, 153, 58, 178, 179, 220, 80, 123, // ......:...P{
233, 229, 140, 221, 63, 35, 171, 132, 63, 60, 222, 24, // ....?#..?<..
39, 24, 227, 214, 169, 233, 29, 222, 103, 125, 227, 100, // '.......g}.d
150, 5, 111, 79, 38, 180, 201, 214, 70, 146, 201, 140, // ..oO&...F...
133, 49, 180, 49, 27, 208, 248, 64, 105, 218, 163, 63, // .1.1...@i..?
192, 161, 1, 43, 216, 52, 166, 154, 247, 248, 39, 180, // ...+.4....'.
223, 130, 1, 160, 56, 96, 45, 44, 186, 158, 132, 219, // ....8`-,....
45, 14, 31, 227, 142, 194, 164, 214, 172, 58, 60, 1, // -........:<.
31, 59, 169, 42, 224, 84, 159, 76, 81, 72, 39, 25, // .;.*.T.LQH'.
124, 144, 15, 201, 50, 53, 71, 88, 242, 186, 61, 135, // |...25GX..=.
146, 177, 122, 73, 246, 144, 102, 23, 172, 131, 56, 171, // ..zI..f...8.
52, 188, 142, 82, 23, 213, 215, 26, 136, 180, 71, 59, // 4..R......G;
5, 250, 194, 157, 172, 221, 89, 48, 171, 253, 188, 143, // ......Y0....
187, 163, 211, 157, 99, 220, 240, 57, 200, 14, 155, 122, // ....c..9...z
178, 128, 147, 90, 106, 246, 24, 129, 122, 231, 49, 225, // ...Zj...z.1.
53, 14, 119, 107, 121, 131, 217, 112, 232, 59, 59, 58, // 5.wky..p.;;:
104, 101, 176, 105, 162, 73, 165, 203, 202, 203, 196, 44, // he.i.I.....,
240, 131, 241, 112, 123, 55, 236, 166, 185, 232, 216, 97, // ...p{7.....a
152, 81, 76, 153, 110, 176, 209, 187, 84, 243, 204, 194, // .QL.n...T...
84, 15, 77, 118, 0, 250, 49, 200, 36, 42, 203, 143, // T.Mv..1.$*..
228, 160, 5, 134, 114, 199, 128, 43, 246, 23, 14, 193, // ....r..+....
43, 210, 62, 248, 252, 68, 233, 140, 220, 230, 74, 4, // +.>..D....J.
208, 20, 246, 246, 155, 230, 206, 193, 99, 113, 128, 35, // ........cq.#
225, 8, 58, 208, 64, 0, 99, 64, 59, 199, 20, 248, // ..:.@.c@;...
28, 194, 83, 135, 216, 59, 57, 25, 230, 142, 124, 163, // ..S..;9...|.
209, 48, 51, 229, 211, 133, 128, 11, 2, 250, 227, 104, // .03........h
167, 168, 100, 155, 134, 145, 246, 94, 156, 133, 91, 176, // ..d....^..[.
71, 127, 242, 130, 166, 132, 61, 67, 95, 149, 191, 205, // G.....=C_...
111, 117, 113, 6, 23, 201, 19, 152, 89, 207, 30, 180, // ouq.....Y...
77, 202, 52, 193, 88, 68, 11, 89, 91, 132, 22, 164, // M.4.XD.Y[...
176, 163, 248, 69, 16, 195, 181, 121, 54, 46, 16, 158, // ...E...y6...
200, 96, 86, 53, 13, 4, 47, 142, 207, 111, 32, 139, // .`V5../..o .
111, 147, 178, 210, 240, 5, 192, 202, 98, 246, 193, 63, // o.......b..?
135, 152, 192, 54, 153, 40, 233, 41, 128, 86, 1, 134, // ...6.(.).V..
113, 152, 93, 233, 34, 175, 203, 116, 247, 65, 87, 223, // q.]."..t.AW.
101, 128, 187, 248, 248, 195, 91, 50, 134, 198, 59, 206, // e.....[2..;.
197, 193, 246, 238, 32, 96, 215, 243, 11, 127, 189, 192, // .... `......
6, 215, 67, 209, 245, 148, 151, 225, 134, 183, 93, 14, // ..C.......].
123, 248, 215, 20, 98, 17, 50, 16, 46, 197, 66, 92, // {...b.2...B.
25, 108, 216, 184, 114, 21, 46, 191, 35, 255, 218, 213, // .l..r...#...
99, 248, 54, 105, 30, 198, 182, 206, 220, 210, 162, 42, // c.6i.......*
118, 247, 15, 5, 53, 152, 90, 101, 217, 71, 97, 21, // v...5.Ze.Ga.
113, 120, 176, 63, 102, 66, 65, 72, 235, 185, 66, 136, // qx.?fBAH..B.
6, 167, 3, 214, 249, 97, 216, 99, 106, 209, 50, 213, // .....a.cj.2.
40, 200, 87, 21, 196, 111, 89, 87, 218, 106, 136, 37, // (.W..oYW.j.%
104, 175, 89, 244, 253, 218, 115, 154, 154, 67, 141, 116, // h.Y...s..C.t
110, 92, 246, 241, 224, 116, 225, 101, 19, 77, 28, 152, // n....t.e.M..
217, 47, 57, 241, 253, 144, 245, 195, 145, 81, 39, 255, // ./9......Q'.
118, 20, 29, 203, 189, 176, 93, 38, 50, 50, 90, 183, // v.....]&22Z.
146, 161, 220, 202, 27, 235, 155, 45, 229, 25, 121, 103, // .......-..yg
240, 200, 204, 9, 114, 96, 27, 131, 73, 47, 58, 19, // ....r`..I/:.
189, 24, 108, 106, 232, 68, 254, 220, 26, 180, 138, 76, // ..lj.D.....L
9, 38, 204, 156, 91, 21, 27, 101, 42, 175, 77, 248, // .&..[..e*.M.
80, 171, 121, 130, 131, 222, 91, 177, 153, 93, 194, 172, // P.y...[..]..
145, 146, 77, 153, 89, 108, 2, 30, 115, 227, 156, 229, // ..M.Yl..s...
138, 252, 31, 227, 45, 157, 41, 140, 62, 119, 49, 218, // ....-.).>w1.
71, 242, 73, 70, 163, 124, 190, 34, 183, 0, 135, 145, // G.IF.|."....
90, 205, 206, 102, 103, 6, 214, 28, 185, 62, 117, 33, // Z..fg....>u!
70, 165, 197, 68, 179, 43, 133, 19, 141, 86, 100, 214, // F..D.+...Vd.
26, 145, 255, 172, 66, 252, 156, 251, 222, 16, 163, 170, // ....B.......
156, 230, 29, 66, 134, 206, 71, 163, 243, 73, 219, 2, // ...B..G..I..
142, 82, 208, 48, 51, 67, 67, 149, 233, 91, 176, 113, // .R.03CC..[.q
41, 239, 132, 239, 245, 219, 98, 110, 147, 97, 159, 98, // ).....bn.a.b
234, 28, 117, 51, 94, 125, 37, 228, 217, 104, 116, 54, // ..u3^}%..ht6
41, 235, 165, 23, 18, 156, 137, 60, 151, 40, 149, 85, // )......<.(.U
88, 105, 200, 158, 45, 177, 34, 135, 109, 48, 122, 39, // Xi..-.".m0z'
105, 149, 153, 202, 229, 150, 215, 75, 129, 16, 55, 173, // i......K..7.
217, 119, 50, 98, 78, 117, 248, 20, 30, 127, 149, 69, // .w2bNu.....E
228, 220, 164, 243, 201, 149, 174, 222, 32, 240, 185, 209, // ........ ...
241, 7, 234, 248, 166, 200, 55, 156, 12, 104, 71, 184, // ......7..hG.
33, 109, 3, 71, 25, 92, 132, 117, 42, 77, 163, 124, // !m.G...u*M.|
26, 145, 183, 116, 192, 66, 222, 40, 187, 57, 248, 132, // ...t.B.(.9..
22, 173, 220, 10, 179, 206, 191, 90, 138, 89, 44, 109, // .......Z.Y,m
124, 179, 205, 51, 72, 244, 47, 73, 154, 254, 128, 192, // |..3H./I....
13, 6, 248, 88, 171, 231, 246, 215, 235, 124, 147, 196, // ...X.....|..
237, 8, 80, 200, 248, 142, 71, 250, 69, 123, 128, 252, // ..P...G.E{..
251, 117, 45, 161, 8, 55, 79, 44, 239, 189, 142, 52, // .u-..7O,...4
198, 88, 200, 191, 232, 52, 210, 49, 8, 141, 83, 230, // .X...4.1..S.
112, 149, 235, 188, 78, 227, 51, 55, 232, 167, 109, 204, // p...N.37..m.
60, 24, 156, 42, 245, 84, 175, 163, 52, 208, 53, 77, // <..*.T..4.5M
192, 65, 160, 98, 137, 190, 17, 247, 143, 132, 202, 200, // .A.b........
5, 43, 226, 205, 192, 130, 89, 22, 67, 140, 78, 5, // .+....Y.C.N.
163, 186, 161, 195, 226, 129, 218, 94, 115, 249, 218, 148, // .......^s...
39, 176, 194, 231, 33, 236, 102, 63, 140, 213, 54, 46, // '...!.f?..6.
87, 129, 216, 27, 60, 107, 235, 166, 140, 70, 169, 165, // W...<k...F..
187, 104, 237, 238, 81, 226, 185, 237, 30, 109, 238, 237, // .h..Q....m..
243, 40, 155, 187, 193, 45, 159, 219, 229, 17, 37, 30, // .(...-....%.
195, 122, 108, 242, 40, 122, 61, 84, 174, 227, 52, 179, // .zl.(z=T..4.
166, 170, 176, 166, 74, 58, 154, 181, 36, 163, 239, 59, // ....J:..$..;
248, 172, 43, 229, 116, 220, 115, 114, 239, 164, 190, 157, // ..+.t.sr....
71, 60, 64, 238, 54, 4, 161, 99, 151, 240, 204, 128, // G<@.6..c....
145, 35, 12, 205, 179, 17, 190, 124, 212, 239, 145, 122, // .#.....|...z
109, 157, 3, 202, 221, 31, 178, 112, 11, 57, 177, 222, // m......p.9..
159, 217, 40, 76, 201, 230, 233, 94, 34, 3, 208, 92, // ..(L...^"...
40, 179, 8, 196, 93, 43, 23, 234, 173, 141, 100, 42, // (...]+....d*
181, 162, 24, 112, 182, 178, 214, 180, 13, 110, 87, 146, // ...p.....nW.
194, 253, 195, 8, 248, 66, 204, 46, 252, 249, 197, 226, // .....B......
209, 89, 131, 229, 119, 217, 158, 254, 89, 240, 132, 172, // .Y..w...Y...
200, 43, 43, 153, 231, 76, 36, 134, 53, 100, 215, 32, // .++..L$.5d.
187, 241, 177, 108, 82, 16, 14, 89, 95, 240, 103, 222, // ...lR..Y_.g.
67, 145, 181, 162, 44, 124, 83, 185, 245, 172, 100, 247, // C...,|S...d.
150, 24, 137, 41, 179, 56, 78, 86, 43, 29, 91, 46, // ...).8NV+.[.
119, 254, 129, 153, 128, 215, 235, 69, 134, 226, 3, 85, // w......E...U
11, 27, 62, 105, 149, 216, 29, 13, 6, 145, 172, 231, // ..>i........
117, 155, 249, 75, 196, 194, 174, 156, 164, 71, 155, 51, // u..K.....G.3
184, 239, 2, 188, 161, 38, 113, 79, 189, 17, 37, 48, // .....&qO..%0
241, 65, 60, 163, 197, 163, 84, 17, 128, 248, 120, 100, // .A<...T...xd
2, 174, 237, 116, 199, 83, 74, 54, 161, 76, 154, 216, // ...t.SJ6.L..
237, 160, 157, 159, 109, 195, 190, 159, 180, 185, 245, 250, // ....m.......
41, 155, 214, 49, 40, 217, 49, 88, 131, 118, 70, 140, // )..1(.1X.vF.
227, 246, 236, 221, 89, 231, 64, 110, 16, 163, 176, 215, // ....Y.@n....
125, 67, 46, 183, 218, 113, 222, 106, 112, 226, 84, 180, // }C...q.jp.T.
102, 191, 121, 186, 121, 93, 187, 144, 101, 99, 50, 56, // f.y.y]..ec28
136, 109, 235, 249, 134, 67, 189, 146, 60, 40, 136, 232, // .m...C..<(..
110, 6, 127, 38, 199, 194, 201, 33, 4, 42, 255, 21, // n..&...!.*..
194, 226, 9, 165, 115, 249, 108, 23, 216, 129, 42, 37, // ....s.l...*%
141, 50, 177, 146, 201, 57, 244, 18, 177, 157, 245, 86, // .2...9.....V
59, 231, 155, 196, 121, 196, 57, 245, 73, 4, 232, 74, // ;...y.9.I..J
147, 247, 79, 65, 152, 23, 139, 105, 166, 138, 217, 131, // ..OA...i....
110, 155, 20, 187, 252, 0, 7, 180, 170, 182, 254, 139, // n...........
23, 183, 183, 183, 147, 219, 87, 147, 188, 184, 122, 241, // ......W...z.
242, 228, 228, 228, 5, 237, 19, 234, 226, 31, 31, 232, // ............
237, 100, 12, 69, 24, 141, 98, 23, 209, 201, 136, 100, // .d.E..b....d
181, 191, 176, 53, 126, 99, 196, 69, 196, 107, 168, 95, // ...5~c.E.k._
104, 170, 158, 173, 196, 221, 65, 84, 147, 92, 91, 78, // h.....AT..[N
102, 70, 231, 104, 233, 37, 116, 64, 121, 45, 83, 154, // fF.h.%t@y-S.
38, 21, 147, 39, 188, 113, 56, 8, 241, 147, 125, 131, // &..'.q8...}.
168, 37, 152, 227, 211, 154, 174, 114, 136, 163, 155, 215, // .%.....r....
217, 36, 116, 46, 107, 217, 103, 221, 122, 222, 239, 1, // .$t.k.g.z...
47, 38, 25, 184, 69, 119, 10, 135, 173, 236, 112, 77, // /&..Ew....pM
61, 216, 166, 144, 120, 188, 37, 85, 30, 141, 182, 36, // =...x.%U...$
179, 213, 198, 250, 39, 84, 106, 154, 174, 13, 72, 18, // ....'Tj...H.
183, 60, 193, 36, 105, 171, 106, 219, 13, 134, 7, 142, // .<.$i.j.....
8, 137, 104, 218, 79, 190, 30, 134, 145, 180, 155, 194, // ..h.O.......
94, 222, 56, 107, 69, 242, 137, 88, 220, 94, 230, 112, // ^.8kE..X.^.p
88, 206, 55, 38, 77, 67, 113, 168, 185, 242, 64, 252, // X.7&MCq...@.
86, 44, 160, 159, 29, 130, 76, 228, 20, 118, 56, 111, // V,....L..v8o
180, 13, 109, 51, 0, 2, 217, 211, 200, 135, 76, 2, // ..m3......L.
87, 137, 214, 58, 186, 214, 177, 173, 210, 44, 28, 55, // W..:.....,.7
16, 14, 59, 59, 77, 108, 103, 223, 67, 29, 99, 185, // ..;;Mlg.C.c.
70, 43, 172, 230, 86, 184, 100, 104, 27, 29, 109, 156, // F+..V.dh..m.
30, 182, 150, 24, 118, 56, 147, 143, 50, 145, 27, 31, // ....v8..2...
172, 112, 138, 109, 130, 39, 157, 92, 101, 63, 46, 127, // .p.m.'..e?..
215, 17, 71, 84, 59, 214, 246, 122, 86, 35, 152, 241, // ..GT;..zV#..
43, 147, 9, 163, 60, 16, 244, 55, 58, 84, 225, 141, // +...<..7:T..
234, 52, 184, 203, 62, 145, 50, 146, 161, 100, 85, 158, // .4..>.2..dU.
194, 44, 122, 118, 215, 32, 92, 12, 84, 46, 108, 192, // .,zv. ..T.l.
240, 216, 136, 4, 9, 195, 134, 147, 44, 92, 5, 101, // ........,..e
176, 145, 171, 66, 151, 165, 53, 31, 131, 13, 64, 46, // ...B..5...@.
176, 27, 139, 9, 49, 201, 218, 192, 74, 242, 79, 90, // ....1...J.OZ
66, 30, 155, 193, 246, 81, 234, 146, 167, 176, 117, 139, // B....Q....u.
207, 141, 100, 140, 182, 66, 56, 197, 254, 113, 162, 232, // ..d..B8..q..
163, 181, 211, 108, 113, 143, 93, 175, 204, 180, 199, 225, // ...lq.].....
93, 84, 23, 148, 216, 81, 193, 99, 59, 123, 96, 229, // ]T...Q.c;{`.
127, 114, 8, 237, 45, 35, 223, 188, 76, 234, 108, 99, // .r..-#..L.lc
156, 211, 182, 8, 4, 146, 83, 97, 38, 59, 234, 229, // ......Sa&;..
110, 6, 132, 54, 174, 72, 25, 36, 154, 163, 105, 62, // n..6.H.$..i>
82, 10, 140, 197, 182, 77, 129, 218, 60, 90, 100, 18, // R....M..<Zd.
199, 249, 3, 79, 202, 76, 194, 49, 241, 241, 46, 239, // ...O.L.1....
216, 129, 33, 246, 185, 57, 186, 115, 118, 113, 56, 201, // ..!..9.svq8.
75, 200, 77, 198, 142, 229, 163, 128, 213, 40, 94, 231, // K.M......(^.
78, 70, 10, 152, 138, 28, 194, 60, 26, 253, 228, 229, // NF.....<....
44, 217, 242, 136, 14, 153, 216, 21, 81, 159, 243, 96, // ,.......Q..`
236, 182, 86, 230, 206, 73, 200, 238, 166, 200, 165, 224, // ..V..I......
58, 138, 126, 229, 114, 84, 150, 123, 238, 194, 206, 197, // :.~.rT.{....
90, 102, 225, 45, 248, 15, 94, 223, 64, 144, 220, 79, // Zf.-..^.@..O
105, 127, 230, 196, 165, 62, 178, 216, 216, 198, 177, 124, // i....>.....|
163, 48, 87, 7, 57, 241, 128, 239, 43, 140, 99, 193, // .0W.9...+.c.
151, 222, 239, 160, 46, 129, 242, 6, 208, 175, 28, 29, // ............
130, 117, 53, 244, 214, 134, 47, 243, 0, 17, 89, 77, // .u5.../...YM
121, 240, 180, 187, 132, 169, 30, 93, 194, 240, 232, 217, // y......]....
60, 95, 248, 181, 153, 170, 154, 172, 146, 162, 52, 190, // <_........4.
158, 187, 30, 174, 250, 103, 128, 185, 167, 73, 236, 200, // .....g...I..
28, 227, 106, 162, 213, 193, 64, 74, 252, 189, 241, 146, // ..j...@J....
3, 50, 124, 99, 46, 170, 248, 2, 82, 221, 243, 229, // .2|c....R...
171, 26, 226, 115, 54, 28, 215, 227, 49, 221, 215, 106, // ...s6...1..j
9, 231, 178, 196, 225, 86, 248, 79, 92, 110, 57, 163, // .....V.O.n9.
227, 101, 98, 47, 17, 204, 220, 36, 241, 1, 176, 69, // .eb/...$...E
47, 171, 105, 159, 53, 15, 92, 87, 216, 10, 119, 129, // /.i.5..W..w.
133, 240, 150, 242, 51, 4, 37, 143, 129, 170, 158, 31, // ....3.%.....
239, 48, 238, 13, 228, 209, 136, 72, 245, 151, 210, 93, // .0.....H...]
234, 26, 43, 194, 202, 195, 37, 114, 26, 217, 115, 66, // ..+...%r..sB
8, 96, 177, 213, 203, 131, 177, 129, 187, 91, 156, 186, // .`.......[..
71, 5, 199, 84, 102, 122, 188, 185, 191, 106, 76, 180, // G..Tfz...jL.
229, 252, 92, 208, 187, 17, 150, 48, 103, 252, 50, 129, // .......0g.2.
185, 75, 215, 157, 36, 240, 237, 237, 116, 123, 197, 29, // .K..$...t{..
76, 28, 141, 57, 186, 154, 56, 246, 244, 51, 31, 176, // L..9..8..3..
63, 112, 65, 77, 18, 80, 102, 196, 88, 253, 128, 123, // ?pAM.Pf.X..{
237, 181, 51, 105, 193, 52, 227, 88, 102, 202, 158, 88, // ..3i.4.Xf..X
192, 229, 136, 46, 80, 233, 164, 97, 227, 64, 237, 213, // ....P..a.@..
131, 108, 143, 139, 82, 171, 99, 161, 241, 121, 81, 228, // .l..R.c..yQ.
5, 59, 195, 37, 162, 12, 106, 245, 158, 134, 195, 102, // .;.%..j....f
133, 204, 205, 253, 131, 179, 91, 193, 65, 128, 118, 70, // ......[.A.vF
54, 136, 209, 61, 106, 37, 202, 181, 99, 243, 254, 117, // 6..=j%..c..u
248, 121, 107, 133, 177, 95, 173, 178, 125, 181, 46, 242, // .yk.._..}...
91, 4, 155, 123, 104, 232, 9, 60, 179, 46, 135, 227, // [..{h..<....
22, 169, 14, 137, 100, 238, 229, 3, 27, 9, 177, 76, // ....d......L
112, 26, 197, 149, 72, 121, 89, 80, 104, 236, 204, 181, // p...HyYPh...
250, 174, 96, 146, 36, 29, 196, 209, 235, 39, 142, 147, // ..`.$....'..
149, 54, 65, 29, 226, 200, 78, 64, 65, 20, 116, 150, // .6A...N@A.t.
198, 251, 183, 215, 240, 237, 220, 20, 166, 103, 109, 205, // .........gm.
6, 184, 153, 144, 27, 143, 154, 112, 136, 29, 236, 15, // .......p....
220, 142, 244, 83, 218, 96, 144, 217, 162, 166, 236, 145, // ...S.`......
126, 136, 88, 63, 133, 216, 38, 175, 214, 230, 54, 59, // ~.X?..&...6;
63, 102, 46, 33, 173, 155, 164, 212, 51, 251, 237, 13, // ?f.!....3...
174, 214, 58, 155, 44, 161, 1, 158, 235, 195, 193, 159, // ..:.,.......
167, 55, 26, 113, 172, 15, 150, 124, 76, 54, 58, 175, // .7.q...|L6:.
43, 105, 110, 219, 79, 96, 144, 79, 88, 243, 222, 202, // +in.O`.OX...
75, 249, 139, 124, 143, 150, 239, 104, 214, 31, 77, 206, // K..|...h..M.
80, 254, 108, 2, 114, 249, 71, 27, 181, 201, 175, 141, // P.l.r.G.....
44, 203, 79, 84, 123, 162, 118, 15, 138, 126, 239, 34, // ,.OT{.v..~."
173, 181, 177, 251, 107, 239, 82, 106, 249, 30, 14, 153, // ....k.Rj....
32, 244, 60, 89, 165, 46, 209, 115, 1, 251, 196, 95, // .<Y...s..._
210, 37, 31, 147, 210, 243, 148, 249, 98, 223, 222, 42, // .%......b..*
233, 47, 57, 164, 108, 195, 86, 174, 48, 237, 40, 91, // ./9.l.V.0.([
71, 181, 185, 94, 116, 118, 247, 219, 222, 19, 148, 247, // G..^tv......
234, 84, 254, 233, 37, 90, 246, 179, 179, 127, 30, 220, // .T..%Z......
149, 169, 223, 189, 183, 48, 199, 47, 219, 233, 170, 73, // .....0./...I
69, 143, 82, 38, 124, 229, 201, 209, 171, 154, 7, 179, // E.R&|.......
0, 18, 224, 39, 218, 230, 103, 33, 193, 242, 209, 195, // ...'..g!....
2, 172, 178, 98, 120, 184, 120, 146, 206, 90, 83, 52, // ...bx.x..ZS4
41, 114, 139, 40, 51, 235, 61, 93, 44, 204, 12, 157, // )r.(3.=],...
14, 99, 47, 98, 111, 91, 213, 165, 217, 87, 183, 230, // .c/bo[...W..
191, 25, 130, 246, 87, 252, 74, 76, 7, 153, 81, 154, // ....W.JL..Q.
84, 51, 250, 11, 73, 175, 74, 204, 76, 102, 7, 23, // T3..I.J.Lf..
42, 144, 76, 219, 78, 222, 42, 209, 35, 197, 247, 143, // *.L.N.*.#...
209, 126, 246, 127, 64, 123, 12, 229, 223, 15, 200, 255, // .~..@{......
185, 252, 213, 123, 116, 166, 220, 91, 239, 202, 39, 123, // ...{t..[..'{
49, 239, 95, 99, 254, 195, 177, 230, 189, 250, 66, 126, // 1._c......B~
223, 31, 249, 132, 103, 152, 145, 60, 155, 124, 152, 243, // ....g..<.|..
15, 169, 105, 47, 221, 21, 68, 224, 179, 105, 133, 177, // ..i/..D..i..
34, 83, 216, 205, 244, 107, 255, 116, 54, 59, 255, 175, // "S...k.t6;..
86, 4, 82, 205, 169, 134, 11, 64, 216, 172, 3, 172, // V.R....@....
136, 224, 112, 254, 66, 101, 54, 31, 160, 77, 67, 135, // ..p.Be6..MC.
242, 159, 7, 135, 247, 123, 245, 223, 199, 246, 14, 27, // .....{......
9, 160, 110, 144, 214, 157, 252, 92, 186, 115, 102, 206, // ..n......sf.
126, 37, 52, 193, 174, 236, 127, 68, 119, 66, 69, 116, // ~%4....DwBEt
87, 235, 46, 223, 3, 246, 171, 204, 10, 97, 92, 50, // W........a.2
78, 113, 95, 146, 135, 218, 63, 136, 201, 123, 238, 47, // Nq_...?..{./
52, 211, 78, 61, 235, 82, 191, 209, 203, 250, 234, 103, // 4.N=.R.....g
115, 74, 63, 104, 240, 72, 236, 31, 92, 109, 4, 189, // sJ?h.H...m..
245, 154, 197, 157, 34, 132, 9, 212, 183, 94, 119, 11, // ...."....^w.
108, 4, 229, 242, 241, 129, 194, 154, 254, 168, 245, 192, // l...........
108, 246, 60, 69, 34, 48, 52, 133, 206, 107, 1, 247, // l.<E"04..k..
142, 180, 170, 127, 208, 83, 151, 209, 70, 232, 77, 183, // .....S..F.M.
64, 168, 146, 184, 255, 238, 104, 222, 52, 49, 222, 238, // @.....h.41..
59, 115, 81, 213, 170, 133, 3, 45, 172, 27, 124, 216, // ;sQ....-..|.
90, 247, 91, 97, 19, 237, 185, 23, 136, 251, 126, 171, // Z.[a......~.
77, 57, 153, 247, 104, 156, 242, 97, 3, 186, 103, 243, // M9..h..a..g.
121, 176, 193, 75, 147, 172, 248, 113, 52, 250, 145, 118, // y..K...q4..v
198, 3, 139, 3, 136, 159, 71, 163, 159, 105, 243, 111, // ......G..i.o
173, 153, 204, 148, 119, 105, 35, 13, 154, 145, 222, 88, // ....wi#....X
122, 217, 163, 149, 103, 143, 86, 157, 217, 181, 241, 36, // z...g.V....$
198, 106, 31, 204, 243, 199, 104, 244, 135, 243, 184, 204, // .j....h.....
19, 178, 104, 26, 152, 252, 222, 133, 253, 28, 164, 11, // ..h.........
61, 186, 149, 251, 206, 168, 60, 89, 132, 95, 56, 143, // =.....<Y._8.
80, 232, 63, 106, 93, 86, 95, 101, 201, 38, 36, 204, // P.?j]V_e.&$.
223, 20, 225, 134, 210, 160, 222, 47, 79, 117, 10, 132, // ......./Ou..
29, 143, 31, 101, 245, 157, 184, 40, 213, 97, 97, 143, // ...e...(.aa.
40, 88, 23, 153, 67, 54, 163, 48, 139, 116, 122, 136, // (X..C6.0.tz.
137, 78, 229, 238, 48, 99, 114, 86, 170, 215, 16, 64, // .N..0crV...@
56, 79, 196, 148, 134, 123, 153, 58, 186, 24, 236, 4, // 8O...{.:....
226, 229, 85, 228, 39, 93, 186, 200, 135, 73, 23, 245, // ..U.']...I..
61, 150, 128, 61, 203, 135, 201, 63, 39, 71, 143, 101, // =..=...?'G.e
104, 173, 108, 123, 146, 86, 186, 56, 24, 100, 180, 132, // h.l{.V.8.d..
31, 216, 53, 77, 173, 89, 188, 173, 80, 85, 71, 103, // ..5M.Y..PUGg
209, 230, 20, 245, 116, 203, 79, 82, 184, 86, 230, 170, // ....t.OR.V..
158, 204, 125, 61, 26, 125, 109, 162, 49, 217, 158, 202, // ..}=.}m.1...
7, 60, 255, 100, 52, 250, 228, 33, 207, 233, 210, 221, // .<.d4..!....
242, 155, 53, 195, 113, 190, 191, 169, 167, 115, 155, 140, // ..5.q....s..
42, 215, 199, 220, 147, 163, 4, 239, 156, 132, 162, 111, // *..........o
250, 166, 71, 95, 86, 114, 162, 150, 63, 30, 177, 167, // ..G_Vr..?...
151, 54, 168, 15, 6, 51, 8, 111, 231, 33, 92, 170, // .6...3.o.!..
251, 86, 121, 160, 233, 94, 222, 8, 52, 231, 45, 76, // .Vy..^..4.-L
17, 65, 241, 3, 178, 247, 195, 93, 122, 65, 64, 111, // .A.....]zA@o
161, 251, 105, 220, 228, 0, 237, 177, 231, 195, 179, 140, // ..i.........
31, 132, 238, 105, 137, 209, 161, 247, 219, 75, 79, 76, // ...i.....KOL
233, 114, 27, 122, 238, 98, 134, 66, 157, 34, 184, 207, // .r.z.b.B."..
250, 193, 189, 121, 59, 67, 25, 172, 241, 120, 33, 83, // ...y;C...x!S
78, 101, 193, 254, 99, 92, 163, 234, 217, 169, 255, 146, // Ne..c.......
94, 66, 112, 231, 2, 243, 205, 199, 227, 98, 49, 125, // ^Bp......b1}
165, 232, 209, 73, 69, 184, 83, 255, 51, 91, 57, 93, // ...IE.S.3[9]
40, 147, 143, 154, 132, 101, 153, 92, 101, 30, 53, 53, // (....e..e.55
13, 124, 227, 84, 248, 159, 51, 16, 55, 41, 215, 142, // .|.T..3.7)..
88, 145, 209, 17, 146, 47, 90, 36, 174, 113, 172, 210, // X..../Z$.q..
241, 112, 136, 112, 152, 179, 19, 225, 118, 155, 238, 188, // .p.p....v...
84, 70, 68, 24, 122, 214, 56, 31, 14, 249, 16, 94, // TFD.z.8....^
8, 242, 101, 216, 104, 228, 66, 166, 244, 190, 197, 172, // ..e.h.B.....
253, 165, 143, 77, 20, 207, 95, 98, 251, 156, 146, 83, // ...M.._b...S
57, 142, 114, 11, 153, 182, 153, 34, 132, 159, 244, 14, // 9.r...."....
86, 223, 62, 251, 33, 220, 202, 213, 161, 143, 109, 216, // V.>.!.....m.
15, 231, 22, 225, 143, 241, 161, 219, 51, 136, 146, 154, // ........3...
237, 176, 146, 195, 16, 134, 160, 231, 74, 18, 93, 88, // ........J.]X
167, 169, 242, 88, 109, 146, 160, 4, 68, 65, 93, 127, // ...Xm...DA].
14, 199, 25, 230, 26, 220, 73, 56, 226, 67, 89, 208, // ......I8.CY.
79, 173, 232, 72, 74, 15, 160, 233, 246, 144, 159, 193, // O..HJ.......
0, 95, 174, 242, 238, 237, 197, 191, 126, 43, 255, 243, // ._......~+..
183, 12, 63, 141, 43, 124, 242, 226, 138, 31, 160, 136, // ..?.+|......
89, 109, 54, 141, 224, 0, 81, 22, 229, 226, 29, 138, // Ym6...Q.....
92, 204, 60, 219, 249, 138, 59, 177, 130, 151, 194, 127, // ..<...;.....
105, 32, 134, 147, 201, 132, 82, 121, 57, 84, 196, 225, // i ....Ry9T..
248, 12, 96, 39, 45, 4, 122, 6, 109, 215, 231, 242, // ..`'-.z.m...
68, 194, 71, 192, 12, 213, 151, 234, 115, 122, 159, 155, // D.G.....sz..
55, 205, 0, 166, 145, 88, 95, 145, 163, 99, 1, 43, // 7....X_..c.+
0, 230, 28, 47, 169, 47, 56, 78, 234, 58, 128, 220, // ..././8N.:..
117, 8, 166, 196, 158, 159, 78, 37, 175, 117, 247, 78, // u.....N%.u.N
138, 222, 54, 210, 97, 97, 150, 144, 66, 33, 83, 47, // ..6.aa..B!S/
17, 162, 21, 241, 8, 3, 162, 215, 122, 158, 44, 220, // ........z.,.
152, 8, 99, 16, 242, 163, 101, 14, 167, 135, 7, 206, // ..c...e.....
134, 175, 249, 185, 199, 204, 35, 4, 160, 116, 13, 215, // ......#..t..
88, 189, 18, 126, 62, 86, 25, 75, 52, 64, 158, 63, // X..~>V.K4@.?
183, 219, 31, 126, 105, 129, 29, 131, 0, 167, 178, 113, // ...~i......q
78, 185, 214, 98, 150, 81, 38, 120, 70, 44, 51, 163, // N..b.Q&xF,3.
63, 29, 126, 106, 238, 65, 134, 159, 154, 97, 5, 26, // ?.~j.A...a..
91, 20, 52, 31, 208, 128, 74, 244, 216, 79, 181, 136, // [.4...J..O..
63, 135, 205, 205, 45, 242, 225, 11, 110, 38, 167, 248, // ?...-...n&..
245, 231, 192, 195, 99, 205, 250, 199, 244, 62, 133, 145, // ....c....>..
56, 62, 214, 138, 114, 190, 132, 180, 150, 174, 98, 232, // 8>..r.....b.
249, 18, 212, 172, 168, 3, 28, 27, 62, 27, 218, 69, // ........>..E
253, 86, 181, 165, 172, 45, 21, 7, 171, 123, 105, 232, // .V...-...{i.
208, 206, 49, 28, 180, 164, 192, 74, 63, 147, 118, 22, // ..1....J?.v.
167, 67, 76, 194, 61, 167, 2, 48, 166, 251, 191, 15, // .CL.=..0....
56, 74, 132, 251, 175, 17, 167, 179, 204, 39, 189, 220, // 8J.......'..
155, 168, 49, 20, 83, 125, 183, 205, 139, 234, 62, 124, // ..1.S}....>|
22, 150, 207, 214, 208, 60, 254, 86, 155, 84, 254, 64, // .....<.V.T.@
37, 19, 157, 202, 152, 202, 109, 82, 72, 126, 67, 85, // %.....mRH~CU
115, 79, 99, 51, 74, 242, 91, 106, 130, 91, 201, 81, // sOc3J.[j.[.Q
142, 252, 211, 214, 222, 235, 184, 142, 48, 254, 111, 182, // ........0.o.
126, 14, 239, 36, 170, 228, 247, 182, 250, 54, 220, 225, // ~..$.....6..
252, 182, 141, 127, 111, 199, 172, 228, 63, 108, 249, 187, // ....o...?l..
205, 86, 23, 56, 84, 110, 244, 69, 152, 197, 169, 150, // .V.8Tn.E....
191, 218, 142, 31, 244, 38, 151, 255, 180, 149, 179, 48, // .....&.....0
77, 151, 97, 116, 45, 181, 118, 45, 118, 85, 153, 107, // M.at-.v-vU.k
232, 188, 93, 25, 184, 54, 206, 156, 124, 141, 179, 51, // ..]..6..|..3
14, 139, 221, 126, 250, 31, 255, 11, 22, 19, 178, 116, // ...~.......t
138, 51, 0, 0, 0 // .3..
};
static const unsigned char v4[] = {
31, 139, 8, 8, 6, 78, 191, 103, 0, 3, 115, 116, // .....N.g..st
121, 108, 101, 46, 99, 115, 115, 0, 149, 85, 193, 142, // yle.css..U..
155, 48, 16, 189, 231, 43, 92, 173, 42, 181, 171, 192, // .0...+..*...
134, 16, 96, 33, 151, 222, 250, 17, 85, 15, 6, 155, // ..`!....U...
196, 90, 99, 35, 227, 108, 146, 86, 249, 247, 142, 109, // .Zc#.l.V...m
192, 70, 75, 85, 245, 146, 144, 153, 225, 205, 155, 247, // .FKU........
198, 206, 51, 250, 141, 106, 121, 139, 6, 246, 139, 137, // ..3..jy.....
83, 5, 207, 138, 80, 21, 65, 232, 136, 30, 155, 179, // S...P.A.....
238, 248, 22, 98, 228, 14, 101, 29, 86, 39, 38, 42, // ...b..e.V'&*
180, 59, 162, 30, 19, 98, 203, 225, 249, 76, 217, 233, // .;...b...L..
172, 43, 148, 236, 118, 159, 143, 168, 149, 194, 60, 231, // .+..v.....<.
253, 13, 13, 88, 12, 209, 64, 21, 107, 13, 212, 64, // ...X..@.k..@
57, 109, 244, 22, 49, 209, 95, 224, 139, 227, 154, 242, // 9m..1._.....
170, 170, 105, 43, 21, 221, 34, 77, 111, 26, 43, 138, // ..i+.."Mo.+.
161, 141, 188, 104, 206, 4, 173, 144, 144, 130, 30, 29, // ...h........
185, 51, 38, 242, 90, 153, 0, 250, 196, 186, 94, 42, // .3&.Z.....^*
141, 133, 62, 142, 100, 161, 157, 233, 38, 57, 35, 232, // ..>.d...&9#.
169, 105, 154, 69, 201, 99, 211, 43, 10, 168, 141, 228, // .i.E.c.+....
18, 42, 159, 210, 34, 117, 36, 163, 22, 119, 140, 223, // .*.."u$..w..
43, 212, 73, 33, 135, 30, 55, 116, 140, 95, 199, 121, // +.I!..7t._.y
106, 201, 1, 124, 12, 130, 60, 192, 104, 232, 48, 231, // j..|..<.h.0.
38, 86, 227, 230, 237, 164, 228, 69, 16, 128, 36, 132, // &V.....E..$.
4, 138, 36, 180, 155, 136, 69, 10, 19, 118, 25, 64, // ..$...E..v.@
165, 120, 111, 162, 143, 205, 52, 230, 40, 131, 213, 126, // .xo...4.(..~
109, 132, 80, 225, 56, 165, 29, 124, 102, 128, 240, 1, // m.P.8..|f...
96, 139, 98, 168, 147, 2, 190, 173, 160, 0, 24, 208, // `.b.........
77, 14, 189, 117, 17, 170, 113, 245, 206, 6, 166, 41, // M..u..q....)
49, 143, 184, 209, 236, 61, 20, 37, 203, 172, 69, 206, // 1....=.%..E.
231, 197, 108, 89, 150, 153, 140, 235, 18, 188, 81, 150, // ..lY......Q.
229, 17, 117, 76, 68, 87, 70, 244, 185, 66, 133, 229, // ..uLDWF..B..
135, 76, 233, 68, 100, 81, 10, 241, 90, 3, 192, 6, // .L.DdQ..Z...
45, 181, 179, 179, 126, 80, 43, 245, 26, 218, 21, 155, // -...~P+.....
160, 218, 22, 120, 54, 23, 53, 152, 95, 189, 100, 66, // ...x6.5._.dB
131, 25, 0, 73, 216, 208, 115, 12, 94, 50, 97, 86, // ...I..s.^2aV
39, 170, 185, 108, 222, 22, 26, 230, 160, 161, 245, 96, // '..l.......`
205, 226, 141, 99, 247, 3, 96, 112, 205, 41, 249, 105, // ...c..`p.).i
182, 16, 54, 130, 233, 187, 121, 55, 243, 61, 241, 69, // ..6...y7.=.E
75, 112, 33, 30, 58, 41, 245, 25, 202, 180, 130, 37, // Kp!.:).....%
103, 154, 73, 56, 23, 176, 27, 40, 222, 15, 118, 216, // g.I8...(..v.
6, 218, 96, 160, 162, 130, 115, 3, 253, 29, 0, 68, // ..`...s....D
110, 147, 112, 249, 126, 103, 76, 10, 69, 185, 158, 193, // n.p.~gL.E...
168, 127, 110, 84, 230, 54, 42, 38, 81, 203, 233, 13, // ..nT.6*&Q...
218, 204, 34, 152, 223, 71, 96, 195, 78, 34, 2, 164, // .."..G`.N"..
14, 202, 27, 106, 165, 114, 245, 246, 24, 5, 245, 238, // ...j.r......
156, 25, 13, 108, 143, 245, 165, 180, 75, 14, 53, 150, // ...l....K.5.
35, 37, 115, 209, 10, 35, 33, 175, 10, 247, 80, 97, // #%s..#!...Pa
7, 137, 236, 217, 50, 93, 76, 212, 22, 116, 195, 201, // ....2]L..t..
188, 191, 56, 67, 180, 157, 103, 228, 180, 5, 111, 50, // ..8C..g...o2
223, 59, 43, 201, 194, 204, 108, 54, 210, 237, 121, 105, // .;+...l6..yi
46, 158, 73, 229, 196, 156, 22, 191, 50, 69, 81, 216, // ..I.....2EQ.
158, 243, 121, 11, 187, 182, 20, 47, 128, 141, 67, 235, // ..y..../..C.
106, 31, 198, 217, 224, 114, 90, 193, 193, 134, 253, 127, // j....rZ.....
225, 244, 42, 74, 0, 101, 124, 39, 82, 110, 31, 189, // ..*J.e|'Rn..
134, 221, 221, 230, 221, 80, 145, 150, 253, 124, 44, 198, // .....P...|,.
80, 45, 181, 150, 221, 20, 53, 111, 240, 240, 13, 167, // P-....5o....
225, 140, 183, 121, 121, 70, 223, 21, 104, 249, 252, 98, // ...yyF..h..b
44, 188, 126, 92, 23, 243, 25, 25, 135, 96, 255, 38, // ,.~......`.&
159, 64, 195, 191, 95, 251, 242, 157, 170, 150, 195, 173, // .@.._.......
60, 238, 180, 171, 143, 146, 189, 49, 222, 237, 182, 251, // <......1....
71, 152, 18, 137, 79, 148, 73, 156, 231, 65, 106, 231, // G...O.I..Aj.
83, 175, 105, 156, 166, 62, 85, 250, 76, 145, 249, 240, // S.i..>U.L...
171, 15, 231, 249, 2, 171, 240, 153, 236, 117, 1, 149, // .........u..
7, 153, 128, 87, 230, 195, 135, 37, 173, 131, 207, 164, // ...W...%....
75, 86, 169, 207, 236, 3, 86, 225, 232, 75, 86, 193, // KV....V..KV.
236, 51, 169, 111, 29, 37, 12, 163, 47, 193, 45, 154, // .3.o.%../.-.
164, 9, 220, 6, 95, 161, 120, 245, 2, 113, 66, 79, // ...._.x..qBO
181, 251, 194, 222, 28, 143, 16, 202, 223, 43, 229, 126, // .........+.~
66, 50, 118, 143, 86, 46, 125, 129, 23, 255, 0, 132, // B2v.V.}.....
122, 103, 55, 254, 7, 0, 0, 0 // zg7....
};
static const struct packed_file {
const char *name;
const unsigned char *data;
size_t size;
time_t mtime;
} packed_files[] = {
{"/web_root/index.html.gz", v1, sizeof(v1), 1740590598},
{"/web_root/main.js.gz", v2, sizeof(v2), 1740590598},
{"/web_root/preact.min.js.gz", v3, sizeof(v3), 1740590598},
{"/web_root/style.css.gz", v4, sizeof(v4), 1740590598},
{NULL, NULL, 0, 0}
};
static int scmp(const char *a, const char *b) {
while (*a && (*a == *b)) a++, b++;
return *(const unsigned char *) a - *(const unsigned char *) b;
}
const char *mg_unlist(size_t no) {
return packed_files[no].name;
}
const char *mg_unpack(const char *name, size_t *size, time_t *mtime) {
const struct packed_file *p;
for (p = packed_files; p->name != NULL; p++) {
if (scmp(p->name, name) != 0) continue;
if (size != NULL) *size = p->size - 1;
if (mtime != NULL) *mtime = p->mtime;
return (const char *) p->data;
}
return NULL;
}
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Device Dashboard</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
</head>
<body></body>
<script type="module" src="main.js"></script>
</html>
+153
View File
@@ -0,0 +1,153 @@
'use strict';
import {Component, h, html, render, useEffect, useState, useRef} from './preact.min.js';
const Message = m => html`<span
style="color: ${m.message.uart ? '#444' : '#373'};">
${m.message.data}
</span>`;
const App = function(props) {
const [cfg, setCfg] = useState({tcp: {}, ws: {}, mqtt: {}});
const [messages, setMessages] = useState([]);
const [connected, setConnected] = useState(false);
const [txt, setTxt] = useState('');
const [ws, setWs] = useState(null);
const refresh = () =>
fetch('/api/config/get').then(r => r.json()).then(r => setCfg(r));
const getport = (url, v) => ((url || '').match(/.*:(\d+)/) || ['', v])[1];
const watchWebsocket = function() {
// Connect to websocker port, to implement WS console
var reconnect = function() {
var port = getport(cfg.ws.url, 4002);
var l = window.location, proto = l.protocol.replace('http', 'ws');
var tid, url = `${proto}//${l.hostname}:${port}/ws`;
// console.log(url);
var ws = new WebSocket(url);
ws.onopen = () => {
setConnected(true);
setWs(ws);
};
ws.onmessage = ev => {
// console.log(ev, ev.data);
setMessages(x => x.concat([{data: ev.data, uart: true}]));
};
ws.onclose = function() {
clearTimeout(tid);
tid = setTimeout(reconnect, 1000);
setConnected(false);
setWs(null);
};
};
reconnect();
};
useEffect(() => {
refresh();
watchWebsocket();
}, []);
const sendmessage = ev => {
setMessages(x => x.concat([{data: txt + '\n', uart: false}]));
if (ws) ws.send(txt + '\n');
setTxt('');
};
const onchange = ev => fetch('/api/config/set', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(cfg),
}).then(r => ws && ws.close());
const set = obj => setCfg(x => Object.assign(x, obj));
const nset = (n,obj) => setCfg(x => Object.assign(x, {[n]: Object.assign(x[n],obj)}));
const setTx = ev => set({tx: parseInt(ev.target.value)});
const setRx = ev => set({rx: parseInt(ev.target.value)});
const setBaud = ev => set({baud: parseInt(ev.target.value)});
const setTcpUrl = ev => nset('tcp', {url: `tcp://0.0.0.0:${ev.target.value}`});
const setWsUrl = ev => nset('ws',{url: `ws://0.0.0.0:${ev.target.value}`});
const setMqttUrl = ev => nset('mqtt',{url: ev.target.value});
const setTcpEna = ev => (nset('tcp',{enable: ev.target.checked}), onchange());
const setWsEna = ev => (nset('ws',{enable: ev.target.checked}), onchange());
const setMqttEna = ev =>(nset('mqtt', {enable: ev.target.checked}), onchange());
return html`
<div class="container">
<h1 style="margin-bottom: 0;">UART \u27F7 network bridge </h1>
<pre class="d-none">${JSON.stringify(cfg, null, 2)}</pre>
<div class="row">
<div class="col col-4">
<h3>UART configuration</h3>
<div class="d-flex pr-1 my-1">
<label class="addon">UART TX pin</label>
<input style="width: 5em;" value=${cfg.tx} onchange=${onchange}
oninput=${setTx} />
</div><div class="d-flex pr-1 my-1">
<label class="addon">UART RX pin</label>
<input style="width: 5em;" value=${cfg.rx} onchange=${onchange}
oninput=${setRx} />
</div><div class="d-flex pr-1 my-y">
<label class="addon">UART Baud</label>
<input style="width: 5em;" value=${cfg.baud} onchange=${onchange}
oninput=${setBaud} />
</div>
</div>
<div class="col col-8">
<h3>Network configuration</h3>
<div class="d-flex my-1">
<label class="addon">Local TCP port</label>
<input style="min-width: 4em; flex: 1 100%;"
value=${getport(cfg.tcp.url, 4001)} onchange=${onchange}
oninput=${setTcpUrl} />
<label class="ml-1 d-flex label"><input type="checkbox"
checked=${cfg.tcp.enable} onchange=${setTcpEna} /> enable</label>
</div><div class="d-flex my-1">
<label class="addon">Local WS port</label>
<input style="flex: 1 100%;"
value=${getport(cfg.ws.url, 4002)} onchange=${onchange}
oninput=${setWsUrl} />
<label class="ml-1 d-flex label"><input type="checkbox"
checked=${cfg.ws.enable} onchange=${setWsEna} /> enable</label>
</div><div class="d-flex my-1">
<label class="addon">Remote MQTT</label>
<input style="flex: 1 100%;"
value=${cfg.mqtt.url} onchange=${onchange}
oninput=${setMqttUrl} />
<label class="ml-1 d-flex label"><input type="checkbox"
checked=${cfg.mqtt.enable} onchange=${setMqttEna} /> enable</label>
</div>
</div>
</div>
<div class="msg">
Note: to connect over MQTT,
open <a href="http://www.hivemq.com/demos/websocket-client/">
console</a>, subscribe to b/tx and publish to b/rx<br/>
Note: to connect over TCP, use netcat utility:<br/>
$ nc ${location.hostname} ${getport(cfg.tcp.url, 4001)}
</div>
<div style="margin-top: 2em;">
<b>UART console</b><span style="margin-left: 1em; color: #777;">works
over the local WS port. WebSocket status: </span><span
style="color: ${connected ? 'teal' : 'red'};">
\u25cf ${connected ? 'connected' : 'disconnected'} </span>
</div>
<div style="margin: 0.5em 0; display: flex">
<input placeholder="to send data, type and press enter..." style="flex: 1 100%;"
value=${txt} onchange=${sendmessage}
oninput=${ev => setTxt(ev.target.value)} />
<button style="margin-left: 1em;"
onclick=${ev => setMessages([])}>clear</button>
</div>
<pre style="height: 15em; overflow: auto;">
${messages.map(message => h(Message, {message}))}
</pre>
</div>`;
};
window.onload = () => render(h(App), document.body);
+1
View File
@@ -0,0 +1 @@
var e,n,_,t,o,r,u,l={},i=[],c=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function s(e,n){for(var _ in n)e[_]=n[_];return e}function f(e){var n=e.parentNode;n&&n.removeChild(e)}function a(n,_,t){var o,r,u,l={};for(u in _)"key"==u?o=_[u]:"ref"==u?r=_[u]:l[u]=_[u];if(arguments.length>2&&(l.children=arguments.length>3?e.call(arguments,2):t),"function"==typeof n&&null!=n.defaultProps)for(u in n.defaultProps)void 0===l[u]&&(l[u]=n.defaultProps[u]);return p(n,l,o,r,null)}function p(e,t,o,r,u){var l={type:e,props:t,key:o,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==u?++_:u};return null!=n.vnode&&n.vnode(l),l}function h(e){return e.children}function d(e,n){this.props=e,this.context=n}function v(e,n){if(null==n)return e.__?v(e.__,e.__.__k.indexOf(e)+1):null;for(var _;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e)return _.__e;return"function"==typeof e.type?v(e):null}function y(e){var n,_;if(null!=(e=e.__)&&null!=e.__c){for(e.__e=e.__c.base=null,n=0;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e){e.__e=e.__c.base=_.__e;break}return y(e)}}function m(e){(!e.__d&&(e.__d=!0)&&t.push(e)&&!g.__r++||r!==n.debounceRendering)&&((r=n.debounceRendering)||o)(g)}function g(){for(var e;g.__r=t.length;)e=t.sort(function(e,n){return e.__v.__b-n.__v.__b}),t=[],e.some(function(e){var n,_,t,o,r,u;e.__d&&(r=(o=(n=e).__v).__e,(u=n.__P)&&(_=[],(t=s({},o)).__v=o.__v+1,P(u,o,t,n.__n,void 0!==u.ownerSVGElement,null!=o.__h?[r]:null,_,null==r?v(o):r,o.__h),D(_,o),o.__e!=r&&y(o)))})}function k(e,n,_,t,o,r,u,c,s,f){var a,d,y,m,g,k,x,H=t&&t.__k||i,E=H.length;for(_.__k=[],a=0;a<n.length;a++)if(null!=(m=_.__k[a]=null==(m=n[a])||"boolean"==typeof m?null:"string"==typeof m||"number"==typeof m||"bigint"==typeof m?p(null,m,null,null,m):Array.isArray(m)?p(h,{children:m},null,null,null):m.__b>0?p(m.type,m.props,m.key,null,m.__v):m)){if(m.__=_,m.__b=_.__b+1,null===(y=H[a])||y&&m.key==y.key&&m.type===y.type)H[a]=void 0;else for(d=0;d<E;d++){if((y=H[d])&&m.key==y.key&&m.type===y.type){H[d]=void 0;break}y=null}P(e,m,y=y||l,o,r,u,c,s,f),g=m.__e,(d=m.ref)&&y.ref!=d&&(x||(x=[]),y.ref&&x.push(y.ref,null,m),x.push(d,m.__c||g,m)),null!=g?(null==k&&(k=g),"function"==typeof m.type&&null!=m.__k&&m.__k===y.__k?m.__d=s=b(m,s,e):s=C(e,m,y,H,g,s),f||"option"!==_.type?"function"==typeof _.type&&(_.__d=s):e.value=""):s&&y.__e==s&&s.parentNode!=e&&(s=v(y))}for(_.__e=k,a=E;a--;)null!=H[a]&&("function"==typeof _.type&&null!=H[a].__e&&H[a].__e==_.__d&&(_.__d=v(t,a+1)),U(H[a],H[a]));if(x)for(a=0;a<x.length;a++)T(x[a],x[++a],x[++a])}function b(e,n,_){var t,o;for(t=0;t<e.__k.length;t++)(o=e.__k[t])&&(o.__=e,n="function"==typeof o.type?b(o,n,_):C(_,o,o,e.__k,o.__e,n));return n}function C(e,n,_,t,o,r){var u,l,i;if(void 0!==n.__d)u=n.__d,n.__d=void 0;else if(null==_||o!=r||null==o.parentNode)e:if(null==r||r.parentNode!==e)e.appendChild(o),u=null;else{for(l=r,i=0;(l=l.nextSibling)&&i<t.length;i+=2)if(l==o)break e;e.insertBefore(o,r),u=r}return void 0!==u?u:o.nextSibling}function x(e,n,_){"-"===n[0]?e.setProperty(n,_):e[n]=null==_?"":"number"!=typeof _||c.test(n)?_:_+"px"}function H(e,n,_,t,o){var r;e:if("style"===n)if("string"==typeof _)e.style.cssText=_;else{if("string"==typeof t&&(e.style.cssText=t=""),t)for(n in t)_&&n in _||x(e.style,n,"");if(_)for(n in _)t&&_[n]===t[n]||x(e.style,n,_[n])}else if("o"===n[0]&&"n"===n[1])r=n!==(n=n.replace(/Capture$/,"")),n=n.toLowerCase()in e?n.toLowerCase().slice(2):n.slice(2),e.l||(e.l={}),e.l[n+r]=_,_?t||e.addEventListener(n,r?S:E,r):e.removeEventListener(n,r?S:E,r);else if("dangerouslySetInnerHTML"!==n){if(o)n=n.replace(/xlink[H:h]/,"h").replace(/sName$/,"s");else if("href"!==n&&"list"!==n&&"form"!==n&&"tabIndex"!==n&&"download"!==n&&n in e)try{e[n]=null==_?"":_;break e}catch(e){}"function"==typeof _||(null!=_&&(!1!==_||"a"===n[0]&&"r"===n[1])?e.setAttribute(n,_):e.removeAttribute(n))}}function E(e){this.l[e.type+!1](n.event?n.event(e):e)}function S(e){this.l[e.type+!0](n.event?n.event(e):e)}function P(e,_,t,o,r,u,l,i,c){var f,a,p,v,y,m,g,b,C,x,H,E=_.type;if(void 0!==_.constructor)return null;null!=t.__h&&(c=t.__h,i=_.__e=t.__e,_.__h=null,u=[i]),(f=n.__b)&&f(_);try{e:if("function"==typeof E){if(b=_.props,C=(f=E.contextType)&&o[f.__c],x=f?C?C.props.value:f.__:o,t.__c?g=(a=_.__c=t.__c).__=a.__E:("prototype"in E&&E.prototype.render?_.__c=a=new E(b,x):(_.__c=a=new d(b,x),a.constructor=E,a.render=A),C&&C.sub(a),a.props=b,a.state||(a.state={}),a.context=x,a.__n=o,p=a.__d=!0,a.__h=[]),null==a.__s&&(a.__s=a.state),null!=E.getDerivedStateFromProps&&(a.__s==a.state&&(a.__s=s({},a.__s)),s(a.__s,E.getDerivedStateFromProps(b,a.__s))),v=a.props,y=a.state,p)null==E.getDerivedStateFromProps&&null!=a.componentWillMount&&a.componentWillMount(),null!=a.componentDidMount&&a.__h.push(a.componentDidMount);else{if(null==E.getDerivedStateFromProps&&b!==v&&null!=a.componentWillReceiveProps&&a.componentWillReceiveProps(b,x),!a.__e&&null!=a.shouldComponentUpdate&&!1===a.shouldCompLine truncated
@@ -0,0 +1,46 @@
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; font: 16px sans-serif; }
select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
pre { color: #373; font-family: monospace; font-weight: bolder; font-size: smaller; background: #ddd; padding: 1em; border-radius: 0.2em; }
textarea, input { border: 1px solid #ccc; padding: 0.3em 0.5em;}
textarea, input, .addon, .label { font-size: 14px; }
a, a:visited, a:active { color: #55f; }
body {background: #555; }
.addon { color: #999; min-width: 7.5em; }
.label { color: #999; }
.btn {
background: #ccc; border-radius: 0.3em; border: 0; color: #fff; cursor: pointer;
display: inline-block; padding: 0.6em 2em; font-weight: bolder;
}
.btn[disabled] { opacity: 0.5; cursor: auto;}
.smooth { transition: all .2s; }
.container { margin: 2em auto; max-width: 620px; background: white; padding: 1em; border-radius: 0.5em; }
.d-flex { display: flex; align-items: center; }
.d-none { display: none; }
.border { border: 1px solid #ddd; }
.rounded { border-radius: 0.5em; }
.nowrap { white-space: nowrap; }
.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; color: #777; }
.input { background: #fea; padding: 0.2em 1em; border-radius: 0.4em; }
.output { background: #aef; padding: 0.2em 1em; border-radius: 0.4em; }
.pr-1 { padding-right: 0.5em; }
.my-1 { margin-top: 0.3em; margin-bottom: 0.3em;}
.ml-1 { margin-left: 0.5em; }
/* Grid */
.row { display: flex; flex-wrap: wrap; }
.col { margin: 0; padding: 0; overflow: auto; }
.col-12 { width: 100%; }
.col-11 { width: 91.66%; }
.col-10 { width: 83.33%; }
.col-9 { width: 75%; }
.col-8 { width: 66.66%; }
.col-7 { width: 58.33%; }
.col-6 { width: 50%; }
.col-5 { width: 41.66%; }
.col-4 { width: 33.33%; }
.col-3 { width: 25%; }
.col-2 { width: 16.66%; }
.col-1 { width: 8.33%; }
@media (min-width: 1310px) { .container { margin: auto; width: 1270px; } }
@media (max-width: 920px) { .row .col { width: 100%; } }