diff --git a/Cargo.lock b/Cargo.lock index fe13934..a1c328d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -554,24 +554,24 @@ dependencies = [ [[package]] name = "cuscuta-chilo" -version = "0.1.6" +version = "0.1.7" dependencies = [ "axum", "base64", "chilo", - "env_logger", "hex", - "log", "serde", "serde_json", "tokio", "tokio-util", + "tracing", + "tracing-subscriber", "unicorn-engine", ] [[package]] name = "cuscuta-common" -version = "0.1.6" +version = "0.1.7" dependencies = [ "base64", "chrono", @@ -589,16 +589,14 @@ dependencies = [ [[package]] name = "cuscuta-entry" -version = "0.1.6" +version = "0.1.7" dependencies = [ "anyhow", "axum", "base64", "chrono", "cuscuta-common", - "env_logger", "itertools 0.14.0", - "log", "rand 0.10.1", "redis", "reqwest", @@ -608,6 +606,10 @@ dependencies = [ "thiserror", "tokio", "tokio-util", + "tower", + "tower-http 0.7.0", + "tracing", + "tracing-subscriber", ] [[package]] @@ -618,26 +620,24 @@ dependencies = [ "base64", "chrono", "cuscuta-common", - "env_logger", - "log", "rand 0.10.1", "serde", "serde_json", "sha2 0.11.0", "tokio", + "tracing", + "tracing-subscriber", ] [[package]] name = "cuscuta-worker" -version = "0.1.6" +version = "0.1.7" dependencies = [ "anyhow", "axum", "chrono", "cuscuta-common", - "env_logger", "gethostname", - "log", "rand 0.10.1", "redis", "reqwest", @@ -646,11 +646,13 @@ dependencies = [ "thiserror", "tokio", "tokio-util", + "tracing", + "tracing-subscriber", ] [[package]] name = "cuscutactl" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", "base64", @@ -752,29 +754,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "env_filter" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -1376,30 +1355,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jiff" -version = "0.2.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-static" -version = "0.2.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "jni" version = "0.22.4" @@ -1569,6 +1524,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "matchit" version = "0.8.4" @@ -1624,6 +1588,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1781,21 +1754,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "portable-atomic-util" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" -dependencies = [ - "portable-atomic", -] - [[package]] name = "potential_utf" version = "0.1.5" @@ -2084,7 +2042,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower", - "tower-http", + "tower-http 0.6.11", "tower-service", "url", "wasm-bindgen", @@ -2408,6 +2366,15 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2793,6 +2760,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -2952,6 +2928,23 @@ dependencies = [ "url", ] +[[package]] +name = "tower-http" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233" +dependencies = [ + "bitflags", + "bytes", + "http", + "http-body", + "percent-encoding", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -2994,6 +2987,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -3109,6 +3132,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index d8fcc04..ba7c5a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.1.6" +version = "0.1.7" [workspace.dependencies] anyhow = "1.0.102" @@ -18,9 +18,7 @@ axum = "0.8.9" base64 = "0.22.1" chrono = { version = "0.4.44", features = ["serde"] } comfy-table = "7.1" -env_logger = "0.11.10" hex = "0.4.3" -log = "0.4.29" rand = "0.10.1" redis = { version = "1.2.0", features = ["streams"] } reqwest = { version = "0.13.2", features = ["json", "query", "form"] } @@ -31,10 +29,12 @@ sqlx = { version = "0.8.6", features = ["postgres", "chrono", "runtime-tokio"] } thiserror = "2.0.18" tokio = { version = "1.52.3", features = ["rt-multi-thread"] } tokio-util = "0.7.18" +tracing = "0.1.44" +tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } [workspace.lints.rust] missing_docs = "deny" [workspace.lints.clippy] pedantic = "deny" -nursery = "deny" \ No newline at end of file +nursery = "deny" diff --git a/cuscuta-chilo/Cargo.toml b/cuscuta-chilo/Cargo.toml index dc85463..781c70c 100644 --- a/cuscuta-chilo/Cargo.toml +++ b/cuscuta-chilo/Cargo.toml @@ -6,9 +6,9 @@ edition = "2024" [dependencies] axum = { workspace = true } base64 = { workspace = true } -env_logger = { workspace = true } +tracing-subscriber = { workspace = true } hex = { workspace = true } -log = { workspace = true } +tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true, features = ["signal"] } diff --git a/cuscuta-chilo/src/main.rs b/cuscuta-chilo/src/main.rs index e9c2f26..d68d7f8 100644 --- a/cuscuta-chilo/src/main.rs +++ b/cuscuta-chilo/src/main.rs @@ -1,606 +1,608 @@ -//! cuscuta-chilo是对[chilo](https://github.com/cuscutaceae/chilo)的Web API包装 -//! -//! # 注意 -//! 这个版本的cuscuta-chilo使用了环境变量中硬编码的常量,为原型版本\ -//! 为了解决这个问题,scirpophaga与这个模块的整合已经加入开发计划……未来可能可以解决这个问题 -//! -//! # 依赖环境变量 -//! cuscuta-chilo使用环境变量注入参数,这个crate依赖的环境变量有: -//! - `BIN_C1` -//! - `BIN_C2` -//! - `BIN_LOGIN_C31` -//! - `BIN_LOGIN_C32` - -#![deny(clippy::nursery)] -#![deny(clippy::pedantic)] - -use std::{env, sync::OnceLock}; - -use axum::{Json, Router, extract::Query, http::StatusCode, response::IntoResponse, routing::get}; -use base64::Engine; -use serde::{Deserialize, Serialize}; -use serde_json::json; -use tokio::net::TcpListener; - -const BIN: [u8; 7180] = [ - 0xF7, 0x7B, 0x44, 0xB9, 0xEB, 0x87, 0x44, 0xB9, 0xEE, 0x8B, 0x44, 0xB9, 0xE5, 0x8F, 0x44, 0xB9, - 0xE3, 0x7F, 0x44, 0xB9, 0xF3, 0x93, 0x44, 0xB9, 0xE9, 0x83, 0x44, 0xB9, 0xEF, 0x97, 0x44, 0xB9, - 0xF5, 0x9B, 0x44, 0xB9, 0xAC, 0x00, 0x0E, 0x0A, 0x6D, 0x01, 0x17, 0x0A, 0x68, 0x02, 0x03, 0x4A, - 0x8C, 0x01, 0x0D, 0x4A, 0x08, 0x01, 0x09, 0x0A, 0x8C, 0x01, 0x13, 0x4A, 0xEF, 0x4B, 0x01, 0xB9, - 0xEF, 0x2D, 0x8F, 0x13, 0x88, 0x01, 0x08, 0x4A, 0xF8, 0x9F, 0x44, 0xB9, 0xB0, 0x00, 0x17, 0x4A, - 0xD1, 0x01, 0x0B, 0x0A, 0xAF, 0x02, 0x0F, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x10, 0x02, 0x03, 0x0A, - 0xB1, 0x00, 0x11, 0x4A, 0xE8, 0x01, 0x08, 0x0B, 0x30, 0x02, 0x10, 0x4A, 0x0F, 0x01, 0x09, 0x0A, - 0x60, 0x2E, 0x93, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xFB, 0xA3, 0x44, 0xB9, 0x61, 0x01, 0x09, 0x0A, - 0x10, 0x01, 0x0E, 0x4A, 0x00, 0x03, 0x00, 0x0B, 0xEF, 0x1D, 0x8F, 0x13, 0xF3, 0x47, 0x01, 0xB9, - 0xC1, 0x01, 0x01, 0x4A, 0x10, 0x02, 0x17, 0x0A, 0x13, 0x00, 0x0F, 0x0B, 0x30, 0x00, 0x10, 0x4A, - 0x6F, 0x02, 0x03, 0x0A, 0xA2, 0x2C, 0x85, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xF9, 0xA7, 0x44, 0xB9, - 0x24, 0x01, 0x03, 0x0A, 0x62, 0x03, 0x02, 0x0B, 0x70, 0x02, 0x0B, 0x4A, 0xEF, 0x1D, 0x8F, 0x13, - 0x64, 0x01, 0x04, 0x4A, 0x10, 0x02, 0x08, 0x0A, 0x42, 0x00, 0x0F, 0x0B, 0x90, 0x00, 0x10, 0x4A, - 0x4F, 0x00, 0x17, 0x0A, 0xC6, 0x2D, 0x8E, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xEA, 0xAB, 0x44, 0xB9, - 0x67, 0x00, 0x17, 0x0A, 0x26, 0x03, 0x06, 0x0B, 0x50, 0x00, 0x09, 0x4A, 0xEF, 0x1D, 0x8F, 0x13, - 0x27, 0x01, 0x07, 0x4A, 0x10, 0x02, 0x13, 0x0A, 0xC6, 0x00, 0x0F, 0x0B, 0xF0, 0x00, 0x10, 0x4A, - 0xCF, 0x00, 0x08, 0x0A, 0x6D, 0x2D, 0x8B, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xF4, 0xAF, 0x44, 0xB9, - 0x4D, 0x01, 0x0D, 0x0B, 0x00, 0x01, 0x17, 0x0A, 0xD0, 0x00, 0x03, 0x4A, 0xEF, 0x1D, 0x8F, 0x13, - 0x00, 0x00, 0x03, 0x4A, 0x10, 0x02, 0x02, 0x0A, 0xA7, 0x01, 0x0F, 0x0B, 0x10, 0x00, 0x10, 0x4A, - 0xED, 0x00, 0x13, 0x0A, 0x31, 0x2D, 0x89, 0x13, 0x0D, 0x02, 0x0D, 0x4A, 0x91, 0x02, 0x11, 0x0B, - 0x64, 0x02, 0x08, 0x0A, 0xEF, 0x00, 0x17, 0x4A, 0xAD, 0x1D, 0x8D, 0x13, 0xF6, 0xB3, 0x44, 0xB9, - 0x84, 0x00, 0x17, 0x4A, 0xEF, 0x01, 0x06, 0x0A, 0x2D, 0x02, 0x0D, 0x0B, 0x8F, 0x00, 0x0F, 0x4A, - 0xB1, 0x01, 0x02, 0x0A, 0x40, 0x00, 0x13, 0x0A, 0xEF, 0x01, 0x11, 0x4A, 0xB1, 0x01, 0x08, 0x4A, - 0x6C, 0x2C, 0x83, 0x13, 0x00, 0x00, 0x08, 0x4A, 0x31, 0x02, 0x07, 0x0A, 0xFA, 0xB7, 0x44, 0xB9, - 0xCC, 0x02, 0x0C, 0x0B, 0x11, 0x00, 0x11, 0x4A, 0xE0, 0x00, 0x06, 0x0A, 0xEF, 0x1D, 0x8F, 0x13, - 0x04, 0x00, 0x02, 0x4A, 0x80, 0x01, 0x0F, 0x0B, 0x0C, 0x00, 0x06, 0x0A, 0xE1, 0x2E, 0x97, 0x13, - 0xD0, 0x00, 0x02, 0x0A, 0x2C, 0x02, 0x0C, 0x4A, 0x0F, 0x00, 0x13, 0x4A, 0xE9, 0x2F, 0x01, 0xB9, - 0x41, 0x03, 0x01, 0x0B, 0x10, 0x02, 0x13, 0x4A, 0xEF, 0x01, 0x0D, 0x0A, 0x8C, 0x1D, 0x8C, 0x13, - 0xE9, 0xBB, 0x44, 0xB9, 0x10, 0x02, 0x0F, 0x4A, 0x2F, 0x00, 0x0C, 0x0B, 0xEC, 0x01, 0x07, 0x0A, - 0xF1, 0x01, 0x02, 0x4A, 0x0C, 0x02, 0x0C, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x30, 0x02, 0x00, 0x0A, - 0x28, 0x01, 0x08, 0x0B, 0x8C, 0x1D, 0x8C, 0x13, 0x91, 0x00, 0x10, 0x4A, 0x10, 0x01, 0x0C, 0x0B, - 0xEA, 0x7F, 0x00, 0xB9, 0xEA, 0xBF, 0x44, 0xB9, 0x08, 0x02, 0x0D, 0x0A, 0x28, 0x02, 0x08, 0x4A, - 0xAC, 0x01, 0x07, 0x0A, 0x11, 0x02, 0x06, 0x4A, 0x8C, 0x01, 0x06, 0x4A, 0x31, 0x02, 0x0F, 0x0A, - 0x8C, 0x01, 0x11, 0x4A, 0x71, 0x2E, 0x93, 0x13, 0x51, 0x01, 0x11, 0x0B, 0x08, 0x1D, 0x88, 0x13, - 0x28, 0x02, 0x08, 0x0B, 0xE9, 0x73, 0x00, 0xB9, 0x11, 0x01, 0x00, 0x0A, 0xE9, 0xC3, 0x44, 0xB9, - 0x8C, 0x01, 0x11, 0x4A, 0x11, 0x00, 0x0D, 0x0A, 0x01, 0x01, 0x07, 0x4A, 0x31, 0x02, 0x07, 0x4A, - 0x21, 0x00, 0x10, 0x0A, 0x21, 0x02, 0x01, 0x4A, 0x51, 0x2C, 0x82, 0x13, 0x31, 0x02, 0x09, 0x0B, - 0x8C, 0x1D, 0x8C, 0x13, 0x31, 0x02, 0x0C, 0x0B, 0xE5, 0x43, 0x01, 0xB9, 0xE5, 0xC7, 0x44, 0xB9, - 0x2C, 0x02, 0x0F, 0x0A, 0x2C, 0x00, 0x0C, 0x4A, 0xE1, 0x01, 0x00, 0x0A, 0x22, 0x02, 0x0D, 0x4A, - 0x21, 0x00, 0x0D, 0x4A, 0x42, 0x00, 0x08, 0x0A, 0x21, 0x00, 0x02, 0x4A, 0xC2, 0x2C, 0x86, 0x13, - 0x42, 0x00, 0x05, 0x0B, 0x8C, 0x1D, 0x8C, 0x13, 0x4C, 0x00, 0x0C, 0x0B, 0xE9, 0x6F, 0x01, 0xB9, - 0x82, 0x01, 0x10, 0x0A, 0xE9, 0xCB, 0x44, 0xB9, 0x21, 0x00, 0x02, 0x4A, 0x02, 0x02, 0x0F, 0x0A, - 0x84, 0x01, 0x00, 0x4A, 0x42, 0x00, 0x00, 0x4A, 0x84, 0x00, 0x11, 0x0A, 0x42, 0x00, 0x04, 0x4A, - 0xE4, 0x2C, 0x87, 0x13, 0xEA, 0x83, 0x00, 0xB9, 0xEA, 0xCF, 0x44, 0xB9, 0x84, 0x00, 0x09, 0x0B, - 0x21, 0x1C, 0x81, 0x13, 0x81, 0x00, 0x01, 0x0B, 0x24, 0x00, 0x08, 0x0A, 0x42, 0x00, 0x04, 0x4A, - 0xAD, 0x2D, 0x8D, 0x13, 0xEA, 0x0B, 0x01, 0xB9, 0x04, 0x01, 0x10, 0x0A, 0x26, 0x00, 0x0F, 0x4A, - 0xAD, 0x01, 0x0A, 0x0B, 0x42, 0x1C, 0x82, 0x13, 0xEA, 0xD3, 0x44, 0xB9, 0x84, 0x00, 0x0F, 0x4A, - 0xC6, 0x00, 0x0C, 0x0A, 0xAD, 0x01, 0x02, 0x0B, 0x84, 0x00, 0x06, 0x4A, 0xA2, 0x01, 0x11, 0x0A, - 0x82, 0x00, 0x02, 0x4A, 0x24, 0x02, 0x08, 0x0A, 0xA6, 0x01, 0x10, 0x4A, 0x00, 0x2C, 0x80, 0x13, - 0xEB, 0x2B, 0x01, 0xB9, 0x84, 0x00, 0x10, 0x4A, 0xC6, 0x00, 0x01, 0x0A, 0xEB, 0xD7, 0x44, 0xB9, - 0x00, 0x00, 0x0A, 0x0B, 0x42, 0x1C, 0x82, 0x13, 0x86, 0x00, 0x06, 0x4A, 0x04, 0x00, 0x02, 0x0B, - 0x80, 0x00, 0x0C, 0x0A, 0xC0, 0x00, 0x00, 0x4A, 0xEF, 0x2D, 0x8F, 0x13, 0xE3, 0x13, 0x01, 0xB9, - 0x82, 0x01, 0x11, 0x0A, 0x86, 0x00, 0x08, 0x4A, 0xEF, 0x01, 0x0B, 0x0B, 0x00, 0x1C, 0x80, 0x13, - 0xE3, 0xDB, 0x44, 0xB9, 0x42, 0x00, 0x08, 0x4A, 0xC6, 0x00, 0x0D, 0x0A, 0xEF, 0x01, 0x00, 0x0B, - 0x42, 0x00, 0x06, 0x4A, 0xE0, 0x01, 0x01, 0x0A, 0x40, 0x00, 0x00, 0x4A, 0x22, 0x00, 0x0C, 0x0A, - 0xE6, 0x01, 0x11, 0x4A, 0x10, 0x2E, 0x90, 0x13, 0xEB, 0xF3, 0x00, 0xB9, 0x42, 0x00, 0x11, 0x4A, - 0xC6, 0x00, 0x04, 0x0A, 0xEB, 0xDF, 0x44, 0xB9, 0x10, 0x02, 0x03, 0x0B, 0x00, 0x1C, 0x80, 0x13, - 0x42, 0x00, 0x06, 0x4A, 0x06, 0x02, 0x00, 0x0B, 0xD0, 0x00, 0x0D, 0x0A, 0x50, 0x00, 0x10, 0x4A, - 0x08, 0x2D, 0x88, 0x13, 0xEB, 0x6F, 0x00, 0xB9, 0xA0, 0x01, 0x01, 0x0A, 0xC2, 0x00, 0x0C, 0x4A, - 0x08, 0x01, 0x0B, 0x0B, 0x10, 0x1E, 0x90, 0x13, 0xEB, 0xE3, 0x44, 0xB9, 0x00, 0x00, 0x0C, 0x4A, - 0x42, 0x00, 0x0F, 0x0A, 0x08, 0x01, 0x10, 0x0B, 0x00, 0x00, 0x02, 0x4A, 0x10, 0x01, 0x04, 0x0A, - 0x00, 0x00, 0x10, 0x4A, 0x31, 0x2E, 0x91, 0x13, 0xE9, 0xFB, 0x00, 0xB9, 0x90, 0x00, 0x0D, 0x0A, - 0x02, 0x01, 0x01, 0x4A, 0xE9, 0xE7, 0x44, 0xB9, 0x31, 0x02, 0x0B, 0x0B, 0x00, 0x1C, 0x80, 0x13, - 0x10, 0x02, 0x01, 0x4A, 0x42, 0x00, 0x06, 0x0A, 0x27, 0x02, 0x00, 0x0B, 0x02, 0x02, 0x02, 0x4A, - 0xF1, 0x00, 0x0F, 0x0A, 0x51, 0x00, 0x11, 0x4A, 0x8C, 0x2D, 0x8C, 0x13, 0xE0, 0x01, 0x04, 0x0A, - 0xE2, 0x00, 0x0D, 0x4A, 0x8C, 0x01, 0x09, 0x0B, 0x31, 0x1E, 0x91, 0x13, 0x00, 0x00, 0x0D, 0x4A, - 0x42, 0x00, 0x08, 0x0A, 0x8C, 0x01, 0x11, 0x0B, 0x00, 0x00, 0x02, 0x4A, 0x91, 0x01, 0x06, 0x0A, - 0x11, 0x00, 0x11, 0x4A, 0xC0, 0x00, 0x0F, 0x0A, 0x02, 0x00, 0x04, 0x4A, 0x80, 0x01, 0x04, 0x4A, - 0x13, 0x00, 0x07, 0x0A, 0xE0, 0xEB, 0x44, 0xB9, 0x21, 0x2C, 0x81, 0x13, 0xF0, 0xEF, 0x44, 0xB9, - 0x31, 0x1E, 0x91, 0x13, 0x21, 0x00, 0x00, 0x0B, 0xFA, 0x53, 0x01, 0xB9, 0x3A, 0x00, 0x11, 0x0B, - 0x42, 0x00, 0x13, 0x4A, 0x51, 0x03, 0x08, 0x0A, 0x51, 0x00, 0x11, 0x4A, 0xAD, 0x2D, 0x8D, 0x13, - 0x01, 0x01, 0x06, 0x0A, 0x42, 0x03, 0x0F, 0x4A, 0xAD, 0x01, 0x10, 0x0B, 0x31, 0x1E, 0x91, 0x13, - 0x21, 0x00, 0x0F, 0x4A, 0x42, 0x00, 0x0C, 0x0A, 0xB1, 0x01, 0x11, 0x0B, 0x21, 0x00, 0x02, 0x4A, - 0x22, 0x02, 0x06, 0x4A, 0x53, 0x00, 0x1A, 0x0A, 0xE2, 0xF3, 0x44, 0xB9, 0x2D, 0x02, 0x07, 0x0A, - 0x2D, 0x00, 0x0D, 0x4A, 0x84, 0x2C, 0x84, 0x13, 0xEE, 0x3F, 0x01, 0xB9, 0xE1, 0x00, 0x08, 0x0A, - 0xEE, 0xF7, 0x44, 0xB9, 0x84, 0x00, 0x02, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xFB, 0x27, 0x01, 0xB9, - 0x21, 0x00, 0x06, 0x4A, 0x9B, 0x00, 0x0D, 0x0B, 0x21, 0x00, 0x13, 0x4A, 0x6D, 0x03, 0x0C, 0x0A, - 0x2D, 0x00, 0x0D, 0x4A, 0xEF, 0x2D, 0x8F, 0x13, 0x81, 0x01, 0x07, 0x0A, 0x64, 0x03, 0x08, 0x4A, - 0xEF, 0x01, 0x0E, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xE9, 0xDB, 0x00, 0xB9, 0x21, 0x00, 0x08, 0x4A, - 0x84, 0x00, 0x11, 0x0A, 0xF3, 0x01, 0x0D, 0x0B, 0xE9, 0xFB, 0x44, 0xB9, 0x21, 0x00, 0x04, 0x4A, - 0x6D, 0x02, 0x1A, 0x0A, 0x2D, 0x00, 0x0D, 0x4A, 0x4F, 0x03, 0x0C, 0x0A, 0x61, 0x02, 0x07, 0x4A, - 0xEF, 0x01, 0x07, 0x4A, 0x21, 0x00, 0x1B, 0x0A, 0xC6, 0x2C, 0x86, 0x13, 0xEF, 0x01, 0x01, 0x4A, - 0xE1, 0xFF, 0x44, 0xB9, 0xC6, 0x00, 0x09, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xF8, 0x63, 0x01, 0xB9, - 0xF8, 0x03, 0x14, 0x2A, 0xF4, 0x1B, 0x01, 0xB9, 0xD4, 0x00, 0x0D, 0x0B, 0x8D, 0x02, 0x11, 0x0A, - 0xED, 0x01, 0x0D, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x2F, 0x02, 0x1A, 0x0A, 0x86, 0x02, 0x0C, 0x4A, - 0x08, 0x01, 0x01, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xF5, 0x5B, 0x01, 0xB9, 0xEF, 0x01, 0x0C, 0x4A, - 0xC6, 0x00, 0x13, 0x0A, 0x15, 0x01, 0x0D, 0x0B, 0xE9, 0x97, 0x00, 0xB9, 0xEF, 0x01, 0x06, 0x4A, - 0xA8, 0x02, 0x1B, 0x0A, 0xE9, 0x03, 0x45, 0xB9, 0xE8, 0x01, 0x08, 0x4A, 0x6D, 0x03, 0x11, 0x0A, - 0xAF, 0x02, 0x1A, 0x4A, 0xAD, 0x01, 0x1A, 0x4A, 0xEF, 0x01, 0x14, 0x0A, 0xAD, 0x01, 0x0F, 0x4A, - 0xEF, 0x2C, 0x87, 0x13, 0xE1, 0xD3, 0x00, 0xB9, 0xE1, 0x07, 0x45, 0xB9, 0xEF, 0x01, 0x09, 0x0B, - 0x08, 0x1D, 0x88, 0x13, 0xF6, 0x5F, 0x01, 0xB9, 0xF6, 0x01, 0x08, 0x0B, 0xC8, 0x02, 0x13, 0x0A, - 0xA8, 0x01, 0x08, 0x4A, 0x8C, 0x2D, 0x8C, 0x13, 0xE1, 0x9B, 0x00, 0xB9, 0x6D, 0x02, 0x1B, 0x0A, - 0xCF, 0x02, 0x11, 0x4A, 0x8C, 0x01, 0x01, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xE1, 0x0B, 0x45, 0xB9, - 0xF9, 0x57, 0x01, 0xB9, 0xAD, 0x01, 0x11, 0x4A, 0xEF, 0x01, 0x15, 0x0A, 0x99, 0x01, 0x08, 0x0B, - 0xAD, 0x01, 0x0F, 0x4A, 0x28, 0x03, 0x14, 0x0A, 0x8C, 0x02, 0x13, 0x0A, 0xA8, 0x01, 0x08, 0x4A, - 0x8D, 0x01, 0x1B, 0x4A, 0x2C, 0x03, 0x1B, 0x4A, 0x46, 0x2F, 0x9A, 0x13, 0x8F, 0x01, 0x16, 0x0A, - 0xEC, 0x0F, 0x45, 0xB9, 0xC6, 0x00, 0x01, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xC7, 0x00, 0x08, 0x0B, - 0xAF, 0x01, 0x0F, 0x4A, 0xE8, 0x00, 0x15, 0x0A, 0xE8, 0x01, 0x08, 0x4A, 0xAF, 0x02, 0x14, 0x0A, - 0xE6, 0x00, 0x13, 0x4A, 0x31, 0x2E, 0x91, 0x13, 0xEF, 0x01, 0x13, 0x4A, 0xC6, 0x00, 0x19, 0x0A, - 0x31, 0x02, 0x0C, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xEC, 0x8F, 0x00, 0xB9, 0xEF, 0x01, 0x06, 0x4A, - 0x26, 0x02, 0x08, 0x0B, 0xEC, 0x13, 0x45, 0xB9, 0xC8, 0x00, 0x16, 0x0A, 0xE8, 0x01, 0x08, 0x4A, - 0xCF, 0x02, 0x15, 0x0A, 0xF1, 0x01, 0x14, 0x4A, 0xCF, 0x00, 0x14, 0x4A, 0x7B, 0x2F, 0x9B, 0x13, - 0xFA, 0x01, 0x07, 0x0A, 0xEF, 0x17, 0x45, 0xB9, 0x7B, 0x03, 0x0C, 0x0B, 0x08, 0x1D, 0x88, 0x13, - 0x68, 0x03, 0x08, 0x0B, 0x3A, 0x02, 0x1A, 0x4A, 0x1B, 0x01, 0x19, 0x0A, 0x5A, 0x03, 0x1B, 0x4A, - 0x73, 0x2E, 0x93, 0x13, 0x73, 0x02, 0x0F, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0xDB, 0x02, 0x15, 0x4A, - 0x1C, 0x01, 0x06, 0x0A, 0x73, 0x02, 0x1A, 0x0B, 0x7B, 0x03, 0x1C, 0x4A, 0x3A, 0x03, 0x33, 0x0A, - 0x7A, 0x03, 0x1A, 0x4A, 0xDB, 0x00, 0x19, 0x4A, 0x1B, 0x01, 0x1B, 0x0A, 0x7B, 0x03, 0x16, 0x4A, - 0x7C, 0x02, 0x06, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x5A, 0x03, 0x07, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, - 0x94, 0x2E, 0x94, 0x13, 0x94, 0x02, 0x18, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0x24, 0xC3, 0x9B, 0x52, - 0x94, 0x02, 0x1A, 0x0B, 0xE4, 0x5A, 0xB7, 0x72, 0x9A, 0x00, 0x14, 0x4B, 0xC4, 0x3C, 0x84, 0x52, - 0x3C, 0x03, 0x16, 0x4A, 0x7E, 0x02, 0x08, 0x0A, 0x1B, 0x01, 0x07, 0x4A, 0x04, 0xA5, 0xA8, 0x72, - 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x07, 0x0A, 0x7B, 0x02, 0x1B, 0x0A, 0x94, 0x02, 0x04, 0x0B, - 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x19, 0x4A, 0x9C, 0x02, 0x08, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, - 0x5A, 0x03, 0x06, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, 0xB5, 0x2E, 0x95, 0x13, 0xB5, 0x02, 0x0A, 0x0B, - 0x5A, 0x1F, 0x9A, 0x13, 0x04, 0x91, 0x9D, 0x52, 0xB5, 0x02, 0x1A, 0x0B, 0xE4, 0xE5, 0xB8, 0x72, - 0x9A, 0x00, 0x15, 0x4B, 0xE4, 0x6E, 0x82, 0x52, 0xFC, 0x00, 0x19, 0x4A, 0x9E, 0x02, 0x13, 0x0A, - 0x7B, 0x02, 0x06, 0x4A, 0x04, 0x1A, 0xA7, 0x72, 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x06, 0x0A, - 0x9B, 0x02, 0x1B, 0x0A, 0xB5, 0x02, 0x04, 0x0B, 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x07, 0x4A, - 0xBC, 0x02, 0x13, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x5A, 0x03, 0x08, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, - 0xD6, 0x2E, 0x96, 0x13, 0xD6, 0x02, 0x09, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0x04, 0x26, 0x93, 0x52, - 0xD6, 0x02, 0x1A, 0x0B, 0x64, 0x35, 0xA8, 0x72, 0x9A, 0x00, 0x16, 0x4B, 0xE4, 0xD9, 0x8C, 0x52, - 0xDC, 0x00, 0x07, 0x4A, 0xBE, 0x02, 0x14, 0x0A, 0x9B, 0x02, 0x08, 0x4A, 0x84, 0xCA, 0xB7, 0x72, - 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x08, 0x0A, 0xBB, 0x02, 0x1B, 0x0A, 0xD6, 0x02, 0x04, 0x0B, - 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x06, 0x4A, 0xDC, 0x02, 0x14, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, - 0x5A, 0x03, 0x13, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, 0x39, 0x2F, 0x99, 0x13, 0x39, 0x03, 0x0B, 0x0B, - 0x5A, 0x1F, 0x9A, 0x13, 0x64, 0x72, 0x9E, 0x52, 0x39, 0x03, 0x1A, 0x0B, 0xC4, 0x62, 0xB9, 0x72, - 0x9A, 0x00, 0x19, 0x4B, 0x84, 0x8D, 0x81, 0x52, 0x1C, 0x01, 0x06, 0x4A, 0xDE, 0x02, 0x15, 0x0A, - 0xBB, 0x02, 0x13, 0x4A, 0x24, 0x9D, 0xA6, 0x72, 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x13, 0x0A, - 0xDB, 0x02, 0x1B, 0x0A, 0x39, 0x03, 0x04, 0x0B, 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x08, 0x4A, - 0x3C, 0x03, 0x15, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x5A, 0x03, 0x14, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, - 0xE7, 0x2C, 0x87, 0x13, 0xE7, 0x00, 0x05, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0x04, 0xC9, 0x9A, 0x52, - 0xE7, 0x00, 0x1A, 0x0B, 0x64, 0xEA, 0xA7, 0x72, 0x7C, 0x02, 0x08, 0x4A, 0x3E, 0x03, 0x16, 0x0A, - 0x9A, 0x00, 0x07, 0x4B, 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x14, 0x0A, 0x9B, 0x03, 0x1A, 0x4A, - 0xDA, 0x02, 0x14, 0x4A, 0xE4, 0x36, 0x85, 0x52, 0x3A, 0x03, 0x1A, 0x0A, 0x84, 0x15, 0xB8, 0x72, - 0x5C, 0x03, 0x13, 0x4A, 0xFA, 0x00, 0x04, 0x0B, 0x47, 0x03, 0x16, 0x0A, 0x87, 0x03, 0x07, 0x4A, - 0x7B, 0x03, 0x15, 0x0A, 0xE7, 0x00, 0x1B, 0x4A, 0xC6, 0x2C, 0x86, 0x13, 0xC6, 0x00, 0x01, 0x0B, - 0xE7, 0x1C, 0x87, 0x13, 0x44, 0xE4, 0x95, 0x52, 0xC6, 0x00, 0x07, 0x0B, 0x64, 0xD0, 0xA6, 0x72, - 0x9C, 0x02, 0x13, 0x4A, 0x5E, 0x03, 0x19, 0x0A, 0x87, 0x00, 0x06, 0x4B, 0x3B, 0x03, 0x15, 0x4A, - 0xA4, 0x1B, 0x8A, 0x52, 0x9C, 0x03, 0x1E, 0x4A, 0xE7, 0x00, 0x15, 0x0A, 0x5B, 0x03, 0x1B, 0x0A, - 0x84, 0x2F, 0xB9, 0x72, 0xE9, 0xC3, 0x00, 0xB9, 0x87, 0x03, 0x07, 0x4A, 0x7C, 0x03, 0x14, 0x4A, - 0xDB, 0x00, 0x04, 0x0B, 0xE9, 0x53, 0x41, 0xB9, 0x66, 0x03, 0x19, 0x0A, 0x86, 0x03, 0x06, 0x4A, - 0xE7, 0x00, 0x16, 0x0A, 0xC6, 0x00, 0x07, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x08, 0x01, 0x09, 0x0B, - 0xC6, 0x1C, 0x86, 0x13, 0x44, 0x49, 0x85, 0x52, 0x08, 0x01, 0x06, 0x0B, 0x64, 0x0F, 0xB8, 0x72, - 0xBC, 0x02, 0x14, 0x4A, 0x7E, 0x03, 0x1A, 0x0A, 0x86, 0x00, 0x08, 0x4B, 0xA4, 0xB6, 0x9A, 0x52, - 0x9C, 0x03, 0x1E, 0x4A, 0xC6, 0x00, 0x16, 0x0A, 0x47, 0x03, 0x16, 0x4A, 0x84, 0xF0, 0xA7, 0x72, - 0x86, 0x03, 0x06, 0x4A, 0x67, 0x03, 0x07, 0x0A, 0x1C, 0x01, 0x04, 0x0B, 0xE7, 0x00, 0x15, 0x4A, - 0x88, 0x03, 0x1A, 0x0A, 0xE8, 0x00, 0x08, 0x4A, 0xC6, 0x00, 0x19, 0x0A, 0x08, 0x01, 0x06, 0x4A, - 0x66, 0x2E, 0x93, 0x13, 0xC6, 0x00, 0x03, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x04, 0xDD, 0x9E, 0x52, - 0xC8, 0x00, 0x08, 0x0B, 0x04, 0x57, 0xA9, 0x72, 0xC7, 0x02, 0x15, 0x4A, 0x9E, 0x03, 0x1B, 0x0A, - 0x86, 0x00, 0x08, 0x4B, 0xE7, 0x00, 0x1E, 0x4A, 0xC6, 0x00, 0x19, 0x0A, 0xE4, 0x22, 0x81, 0x52, - 0xE6, 0x00, 0x06, 0x4A, 0x67, 0x03, 0x19, 0x4A, 0xE4, 0xA8, 0xB6, 0x72, 0xEA, 0xA3, 0x00, 0xB9, - 0x87, 0x03, 0x07, 0x0A, 0x08, 0x01, 0x04, 0x0B, 0xEA, 0x5B, 0x41, 0xB9, 0xE7, 0x00, 0x16, 0x4A, - 0x13, 0x01, 0x1B, 0x0A, 0xE7, 0x00, 0x13, 0x4A, 0xC6, 0x00, 0x1A, 0x0A, 0xE6, 0x00, 0x06, 0x4A, - 0x87, 0x2E, 0x94, 0x13, 0xE7, 0x00, 0x0A, 0x0B, 0xC6, 0x1C, 0x86, 0x13, 0xC4, 0x44, 0x85, 0x52, - 0xE6, 0x00, 0x06, 0x0B, 0x24, 0xBD, 0xAD, 0x72, 0x33, 0x03, 0x16, 0x4A, 0x1E, 0x01, 0x1C, 0x0A, - 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x1E, 0x4A, 0xE7, 0x00, 0x1A, 0x0A, 0x24, 0xBB, 0x9A, 0x52, - 0x67, 0x02, 0x07, 0x4A, 0x93, 0x03, 0x1A, 0x4A, 0xC4, 0x42, 0xB2, 0x72, 0x13, 0x01, 0x13, 0x0A, - 0xD4, 0x00, 0x04, 0x0B, 0x73, 0x02, 0x19, 0x4A, 0x86, 0x02, 0x1C, 0x0A, 0x66, 0x02, 0x06, 0x4A, - 0xE7, 0x00, 0x1B, 0x0A, 0xC6, 0x00, 0x07, 0x4A, 0xA7, 0x2E, 0x95, 0x13, 0xE7, 0x00, 0x0E, 0x0B, - 0xC6, 0x1C, 0x86, 0x13, 0x84, 0x9C, 0x80, 0x52, 0xE6, 0x00, 0x06, 0x0B, 0xC4, 0xD0, 0xAE, 0x72, - 0x53, 0x03, 0x19, 0x4A, 0x9E, 0x02, 0x08, 0x0A, 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x1E, 0x4A, - 0xE7, 0x00, 0x1B, 0x0A, 0x64, 0x63, 0x9F, 0x52, 0x67, 0x02, 0x07, 0x4A, 0x13, 0x01, 0x1B, 0x4A, - 0x24, 0x2F, 0xB1, 0x72, 0x93, 0x02, 0x13, 0x0A, 0xD5, 0x00, 0x04, 0x0B, 0x73, 0x02, 0x1A, 0x4A, - 0xA6, 0x02, 0x08, 0x0A, 0x66, 0x02, 0x06, 0x4A, 0xE7, 0x00, 0x1C, 0x0A, 0xC6, 0x00, 0x07, 0x4A, - 0xC7, 0x2E, 0x96, 0x13, 0xE7, 0x00, 0x00, 0x0B, 0xC6, 0x1C, 0x86, 0x13, 0x24, 0x8B, 0x9E, 0x52, - 0xE6, 0x00, 0x06, 0x0B, 0xC4, 0xD9, 0xA5, 0x72, 0x73, 0x03, 0x1A, 0x4A, 0xBE, 0x02, 0x14, 0x0A, - 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x1E, 0x4A, 0xE7, 0x00, 0x1C, 0x0A, 0xC4, 0x74, 0x81, 0x52, - 0x67, 0x02, 0x07, 0x4A, 0x93, 0x02, 0x1C, 0x4A, 0x24, 0x26, 0xBA, 0x72, 0xB3, 0x02, 0x13, 0x0A, - 0xDE, 0x00, 0x04, 0x0B, 0x73, 0x02, 0x1B, 0x4A, 0xC6, 0x03, 0x14, 0x0A, 0x66, 0x02, 0x06, 0x4A, - 0xE7, 0x00, 0x08, 0x0A, 0xC6, 0x00, 0x07, 0x4A, 0x27, 0x2F, 0x99, 0x13, 0xE7, 0x00, 0x02, 0x0B, - 0xC6, 0x1C, 0x86, 0x13, 0x64, 0x4A, 0x89, 0x52, 0xE6, 0x00, 0x06, 0x0B, 0x04, 0xE4, 0xAC, 0x72, - 0x93, 0x03, 0x1B, 0x4A, 0xD6, 0x03, 0x15, 0x0A, 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x16, 0x4A, - 0xE7, 0x00, 0x08, 0x0A, 0x73, 0x02, 0x07, 0x4A, 0xA7, 0x02, 0x08, 0x4A, 0x84, 0xB5, 0x96, 0x52, - 0xC7, 0x03, 0x07, 0x0A, 0xE4, 0x1B, 0xB3, 0x72, 0xF6, 0x00, 0x1C, 0x4A, 0xC7, 0x00, 0x04, 0x0B, - 0xE9, 0x63, 0x41, 0xB9, 0xE6, 0x00, 0x15, 0x0A, 0xC6, 0x02, 0x06, 0x4A, 0x73, 0x02, 0x14, 0x0A, - 0xC6, 0x00, 0x13, 0x4A, 0x53, 0x2F, 0x9A, 0x13, 0x73, 0x02, 0x09, 0x0B, 0xC6, 0x1C, 0x86, 0x13, - 0x84, 0xA4, 0x91, 0x52, 0x66, 0x02, 0x06, 0x0B, 0x44, 0x00, 0xBA, 0x72, 0x16, 0x01, 0x1C, 0x4A, - 0xF9, 0x00, 0x1E, 0x0A, 0x93, 0x00, 0x06, 0x4B, 0xD6, 0x02, 0x19, 0x4A, 0x73, 0x02, 0x14, 0x0A, - 0x64, 0x5B, 0x8E, 0x52, 0xD3, 0x02, 0x13, 0x4A, 0xD6, 0x03, 0x14, 0x4A, 0xA4, 0xFF, 0xA5, 0x72, - 0xF6, 0x00, 0x16, 0x0A, 0xC6, 0x00, 0x04, 0x0B, 0xE9, 0x6F, 0x41, 0xB9, 0xD6, 0x02, 0x08, 0x4A, - 0xD9, 0x00, 0x1E, 0x0A, 0xD6, 0x02, 0x19, 0x4A, 0x73, 0x02, 0x15, 0x0A, 0xD3, 0x02, 0x13, 0x4A, - 0x76, 0x2F, 0x9B, 0x13, 0xD6, 0x02, 0x09, 0x0B, 0x73, 0x1E, 0x93, 0x13, 0x04, 0x09, 0x84, 0x52, - 0xD3, 0x02, 0x13, 0x0B, 0xA4, 0xFC, 0xA5, 0x72, 0x99, 0x02, 0x08, 0x4A, 0xDA, 0x00, 0x07, 0x0A, - 0x96, 0x00, 0x13, 0x4B, 0x39, 0x03, 0x1A, 0x4A, 0xD6, 0x02, 0x15, 0x0A, 0xE4, 0xF6, 0x9B, 0x52, - 0x36, 0x03, 0x16, 0x4A, 0xF9, 0x00, 0x15, 0x4A, 0x44, 0x03, 0xBA, 0x72, 0xED, 0x03, 0x0C, 0x2A, - 0xEC, 0x87, 0x00, 0xB9, 0xD9, 0x00, 0x19, 0x0A, 0x7B, 0x02, 0x04, 0x0B, 0xEC, 0x7F, 0x40, 0xB9, - 0x39, 0x03, 0x14, 0x4A, 0x73, 0x03, 0x07, 0x0A, 0x33, 0x03, 0x13, 0x4A, 0xD6, 0x02, 0x1E, 0x0A, - 0x73, 0x02, 0x16, 0x4A, 0x96, 0x2F, 0x9C, 0x13, 0xD6, 0x02, 0x0C, 0x0B, 0x73, 0x1E, 0x93, 0x13, - 0x44, 0x02, 0x8A, 0x52, 0xD3, 0x02, 0x13, 0x0B, 0xC4, 0xE3, 0xA8, 0x72, 0xB9, 0x02, 0x14, 0x4A, - 0x7A, 0x03, 0x06, 0x0A, 0x96, 0x00, 0x13, 0x4B, 0x39, 0x03, 0x1A, 0x4A, 0xD6, 0x02, 0x1E, 0x0A, - 0xA4, 0xFD, 0x95, 0x52, 0x36, 0x03, 0x16, 0x4A, 0xD9, 0x00, 0x1E, 0x4A, 0x24, 0x1C, 0xB7, 0x72, - 0x79, 0x03, 0x19, 0x0A, 0x73, 0x02, 0x04, 0x0B, 0xF1, 0x73, 0x40, 0xB9, 0x39, 0x03, 0x15, 0x4A, - 0x7A, 0x02, 0x06, 0x0A, 0x39, 0x03, 0x1A, 0x4A, 0xD6, 0x02, 0x07, 0x0A, 0x36, 0x03, 0x16, 0x4A, - 0x08, 0x2D, 0x88, 0x13, 0x08, 0x01, 0x11, 0x0B, 0xD6, 0x1E, 0x96, 0x13, 0x24, 0x2D, 0x90, 0x52, - 0x08, 0x01, 0x16, 0x0B, 0x24, 0xBB, 0xB2, 0x72, 0xDA, 0x03, 0x15, 0x4A, 0x7C, 0x02, 0x1B, 0x0A, - 0x96, 0x00, 0x08, 0x4B, 0x5A, 0x03, 0x1C, 0x4A, 0xD6, 0x02, 0x07, 0x0A, 0x59, 0x03, 0x16, 0x4A, - 0x76, 0x03, 0x07, 0x4A, 0xC4, 0xD2, 0x8F, 0x52, 0x76, 0x02, 0x16, 0x0A, 0xC4, 0x44, 0xAD, 0x72, - 0xDA, 0x02, 0x1E, 0x4A, 0x16, 0x01, 0x04, 0x0B, 0xC8, 0x02, 0x1B, 0x0A, 0x48, 0x03, 0x08, 0x4A, - 0x39, 0x03, 0x06, 0x0A, 0x08, 0x01, 0x19, 0x4A, 0x94, 0x2E, 0x94, 0x13, 0x94, 0x02, 0x0D, 0x0B, - 0x08, 0x1D, 0x88, 0x13, 0x44, 0xF7, 0x8D, 0x52, 0x88, 0x02, 0x08, 0x0B, 0x64, 0xB0, 0xA8, 0x72, - 0xFA, 0x00, 0x1E, 0x4A, 0xDC, 0x02, 0x13, 0x0A, 0x94, 0x00, 0x08, 0x4B, 0x79, 0x02, 0x06, 0x4A, - 0xA4, 0x08, 0x92, 0x52, 0x5A, 0x03, 0x1C, 0x4A, 0x94, 0x02, 0x06, 0x0A, 0xD9, 0x02, 0x19, 0x0A, - 0x84, 0x4F, 0xB7, 0x72, 0x54, 0x03, 0x14, 0x4A, 0x3A, 0x03, 0x07, 0x4A, 0x19, 0x01, 0x04, 0x0B, - 0xE9, 0x57, 0x41, 0xB9, 0x28, 0x03, 0x13, 0x0A, 0x48, 0x03, 0x08, 0x4A, 0x94, 0x02, 0x1B, 0x0A, - 0x08, 0x01, 0x14, 0x4A, 0xB4, 0x2E, 0x95, 0x13, 0x94, 0x02, 0x09, 0x0B, 0x08, 0x1D, 0x88, 0x13, - 0xC4, 0x0C, 0x90, 0x52, 0x88, 0x02, 0x08, 0x0B, 0x64, 0xDA, 0xA1, 0x72, 0xDA, 0x00, 0x07, 0x4A, - 0x3C, 0x03, 0x16, 0x0A, 0x94, 0x00, 0x08, 0x4B, 0x24, 0xF3, 0x8F, 0x52, 0x5A, 0x03, 0x1C, 0x4A, - 0x94, 0x02, 0x1B, 0x0A, 0xD5, 0x02, 0x1B, 0x4A, 0x84, 0x25, 0xBE, 0x72, 0x54, 0x03, 0x14, 0x4A, - 0x35, 0x03, 0x15, 0x0A, 0x1A, 0x01, 0x04, 0x0B, 0xB5, 0x02, 0x06, 0x4A, 0x48, 0x03, 0x16, 0x0A, - 0xA8, 0x02, 0x08, 0x4A, 0x94, 0x02, 0x13, 0x0A, 0x08, 0x01, 0x14, 0x4A, 0xD4, 0x2F, 0x9E, 0x13, - 0x94, 0x02, 0x10, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x04, 0xD7, 0x8C, 0x52, 0x88, 0x02, 0x08, 0x0B, - 0xC4, 0x6B, 0xBB, 0x72, 0x75, 0x03, 0x06, 0x4A, 0x5C, 0x03, 0x19, 0x0A, 0x94, 0x00, 0x08, 0x4B, - 0xB5, 0x02, 0x1C, 0x4A, 0x94, 0x02, 0x13, 0x0A, 0xE4, 0x28, 0x93, 0x52, 0xB4, 0x02, 0x14, 0x4A, - 0x35, 0x03, 0x13, 0x4A, 0x24, 0x94, 0xA4, 0x72, 0xE3, 0x93, 0x00, 0xB9, 0x55, 0x03, 0x15, 0x0A, - 0x1C, 0x01, 0x04, 0x0B, 0xE3, 0x83, 0x40, 0xB9, 0xB5, 0x02, 0x1B, 0x4A, 0x88, 0x03, 0x19, 0x0A, - 0xA8, 0x02, 0x08, 0x4A, 0x94, 0x02, 0x16, 0x0A, 0x08, 0x01, 0x14, 0x4A, 0xE7, 0x2C, 0x87, 0x13, - 0xE7, 0x00, 0x03, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x04, 0x61, 0x92, 0x52, 0xE8, 0x00, 0x08, 0x0B, - 0xC4, 0x8D, 0xA9, 0x72, 0x75, 0x02, 0x1B, 0x4A, 0x9E, 0x03, 0x1A, 0x0A, 0x87, 0x00, 0x08, 0x4B, - 0xE4, 0x9E, 0x8D, 0x52, 0xB5, 0x02, 0x1E, 0x4A, 0xE7, 0x00, 0x16, 0x0A, 0x54, 0x03, 0x16, 0x4A, - 0x24, 0x72, 0xB6, 0x72, 0xA7, 0x02, 0x07, 0x4A, 0x94, 0x03, 0x14, 0x0A, 0x15, 0x01, 0x04, 0x0B, - 0xEA, 0x6F, 0x40, 0xB9, 0x94, 0x02, 0x13, 0x4A, 0xA8, 0x02, 0x1A, 0x0A, 0x88, 0x02, 0x08, 0x4A, - 0xE7, 0x00, 0x19, 0x0A, 0x08, 0x01, 0x07, 0x4A, 0xC6, 0x2C, 0x86, 0x13, 0xC6, 0x00, 0x0A, 0x0B, - 0x08, 0x1D, 0x88, 0x13, 0xA4, 0xA3, 0x81, 0x52, 0xC8, 0x00, 0x08, 0x0B, 0xC4, 0xFF, 0xBE, 0x72, - 0xD4, 0x02, 0x13, 0x4A, 0xBE, 0x02, 0x1C, 0x0A, 0x86, 0x00, 0x08, 0x4B, 0x94, 0x02, 0x1E, 0x4A, - 0xC6, 0x00, 0x19, 0x0A, 0x44, 0x5C, 0x9E, 0x52, 0xEB, 0xE3, 0x00, 0xB9, 0x8B, 0x02, 0x06, 0x4A, - 0x86, 0x03, 0x19, 0x4A, 0x24, 0x00, 0xA1, 0x72, 0xA6, 0x02, 0x06, 0x0A, 0x14, 0x01, 0x04, 0x0B, - 0xC6, 0x00, 0x16, 0x4A, 0x88, 0x02, 0x1C, 0x0A, 0xCD, 0x00, 0x08, 0x4A, 0x28, 0x03, 0x16, 0x4A, - 0x86, 0x02, 0x15, 0x0A, 0xE9, 0x27, 0x41, 0xB9, 0xEE, 0xB3, 0x00, 0xB9, 0x0E, 0x01, 0x06, 0x4A, - 0x88, 0xEB, 0x99, 0x52, 0x88, 0x84, 0xAD, 0x72, 0xE8, 0x02, 0x08, 0x0B, 0xEF, 0x9F, 0x00, 0xB9, - 0x08, 0x01, 0x09, 0x0B, 0xEF, 0x97, 0x40, 0xB9, 0xE8, 0x63, 0x00, 0xB9, 0x88, 0x2E, 0x94, 0x13, - 0x1E, 0x01, 0x09, 0x0B, 0x68, 0x01, 0x1A, 0x0A, 0xA8, 0x01, 0x08, 0x4A, 0x66, 0x2F, 0x9B, 0x13, - 0xC6, 0x00, 0x0F, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x24, 0x7D, 0x80, 0x52, 0xC8, 0x00, 0x08, 0x0B, - 0x24, 0x4E, 0xAF, 0x72, 0x86, 0x00, 0x08, 0x4B, 0xA7, 0x02, 0x1A, 0x4A, 0xC4, 0x82, 0x9F, 0x52, - 0x87, 0x02, 0x07, 0x0A, 0xC4, 0xB1, 0xB0, 0x72, 0xC6, 0x00, 0x1A, 0x0A, 0xFB, 0x00, 0x19, 0x4A, - 0x07, 0x01, 0x04, 0x0B, 0xED, 0x8F, 0x40, 0xB9, 0xC6, 0x01, 0x06, 0x4A, 0xE8, 0x00, 0x15, 0x0A, - 0x68, 0x03, 0x08, 0x4A, 0xC6, 0x00, 0x1C, 0x0A, 0x08, 0x01, 0x06, 0x4A, 0x66, 0x2E, 0x93, 0x13, - 0xC6, 0x00, 0x0D, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xE4, 0xE4, 0x9B, 0x52, 0xC8, 0x00, 0x08, 0x0B, - 0xC4, 0x92, 0xB3, 0x72, 0x86, 0x00, 0x08, 0x4B, 0x04, 0x1B, 0x84, 0x52, 0x5B, 0x03, 0x19, 0x4A, - 0xF7, 0x00, 0x14, 0x0A, 0x93, 0x02, 0x1C, 0x4A, 0x24, 0x6D, 0xAC, 0x72, 0x77, 0x03, 0x17, 0x4A, - 0xC6, 0x00, 0x1C, 0x0A, 0xF3, 0x00, 0x13, 0x0A, 0x08, 0x01, 0x04, 0x0B, 0xE9, 0x5F, 0x41, 0xB9, - 0xE6, 0x02, 0x06, 0x4A, 0x73, 0x02, 0x1A, 0x4A, 0x17, 0x01, 0x14, 0x0A, 0x73, 0x02, 0x17, 0x4A, - 0xC6, 0x00, 0x15, 0x0A, 0x66, 0x02, 0x06, 0x4A, 0xD3, 0x2E, 0x96, 0x13, 0x73, 0x02, 0x09, 0x0B, - 0xC6, 0x1C, 0x86, 0x13, 0xC4, 0x32, 0x96, 0x52, 0x66, 0x02, 0x06, 0x0B, 0x04, 0xD5, 0xB1, 0x72, - 0x97, 0x03, 0x1A, 0x4A, 0x1B, 0x01, 0x07, 0x0A, 0x93, 0x00, 0x06, 0x4B, 0x24, 0xCD, 0x89, 0x52, - 0xF7, 0x02, 0x1B, 0x4A, 0x73, 0x02, 0x15, 0x0A, 0xF6, 0x00, 0x15, 0x4A, 0xE4, 0x2A, 0xAE, 0x72, - 0xE5, 0xEB, 0x00, 0xB9, 0xF3, 0x02, 0x13, 0x4A, 0x16, 0x01, 0x16, 0x0A, 0xD7, 0x00, 0x04, 0x0B, - 0xE5, 0x9B, 0x40, 0xB9, 0xE9, 0xDB, 0x40, 0xB9, 0xD6, 0x02, 0x1C, 0x4A, 0xE6, 0x02, 0x07, 0x0A, - 0xD6, 0x02, 0x06, 0x4A, 0xA4, 0x36, 0x8B, 0x52, 0x73, 0x02, 0x14, 0x0A, 0xA6, 0x02, 0x1C, 0x4A, - 0xFB, 0x02, 0x08, 0x0A, 0x44, 0x4B, 0xB8, 0x72, 0xD3, 0x02, 0x13, 0x4A, 0x36, 0x2F, 0x99, 0x13, - 0xDB, 0x00, 0x1B, 0x4A, 0xA6, 0x00, 0x04, 0x0B, 0xD6, 0x02, 0x09, 0x0B, 0x73, 0x1E, 0x93, 0x13, - 0x84, 0x2B, 0x80, 0x52, 0xD3, 0x02, 0x13, 0x0B, 0xE4, 0x74, 0xAB, 0x72, 0xF8, 0x2E, 0x97, 0x13, - 0x96, 0x00, 0x13, 0x4B, 0x64, 0xD4, 0x9F, 0x52, 0xC6, 0x00, 0x18, 0x0B, 0x18, 0x01, 0x14, 0x4A, - 0x04, 0x8B, 0xB4, 0x72, 0xD6, 0x02, 0x14, 0x0A, 0xF8, 0x02, 0x18, 0x0A, 0x79, 0x02, 0x04, 0x0B, - 0xEB, 0xFB, 0x40, 0xB9, 0x76, 0x03, 0x16, 0x4A, 0x18, 0x03, 0x15, 0x4A, 0x33, 0x03, 0x08, 0x0A, - 0x13, 0x03, 0x13, 0x4A, 0x24, 0xA7, 0x9A, 0x52, 0xD6, 0x02, 0x07, 0x0A, 0x04, 0x86, 0xB3, 0x72, - 0x73, 0x02, 0x16, 0x4A, 0x56, 0x2F, 0x9A, 0x13, 0xF0, 0x8B, 0x00, 0xB9, 0x30, 0x01, 0x04, 0x0B, - 0xD6, 0x02, 0x0B, 0x0B, 0x73, 0x1E, 0x93, 0x13, 0x24, 0x50, 0x98, 0x52, 0xD3, 0x02, 0x13, 0x0B, - 0x84, 0x6D, 0xA1, 0x72, 0x98, 0x02, 0x15, 0x4A, 0x3B, 0x03, 0x17, 0x0A, 0x96, 0x00, 0x13, 0x4B, - 0x18, 0x03, 0x1B, 0x4A, 0xD6, 0x02, 0x07, 0x0A, 0xC4, 0xAF, 0x87, 0x52, 0x16, 0x03, 0x16, 0x4A, - 0xF8, 0x02, 0x07, 0x4A, 0x64, 0x92, 0xBE, 0x72, 0x38, 0x03, 0x18, 0x0A, 0x7A, 0x02, 0x04, 0x0B, - 0xEE, 0xF3, 0x40, 0xB9, 0x18, 0x03, 0x14, 0x4A, 0x53, 0x03, 0x17, 0x0A, 0x18, 0x03, 0x13, 0x4A, - 0x64, 0x02, 0x8C, 0x52, 0xD6, 0x02, 0x08, 0x0A, 0xF3, 0x00, 0x14, 0x4A, 0x54, 0x03, 0x19, 0x0A, - 0x44, 0x5E, 0xA5, 0x72, 0x16, 0x03, 0x16, 0x4A, 0x98, 0x2F, 0x9C, 0x13, 0x74, 0x02, 0x14, 0x4A, - 0x73, 0x00, 0x04, 0x0B, 0x18, 0x03, 0x0E, 0x0B, 0xD6, 0x1E, 0x96, 0x13, 0x04, 0x6E, 0x91, 0x52, - 0x16, 0x03, 0x16, 0x0B, 0x44, 0x4D, 0xBE, 0x72, 0x98, 0x00, 0x16, 0x4B, 0x18, 0x03, 0x08, 0x0A, - 0xE4, 0x91, 0x8E, 0x52, 0x3B, 0x2F, 0x99, 0x13, 0x94, 0x02, 0x18, 0x4A, 0x38, 0x03, 0x08, 0x4A, - 0xA4, 0xB2, 0xA1, 0x72, 0x10, 0x02, 0x1B, 0x0B, 0x5B, 0x2F, 0x9A, 0x13, 0x58, 0x03, 0x18, 0x0A, - 0xD6, 0x02, 0x04, 0x0B, 0xE3, 0x0B, 0x41, 0xB9, 0x73, 0x02, 0x1B, 0x0B, 0x18, 0x03, 0x07, 0x4A, - 0xDB, 0x02, 0x19, 0x0A, 0x18, 0x03, 0x1B, 0x4A, 0x94, 0x02, 0x17, 0x0A, 0x14, 0x03, 0x14, 0x4A, - 0xB5, 0x2E, 0x95, 0x13, 0xB5, 0x02, 0x03, 0x0B, 0x94, 0x1E, 0x94, 0x13, 0xB4, 0x02, 0x14, 0x0B, - 0xF5, 0x34, 0x89, 0x52, 0x35, 0xAE, 0xB1, 0x72, 0x1B, 0x01, 0x07, 0x4A, 0xDC, 0x02, 0x1A, 0x0A, - 0xB5, 0x02, 0x14, 0x4B, 0x7B, 0x03, 0x1C, 0x4A, 0xB5, 0x02, 0x17, 0x0A, 0x75, 0x03, 0x15, 0x4A, - 0x1B, 0xCB, 0x96, 0x52, 0x58, 0x03, 0x17, 0x4A, 0xDB, 0x51, 0xAE, 0x72, 0x64, 0x04, 0x96, 0x52, - 0xD8, 0x02, 0x18, 0x0A, 0x94, 0x02, 0x1B, 0x0B, 0x24, 0xBA, 0xB8, 0x72, 0x18, 0x03, 0x08, 0x4A, - 0x9B, 0x02, 0x1A, 0x0A, 0x9C, 0x01, 0x04, 0x0B, 0xC4, 0x2E, 0x96, 0x13, 0x18, 0x03, 0x1B, 0x4A, - 0xB5, 0x02, 0x19, 0x0A, 0x84, 0x03, 0x04, 0x0B, 0xFB, 0x02, 0x08, 0x4A, 0x9C, 0x02, 0x16, 0x0A, - 0x15, 0x03, 0x15, 0x4A, 0x7B, 0x03, 0x1C, 0x4A, 0x1C, 0xBE, 0x90, 0x52, 0xB5, 0x1E, 0x95, 0x13, - 0xF8, 0x54, 0x86, 0x52, 0x1C, 0x0C, 0xA5, 0x72, 0xD5, 0x03, 0x15, 0x0B, 0x98, 0xCE, 0xB1, 0x72, - 0x00, 0x00, 0x1C, 0x0B, 0x9C, 0x2E, 0x94, 0x13, 0x18, 0x03, 0x15, 0x4B, 0x00, 0x00, 0x1C, 0x0B, - 0x18, 0x03, 0x19, 0x0A, 0x1C, 0xAB, 0x99, 0x52, 0x78, 0x03, 0x18, 0x4A, 0xDB, 0x02, 0x19, 0x4A, - 0x7C, 0x31, 0xAE, 0x72, 0x9B, 0x02, 0x1B, 0x0A, 0xB5, 0x02, 0x1C, 0x0B, 0xE9, 0xD3, 0x40, 0xB9, - 0x7B, 0x03, 0x17, 0x4A, 0xBC, 0x02, 0x16, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x18, 0x03, 0x1A, 0x0A, - 0x78, 0x03, 0x18, 0x4A, 0xE7, 0x2C, 0x87, 0x13, 0xE7, 0x00, 0x09, 0x0B, 0x18, 0x1F, 0x98, 0x13, - 0xE7, 0x00, 0x18, 0x0B, 0x38, 0xA2, 0x96, 0x52, 0x58, 0xBD, 0xAF, 0x72, 0x37, 0x03, 0x17, 0x4A, - 0xBC, 0x02, 0x14, 0x0A, 0x18, 0x03, 0x07, 0x4B, 0xF7, 0x02, 0x1C, 0x4A, 0x18, 0x03, 0x1A, 0x0A, - 0xDB, 0x5D, 0x89, 0x52, 0xF7, 0x02, 0x18, 0x4A, 0x98, 0x02, 0x1A, 0x4A, 0xBB, 0x42, 0xB0, 0x72, - 0xB8, 0x02, 0x18, 0x0A, 0xE7, 0x00, 0x1B, 0x0B, 0x18, 0x03, 0x19, 0x4A, 0xFB, 0x00, 0x14, 0x0A, - 0x18, 0x03, 0x1B, 0x4A, 0x59, 0x03, 0x19, 0x4A, 0xFB, 0x00, 0x15, 0x0A, 0x39, 0x03, 0x1B, 0x4A, - 0xFB, 0x1D, 0x87, 0x52, 0x7B, 0x1B, 0xB7, 0x72, 0x5B, 0x01, 0x1B, 0x0B, 0xEA, 0x9F, 0x40, 0xB9, - 0xF7, 0x02, 0x16, 0x0A, 0x17, 0x03, 0x17, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x08, 0x01, 0x0A, 0x0B, - 0xF7, 0x1E, 0x97, 0x13, 0x08, 0x01, 0x17, 0x0B, 0x57, 0x7C, 0x8B, 0x52, 0x77, 0x95, 0xB0, 0x72, - 0xF7, 0x02, 0x08, 0x4B, 0xF7, 0x02, 0x16, 0x0A, 0x37, 0x03, 0x17, 0x4A, 0xB9, 0x83, 0x94, 0x52, - 0x1C, 0x23, 0x8F, 0x52, 0xB8, 0x02, 0x16, 0x4A, 0x99, 0x6A, 0xAF, 0x72, 0x3C, 0x48, 0xB9, 0x72, - 0xF8, 0x00, 0x18, 0x0A, 0x08, 0x01, 0x19, 0x0B, 0x2C, 0x00, 0x1C, 0x0B, 0xBC, 0x2E, 0x95, 0x13, - 0x18, 0x03, 0x1A, 0x4A, 0x19, 0x01, 0x15, 0x0A, 0x8C, 0x01, 0x1C, 0x0B, 0xFC, 0x2C, 0x87, 0x13, - 0x18, 0x03, 0x19, 0x4A, 0xF7, 0x02, 0x14, 0x0A, 0x7B, 0x03, 0x1C, 0x0B, 0xFC, 0x00, 0x16, 0x4A, - 0x17, 0x03, 0x17, 0x4A, 0x99, 0x03, 0x19, 0x4A, 0xB8, 0x02, 0x1A, 0x0A, 0xF7, 0x1E, 0x97, 0x13, - 0x18, 0x03, 0x16, 0x4A, 0x39, 0x03, 0x14, 0x0A, 0xC6, 0x00, 0x17, 0x0B, 0x18, 0x03, 0x19, 0x4A, - 0xD7, 0x00, 0x08, 0x0A, 0x17, 0x03, 0x17, 0x4A, 0x18, 0x01, 0x14, 0x4A, 0xD9, 0x00, 0x07, 0x0A, - 0x18, 0x03, 0x19, 0x4A, 0xF7, 0x1E, 0x97, 0x13, 0xF6, 0x00, 0x16, 0x0A, 0x10, 0x02, 0x17, 0x0B, - 0xD6, 0x02, 0x14, 0x4A, 0x17, 0x03, 0x15, 0x0A, 0xD6, 0x02, 0x17, 0x4A, 0x17, 0x02, 0x06, 0x0A, - 0xD6, 0x02, 0x17, 0x4A, 0xD7, 0x00, 0x15, 0x4A, 0x18, 0x02, 0x08, 0x0A, 0xF7, 0x02, 0x18, 0x4A, - 0xD6, 0x1E, 0x96, 0x13, 0x14, 0x01, 0x14, 0x0A, 0x73, 0x02, 0x16, 0x0B, 0x94, 0x02, 0x15, 0x4A, - 0xF6, 0x02, 0x07, 0x0A, 0x94, 0x02, 0x16, 0x4A, 0x76, 0x02, 0x10, 0x0A, 0x94, 0x02, 0x16, 0x4A, - 0x16, 0x02, 0x07, 0x4A, 0x77, 0x02, 0x06, 0x0A, 0x94, 0x1E, 0x94, 0x13, 0xD6, 0x02, 0x17, 0x4A, - 0x84, 0x00, 0x14, 0x0B, 0xD4, 0x00, 0x15, 0x0A, 0x94, 0x02, 0x07, 0x4A, 0xD5, 0x02, 0x08, 0x0A, - 0x94, 0x02, 0x15, 0x4A, 0x95, 0x00, 0x13, 0x0A, 0x94, 0x02, 0x15, 0x4A, 0x75, 0x02, 0x08, 0x4A, - 0x96, 0x00, 0x10, 0x0A, 0xB5, 0x02, 0x16, 0x4A, 0x94, 0x1E, 0x94, 0x13, 0x07, 0x02, 0x07, 0x0A, - 0x00, 0x00, 0x14, 0x0B, 0xE7, 0x00, 0x08, 0x4A, 0xB4, 0x02, 0x06, 0x0A, 0xE7, 0x00, 0x14, 0x4A, - 0x14, 0x00, 0x04, 0x0A, 0xE7, 0x00, 0x14, 0x4A, 0x94, 0x00, 0x06, 0x4A, 0x15, 0x00, 0x13, 0x0A, - 0x1E, 0x2D, 0x88, 0x13, 0x94, 0x02, 0x15, 0x4A, 0xE7, 0x1C, 0x87, 0x13, 0x68, 0x02, 0x08, 0x0A, - 0x8C, 0x01, 0x07, 0x0B, 0x08, 0x01, 0x06, 0x4A, 0x87, 0x02, 0x10, 0x0A, 0xD9, 0x01, 0x83, 0x52, - 0x36, 0xF8, 0x8E, 0x52, 0x08, 0x01, 0x07, 0x4A, 0x87, 0x01, 0x00, 0x0A, 0x59, 0x07, 0xAC, 0x72, - 0xB6, 0xE2, 0xBA, 0x72, 0xF5, 0x64, 0x89, 0x52, 0x08, 0x01, 0x07, 0x4A, 0x07, 0x00, 0x10, 0x4A, - 0x94, 0x01, 0x04, 0x0A, 0x42, 0x00, 0x19, 0x0B, 0xD9, 0x2C, 0x86, 0x13, 0x21, 0x01, 0x16, 0x0B, - 0x96, 0x2C, 0x84, 0x13, 0x35, 0xA6, 0xB7, 0x72, 0xE7, 0x00, 0x14, 0x4A, 0x54, 0xFB, 0x85, 0x52, - 0x08, 0x1D, 0x88, 0x13, 0x86, 0x00, 0x06, 0x0A, 0x1C, 0x96, 0x9B, 0x52, 0x21, 0x00, 0x16, 0x0B, - 0x75, 0x01, 0x15, 0x0B, 0x16, 0x2C, 0x80, 0x13, 0xF4, 0x15, 0xAF, 0x72, 0x68, 0x03, 0x08, 0x0B, - 0xC6, 0x00, 0x10, 0x4A, 0xE7, 0x00, 0x13, 0x0A, 0x3C, 0xCF, 0xB1, 0x72, 0x78, 0xD1, 0x81, 0x52, - 0xB5, 0x02, 0x16, 0x0B, 0xF4, 0x01, 0x14, 0x0B, 0x96, 0x2D, 0x8C, 0x13, 0xC6, 0x00, 0x07, 0x4A, - 0x07, 0x01, 0x0C, 0x0A, 0x3C, 0x02, 0x1C, 0x0B, 0xD8, 0x93, 0xAD, 0x72, 0x94, 0x02, 0x16, 0x0B, - 0xC6, 0x00, 0x07, 0x4A, 0x87, 0x01, 0x13, 0x4A, 0x16, 0x01, 0x00, 0x0A, 0xE9, 0x87, 0x40, 0xB9, - 0x9C, 0x03, 0x1E, 0x0B, 0xAD, 0x01, 0x18, 0x0B, 0x18, 0x2E, 0x90, 0x13, 0xE7, 0x00, 0x16, 0x4A, - 0xC6, 0x1C, 0x86, 0x13, 0x10, 0x00, 0x10, 0x0A, 0x16, 0x8C, 0x8B, 0x52, 0x86, 0x03, 0x06, 0x0B, - 0x10, 0x02, 0x13, 0x4A, 0xE7, 0x00, 0x04, 0x0A, 0x16, 0xAC, 0xAA, 0x72, 0x10, 0x02, 0x07, 0x4A, - 0xC7, 0x00, 0x08, 0x0A, 0x2F, 0x01, 0x16, 0x0B, 0x16, 0x2D, 0x88, 0x13, 0x10, 0x02, 0x07, 0x4A, - 0x42, 0x00, 0x19, 0x0B, 0xEF, 0x01, 0x16, 0x0B, 0x07, 0x01, 0x04, 0x4A, 0xD6, 0x00, 0x0C, 0x0A, - 0x10, 0x1E, 0x90, 0x13, 0xE7, 0x00, 0x16, 0x4A, 0x50, 0x00, 0x10, 0x0B, 0x82, 0x01, 0x13, 0x0A, - 0x42, 0x00, 0x04, 0x4A, 0xE7, 0x00, 0x00, 0x0A, 0x42, 0x00, 0x07, 0x4A, 0x07, 0x02, 0x06, 0x0A, - 0x42, 0x00, 0x07, 0x4A, 0xAD, 0x01, 0x18, 0x0B, 0xF1, 0xA3, 0x40, 0xB9, 0x78, 0x2E, 0x93, 0x13, - 0xC7, 0x00, 0x00, 0x4A, 0x13, 0x02, 0x08, 0x0A, 0x42, 0x1C, 0x82, 0x13, 0xE7, 0x00, 0x13, 0x4A, - 0xAD, 0x01, 0x02, 0x0B, 0x02, 0x01, 0x04, 0x0A, 0xD7, 0x47, 0x91, 0x52, 0x42, 0x00, 0x00, 0x4A, - 0xE4, 0x00, 0x0C, 0x0A, 0xD7, 0x03, 0xB6, 0x72, 0x42, 0x00, 0x04, 0x4A, 0xA4, 0x01, 0x10, 0x0A, - 0x37, 0x02, 0x17, 0x0B, 0x42, 0x00, 0x04, 0x4A, 0x04, 0x02, 0x0C, 0x4A, 0xA7, 0x01, 0x06, 0x0A, - 0xF7, 0x02, 0x18, 0x0B, 0x84, 0x00, 0x07, 0x4A, 0x47, 0x0C, 0x93, 0x52, 0x42, 0x1C, 0x82, 0x13, - 0xC0, 0x00, 0x00, 0x0A, 0x07, 0xE9, 0xAA, 0x72, 0xE2, 0x02, 0x02, 0x0B, 0x00, 0x00, 0x0C, 0x4A, - 0x84, 0x00, 0x08, 0x0A, 0x51, 0x01, 0x07, 0x0B, 0xA7, 0x2D, 0x8D, 0x13, 0x00, 0x00, 0x04, 0x4A, - 0x44, 0x00, 0x0D, 0x0A, 0x31, 0x02, 0x07, 0x0B, 0x00, 0x00, 0x04, 0x4A, 0xA4, 0x01, 0x08, 0x4A, - 0x47, 0x00, 0x10, 0x0A, 0x84, 0x00, 0x07, 0x4A, 0x00, 0x1C, 0x80, 0x13, 0x0C, 0x02, 0x0C, 0x0A, - 0x20, 0x00, 0x00, 0x0B, 0x8C, 0x01, 0x08, 0x4A, 0x81, 0x00, 0x06, 0x0A, 0x8C, 0x01, 0x01, 0x4A, - 0x01, 0x00, 0x02, 0x0A, 0x8C, 0x01, 0x01, 0x4A, 0x41, 0x00, 0x06, 0x4A, 0x04, 0x00, 0x0D, 0x0A, - 0x21, 0x00, 0x04, 0x4A, 0x8C, 0x1D, 0x8C, 0x13, 0xA8, 0x01, 0x08, 0x0A, 0xAC, 0x02, 0x0C, 0x0B, - 0x08, 0x01, 0x06, 0x4A, 0x21, 0x00, 0x10, 0x0A, 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x00, 0x0A, - 0xE9, 0x93, 0x40, 0xB9, 0x08, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x10, 0x4A, 0x95, 0x01, 0x02, 0x0A, - 0xD8, 0x2C, 0x86, 0x13, 0x21, 0x00, 0x15, 0x4A, 0x08, 0x1D, 0x88, 0x13, 0x46, 0x00, 0x06, 0x0A, - 0x76, 0xBE, 0x84, 0x52, 0x88, 0x02, 0x08, 0x0B, 0xC6, 0x00, 0x10, 0x4A, 0x21, 0x00, 0x0D, 0x0A, - 0xB6, 0xCA, 0xBC, 0x72, 0xC1, 0x00, 0x01, 0x4A, 0x06, 0x01, 0x0C, 0x0A, 0x36, 0x01, 0x16, 0x0B, - 0x21, 0x00, 0x06, 0x4A, 0x86, 0x01, 0x0D, 0x4A, 0x14, 0x01, 0x00, 0x0A, 0xD6, 0x02, 0x18, 0x0B, - 0x18, 0x2E, 0x90, 0x13, 0xC6, 0x00, 0x14, 0x4A, 0x21, 0x1C, 0x81, 0x13, 0x10, 0x00, 0x10, 0x0A, - 0xEF, 0x01, 0x01, 0x0B, 0x10, 0x02, 0x0D, 0x4A, 0xC1, 0x00, 0x02, 0x0A, 0x10, 0x02, 0x01, 0x4A, - 0xE1, 0x01, 0x08, 0x0A, 0xE9, 0xC3, 0x40, 0xB9, 0x10, 0x02, 0x01, 0x4A, 0x01, 0x01, 0x02, 0x4A, - 0xE6, 0x01, 0x0C, 0x0A, 0x21, 0x00, 0x06, 0x4A, 0x10, 0x1E, 0x90, 0x13, 0x8D, 0x01, 0x0D, 0x0A, - 0x93, 0x72, 0x95, 0x52, 0xD0, 0x02, 0x10, 0x0B, 0xAD, 0x01, 0x02, 0x4A, 0x21, 0x00, 0x00, 0x0A, - 0xB3, 0x4A, 0xB5, 0x72, 0xAD, 0x01, 0x01, 0x4A, 0x01, 0x02, 0x0F, 0x0A, 0x33, 0x01, 0x13, 0x0B, - 0xAD, 0x01, 0x01, 0x4A, 0xE1, 0x01, 0x00, 0x4A, 0x16, 0x02, 0x08, 0x0A, 0x73, 0x02, 0x18, 0x0B, - 0x57, 0x2C, 0x82, 0x13, 0x21, 0x00, 0x16, 0x4A, 0xAD, 0x1D, 0x8D, 0x13, 0x02, 0x01, 0x02, 0x0A, - 0x07, 0x88, 0x82, 0x52, 0x6D, 0x02, 0x0D, 0x0B, 0x42, 0x00, 0x00, 0x4A, 0x21, 0x00, 0x0C, 0x0A, - 0x07, 0x7D, 0xAC, 0x72, 0x41, 0x00, 0x01, 0x4A, 0xA2, 0x01, 0x10, 0x0A, 0xC7, 0x01, 0x07, 0x0B, - 0xE9, 0x53, 0x41, 0xB9, 0x21, 0x00, 0x02, 0x4A, 0x02, 0x02, 0x0C, 0x4A, 0xB3, 0x01, 0x0F, 0x0A, - 0xE7, 0x00, 0x17, 0x0B, 0x17, 0x2C, 0x80, 0x13, 0x42, 0x00, 0x13, 0x4A, 0x21, 0x1C, 0x81, 0x13, - 0xE0, 0x01, 0x00, 0x0A, 0x44, 0x2D, 0x87, 0x52, 0x31, 0x02, 0x01, 0x0B, 0x00, 0x00, 0x0C, 0x4A, - 0x41, 0x00, 0x08, 0x0A, 0x44, 0xB9, 0xAA, 0x72, 0x00, 0x00, 0x01, 0x4A, 0x21, 0x02, 0x0D, 0x0A, - 0x24, 0x01, 0x04, 0x0B, 0xE9, 0x57, 0x41, 0xB9, 0x00, 0x00, 0x01, 0x4A, 0xA1, 0x01, 0x08, 0x4A, - 0x22, 0x02, 0x10, 0x0A, 0x84, 0x00, 0x17, 0x0B, 0x97, 0x2D, 0x8C, 0x13, 0x21, 0x00, 0x02, 0x4A, - 0x00, 0x1C, 0x80, 0x13, 0x0C, 0x02, 0x0C, 0x0A, 0xD5, 0x16, 0x82, 0x52, 0xE0, 0x00, 0x00, 0x0B, - 0x8C, 0x01, 0x08, 0x4A, 0x21, 0x00, 0x0F, 0x0A, 0x75, 0x55, 0xA5, 0x72, 0x8C, 0x01, 0x01, 0x4A, - 0x01, 0x00, 0x11, 0x0A, 0x35, 0x01, 0x15, 0x0B, 0xE9, 0x63, 0x41, 0xB9, 0x8C, 0x01, 0x01, 0x4A, - 0x21, 0x02, 0x0F, 0x4A, 0x07, 0x00, 0x0D, 0x0A, 0xB5, 0x02, 0x17, 0x0B, 0x17, 0x2D, 0x88, 0x13, - 0x21, 0x00, 0x07, 0x4A, 0x8C, 0x1D, 0x8C, 0x13, 0xA8, 0x01, 0x08, 0x0A, 0x94, 0x86, 0x8B, 0x52, - 0x8C, 0x00, 0x0C, 0x0B, 0x08, 0x01, 0x0F, 0x4A, 0x21, 0x00, 0x10, 0x0A, 0x94, 0x99, 0xB6, 0x72, - 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x00, 0x0A, 0x34, 0x01, 0x14, 0x0B, 0xE9, 0x5B, 0x41, 0xB9, - 0x08, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x10, 0x4A, 0x84, 0x01, 0x11, 0x0A, 0x94, 0x02, 0x17, 0x0B, - 0xF7, 0x2D, 0x8F, 0x13, 0x21, 0x00, 0x04, 0x4A, 0x2F, 0x02, 0x0F, 0x0A, 0x08, 0x1D, 0x88, 0x13, - 0xC6, 0x19, 0x9D, 0x52, 0xEF, 0x01, 0x10, 0x4A, 0xA8, 0x02, 0x08, 0x0B, 0x21, 0x00, 0x0D, 0x0A, - 0x26, 0x28, 0xA2, 0x72, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x0C, 0x0A, 0x26, 0x01, 0x06, 0x0B, - 0xE9, 0xE3, 0x40, 0xB9, 0x95, 0x01, 0x0D, 0x4A, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x00, 0x0A, - 0xC6, 0x00, 0x17, 0x0B, 0x17, 0x2E, 0x90, 0x13, 0xA1, 0x02, 0x01, 0x4A, 0x10, 0x00, 0x10, 0x0A, - 0xEF, 0x1D, 0x8F, 0x13, 0xF6, 0xD5, 0x90, 0x52, 0x10, 0x02, 0x0D, 0x4A, 0x8F, 0x02, 0x0F, 0x0B, - 0x21, 0x00, 0x11, 0x0A, 0x96, 0x2A, 0xB4, 0x72, 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x08, 0x0A, - 0x36, 0x01, 0x16, 0x0B, 0x14, 0x01, 0x11, 0x4A, 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x0C, 0x0A, - 0xD6, 0x02, 0x17, 0x0B, 0xB7, 0x2D, 0x8D, 0x13, 0x81, 0x02, 0x01, 0x4A, 0x8D, 0x01, 0x0D, 0x0A, - 0x10, 0x1E, 0x90, 0x13, 0x73, 0x32, 0x9D, 0x52, 0xAD, 0x01, 0x11, 0x4A, 0xD0, 0x00, 0x10, 0x0B, - 0x21, 0x00, 0x00, 0x0A, 0x53, 0x8E, 0xAF, 0x72, 0xAD, 0x01, 0x01, 0x4A, 0x01, 0x02, 0x0F, 0x0A, - 0xB3, 0x00, 0x13, 0x0B, 0xE6, 0x01, 0x00, 0x4A, 0xAD, 0x01, 0x01, 0x4A, 0x01, 0x02, 0x08, 0x0A, - 0x73, 0x02, 0x17, 0x0B, 0x37, 0x2E, 0x91, 0x13, 0xE9, 0x5F, 0x41, 0xB9, 0xC1, 0x00, 0x01, 0x4A, - 0x11, 0x01, 0x11, 0x0A, 0xAD, 0x1D, 0x8D, 0x13, 0x22, 0x82, 0x82, 0x52, 0x31, 0x02, 0x00, 0x4A, - 0xCD, 0x02, 0x0D, 0x0B, 0x21, 0x00, 0x0C, 0x0A, 0xC2, 0x7D, 0xB6, 0x72, 0x47, 0x85, 0x97, 0x52, - 0x31, 0x02, 0x01, 0x4A, 0xA1, 0x01, 0x10, 0x0A, 0x62, 0x00, 0x02, 0x0B, 0xE7, 0x6D, 0xAC, 0x72, - 0x16, 0x02, 0x0C, 0x4A, 0x31, 0x02, 0x01, 0x4A, 0xA1, 0x01, 0x0F, 0x0A, 0x42, 0x00, 0x17, 0x0B, - 0x27, 0x01, 0x07, 0x0B, 0x17, 0x2C, 0x80, 0x13, 0xE9, 0x8B, 0x40, 0xB9, 0xC1, 0x02, 0x01, 0x4A, - 0xE0, 0x01, 0x00, 0x0A, 0x31, 0x1E, 0x91, 0x13, 0x00, 0x00, 0x0C, 0x4A, 0x71, 0x02, 0x11, 0x0B, - 0x21, 0x00, 0x08, 0x0A, 0xA4, 0xAB, 0x98, 0x52, 0x00, 0x00, 0x01, 0x4A, 0x21, 0x02, 0x0D, 0x0A, - 0x24, 0x75, 0xA5, 0x72, 0xB3, 0x01, 0x08, 0x4A, 0x00, 0x00, 0x01, 0x4A, 0x21, 0x02, 0x10, 0x0A, - 0xE7, 0x00, 0x17, 0x0B, 0x24, 0x01, 0x04, 0x0B, 0x97, 0x2D, 0x8C, 0x13, 0xE9, 0x6F, 0x41, 0xB9, - 0x61, 0x02, 0x01, 0x4A, 0x0C, 0x02, 0x0C, 0x0A, 0x00, 0x1C, 0x80, 0x13, 0x8C, 0x01, 0x08, 0x4A, - 0x40, 0x00, 0x00, 0x0B, 0x21, 0x00, 0x0F, 0x0A, 0x84, 0x00, 0x17, 0x0B, 0xD7, 0x3E, 0x86, 0x52, - 0x8C, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x11, 0x0A, 0x17, 0x83, 0xAE, 0x72, 0x22, 0x02, 0x0F, 0x4A, - 0x8C, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x0D, 0x0A, 0x37, 0x01, 0x17, 0x0B, 0x15, 0x2D, 0x88, 0x13, - 0xE9, 0xB3, 0x40, 0xB9, 0x41, 0x00, 0x01, 0x4A, 0xA8, 0x01, 0x08, 0x0A, 0x8C, 0x1D, 0x8C, 0x13, - 0x08, 0x01, 0x0F, 0x4A, 0xEC, 0x00, 0x0C, 0x0B, 0x21, 0x00, 0x10, 0x0A, 0xF5, 0x02, 0x15, 0x0B, - 0xD7, 0xC2, 0x87, 0x52, 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x00, 0x0A, 0x97, 0xCB, 0xB9, 0x72, - 0x02, 0x00, 0x10, 0x4A, 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x11, 0x0A, 0x37, 0x01, 0x17, 0x0B, - 0xF4, 0x2D, 0x8F, 0x13, 0xE9, 0xEB, 0x40, 0xB9, 0x41, 0x00, 0x01, 0x4A, 0x2F, 0x02, 0x0F, 0x0A, - 0x08, 0x1D, 0x88, 0x13, 0xEF, 0x01, 0x10, 0x4A, 0x88, 0x00, 0x08, 0x0B, 0x21, 0x00, 0x0D, 0x0A, - 0xF4, 0x02, 0x14, 0x0B, 0xD7, 0x63, 0x92, 0x52, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x0C, 0x0A, - 0xF7, 0x70, 0xB3, 0x72, 0x82, 0x01, 0x0D, 0x4A, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x00, 0x0A, - 0xEE, 0x3F, 0x41, 0xB9, 0x37, 0x01, 0x17, 0x0B, 0x06, 0x2E, 0x90, 0x13, 0xE9, 0x1B, 0x41, 0xB9, - 0x41, 0x00, 0x01, 0x4A, 0x10, 0x00, 0x10, 0x0A, 0xEF, 0x1D, 0x8F, 0x13, 0x10, 0x02, 0x0D, 0x4A, - 0xAF, 0x02, 0x0F, 0x0B, 0x21, 0x00, 0x11, 0x0A, 0xE6, 0x02, 0x06, 0x0B, 0x77, 0x46, 0x97, 0x52, - 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x08, 0x0A, 0xD7, 0xFA, 0xB5, 0x72, 0x02, 0x01, 0x11, 0x4A, - 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x0C, 0x0A, 0xEE, 0x01, 0x0E, 0x0B, 0x37, 0x01, 0x17, 0x0B, - 0xB6, 0x2D, 0x8D, 0x13, 0xE9, 0x63, 0x40, 0xB9, 0x41, 0x00, 0x01, 0x4A, 0xEE, 0x8B, 0x04, 0xB9, - 0x8D, 0x01, 0x0D, 0x0A, 0x0E, 0x1E, 0x90, 0x13, 0xAD, 0x01, 0x11, 0x4A, 0x8E, 0x02, 0x0E, 0x0B, - 0x21, 0x00, 0x00, 0x0A, 0xAD, 0x01, 0x01, 0x4A, 0xC1, 0x01, 0x0F, 0x0A, 0x33, 0x2E, 0x91, 0x13, - 0xF0, 0x01, 0x00, 0x4A, 0xAD, 0x01, 0x01, 0x4A, 0xC1, 0x01, 0x08, 0x0A, 0x33, 0x01, 0x13, 0x0B, - 0x10, 0x02, 0x01, 0x4A, 0x11, 0x01, 0x11, 0x0A, 0xE9, 0x2B, 0x41, 0xB9, 0x31, 0x02, 0x00, 0x4A, - 0x10, 0x02, 0x0C, 0x0A, 0x30, 0x02, 0x10, 0x4A, 0xF1, 0x4B, 0x41, 0xB9, 0xE1, 0x47, 0x41, 0xB9, - 0xCB, 0x01, 0x09, 0x0B, 0xE9, 0x2F, 0x41, 0xB9, 0xAD, 0x1D, 0x8D, 0x13, 0xE2, 0x43, 0x41, 0xB9, - 0xCD, 0x00, 0x0D, 0x0B, 0x11, 0x00, 0x11, 0x0B, 0xE0, 0x01, 0x00, 0x0A, 0x81, 0x01, 0x01, 0x0B, - 0x00, 0x00, 0x0C, 0x4A, 0xCC, 0x01, 0x0C, 0x4A, 0xAE, 0x01, 0x0E, 0x0A, 0x0E, 0x02, 0x0E, 0x4A, - 0xAF, 0x01, 0x0F, 0x0A, 0xAA, 0x01, 0x09, 0x0B, 0xF6, 0x02, 0x16, 0x0B, 0x8C, 0x01, 0x0F, 0x4A, - 0xEA, 0x83, 0x04, 0xB9, 0xCA, 0x1D, 0x8E, 0x13, 0x02, 0x01, 0x02, 0x0B, 0x88, 0x01, 0x08, 0x0A, - 0xCA, 0x02, 0x0A, 0x0B, 0xFF, 0x9B, 0x00, 0xF9, 0xEB, 0x87, 0x04, 0xB9, 0x08, 0x00, 0x08, 0x4A, - 0x4B, 0x01, 0x0D, 0x0A, 0xE9, 0x13, 0x41, 0xB9, 0xEC, 0x9B, 0x40, 0xF9, 0x08, 0x01, 0x0B, 0x4A, - 0xEB, 0x4F, 0x41, 0xB9, 0x08, 0x1D, 0x88, 0x13, 0x49, 0x01, 0x09, 0x0B, 0x68, 0x02, 0x08, 0x0B, - 0xE2, 0x8F, 0x04, 0xB9, 0xE8, 0x7B, 0x04, 0xB9, 0xE9, 0x7F, 0x04, 0xB9, 0xE1, 0x93, 0x04, 0xB9, - 0xF1, 0x97, 0x04, 0xB9, 0xE8, 0xC3, 0x11, 0x91, 0xE9, 0x63, 0x0E, 0x91, 0x4A, 0x01, 0x80, 0x52, - 0x08, 0x01, 0x0C, 0x8B, 0x21, 0x41, 0x2B, 0x8B, 0x00, 0xA1, 0x02, 0x91, 0x42, 0x01, 0x0B, 0x4B, - 0xE8, 0x93, 0x44, 0xB9, 0xE9, 0x97, 0x44, 0xB9, 0xEA, 0x43, 0x47, 0x39, 0xEB, 0xEF, 0x40, 0xF9, - 0x0F, 0x11, 0x1B, 0x12, 0x2E, 0x15, 0x16, 0x12, 0x30, 0x11, 0x10, 0x12, 0x11, 0x15, 0x16, 0x12, - 0x0C, 0x7D, 0x1A, 0x53, 0x2D, 0x11, 0x1B, 0x12, 0xEE, 0x01, 0x0E, 0x2A, 0x2F, 0x11, 0x0B, 0x12, - 0x30, 0x02, 0x10, 0x2A, 0x11, 0x11, 0x10, 0x12, 0x2C, 0x11, 0x1A, 0x33, 0x29, 0x15, 0x06, 0x12, - 0x0D, 0x11, 0x00, 0x33, 0x08, 0x11, 0x0B, 0x12, 0x2F, 0x02, 0x0F, 0x2A, 0xF1, 0x7B, 0x44, 0xB9, - 0x09, 0x01, 0x09, 0x2A, 0x5F, 0x01, 0x00, 0x72, 0x48, 0xFD, 0x41, 0xD3, 0xEA, 0x83, 0x44, 0xB9, - 0x08, 0x01, 0x8B, 0x9A, 0x8B, 0x01, 0x11, 0x0B, 0xEC, 0x87, 0x44, 0xB9, 0x4A, 0x15, 0x4E, 0x0B, - 0xEE, 0x8B, 0x44, 0xB9, 0xE0, 0x7F, 0x44, 0xB9, 0x8C, 0x29, 0x50, 0x0B, 0xF0, 0x03, 0x50, 0x39, - 0xCE, 0x41, 0x4F, 0x0B, 0xEF, 0x07, 0x42, 0xF9, 0x0D, 0x00, 0x0D, 0x0B, 0x1F, 0x02, 0x00, 0x72, - 0x10, 0xFE, 0x41, 0xD3, 0x0F, 0x02, 0x8F, 0x9A, 0x70, 0x7D, 0x08, 0x53, 0xF1, 0x8F, 0x44, 0xB9, - 0xF0, 0xC7, 0x0E, 0x39, 0x70, 0x7D, 0x10, 0x53, 0xEB, 0xC3, 0x0E, 0x39, 0x6B, 0x7D, 0x18, 0x53, - 0xEB, 0xCF, 0x0E, 0x39, 0xAB, 0x7D, 0x08, 0x53, 0xED, 0xD3, 0x0E, 0x39, 0xEB, 0xD7, 0x0E, 0x39, - 0xAB, 0x7D, 0x10, 0x53, 0xAD, 0x7D, 0x18, 0x53, 0xEB, 0xDB, 0x0E, 0x39, 0x4B, 0x7D, 0x08, 0x53, - 0xED, 0xDF, 0x0E, 0x39, 0x4D, 0x7D, 0x10, 0x53, 0x29, 0x56, 0x49, 0x0B, 0xEC, 0xF3, 0x0E, 0x39, - 0xEB, 0xE7, 0x0E, 0x39, 0x8B, 0x7D, 0x08, 0x53, 0xED, 0xEB, 0x0E, 0x39, 0x8D, 0x7D, 0x10, 0x53, - 0x8C, 0x7D, 0x18, 0x53, 0xEA, 0xE3, 0x0E, 0x39, 0x4A, 0x7D, 0x18, 0x53, 0xE9, 0x13, 0x0F, 0x39, - 0xED, 0xFB, 0x0E, 0x39, 0x2D, 0x7D, 0x08, 0x53, 0xEC, 0xFF, 0x0E, 0x39, 0x2C, 0x7D, 0x10, 0x53, - 0x29, 0x7D, 0x18, 0x53, 0xEE, 0x03, 0x0F, 0x39, 0xEA, 0xEF, 0x0E, 0x39, 0xCA, 0x7D, 0x08, 0x53, - 0xEB, 0xF7, 0x0E, 0x39, 0xCB, 0x7D, 0x10, 0x53, 0xCE, 0x7D, 0x18, 0x53, 0xE9, 0x1F, 0x0F, 0x39, - 0xE9, 0x17, 0x9F, 0x1A, 0xF0, 0xCB, 0x0E, 0x39, 0xEA, 0x07, 0x0F, 0x39, 0xEB, 0x0B, 0x0F, 0x39, - 0xEE, 0x0F, 0x0F, 0x39, 0xED, 0x17, 0x0F, 0x39, 0xEC, 0x1B, 0x0F, 0x39, -]; - -static C1: OnceLock> = OnceLock::new(); -static C2: OnceLock> = OnceLock::new(); -static LOGIN_C31: OnceLock> = OnceLock::new(); -static LOGIN_C32: OnceLock> = OnceLock::new(); - -#[tokio::main] -async fn main() { - env_logger::init(); - log::info!("starting..."); - C1.set(read_hex("BIN_C1")).expect("failed to set C1"); - C2.set(read_hex("BIN_C2")).expect("failed to set C2"); - LOGIN_C31 - .set(read_hex("BIN_LOGIN_C31")) - .expect("failed to set C31"); - LOGIN_C32 - .set(read_hex("BIN_LOGIN_C32")) - .expect("failed to set C32"); - let service = Router::new() - .route("/healthz", get(healthz)) - .route("/readyz", get(readyz)) - .route("/generate", get(generate)); - let addr = TcpListener::bind("0.0.0.0:8080") - .await - .expect("failed to bind 0.0.0.0:8080"); - log::info!("listening in 0.0.0.0:8080..."); - axum::serve(addr, service) - .await - .unwrap_or_else(|e| panic!("{e:?}")); -} - -fn read_hex(var: &str) -> Vec { - hex::decode(env::var(var).unwrap_or_else(|_| panic!("var: {var} not declared"))) - .unwrap_or_else(|_| panic!("failed to decode {var}")) -} - -#[derive(Debug, Deserialize)] -struct GenerateQuery { - timestamp: String, - path: String, - kind: String, -} - -#[derive(Debug, Serialize)] -#[serde(untagged)] -enum GenerateResult { - Success { success: bool, value: String }, - Failed { success: bool, message: String }, -} - -async fn generate(Query(query): Query) -> impl IntoResponse { - log::info!("Generated from: {query:?}"); - let (c31, c32): (&[u8], &[u8]) = match query.kind.as_str() { - "login" => ( - LOGIN_C31 - .get() - .expect("C31 is not initialized, this should not happen"), - LOGIN_C32 - .get() - .expect("C32 is not initialized, this should not happen"), - ), - _ => { - return ( - StatusCode::BAD_REQUEST, - Json(GenerateResult::Failed { - success: false, - message: format!("no kind named \"{}\"", query.kind), - }), - ); - } - }; - let Ok(timestamp) = query.timestamp.parse::() else { - return ( - StatusCode::BAD_REQUEST, - Json(GenerateResult::Failed { - success: false, - message: "timestamp is not a valid number".into(), - }), - ); - }; - let result = match chilo::generate( - &BIN, - C1.get() - .expect("C1 is not initialized, this should not happen"), - C2.get() - .expect("C2 is not initialized, this should not happen"), - c31, - c32, - timestamp, - query.path.as_bytes(), - ) { - Ok(result) => result, - Err(e) => { - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(GenerateResult::Failed { - success: false, - message: format!("failed to generate value: {e}"), - }), - ); - } - }; - let base64 = base64::prelude::BASE64_STANDARD.encode(result); - ( - StatusCode::OK, - Json(GenerateResult::Success { - success: true, - value: base64, - }), - ) -} - -const fn check_ready() -> Option { - None -} - -async fn readyz() -> impl IntoResponse { - check_ready().map_or_else( - || (StatusCode::OK, Json(json!({"status":"ready"}))), - |e| { - ( - StatusCode::SERVICE_UNAVAILABLE, - Json(json!({"status": "not ready", "reason": e})), - ) - }, - ) -} - -async fn healthz() -> impl IntoResponse { - (StatusCode::OK, Json(json!({"health":"ok"}))) -} +//! cuscuta-chilo是对[chilo](https://github.com/cuscutaceae/chilo)的Web API包装 +//! +//! # 注意 +//! 这个版本的cuscuta-chilo使用了环境变量中硬编码的常量,为原型版本\ +//! 为了解决这个问题,scirpophaga与这个模块的整合已经加入开发计划……未来可能可以解决这个问题 +//! +//! # 依赖环境变量 +//! cuscuta-chilo使用环境变量注入参数,这个crate依赖的环境变量有: +//! - `BIN_C1` +//! - `BIN_C2` +//! - `BIN_LOGIN_C31` +//! - `BIN_LOGIN_C32` + +#![deny(clippy::nursery)] +#![deny(clippy::pedantic)] + +use std::{env, sync::OnceLock}; + +use axum::{Json, Router, extract::Query, http::StatusCode, response::IntoResponse, routing::get}; +use base64::Engine; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use tokio::net::TcpListener; + +const BIN: [u8; 7180] = [ + 0xF7, 0x7B, 0x44, 0xB9, 0xEB, 0x87, 0x44, 0xB9, 0xEE, 0x8B, 0x44, 0xB9, 0xE5, 0x8F, 0x44, 0xB9, + 0xE3, 0x7F, 0x44, 0xB9, 0xF3, 0x93, 0x44, 0xB9, 0xE9, 0x83, 0x44, 0xB9, 0xEF, 0x97, 0x44, 0xB9, + 0xF5, 0x9B, 0x44, 0xB9, 0xAC, 0x00, 0x0E, 0x0A, 0x6D, 0x01, 0x17, 0x0A, 0x68, 0x02, 0x03, 0x4A, + 0x8C, 0x01, 0x0D, 0x4A, 0x08, 0x01, 0x09, 0x0A, 0x8C, 0x01, 0x13, 0x4A, 0xEF, 0x4B, 0x01, 0xB9, + 0xEF, 0x2D, 0x8F, 0x13, 0x88, 0x01, 0x08, 0x4A, 0xF8, 0x9F, 0x44, 0xB9, 0xB0, 0x00, 0x17, 0x4A, + 0xD1, 0x01, 0x0B, 0x0A, 0xAF, 0x02, 0x0F, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x10, 0x02, 0x03, 0x0A, + 0xB1, 0x00, 0x11, 0x4A, 0xE8, 0x01, 0x08, 0x0B, 0x30, 0x02, 0x10, 0x4A, 0x0F, 0x01, 0x09, 0x0A, + 0x60, 0x2E, 0x93, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xFB, 0xA3, 0x44, 0xB9, 0x61, 0x01, 0x09, 0x0A, + 0x10, 0x01, 0x0E, 0x4A, 0x00, 0x03, 0x00, 0x0B, 0xEF, 0x1D, 0x8F, 0x13, 0xF3, 0x47, 0x01, 0xB9, + 0xC1, 0x01, 0x01, 0x4A, 0x10, 0x02, 0x17, 0x0A, 0x13, 0x00, 0x0F, 0x0B, 0x30, 0x00, 0x10, 0x4A, + 0x6F, 0x02, 0x03, 0x0A, 0xA2, 0x2C, 0x85, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xF9, 0xA7, 0x44, 0xB9, + 0x24, 0x01, 0x03, 0x0A, 0x62, 0x03, 0x02, 0x0B, 0x70, 0x02, 0x0B, 0x4A, 0xEF, 0x1D, 0x8F, 0x13, + 0x64, 0x01, 0x04, 0x4A, 0x10, 0x02, 0x08, 0x0A, 0x42, 0x00, 0x0F, 0x0B, 0x90, 0x00, 0x10, 0x4A, + 0x4F, 0x00, 0x17, 0x0A, 0xC6, 0x2D, 0x8E, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xEA, 0xAB, 0x44, 0xB9, + 0x67, 0x00, 0x17, 0x0A, 0x26, 0x03, 0x06, 0x0B, 0x50, 0x00, 0x09, 0x4A, 0xEF, 0x1D, 0x8F, 0x13, + 0x27, 0x01, 0x07, 0x4A, 0x10, 0x02, 0x13, 0x0A, 0xC6, 0x00, 0x0F, 0x0B, 0xF0, 0x00, 0x10, 0x4A, + 0xCF, 0x00, 0x08, 0x0A, 0x6D, 0x2D, 0x8B, 0x13, 0x0F, 0x02, 0x0F, 0x4A, 0xF4, 0xAF, 0x44, 0xB9, + 0x4D, 0x01, 0x0D, 0x0B, 0x00, 0x01, 0x17, 0x0A, 0xD0, 0x00, 0x03, 0x4A, 0xEF, 0x1D, 0x8F, 0x13, + 0x00, 0x00, 0x03, 0x4A, 0x10, 0x02, 0x02, 0x0A, 0xA7, 0x01, 0x0F, 0x0B, 0x10, 0x00, 0x10, 0x4A, + 0xED, 0x00, 0x13, 0x0A, 0x31, 0x2D, 0x89, 0x13, 0x0D, 0x02, 0x0D, 0x4A, 0x91, 0x02, 0x11, 0x0B, + 0x64, 0x02, 0x08, 0x0A, 0xEF, 0x00, 0x17, 0x4A, 0xAD, 0x1D, 0x8D, 0x13, 0xF6, 0xB3, 0x44, 0xB9, + 0x84, 0x00, 0x17, 0x4A, 0xEF, 0x01, 0x06, 0x0A, 0x2D, 0x02, 0x0D, 0x0B, 0x8F, 0x00, 0x0F, 0x4A, + 0xB1, 0x01, 0x02, 0x0A, 0x40, 0x00, 0x13, 0x0A, 0xEF, 0x01, 0x11, 0x4A, 0xB1, 0x01, 0x08, 0x4A, + 0x6C, 0x2C, 0x83, 0x13, 0x00, 0x00, 0x08, 0x4A, 0x31, 0x02, 0x07, 0x0A, 0xFA, 0xB7, 0x44, 0xB9, + 0xCC, 0x02, 0x0C, 0x0B, 0x11, 0x00, 0x11, 0x4A, 0xE0, 0x00, 0x06, 0x0A, 0xEF, 0x1D, 0x8F, 0x13, + 0x04, 0x00, 0x02, 0x4A, 0x80, 0x01, 0x0F, 0x0B, 0x0C, 0x00, 0x06, 0x0A, 0xE1, 0x2E, 0x97, 0x13, + 0xD0, 0x00, 0x02, 0x0A, 0x2C, 0x02, 0x0C, 0x4A, 0x0F, 0x00, 0x13, 0x4A, 0xE9, 0x2F, 0x01, 0xB9, + 0x41, 0x03, 0x01, 0x0B, 0x10, 0x02, 0x13, 0x4A, 0xEF, 0x01, 0x0D, 0x0A, 0x8C, 0x1D, 0x8C, 0x13, + 0xE9, 0xBB, 0x44, 0xB9, 0x10, 0x02, 0x0F, 0x4A, 0x2F, 0x00, 0x0C, 0x0B, 0xEC, 0x01, 0x07, 0x0A, + 0xF1, 0x01, 0x02, 0x4A, 0x0C, 0x02, 0x0C, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x30, 0x02, 0x00, 0x0A, + 0x28, 0x01, 0x08, 0x0B, 0x8C, 0x1D, 0x8C, 0x13, 0x91, 0x00, 0x10, 0x4A, 0x10, 0x01, 0x0C, 0x0B, + 0xEA, 0x7F, 0x00, 0xB9, 0xEA, 0xBF, 0x44, 0xB9, 0x08, 0x02, 0x0D, 0x0A, 0x28, 0x02, 0x08, 0x4A, + 0xAC, 0x01, 0x07, 0x0A, 0x11, 0x02, 0x06, 0x4A, 0x8C, 0x01, 0x06, 0x4A, 0x31, 0x02, 0x0F, 0x0A, + 0x8C, 0x01, 0x11, 0x4A, 0x71, 0x2E, 0x93, 0x13, 0x51, 0x01, 0x11, 0x0B, 0x08, 0x1D, 0x88, 0x13, + 0x28, 0x02, 0x08, 0x0B, 0xE9, 0x73, 0x00, 0xB9, 0x11, 0x01, 0x00, 0x0A, 0xE9, 0xC3, 0x44, 0xB9, + 0x8C, 0x01, 0x11, 0x4A, 0x11, 0x00, 0x0D, 0x0A, 0x01, 0x01, 0x07, 0x4A, 0x31, 0x02, 0x07, 0x4A, + 0x21, 0x00, 0x10, 0x0A, 0x21, 0x02, 0x01, 0x4A, 0x51, 0x2C, 0x82, 0x13, 0x31, 0x02, 0x09, 0x0B, + 0x8C, 0x1D, 0x8C, 0x13, 0x31, 0x02, 0x0C, 0x0B, 0xE5, 0x43, 0x01, 0xB9, 0xE5, 0xC7, 0x44, 0xB9, + 0x2C, 0x02, 0x0F, 0x0A, 0x2C, 0x00, 0x0C, 0x4A, 0xE1, 0x01, 0x00, 0x0A, 0x22, 0x02, 0x0D, 0x4A, + 0x21, 0x00, 0x0D, 0x4A, 0x42, 0x00, 0x08, 0x0A, 0x21, 0x00, 0x02, 0x4A, 0xC2, 0x2C, 0x86, 0x13, + 0x42, 0x00, 0x05, 0x0B, 0x8C, 0x1D, 0x8C, 0x13, 0x4C, 0x00, 0x0C, 0x0B, 0xE9, 0x6F, 0x01, 0xB9, + 0x82, 0x01, 0x10, 0x0A, 0xE9, 0xCB, 0x44, 0xB9, 0x21, 0x00, 0x02, 0x4A, 0x02, 0x02, 0x0F, 0x0A, + 0x84, 0x01, 0x00, 0x4A, 0x42, 0x00, 0x00, 0x4A, 0x84, 0x00, 0x11, 0x0A, 0x42, 0x00, 0x04, 0x4A, + 0xE4, 0x2C, 0x87, 0x13, 0xEA, 0x83, 0x00, 0xB9, 0xEA, 0xCF, 0x44, 0xB9, 0x84, 0x00, 0x09, 0x0B, + 0x21, 0x1C, 0x81, 0x13, 0x81, 0x00, 0x01, 0x0B, 0x24, 0x00, 0x08, 0x0A, 0x42, 0x00, 0x04, 0x4A, + 0xAD, 0x2D, 0x8D, 0x13, 0xEA, 0x0B, 0x01, 0xB9, 0x04, 0x01, 0x10, 0x0A, 0x26, 0x00, 0x0F, 0x4A, + 0xAD, 0x01, 0x0A, 0x0B, 0x42, 0x1C, 0x82, 0x13, 0xEA, 0xD3, 0x44, 0xB9, 0x84, 0x00, 0x0F, 0x4A, + 0xC6, 0x00, 0x0C, 0x0A, 0xAD, 0x01, 0x02, 0x0B, 0x84, 0x00, 0x06, 0x4A, 0xA2, 0x01, 0x11, 0x0A, + 0x82, 0x00, 0x02, 0x4A, 0x24, 0x02, 0x08, 0x0A, 0xA6, 0x01, 0x10, 0x4A, 0x00, 0x2C, 0x80, 0x13, + 0xEB, 0x2B, 0x01, 0xB9, 0x84, 0x00, 0x10, 0x4A, 0xC6, 0x00, 0x01, 0x0A, 0xEB, 0xD7, 0x44, 0xB9, + 0x00, 0x00, 0x0A, 0x0B, 0x42, 0x1C, 0x82, 0x13, 0x86, 0x00, 0x06, 0x4A, 0x04, 0x00, 0x02, 0x0B, + 0x80, 0x00, 0x0C, 0x0A, 0xC0, 0x00, 0x00, 0x4A, 0xEF, 0x2D, 0x8F, 0x13, 0xE3, 0x13, 0x01, 0xB9, + 0x82, 0x01, 0x11, 0x0A, 0x86, 0x00, 0x08, 0x4A, 0xEF, 0x01, 0x0B, 0x0B, 0x00, 0x1C, 0x80, 0x13, + 0xE3, 0xDB, 0x44, 0xB9, 0x42, 0x00, 0x08, 0x4A, 0xC6, 0x00, 0x0D, 0x0A, 0xEF, 0x01, 0x00, 0x0B, + 0x42, 0x00, 0x06, 0x4A, 0xE0, 0x01, 0x01, 0x0A, 0x40, 0x00, 0x00, 0x4A, 0x22, 0x00, 0x0C, 0x0A, + 0xE6, 0x01, 0x11, 0x4A, 0x10, 0x2E, 0x90, 0x13, 0xEB, 0xF3, 0x00, 0xB9, 0x42, 0x00, 0x11, 0x4A, + 0xC6, 0x00, 0x04, 0x0A, 0xEB, 0xDF, 0x44, 0xB9, 0x10, 0x02, 0x03, 0x0B, 0x00, 0x1C, 0x80, 0x13, + 0x42, 0x00, 0x06, 0x4A, 0x06, 0x02, 0x00, 0x0B, 0xD0, 0x00, 0x0D, 0x0A, 0x50, 0x00, 0x10, 0x4A, + 0x08, 0x2D, 0x88, 0x13, 0xEB, 0x6F, 0x00, 0xB9, 0xA0, 0x01, 0x01, 0x0A, 0xC2, 0x00, 0x0C, 0x4A, + 0x08, 0x01, 0x0B, 0x0B, 0x10, 0x1E, 0x90, 0x13, 0xEB, 0xE3, 0x44, 0xB9, 0x00, 0x00, 0x0C, 0x4A, + 0x42, 0x00, 0x0F, 0x0A, 0x08, 0x01, 0x10, 0x0B, 0x00, 0x00, 0x02, 0x4A, 0x10, 0x01, 0x04, 0x0A, + 0x00, 0x00, 0x10, 0x4A, 0x31, 0x2E, 0x91, 0x13, 0xE9, 0xFB, 0x00, 0xB9, 0x90, 0x00, 0x0D, 0x0A, + 0x02, 0x01, 0x01, 0x4A, 0xE9, 0xE7, 0x44, 0xB9, 0x31, 0x02, 0x0B, 0x0B, 0x00, 0x1C, 0x80, 0x13, + 0x10, 0x02, 0x01, 0x4A, 0x42, 0x00, 0x06, 0x0A, 0x27, 0x02, 0x00, 0x0B, 0x02, 0x02, 0x02, 0x4A, + 0xF1, 0x00, 0x0F, 0x0A, 0x51, 0x00, 0x11, 0x4A, 0x8C, 0x2D, 0x8C, 0x13, 0xE0, 0x01, 0x04, 0x0A, + 0xE2, 0x00, 0x0D, 0x4A, 0x8C, 0x01, 0x09, 0x0B, 0x31, 0x1E, 0x91, 0x13, 0x00, 0x00, 0x0D, 0x4A, + 0x42, 0x00, 0x08, 0x0A, 0x8C, 0x01, 0x11, 0x0B, 0x00, 0x00, 0x02, 0x4A, 0x91, 0x01, 0x06, 0x0A, + 0x11, 0x00, 0x11, 0x4A, 0xC0, 0x00, 0x0F, 0x0A, 0x02, 0x00, 0x04, 0x4A, 0x80, 0x01, 0x04, 0x4A, + 0x13, 0x00, 0x07, 0x0A, 0xE0, 0xEB, 0x44, 0xB9, 0x21, 0x2C, 0x81, 0x13, 0xF0, 0xEF, 0x44, 0xB9, + 0x31, 0x1E, 0x91, 0x13, 0x21, 0x00, 0x00, 0x0B, 0xFA, 0x53, 0x01, 0xB9, 0x3A, 0x00, 0x11, 0x0B, + 0x42, 0x00, 0x13, 0x4A, 0x51, 0x03, 0x08, 0x0A, 0x51, 0x00, 0x11, 0x4A, 0xAD, 0x2D, 0x8D, 0x13, + 0x01, 0x01, 0x06, 0x0A, 0x42, 0x03, 0x0F, 0x4A, 0xAD, 0x01, 0x10, 0x0B, 0x31, 0x1E, 0x91, 0x13, + 0x21, 0x00, 0x0F, 0x4A, 0x42, 0x00, 0x0C, 0x0A, 0xB1, 0x01, 0x11, 0x0B, 0x21, 0x00, 0x02, 0x4A, + 0x22, 0x02, 0x06, 0x4A, 0x53, 0x00, 0x1A, 0x0A, 0xE2, 0xF3, 0x44, 0xB9, 0x2D, 0x02, 0x07, 0x0A, + 0x2D, 0x00, 0x0D, 0x4A, 0x84, 0x2C, 0x84, 0x13, 0xEE, 0x3F, 0x01, 0xB9, 0xE1, 0x00, 0x08, 0x0A, + 0xEE, 0xF7, 0x44, 0xB9, 0x84, 0x00, 0x02, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xFB, 0x27, 0x01, 0xB9, + 0x21, 0x00, 0x06, 0x4A, 0x9B, 0x00, 0x0D, 0x0B, 0x21, 0x00, 0x13, 0x4A, 0x6D, 0x03, 0x0C, 0x0A, + 0x2D, 0x00, 0x0D, 0x4A, 0xEF, 0x2D, 0x8F, 0x13, 0x81, 0x01, 0x07, 0x0A, 0x64, 0x03, 0x08, 0x4A, + 0xEF, 0x01, 0x0E, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xE9, 0xDB, 0x00, 0xB9, 0x21, 0x00, 0x08, 0x4A, + 0x84, 0x00, 0x11, 0x0A, 0xF3, 0x01, 0x0D, 0x0B, 0xE9, 0xFB, 0x44, 0xB9, 0x21, 0x00, 0x04, 0x4A, + 0x6D, 0x02, 0x1A, 0x0A, 0x2D, 0x00, 0x0D, 0x4A, 0x4F, 0x03, 0x0C, 0x0A, 0x61, 0x02, 0x07, 0x4A, + 0xEF, 0x01, 0x07, 0x4A, 0x21, 0x00, 0x1B, 0x0A, 0xC6, 0x2C, 0x86, 0x13, 0xEF, 0x01, 0x01, 0x4A, + 0xE1, 0xFF, 0x44, 0xB9, 0xC6, 0x00, 0x09, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xF8, 0x63, 0x01, 0xB9, + 0xF8, 0x03, 0x14, 0x2A, 0xF4, 0x1B, 0x01, 0xB9, 0xD4, 0x00, 0x0D, 0x0B, 0x8D, 0x02, 0x11, 0x0A, + 0xED, 0x01, 0x0D, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x2F, 0x02, 0x1A, 0x0A, 0x86, 0x02, 0x0C, 0x4A, + 0x08, 0x01, 0x01, 0x0B, 0xAD, 0x1D, 0x8D, 0x13, 0xF5, 0x5B, 0x01, 0xB9, 0xEF, 0x01, 0x0C, 0x4A, + 0xC6, 0x00, 0x13, 0x0A, 0x15, 0x01, 0x0D, 0x0B, 0xE9, 0x97, 0x00, 0xB9, 0xEF, 0x01, 0x06, 0x4A, + 0xA8, 0x02, 0x1B, 0x0A, 0xE9, 0x03, 0x45, 0xB9, 0xE8, 0x01, 0x08, 0x4A, 0x6D, 0x03, 0x11, 0x0A, + 0xAF, 0x02, 0x1A, 0x4A, 0xAD, 0x01, 0x1A, 0x4A, 0xEF, 0x01, 0x14, 0x0A, 0xAD, 0x01, 0x0F, 0x4A, + 0xEF, 0x2C, 0x87, 0x13, 0xE1, 0xD3, 0x00, 0xB9, 0xE1, 0x07, 0x45, 0xB9, 0xEF, 0x01, 0x09, 0x0B, + 0x08, 0x1D, 0x88, 0x13, 0xF6, 0x5F, 0x01, 0xB9, 0xF6, 0x01, 0x08, 0x0B, 0xC8, 0x02, 0x13, 0x0A, + 0xA8, 0x01, 0x08, 0x4A, 0x8C, 0x2D, 0x8C, 0x13, 0xE1, 0x9B, 0x00, 0xB9, 0x6D, 0x02, 0x1B, 0x0A, + 0xCF, 0x02, 0x11, 0x4A, 0x8C, 0x01, 0x01, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xE1, 0x0B, 0x45, 0xB9, + 0xF9, 0x57, 0x01, 0xB9, 0xAD, 0x01, 0x11, 0x4A, 0xEF, 0x01, 0x15, 0x0A, 0x99, 0x01, 0x08, 0x0B, + 0xAD, 0x01, 0x0F, 0x4A, 0x28, 0x03, 0x14, 0x0A, 0x8C, 0x02, 0x13, 0x0A, 0xA8, 0x01, 0x08, 0x4A, + 0x8D, 0x01, 0x1B, 0x4A, 0x2C, 0x03, 0x1B, 0x4A, 0x46, 0x2F, 0x9A, 0x13, 0x8F, 0x01, 0x16, 0x0A, + 0xEC, 0x0F, 0x45, 0xB9, 0xC6, 0x00, 0x01, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xC7, 0x00, 0x08, 0x0B, + 0xAF, 0x01, 0x0F, 0x4A, 0xE8, 0x00, 0x15, 0x0A, 0xE8, 0x01, 0x08, 0x4A, 0xAF, 0x02, 0x14, 0x0A, + 0xE6, 0x00, 0x13, 0x4A, 0x31, 0x2E, 0x91, 0x13, 0xEF, 0x01, 0x13, 0x4A, 0xC6, 0x00, 0x19, 0x0A, + 0x31, 0x02, 0x0C, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xEC, 0x8F, 0x00, 0xB9, 0xEF, 0x01, 0x06, 0x4A, + 0x26, 0x02, 0x08, 0x0B, 0xEC, 0x13, 0x45, 0xB9, 0xC8, 0x00, 0x16, 0x0A, 0xE8, 0x01, 0x08, 0x4A, + 0xCF, 0x02, 0x15, 0x0A, 0xF1, 0x01, 0x14, 0x4A, 0xCF, 0x00, 0x14, 0x4A, 0x7B, 0x2F, 0x9B, 0x13, + 0xFA, 0x01, 0x07, 0x0A, 0xEF, 0x17, 0x45, 0xB9, 0x7B, 0x03, 0x0C, 0x0B, 0x08, 0x1D, 0x88, 0x13, + 0x68, 0x03, 0x08, 0x0B, 0x3A, 0x02, 0x1A, 0x4A, 0x1B, 0x01, 0x19, 0x0A, 0x5A, 0x03, 0x1B, 0x4A, + 0x73, 0x2E, 0x93, 0x13, 0x73, 0x02, 0x0F, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0xDB, 0x02, 0x15, 0x4A, + 0x1C, 0x01, 0x06, 0x0A, 0x73, 0x02, 0x1A, 0x0B, 0x7B, 0x03, 0x1C, 0x4A, 0x3A, 0x03, 0x33, 0x0A, + 0x7A, 0x03, 0x1A, 0x4A, 0xDB, 0x00, 0x19, 0x4A, 0x1B, 0x01, 0x1B, 0x0A, 0x7B, 0x03, 0x16, 0x4A, + 0x7C, 0x02, 0x06, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x5A, 0x03, 0x07, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, + 0x94, 0x2E, 0x94, 0x13, 0x94, 0x02, 0x18, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0x24, 0xC3, 0x9B, 0x52, + 0x94, 0x02, 0x1A, 0x0B, 0xE4, 0x5A, 0xB7, 0x72, 0x9A, 0x00, 0x14, 0x4B, 0xC4, 0x3C, 0x84, 0x52, + 0x3C, 0x03, 0x16, 0x4A, 0x7E, 0x02, 0x08, 0x0A, 0x1B, 0x01, 0x07, 0x4A, 0x04, 0xA5, 0xA8, 0x72, + 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x07, 0x0A, 0x7B, 0x02, 0x1B, 0x0A, 0x94, 0x02, 0x04, 0x0B, + 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x19, 0x4A, 0x9C, 0x02, 0x08, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, + 0x5A, 0x03, 0x06, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, 0xB5, 0x2E, 0x95, 0x13, 0xB5, 0x02, 0x0A, 0x0B, + 0x5A, 0x1F, 0x9A, 0x13, 0x04, 0x91, 0x9D, 0x52, 0xB5, 0x02, 0x1A, 0x0B, 0xE4, 0xE5, 0xB8, 0x72, + 0x9A, 0x00, 0x15, 0x4B, 0xE4, 0x6E, 0x82, 0x52, 0xFC, 0x00, 0x19, 0x4A, 0x9E, 0x02, 0x13, 0x0A, + 0x7B, 0x02, 0x06, 0x4A, 0x04, 0x1A, 0xA7, 0x72, 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x06, 0x0A, + 0x9B, 0x02, 0x1B, 0x0A, 0xB5, 0x02, 0x04, 0x0B, 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x07, 0x4A, + 0xBC, 0x02, 0x13, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x5A, 0x03, 0x08, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, + 0xD6, 0x2E, 0x96, 0x13, 0xD6, 0x02, 0x09, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0x04, 0x26, 0x93, 0x52, + 0xD6, 0x02, 0x1A, 0x0B, 0x64, 0x35, 0xA8, 0x72, 0x9A, 0x00, 0x16, 0x4B, 0xE4, 0xD9, 0x8C, 0x52, + 0xDC, 0x00, 0x07, 0x4A, 0xBE, 0x02, 0x14, 0x0A, 0x9B, 0x02, 0x08, 0x4A, 0x84, 0xCA, 0xB7, 0x72, + 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x08, 0x0A, 0xBB, 0x02, 0x1B, 0x0A, 0xD6, 0x02, 0x04, 0x0B, + 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x06, 0x4A, 0xDC, 0x02, 0x14, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, + 0x5A, 0x03, 0x13, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, 0x39, 0x2F, 0x99, 0x13, 0x39, 0x03, 0x0B, 0x0B, + 0x5A, 0x1F, 0x9A, 0x13, 0x64, 0x72, 0x9E, 0x52, 0x39, 0x03, 0x1A, 0x0B, 0xC4, 0x62, 0xB9, 0x72, + 0x9A, 0x00, 0x19, 0x4B, 0x84, 0x8D, 0x81, 0x52, 0x1C, 0x01, 0x06, 0x4A, 0xDE, 0x02, 0x15, 0x0A, + 0xBB, 0x02, 0x13, 0x4A, 0x24, 0x9D, 0xA6, 0x72, 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x13, 0x0A, + 0xDB, 0x02, 0x1B, 0x0A, 0x39, 0x03, 0x04, 0x0B, 0x9A, 0x03, 0x1A, 0x4A, 0x7B, 0x03, 0x08, 0x4A, + 0x3C, 0x03, 0x15, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x5A, 0x03, 0x14, 0x0A, 0x7A, 0x03, 0x1A, 0x4A, + 0xE7, 0x2C, 0x87, 0x13, 0xE7, 0x00, 0x05, 0x0B, 0x5A, 0x1F, 0x9A, 0x13, 0x04, 0xC9, 0x9A, 0x52, + 0xE7, 0x00, 0x1A, 0x0B, 0x64, 0xEA, 0xA7, 0x72, 0x7C, 0x02, 0x08, 0x4A, 0x3E, 0x03, 0x16, 0x0A, + 0x9A, 0x00, 0x07, 0x4B, 0x9C, 0x03, 0x1E, 0x4A, 0x5A, 0x03, 0x14, 0x0A, 0x9B, 0x03, 0x1A, 0x4A, + 0xDA, 0x02, 0x14, 0x4A, 0xE4, 0x36, 0x85, 0x52, 0x3A, 0x03, 0x1A, 0x0A, 0x84, 0x15, 0xB8, 0x72, + 0x5C, 0x03, 0x13, 0x4A, 0xFA, 0x00, 0x04, 0x0B, 0x47, 0x03, 0x16, 0x0A, 0x87, 0x03, 0x07, 0x4A, + 0x7B, 0x03, 0x15, 0x0A, 0xE7, 0x00, 0x1B, 0x4A, 0xC6, 0x2C, 0x86, 0x13, 0xC6, 0x00, 0x01, 0x0B, + 0xE7, 0x1C, 0x87, 0x13, 0x44, 0xE4, 0x95, 0x52, 0xC6, 0x00, 0x07, 0x0B, 0x64, 0xD0, 0xA6, 0x72, + 0x9C, 0x02, 0x13, 0x4A, 0x5E, 0x03, 0x19, 0x0A, 0x87, 0x00, 0x06, 0x4B, 0x3B, 0x03, 0x15, 0x4A, + 0xA4, 0x1B, 0x8A, 0x52, 0x9C, 0x03, 0x1E, 0x4A, 0xE7, 0x00, 0x15, 0x0A, 0x5B, 0x03, 0x1B, 0x0A, + 0x84, 0x2F, 0xB9, 0x72, 0xE9, 0xC3, 0x00, 0xB9, 0x87, 0x03, 0x07, 0x4A, 0x7C, 0x03, 0x14, 0x4A, + 0xDB, 0x00, 0x04, 0x0B, 0xE9, 0x53, 0x41, 0xB9, 0x66, 0x03, 0x19, 0x0A, 0x86, 0x03, 0x06, 0x4A, + 0xE7, 0x00, 0x16, 0x0A, 0xC6, 0x00, 0x07, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x08, 0x01, 0x09, 0x0B, + 0xC6, 0x1C, 0x86, 0x13, 0x44, 0x49, 0x85, 0x52, 0x08, 0x01, 0x06, 0x0B, 0x64, 0x0F, 0xB8, 0x72, + 0xBC, 0x02, 0x14, 0x4A, 0x7E, 0x03, 0x1A, 0x0A, 0x86, 0x00, 0x08, 0x4B, 0xA4, 0xB6, 0x9A, 0x52, + 0x9C, 0x03, 0x1E, 0x4A, 0xC6, 0x00, 0x16, 0x0A, 0x47, 0x03, 0x16, 0x4A, 0x84, 0xF0, 0xA7, 0x72, + 0x86, 0x03, 0x06, 0x4A, 0x67, 0x03, 0x07, 0x0A, 0x1C, 0x01, 0x04, 0x0B, 0xE7, 0x00, 0x15, 0x4A, + 0x88, 0x03, 0x1A, 0x0A, 0xE8, 0x00, 0x08, 0x4A, 0xC6, 0x00, 0x19, 0x0A, 0x08, 0x01, 0x06, 0x4A, + 0x66, 0x2E, 0x93, 0x13, 0xC6, 0x00, 0x03, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x04, 0xDD, 0x9E, 0x52, + 0xC8, 0x00, 0x08, 0x0B, 0x04, 0x57, 0xA9, 0x72, 0xC7, 0x02, 0x15, 0x4A, 0x9E, 0x03, 0x1B, 0x0A, + 0x86, 0x00, 0x08, 0x4B, 0xE7, 0x00, 0x1E, 0x4A, 0xC6, 0x00, 0x19, 0x0A, 0xE4, 0x22, 0x81, 0x52, + 0xE6, 0x00, 0x06, 0x4A, 0x67, 0x03, 0x19, 0x4A, 0xE4, 0xA8, 0xB6, 0x72, 0xEA, 0xA3, 0x00, 0xB9, + 0x87, 0x03, 0x07, 0x0A, 0x08, 0x01, 0x04, 0x0B, 0xEA, 0x5B, 0x41, 0xB9, 0xE7, 0x00, 0x16, 0x4A, + 0x13, 0x01, 0x1B, 0x0A, 0xE7, 0x00, 0x13, 0x4A, 0xC6, 0x00, 0x1A, 0x0A, 0xE6, 0x00, 0x06, 0x4A, + 0x87, 0x2E, 0x94, 0x13, 0xE7, 0x00, 0x0A, 0x0B, 0xC6, 0x1C, 0x86, 0x13, 0xC4, 0x44, 0x85, 0x52, + 0xE6, 0x00, 0x06, 0x0B, 0x24, 0xBD, 0xAD, 0x72, 0x33, 0x03, 0x16, 0x4A, 0x1E, 0x01, 0x1C, 0x0A, + 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x1E, 0x4A, 0xE7, 0x00, 0x1A, 0x0A, 0x24, 0xBB, 0x9A, 0x52, + 0x67, 0x02, 0x07, 0x4A, 0x93, 0x03, 0x1A, 0x4A, 0xC4, 0x42, 0xB2, 0x72, 0x13, 0x01, 0x13, 0x0A, + 0xD4, 0x00, 0x04, 0x0B, 0x73, 0x02, 0x19, 0x4A, 0x86, 0x02, 0x1C, 0x0A, 0x66, 0x02, 0x06, 0x4A, + 0xE7, 0x00, 0x1B, 0x0A, 0xC6, 0x00, 0x07, 0x4A, 0xA7, 0x2E, 0x95, 0x13, 0xE7, 0x00, 0x0E, 0x0B, + 0xC6, 0x1C, 0x86, 0x13, 0x84, 0x9C, 0x80, 0x52, 0xE6, 0x00, 0x06, 0x0B, 0xC4, 0xD0, 0xAE, 0x72, + 0x53, 0x03, 0x19, 0x4A, 0x9E, 0x02, 0x08, 0x0A, 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x1E, 0x4A, + 0xE7, 0x00, 0x1B, 0x0A, 0x64, 0x63, 0x9F, 0x52, 0x67, 0x02, 0x07, 0x4A, 0x13, 0x01, 0x1B, 0x4A, + 0x24, 0x2F, 0xB1, 0x72, 0x93, 0x02, 0x13, 0x0A, 0xD5, 0x00, 0x04, 0x0B, 0x73, 0x02, 0x1A, 0x4A, + 0xA6, 0x02, 0x08, 0x0A, 0x66, 0x02, 0x06, 0x4A, 0xE7, 0x00, 0x1C, 0x0A, 0xC6, 0x00, 0x07, 0x4A, + 0xC7, 0x2E, 0x96, 0x13, 0xE7, 0x00, 0x00, 0x0B, 0xC6, 0x1C, 0x86, 0x13, 0x24, 0x8B, 0x9E, 0x52, + 0xE6, 0x00, 0x06, 0x0B, 0xC4, 0xD9, 0xA5, 0x72, 0x73, 0x03, 0x1A, 0x4A, 0xBE, 0x02, 0x14, 0x0A, + 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x1E, 0x4A, 0xE7, 0x00, 0x1C, 0x0A, 0xC4, 0x74, 0x81, 0x52, + 0x67, 0x02, 0x07, 0x4A, 0x93, 0x02, 0x1C, 0x4A, 0x24, 0x26, 0xBA, 0x72, 0xB3, 0x02, 0x13, 0x0A, + 0xDE, 0x00, 0x04, 0x0B, 0x73, 0x02, 0x1B, 0x4A, 0xC6, 0x03, 0x14, 0x0A, 0x66, 0x02, 0x06, 0x4A, + 0xE7, 0x00, 0x08, 0x0A, 0xC6, 0x00, 0x07, 0x4A, 0x27, 0x2F, 0x99, 0x13, 0xE7, 0x00, 0x02, 0x0B, + 0xC6, 0x1C, 0x86, 0x13, 0x64, 0x4A, 0x89, 0x52, 0xE6, 0x00, 0x06, 0x0B, 0x04, 0xE4, 0xAC, 0x72, + 0x93, 0x03, 0x1B, 0x4A, 0xD6, 0x03, 0x15, 0x0A, 0x87, 0x00, 0x06, 0x4B, 0x73, 0x02, 0x16, 0x4A, + 0xE7, 0x00, 0x08, 0x0A, 0x73, 0x02, 0x07, 0x4A, 0xA7, 0x02, 0x08, 0x4A, 0x84, 0xB5, 0x96, 0x52, + 0xC7, 0x03, 0x07, 0x0A, 0xE4, 0x1B, 0xB3, 0x72, 0xF6, 0x00, 0x1C, 0x4A, 0xC7, 0x00, 0x04, 0x0B, + 0xE9, 0x63, 0x41, 0xB9, 0xE6, 0x00, 0x15, 0x0A, 0xC6, 0x02, 0x06, 0x4A, 0x73, 0x02, 0x14, 0x0A, + 0xC6, 0x00, 0x13, 0x4A, 0x53, 0x2F, 0x9A, 0x13, 0x73, 0x02, 0x09, 0x0B, 0xC6, 0x1C, 0x86, 0x13, + 0x84, 0xA4, 0x91, 0x52, 0x66, 0x02, 0x06, 0x0B, 0x44, 0x00, 0xBA, 0x72, 0x16, 0x01, 0x1C, 0x4A, + 0xF9, 0x00, 0x1E, 0x0A, 0x93, 0x00, 0x06, 0x4B, 0xD6, 0x02, 0x19, 0x4A, 0x73, 0x02, 0x14, 0x0A, + 0x64, 0x5B, 0x8E, 0x52, 0xD3, 0x02, 0x13, 0x4A, 0xD6, 0x03, 0x14, 0x4A, 0xA4, 0xFF, 0xA5, 0x72, + 0xF6, 0x00, 0x16, 0x0A, 0xC6, 0x00, 0x04, 0x0B, 0xE9, 0x6F, 0x41, 0xB9, 0xD6, 0x02, 0x08, 0x4A, + 0xD9, 0x00, 0x1E, 0x0A, 0xD6, 0x02, 0x19, 0x4A, 0x73, 0x02, 0x15, 0x0A, 0xD3, 0x02, 0x13, 0x4A, + 0x76, 0x2F, 0x9B, 0x13, 0xD6, 0x02, 0x09, 0x0B, 0x73, 0x1E, 0x93, 0x13, 0x04, 0x09, 0x84, 0x52, + 0xD3, 0x02, 0x13, 0x0B, 0xA4, 0xFC, 0xA5, 0x72, 0x99, 0x02, 0x08, 0x4A, 0xDA, 0x00, 0x07, 0x0A, + 0x96, 0x00, 0x13, 0x4B, 0x39, 0x03, 0x1A, 0x4A, 0xD6, 0x02, 0x15, 0x0A, 0xE4, 0xF6, 0x9B, 0x52, + 0x36, 0x03, 0x16, 0x4A, 0xF9, 0x00, 0x15, 0x4A, 0x44, 0x03, 0xBA, 0x72, 0xED, 0x03, 0x0C, 0x2A, + 0xEC, 0x87, 0x00, 0xB9, 0xD9, 0x00, 0x19, 0x0A, 0x7B, 0x02, 0x04, 0x0B, 0xEC, 0x7F, 0x40, 0xB9, + 0x39, 0x03, 0x14, 0x4A, 0x73, 0x03, 0x07, 0x0A, 0x33, 0x03, 0x13, 0x4A, 0xD6, 0x02, 0x1E, 0x0A, + 0x73, 0x02, 0x16, 0x4A, 0x96, 0x2F, 0x9C, 0x13, 0xD6, 0x02, 0x0C, 0x0B, 0x73, 0x1E, 0x93, 0x13, + 0x44, 0x02, 0x8A, 0x52, 0xD3, 0x02, 0x13, 0x0B, 0xC4, 0xE3, 0xA8, 0x72, 0xB9, 0x02, 0x14, 0x4A, + 0x7A, 0x03, 0x06, 0x0A, 0x96, 0x00, 0x13, 0x4B, 0x39, 0x03, 0x1A, 0x4A, 0xD6, 0x02, 0x1E, 0x0A, + 0xA4, 0xFD, 0x95, 0x52, 0x36, 0x03, 0x16, 0x4A, 0xD9, 0x00, 0x1E, 0x4A, 0x24, 0x1C, 0xB7, 0x72, + 0x79, 0x03, 0x19, 0x0A, 0x73, 0x02, 0x04, 0x0B, 0xF1, 0x73, 0x40, 0xB9, 0x39, 0x03, 0x15, 0x4A, + 0x7A, 0x02, 0x06, 0x0A, 0x39, 0x03, 0x1A, 0x4A, 0xD6, 0x02, 0x07, 0x0A, 0x36, 0x03, 0x16, 0x4A, + 0x08, 0x2D, 0x88, 0x13, 0x08, 0x01, 0x11, 0x0B, 0xD6, 0x1E, 0x96, 0x13, 0x24, 0x2D, 0x90, 0x52, + 0x08, 0x01, 0x16, 0x0B, 0x24, 0xBB, 0xB2, 0x72, 0xDA, 0x03, 0x15, 0x4A, 0x7C, 0x02, 0x1B, 0x0A, + 0x96, 0x00, 0x08, 0x4B, 0x5A, 0x03, 0x1C, 0x4A, 0xD6, 0x02, 0x07, 0x0A, 0x59, 0x03, 0x16, 0x4A, + 0x76, 0x03, 0x07, 0x4A, 0xC4, 0xD2, 0x8F, 0x52, 0x76, 0x02, 0x16, 0x0A, 0xC4, 0x44, 0xAD, 0x72, + 0xDA, 0x02, 0x1E, 0x4A, 0x16, 0x01, 0x04, 0x0B, 0xC8, 0x02, 0x1B, 0x0A, 0x48, 0x03, 0x08, 0x4A, + 0x39, 0x03, 0x06, 0x0A, 0x08, 0x01, 0x19, 0x4A, 0x94, 0x2E, 0x94, 0x13, 0x94, 0x02, 0x0D, 0x0B, + 0x08, 0x1D, 0x88, 0x13, 0x44, 0xF7, 0x8D, 0x52, 0x88, 0x02, 0x08, 0x0B, 0x64, 0xB0, 0xA8, 0x72, + 0xFA, 0x00, 0x1E, 0x4A, 0xDC, 0x02, 0x13, 0x0A, 0x94, 0x00, 0x08, 0x4B, 0x79, 0x02, 0x06, 0x4A, + 0xA4, 0x08, 0x92, 0x52, 0x5A, 0x03, 0x1C, 0x4A, 0x94, 0x02, 0x06, 0x0A, 0xD9, 0x02, 0x19, 0x0A, + 0x84, 0x4F, 0xB7, 0x72, 0x54, 0x03, 0x14, 0x4A, 0x3A, 0x03, 0x07, 0x4A, 0x19, 0x01, 0x04, 0x0B, + 0xE9, 0x57, 0x41, 0xB9, 0x28, 0x03, 0x13, 0x0A, 0x48, 0x03, 0x08, 0x4A, 0x94, 0x02, 0x1B, 0x0A, + 0x08, 0x01, 0x14, 0x4A, 0xB4, 0x2E, 0x95, 0x13, 0x94, 0x02, 0x09, 0x0B, 0x08, 0x1D, 0x88, 0x13, + 0xC4, 0x0C, 0x90, 0x52, 0x88, 0x02, 0x08, 0x0B, 0x64, 0xDA, 0xA1, 0x72, 0xDA, 0x00, 0x07, 0x4A, + 0x3C, 0x03, 0x16, 0x0A, 0x94, 0x00, 0x08, 0x4B, 0x24, 0xF3, 0x8F, 0x52, 0x5A, 0x03, 0x1C, 0x4A, + 0x94, 0x02, 0x1B, 0x0A, 0xD5, 0x02, 0x1B, 0x4A, 0x84, 0x25, 0xBE, 0x72, 0x54, 0x03, 0x14, 0x4A, + 0x35, 0x03, 0x15, 0x0A, 0x1A, 0x01, 0x04, 0x0B, 0xB5, 0x02, 0x06, 0x4A, 0x48, 0x03, 0x16, 0x0A, + 0xA8, 0x02, 0x08, 0x4A, 0x94, 0x02, 0x13, 0x0A, 0x08, 0x01, 0x14, 0x4A, 0xD4, 0x2F, 0x9E, 0x13, + 0x94, 0x02, 0x10, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x04, 0xD7, 0x8C, 0x52, 0x88, 0x02, 0x08, 0x0B, + 0xC4, 0x6B, 0xBB, 0x72, 0x75, 0x03, 0x06, 0x4A, 0x5C, 0x03, 0x19, 0x0A, 0x94, 0x00, 0x08, 0x4B, + 0xB5, 0x02, 0x1C, 0x4A, 0x94, 0x02, 0x13, 0x0A, 0xE4, 0x28, 0x93, 0x52, 0xB4, 0x02, 0x14, 0x4A, + 0x35, 0x03, 0x13, 0x4A, 0x24, 0x94, 0xA4, 0x72, 0xE3, 0x93, 0x00, 0xB9, 0x55, 0x03, 0x15, 0x0A, + 0x1C, 0x01, 0x04, 0x0B, 0xE3, 0x83, 0x40, 0xB9, 0xB5, 0x02, 0x1B, 0x4A, 0x88, 0x03, 0x19, 0x0A, + 0xA8, 0x02, 0x08, 0x4A, 0x94, 0x02, 0x16, 0x0A, 0x08, 0x01, 0x14, 0x4A, 0xE7, 0x2C, 0x87, 0x13, + 0xE7, 0x00, 0x03, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x04, 0x61, 0x92, 0x52, 0xE8, 0x00, 0x08, 0x0B, + 0xC4, 0x8D, 0xA9, 0x72, 0x75, 0x02, 0x1B, 0x4A, 0x9E, 0x03, 0x1A, 0x0A, 0x87, 0x00, 0x08, 0x4B, + 0xE4, 0x9E, 0x8D, 0x52, 0xB5, 0x02, 0x1E, 0x4A, 0xE7, 0x00, 0x16, 0x0A, 0x54, 0x03, 0x16, 0x4A, + 0x24, 0x72, 0xB6, 0x72, 0xA7, 0x02, 0x07, 0x4A, 0x94, 0x03, 0x14, 0x0A, 0x15, 0x01, 0x04, 0x0B, + 0xEA, 0x6F, 0x40, 0xB9, 0x94, 0x02, 0x13, 0x4A, 0xA8, 0x02, 0x1A, 0x0A, 0x88, 0x02, 0x08, 0x4A, + 0xE7, 0x00, 0x19, 0x0A, 0x08, 0x01, 0x07, 0x4A, 0xC6, 0x2C, 0x86, 0x13, 0xC6, 0x00, 0x0A, 0x0B, + 0x08, 0x1D, 0x88, 0x13, 0xA4, 0xA3, 0x81, 0x52, 0xC8, 0x00, 0x08, 0x0B, 0xC4, 0xFF, 0xBE, 0x72, + 0xD4, 0x02, 0x13, 0x4A, 0xBE, 0x02, 0x1C, 0x0A, 0x86, 0x00, 0x08, 0x4B, 0x94, 0x02, 0x1E, 0x4A, + 0xC6, 0x00, 0x19, 0x0A, 0x44, 0x5C, 0x9E, 0x52, 0xEB, 0xE3, 0x00, 0xB9, 0x8B, 0x02, 0x06, 0x4A, + 0x86, 0x03, 0x19, 0x4A, 0x24, 0x00, 0xA1, 0x72, 0xA6, 0x02, 0x06, 0x0A, 0x14, 0x01, 0x04, 0x0B, + 0xC6, 0x00, 0x16, 0x4A, 0x88, 0x02, 0x1C, 0x0A, 0xCD, 0x00, 0x08, 0x4A, 0x28, 0x03, 0x16, 0x4A, + 0x86, 0x02, 0x15, 0x0A, 0xE9, 0x27, 0x41, 0xB9, 0xEE, 0xB3, 0x00, 0xB9, 0x0E, 0x01, 0x06, 0x4A, + 0x88, 0xEB, 0x99, 0x52, 0x88, 0x84, 0xAD, 0x72, 0xE8, 0x02, 0x08, 0x0B, 0xEF, 0x9F, 0x00, 0xB9, + 0x08, 0x01, 0x09, 0x0B, 0xEF, 0x97, 0x40, 0xB9, 0xE8, 0x63, 0x00, 0xB9, 0x88, 0x2E, 0x94, 0x13, + 0x1E, 0x01, 0x09, 0x0B, 0x68, 0x01, 0x1A, 0x0A, 0xA8, 0x01, 0x08, 0x4A, 0x66, 0x2F, 0x9B, 0x13, + 0xC6, 0x00, 0x0F, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0x24, 0x7D, 0x80, 0x52, 0xC8, 0x00, 0x08, 0x0B, + 0x24, 0x4E, 0xAF, 0x72, 0x86, 0x00, 0x08, 0x4B, 0xA7, 0x02, 0x1A, 0x4A, 0xC4, 0x82, 0x9F, 0x52, + 0x87, 0x02, 0x07, 0x0A, 0xC4, 0xB1, 0xB0, 0x72, 0xC6, 0x00, 0x1A, 0x0A, 0xFB, 0x00, 0x19, 0x4A, + 0x07, 0x01, 0x04, 0x0B, 0xED, 0x8F, 0x40, 0xB9, 0xC6, 0x01, 0x06, 0x4A, 0xE8, 0x00, 0x15, 0x0A, + 0x68, 0x03, 0x08, 0x4A, 0xC6, 0x00, 0x1C, 0x0A, 0x08, 0x01, 0x06, 0x4A, 0x66, 0x2E, 0x93, 0x13, + 0xC6, 0x00, 0x0D, 0x0B, 0x08, 0x1D, 0x88, 0x13, 0xE4, 0xE4, 0x9B, 0x52, 0xC8, 0x00, 0x08, 0x0B, + 0xC4, 0x92, 0xB3, 0x72, 0x86, 0x00, 0x08, 0x4B, 0x04, 0x1B, 0x84, 0x52, 0x5B, 0x03, 0x19, 0x4A, + 0xF7, 0x00, 0x14, 0x0A, 0x93, 0x02, 0x1C, 0x4A, 0x24, 0x6D, 0xAC, 0x72, 0x77, 0x03, 0x17, 0x4A, + 0xC6, 0x00, 0x1C, 0x0A, 0xF3, 0x00, 0x13, 0x0A, 0x08, 0x01, 0x04, 0x0B, 0xE9, 0x5F, 0x41, 0xB9, + 0xE6, 0x02, 0x06, 0x4A, 0x73, 0x02, 0x1A, 0x4A, 0x17, 0x01, 0x14, 0x0A, 0x73, 0x02, 0x17, 0x4A, + 0xC6, 0x00, 0x15, 0x0A, 0x66, 0x02, 0x06, 0x4A, 0xD3, 0x2E, 0x96, 0x13, 0x73, 0x02, 0x09, 0x0B, + 0xC6, 0x1C, 0x86, 0x13, 0xC4, 0x32, 0x96, 0x52, 0x66, 0x02, 0x06, 0x0B, 0x04, 0xD5, 0xB1, 0x72, + 0x97, 0x03, 0x1A, 0x4A, 0x1B, 0x01, 0x07, 0x0A, 0x93, 0x00, 0x06, 0x4B, 0x24, 0xCD, 0x89, 0x52, + 0xF7, 0x02, 0x1B, 0x4A, 0x73, 0x02, 0x15, 0x0A, 0xF6, 0x00, 0x15, 0x4A, 0xE4, 0x2A, 0xAE, 0x72, + 0xE5, 0xEB, 0x00, 0xB9, 0xF3, 0x02, 0x13, 0x4A, 0x16, 0x01, 0x16, 0x0A, 0xD7, 0x00, 0x04, 0x0B, + 0xE5, 0x9B, 0x40, 0xB9, 0xE9, 0xDB, 0x40, 0xB9, 0xD6, 0x02, 0x1C, 0x4A, 0xE6, 0x02, 0x07, 0x0A, + 0xD6, 0x02, 0x06, 0x4A, 0xA4, 0x36, 0x8B, 0x52, 0x73, 0x02, 0x14, 0x0A, 0xA6, 0x02, 0x1C, 0x4A, + 0xFB, 0x02, 0x08, 0x0A, 0x44, 0x4B, 0xB8, 0x72, 0xD3, 0x02, 0x13, 0x4A, 0x36, 0x2F, 0x99, 0x13, + 0xDB, 0x00, 0x1B, 0x4A, 0xA6, 0x00, 0x04, 0x0B, 0xD6, 0x02, 0x09, 0x0B, 0x73, 0x1E, 0x93, 0x13, + 0x84, 0x2B, 0x80, 0x52, 0xD3, 0x02, 0x13, 0x0B, 0xE4, 0x74, 0xAB, 0x72, 0xF8, 0x2E, 0x97, 0x13, + 0x96, 0x00, 0x13, 0x4B, 0x64, 0xD4, 0x9F, 0x52, 0xC6, 0x00, 0x18, 0x0B, 0x18, 0x01, 0x14, 0x4A, + 0x04, 0x8B, 0xB4, 0x72, 0xD6, 0x02, 0x14, 0x0A, 0xF8, 0x02, 0x18, 0x0A, 0x79, 0x02, 0x04, 0x0B, + 0xEB, 0xFB, 0x40, 0xB9, 0x76, 0x03, 0x16, 0x4A, 0x18, 0x03, 0x15, 0x4A, 0x33, 0x03, 0x08, 0x0A, + 0x13, 0x03, 0x13, 0x4A, 0x24, 0xA7, 0x9A, 0x52, 0xD6, 0x02, 0x07, 0x0A, 0x04, 0x86, 0xB3, 0x72, + 0x73, 0x02, 0x16, 0x4A, 0x56, 0x2F, 0x9A, 0x13, 0xF0, 0x8B, 0x00, 0xB9, 0x30, 0x01, 0x04, 0x0B, + 0xD6, 0x02, 0x0B, 0x0B, 0x73, 0x1E, 0x93, 0x13, 0x24, 0x50, 0x98, 0x52, 0xD3, 0x02, 0x13, 0x0B, + 0x84, 0x6D, 0xA1, 0x72, 0x98, 0x02, 0x15, 0x4A, 0x3B, 0x03, 0x17, 0x0A, 0x96, 0x00, 0x13, 0x4B, + 0x18, 0x03, 0x1B, 0x4A, 0xD6, 0x02, 0x07, 0x0A, 0xC4, 0xAF, 0x87, 0x52, 0x16, 0x03, 0x16, 0x4A, + 0xF8, 0x02, 0x07, 0x4A, 0x64, 0x92, 0xBE, 0x72, 0x38, 0x03, 0x18, 0x0A, 0x7A, 0x02, 0x04, 0x0B, + 0xEE, 0xF3, 0x40, 0xB9, 0x18, 0x03, 0x14, 0x4A, 0x53, 0x03, 0x17, 0x0A, 0x18, 0x03, 0x13, 0x4A, + 0x64, 0x02, 0x8C, 0x52, 0xD6, 0x02, 0x08, 0x0A, 0xF3, 0x00, 0x14, 0x4A, 0x54, 0x03, 0x19, 0x0A, + 0x44, 0x5E, 0xA5, 0x72, 0x16, 0x03, 0x16, 0x4A, 0x98, 0x2F, 0x9C, 0x13, 0x74, 0x02, 0x14, 0x4A, + 0x73, 0x00, 0x04, 0x0B, 0x18, 0x03, 0x0E, 0x0B, 0xD6, 0x1E, 0x96, 0x13, 0x04, 0x6E, 0x91, 0x52, + 0x16, 0x03, 0x16, 0x0B, 0x44, 0x4D, 0xBE, 0x72, 0x98, 0x00, 0x16, 0x4B, 0x18, 0x03, 0x08, 0x0A, + 0xE4, 0x91, 0x8E, 0x52, 0x3B, 0x2F, 0x99, 0x13, 0x94, 0x02, 0x18, 0x4A, 0x38, 0x03, 0x08, 0x4A, + 0xA4, 0xB2, 0xA1, 0x72, 0x10, 0x02, 0x1B, 0x0B, 0x5B, 0x2F, 0x9A, 0x13, 0x58, 0x03, 0x18, 0x0A, + 0xD6, 0x02, 0x04, 0x0B, 0xE3, 0x0B, 0x41, 0xB9, 0x73, 0x02, 0x1B, 0x0B, 0x18, 0x03, 0x07, 0x4A, + 0xDB, 0x02, 0x19, 0x0A, 0x18, 0x03, 0x1B, 0x4A, 0x94, 0x02, 0x17, 0x0A, 0x14, 0x03, 0x14, 0x4A, + 0xB5, 0x2E, 0x95, 0x13, 0xB5, 0x02, 0x03, 0x0B, 0x94, 0x1E, 0x94, 0x13, 0xB4, 0x02, 0x14, 0x0B, + 0xF5, 0x34, 0x89, 0x52, 0x35, 0xAE, 0xB1, 0x72, 0x1B, 0x01, 0x07, 0x4A, 0xDC, 0x02, 0x1A, 0x0A, + 0xB5, 0x02, 0x14, 0x4B, 0x7B, 0x03, 0x1C, 0x4A, 0xB5, 0x02, 0x17, 0x0A, 0x75, 0x03, 0x15, 0x4A, + 0x1B, 0xCB, 0x96, 0x52, 0x58, 0x03, 0x17, 0x4A, 0xDB, 0x51, 0xAE, 0x72, 0x64, 0x04, 0x96, 0x52, + 0xD8, 0x02, 0x18, 0x0A, 0x94, 0x02, 0x1B, 0x0B, 0x24, 0xBA, 0xB8, 0x72, 0x18, 0x03, 0x08, 0x4A, + 0x9B, 0x02, 0x1A, 0x0A, 0x9C, 0x01, 0x04, 0x0B, 0xC4, 0x2E, 0x96, 0x13, 0x18, 0x03, 0x1B, 0x4A, + 0xB5, 0x02, 0x19, 0x0A, 0x84, 0x03, 0x04, 0x0B, 0xFB, 0x02, 0x08, 0x4A, 0x9C, 0x02, 0x16, 0x0A, + 0x15, 0x03, 0x15, 0x4A, 0x7B, 0x03, 0x1C, 0x4A, 0x1C, 0xBE, 0x90, 0x52, 0xB5, 0x1E, 0x95, 0x13, + 0xF8, 0x54, 0x86, 0x52, 0x1C, 0x0C, 0xA5, 0x72, 0xD5, 0x03, 0x15, 0x0B, 0x98, 0xCE, 0xB1, 0x72, + 0x00, 0x00, 0x1C, 0x0B, 0x9C, 0x2E, 0x94, 0x13, 0x18, 0x03, 0x15, 0x4B, 0x00, 0x00, 0x1C, 0x0B, + 0x18, 0x03, 0x19, 0x0A, 0x1C, 0xAB, 0x99, 0x52, 0x78, 0x03, 0x18, 0x4A, 0xDB, 0x02, 0x19, 0x4A, + 0x7C, 0x31, 0xAE, 0x72, 0x9B, 0x02, 0x1B, 0x0A, 0xB5, 0x02, 0x1C, 0x0B, 0xE9, 0xD3, 0x40, 0xB9, + 0x7B, 0x03, 0x17, 0x4A, 0xBC, 0x02, 0x16, 0x0A, 0x7B, 0x03, 0x1C, 0x4A, 0x18, 0x03, 0x1A, 0x0A, + 0x78, 0x03, 0x18, 0x4A, 0xE7, 0x2C, 0x87, 0x13, 0xE7, 0x00, 0x09, 0x0B, 0x18, 0x1F, 0x98, 0x13, + 0xE7, 0x00, 0x18, 0x0B, 0x38, 0xA2, 0x96, 0x52, 0x58, 0xBD, 0xAF, 0x72, 0x37, 0x03, 0x17, 0x4A, + 0xBC, 0x02, 0x14, 0x0A, 0x18, 0x03, 0x07, 0x4B, 0xF7, 0x02, 0x1C, 0x4A, 0x18, 0x03, 0x1A, 0x0A, + 0xDB, 0x5D, 0x89, 0x52, 0xF7, 0x02, 0x18, 0x4A, 0x98, 0x02, 0x1A, 0x4A, 0xBB, 0x42, 0xB0, 0x72, + 0xB8, 0x02, 0x18, 0x0A, 0xE7, 0x00, 0x1B, 0x0B, 0x18, 0x03, 0x19, 0x4A, 0xFB, 0x00, 0x14, 0x0A, + 0x18, 0x03, 0x1B, 0x4A, 0x59, 0x03, 0x19, 0x4A, 0xFB, 0x00, 0x15, 0x0A, 0x39, 0x03, 0x1B, 0x4A, + 0xFB, 0x1D, 0x87, 0x52, 0x7B, 0x1B, 0xB7, 0x72, 0x5B, 0x01, 0x1B, 0x0B, 0xEA, 0x9F, 0x40, 0xB9, + 0xF7, 0x02, 0x16, 0x0A, 0x17, 0x03, 0x17, 0x4A, 0x08, 0x2D, 0x88, 0x13, 0x08, 0x01, 0x0A, 0x0B, + 0xF7, 0x1E, 0x97, 0x13, 0x08, 0x01, 0x17, 0x0B, 0x57, 0x7C, 0x8B, 0x52, 0x77, 0x95, 0xB0, 0x72, + 0xF7, 0x02, 0x08, 0x4B, 0xF7, 0x02, 0x16, 0x0A, 0x37, 0x03, 0x17, 0x4A, 0xB9, 0x83, 0x94, 0x52, + 0x1C, 0x23, 0x8F, 0x52, 0xB8, 0x02, 0x16, 0x4A, 0x99, 0x6A, 0xAF, 0x72, 0x3C, 0x48, 0xB9, 0x72, + 0xF8, 0x00, 0x18, 0x0A, 0x08, 0x01, 0x19, 0x0B, 0x2C, 0x00, 0x1C, 0x0B, 0xBC, 0x2E, 0x95, 0x13, + 0x18, 0x03, 0x1A, 0x4A, 0x19, 0x01, 0x15, 0x0A, 0x8C, 0x01, 0x1C, 0x0B, 0xFC, 0x2C, 0x87, 0x13, + 0x18, 0x03, 0x19, 0x4A, 0xF7, 0x02, 0x14, 0x0A, 0x7B, 0x03, 0x1C, 0x0B, 0xFC, 0x00, 0x16, 0x4A, + 0x17, 0x03, 0x17, 0x4A, 0x99, 0x03, 0x19, 0x4A, 0xB8, 0x02, 0x1A, 0x0A, 0xF7, 0x1E, 0x97, 0x13, + 0x18, 0x03, 0x16, 0x4A, 0x39, 0x03, 0x14, 0x0A, 0xC6, 0x00, 0x17, 0x0B, 0x18, 0x03, 0x19, 0x4A, + 0xD7, 0x00, 0x08, 0x0A, 0x17, 0x03, 0x17, 0x4A, 0x18, 0x01, 0x14, 0x4A, 0xD9, 0x00, 0x07, 0x0A, + 0x18, 0x03, 0x19, 0x4A, 0xF7, 0x1E, 0x97, 0x13, 0xF6, 0x00, 0x16, 0x0A, 0x10, 0x02, 0x17, 0x0B, + 0xD6, 0x02, 0x14, 0x4A, 0x17, 0x03, 0x15, 0x0A, 0xD6, 0x02, 0x17, 0x4A, 0x17, 0x02, 0x06, 0x0A, + 0xD6, 0x02, 0x17, 0x4A, 0xD7, 0x00, 0x15, 0x4A, 0x18, 0x02, 0x08, 0x0A, 0xF7, 0x02, 0x18, 0x4A, + 0xD6, 0x1E, 0x96, 0x13, 0x14, 0x01, 0x14, 0x0A, 0x73, 0x02, 0x16, 0x0B, 0x94, 0x02, 0x15, 0x4A, + 0xF6, 0x02, 0x07, 0x0A, 0x94, 0x02, 0x16, 0x4A, 0x76, 0x02, 0x10, 0x0A, 0x94, 0x02, 0x16, 0x4A, + 0x16, 0x02, 0x07, 0x4A, 0x77, 0x02, 0x06, 0x0A, 0x94, 0x1E, 0x94, 0x13, 0xD6, 0x02, 0x17, 0x4A, + 0x84, 0x00, 0x14, 0x0B, 0xD4, 0x00, 0x15, 0x0A, 0x94, 0x02, 0x07, 0x4A, 0xD5, 0x02, 0x08, 0x0A, + 0x94, 0x02, 0x15, 0x4A, 0x95, 0x00, 0x13, 0x0A, 0x94, 0x02, 0x15, 0x4A, 0x75, 0x02, 0x08, 0x4A, + 0x96, 0x00, 0x10, 0x0A, 0xB5, 0x02, 0x16, 0x4A, 0x94, 0x1E, 0x94, 0x13, 0x07, 0x02, 0x07, 0x0A, + 0x00, 0x00, 0x14, 0x0B, 0xE7, 0x00, 0x08, 0x4A, 0xB4, 0x02, 0x06, 0x0A, 0xE7, 0x00, 0x14, 0x4A, + 0x14, 0x00, 0x04, 0x0A, 0xE7, 0x00, 0x14, 0x4A, 0x94, 0x00, 0x06, 0x4A, 0x15, 0x00, 0x13, 0x0A, + 0x1E, 0x2D, 0x88, 0x13, 0x94, 0x02, 0x15, 0x4A, 0xE7, 0x1C, 0x87, 0x13, 0x68, 0x02, 0x08, 0x0A, + 0x8C, 0x01, 0x07, 0x0B, 0x08, 0x01, 0x06, 0x4A, 0x87, 0x02, 0x10, 0x0A, 0xD9, 0x01, 0x83, 0x52, + 0x36, 0xF8, 0x8E, 0x52, 0x08, 0x01, 0x07, 0x4A, 0x87, 0x01, 0x00, 0x0A, 0x59, 0x07, 0xAC, 0x72, + 0xB6, 0xE2, 0xBA, 0x72, 0xF5, 0x64, 0x89, 0x52, 0x08, 0x01, 0x07, 0x4A, 0x07, 0x00, 0x10, 0x4A, + 0x94, 0x01, 0x04, 0x0A, 0x42, 0x00, 0x19, 0x0B, 0xD9, 0x2C, 0x86, 0x13, 0x21, 0x01, 0x16, 0x0B, + 0x96, 0x2C, 0x84, 0x13, 0x35, 0xA6, 0xB7, 0x72, 0xE7, 0x00, 0x14, 0x4A, 0x54, 0xFB, 0x85, 0x52, + 0x08, 0x1D, 0x88, 0x13, 0x86, 0x00, 0x06, 0x0A, 0x1C, 0x96, 0x9B, 0x52, 0x21, 0x00, 0x16, 0x0B, + 0x75, 0x01, 0x15, 0x0B, 0x16, 0x2C, 0x80, 0x13, 0xF4, 0x15, 0xAF, 0x72, 0x68, 0x03, 0x08, 0x0B, + 0xC6, 0x00, 0x10, 0x4A, 0xE7, 0x00, 0x13, 0x0A, 0x3C, 0xCF, 0xB1, 0x72, 0x78, 0xD1, 0x81, 0x52, + 0xB5, 0x02, 0x16, 0x0B, 0xF4, 0x01, 0x14, 0x0B, 0x96, 0x2D, 0x8C, 0x13, 0xC6, 0x00, 0x07, 0x4A, + 0x07, 0x01, 0x0C, 0x0A, 0x3C, 0x02, 0x1C, 0x0B, 0xD8, 0x93, 0xAD, 0x72, 0x94, 0x02, 0x16, 0x0B, + 0xC6, 0x00, 0x07, 0x4A, 0x87, 0x01, 0x13, 0x4A, 0x16, 0x01, 0x00, 0x0A, 0xE9, 0x87, 0x40, 0xB9, + 0x9C, 0x03, 0x1E, 0x0B, 0xAD, 0x01, 0x18, 0x0B, 0x18, 0x2E, 0x90, 0x13, 0xE7, 0x00, 0x16, 0x4A, + 0xC6, 0x1C, 0x86, 0x13, 0x10, 0x00, 0x10, 0x0A, 0x16, 0x8C, 0x8B, 0x52, 0x86, 0x03, 0x06, 0x0B, + 0x10, 0x02, 0x13, 0x4A, 0xE7, 0x00, 0x04, 0x0A, 0x16, 0xAC, 0xAA, 0x72, 0x10, 0x02, 0x07, 0x4A, + 0xC7, 0x00, 0x08, 0x0A, 0x2F, 0x01, 0x16, 0x0B, 0x16, 0x2D, 0x88, 0x13, 0x10, 0x02, 0x07, 0x4A, + 0x42, 0x00, 0x19, 0x0B, 0xEF, 0x01, 0x16, 0x0B, 0x07, 0x01, 0x04, 0x4A, 0xD6, 0x00, 0x0C, 0x0A, + 0x10, 0x1E, 0x90, 0x13, 0xE7, 0x00, 0x16, 0x4A, 0x50, 0x00, 0x10, 0x0B, 0x82, 0x01, 0x13, 0x0A, + 0x42, 0x00, 0x04, 0x4A, 0xE7, 0x00, 0x00, 0x0A, 0x42, 0x00, 0x07, 0x4A, 0x07, 0x02, 0x06, 0x0A, + 0x42, 0x00, 0x07, 0x4A, 0xAD, 0x01, 0x18, 0x0B, 0xF1, 0xA3, 0x40, 0xB9, 0x78, 0x2E, 0x93, 0x13, + 0xC7, 0x00, 0x00, 0x4A, 0x13, 0x02, 0x08, 0x0A, 0x42, 0x1C, 0x82, 0x13, 0xE7, 0x00, 0x13, 0x4A, + 0xAD, 0x01, 0x02, 0x0B, 0x02, 0x01, 0x04, 0x0A, 0xD7, 0x47, 0x91, 0x52, 0x42, 0x00, 0x00, 0x4A, + 0xE4, 0x00, 0x0C, 0x0A, 0xD7, 0x03, 0xB6, 0x72, 0x42, 0x00, 0x04, 0x4A, 0xA4, 0x01, 0x10, 0x0A, + 0x37, 0x02, 0x17, 0x0B, 0x42, 0x00, 0x04, 0x4A, 0x04, 0x02, 0x0C, 0x4A, 0xA7, 0x01, 0x06, 0x0A, + 0xF7, 0x02, 0x18, 0x0B, 0x84, 0x00, 0x07, 0x4A, 0x47, 0x0C, 0x93, 0x52, 0x42, 0x1C, 0x82, 0x13, + 0xC0, 0x00, 0x00, 0x0A, 0x07, 0xE9, 0xAA, 0x72, 0xE2, 0x02, 0x02, 0x0B, 0x00, 0x00, 0x0C, 0x4A, + 0x84, 0x00, 0x08, 0x0A, 0x51, 0x01, 0x07, 0x0B, 0xA7, 0x2D, 0x8D, 0x13, 0x00, 0x00, 0x04, 0x4A, + 0x44, 0x00, 0x0D, 0x0A, 0x31, 0x02, 0x07, 0x0B, 0x00, 0x00, 0x04, 0x4A, 0xA4, 0x01, 0x08, 0x4A, + 0x47, 0x00, 0x10, 0x0A, 0x84, 0x00, 0x07, 0x4A, 0x00, 0x1C, 0x80, 0x13, 0x0C, 0x02, 0x0C, 0x0A, + 0x20, 0x00, 0x00, 0x0B, 0x8C, 0x01, 0x08, 0x4A, 0x81, 0x00, 0x06, 0x0A, 0x8C, 0x01, 0x01, 0x4A, + 0x01, 0x00, 0x02, 0x0A, 0x8C, 0x01, 0x01, 0x4A, 0x41, 0x00, 0x06, 0x4A, 0x04, 0x00, 0x0D, 0x0A, + 0x21, 0x00, 0x04, 0x4A, 0x8C, 0x1D, 0x8C, 0x13, 0xA8, 0x01, 0x08, 0x0A, 0xAC, 0x02, 0x0C, 0x0B, + 0x08, 0x01, 0x06, 0x4A, 0x21, 0x00, 0x10, 0x0A, 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x00, 0x0A, + 0xE9, 0x93, 0x40, 0xB9, 0x08, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x10, 0x4A, 0x95, 0x01, 0x02, 0x0A, + 0xD8, 0x2C, 0x86, 0x13, 0x21, 0x00, 0x15, 0x4A, 0x08, 0x1D, 0x88, 0x13, 0x46, 0x00, 0x06, 0x0A, + 0x76, 0xBE, 0x84, 0x52, 0x88, 0x02, 0x08, 0x0B, 0xC6, 0x00, 0x10, 0x4A, 0x21, 0x00, 0x0D, 0x0A, + 0xB6, 0xCA, 0xBC, 0x72, 0xC1, 0x00, 0x01, 0x4A, 0x06, 0x01, 0x0C, 0x0A, 0x36, 0x01, 0x16, 0x0B, + 0x21, 0x00, 0x06, 0x4A, 0x86, 0x01, 0x0D, 0x4A, 0x14, 0x01, 0x00, 0x0A, 0xD6, 0x02, 0x18, 0x0B, + 0x18, 0x2E, 0x90, 0x13, 0xC6, 0x00, 0x14, 0x4A, 0x21, 0x1C, 0x81, 0x13, 0x10, 0x00, 0x10, 0x0A, + 0xEF, 0x01, 0x01, 0x0B, 0x10, 0x02, 0x0D, 0x4A, 0xC1, 0x00, 0x02, 0x0A, 0x10, 0x02, 0x01, 0x4A, + 0xE1, 0x01, 0x08, 0x0A, 0xE9, 0xC3, 0x40, 0xB9, 0x10, 0x02, 0x01, 0x4A, 0x01, 0x01, 0x02, 0x4A, + 0xE6, 0x01, 0x0C, 0x0A, 0x21, 0x00, 0x06, 0x4A, 0x10, 0x1E, 0x90, 0x13, 0x8D, 0x01, 0x0D, 0x0A, + 0x93, 0x72, 0x95, 0x52, 0xD0, 0x02, 0x10, 0x0B, 0xAD, 0x01, 0x02, 0x4A, 0x21, 0x00, 0x00, 0x0A, + 0xB3, 0x4A, 0xB5, 0x72, 0xAD, 0x01, 0x01, 0x4A, 0x01, 0x02, 0x0F, 0x0A, 0x33, 0x01, 0x13, 0x0B, + 0xAD, 0x01, 0x01, 0x4A, 0xE1, 0x01, 0x00, 0x4A, 0x16, 0x02, 0x08, 0x0A, 0x73, 0x02, 0x18, 0x0B, + 0x57, 0x2C, 0x82, 0x13, 0x21, 0x00, 0x16, 0x4A, 0xAD, 0x1D, 0x8D, 0x13, 0x02, 0x01, 0x02, 0x0A, + 0x07, 0x88, 0x82, 0x52, 0x6D, 0x02, 0x0D, 0x0B, 0x42, 0x00, 0x00, 0x4A, 0x21, 0x00, 0x0C, 0x0A, + 0x07, 0x7D, 0xAC, 0x72, 0x41, 0x00, 0x01, 0x4A, 0xA2, 0x01, 0x10, 0x0A, 0xC7, 0x01, 0x07, 0x0B, + 0xE9, 0x53, 0x41, 0xB9, 0x21, 0x00, 0x02, 0x4A, 0x02, 0x02, 0x0C, 0x4A, 0xB3, 0x01, 0x0F, 0x0A, + 0xE7, 0x00, 0x17, 0x0B, 0x17, 0x2C, 0x80, 0x13, 0x42, 0x00, 0x13, 0x4A, 0x21, 0x1C, 0x81, 0x13, + 0xE0, 0x01, 0x00, 0x0A, 0x44, 0x2D, 0x87, 0x52, 0x31, 0x02, 0x01, 0x0B, 0x00, 0x00, 0x0C, 0x4A, + 0x41, 0x00, 0x08, 0x0A, 0x44, 0xB9, 0xAA, 0x72, 0x00, 0x00, 0x01, 0x4A, 0x21, 0x02, 0x0D, 0x0A, + 0x24, 0x01, 0x04, 0x0B, 0xE9, 0x57, 0x41, 0xB9, 0x00, 0x00, 0x01, 0x4A, 0xA1, 0x01, 0x08, 0x4A, + 0x22, 0x02, 0x10, 0x0A, 0x84, 0x00, 0x17, 0x0B, 0x97, 0x2D, 0x8C, 0x13, 0x21, 0x00, 0x02, 0x4A, + 0x00, 0x1C, 0x80, 0x13, 0x0C, 0x02, 0x0C, 0x0A, 0xD5, 0x16, 0x82, 0x52, 0xE0, 0x00, 0x00, 0x0B, + 0x8C, 0x01, 0x08, 0x4A, 0x21, 0x00, 0x0F, 0x0A, 0x75, 0x55, 0xA5, 0x72, 0x8C, 0x01, 0x01, 0x4A, + 0x01, 0x00, 0x11, 0x0A, 0x35, 0x01, 0x15, 0x0B, 0xE9, 0x63, 0x41, 0xB9, 0x8C, 0x01, 0x01, 0x4A, + 0x21, 0x02, 0x0F, 0x4A, 0x07, 0x00, 0x0D, 0x0A, 0xB5, 0x02, 0x17, 0x0B, 0x17, 0x2D, 0x88, 0x13, + 0x21, 0x00, 0x07, 0x4A, 0x8C, 0x1D, 0x8C, 0x13, 0xA8, 0x01, 0x08, 0x0A, 0x94, 0x86, 0x8B, 0x52, + 0x8C, 0x00, 0x0C, 0x0B, 0x08, 0x01, 0x0F, 0x4A, 0x21, 0x00, 0x10, 0x0A, 0x94, 0x99, 0xB6, 0x72, + 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x00, 0x0A, 0x34, 0x01, 0x14, 0x0B, 0xE9, 0x5B, 0x41, 0xB9, + 0x08, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x10, 0x4A, 0x84, 0x01, 0x11, 0x0A, 0x94, 0x02, 0x17, 0x0B, + 0xF7, 0x2D, 0x8F, 0x13, 0x21, 0x00, 0x04, 0x4A, 0x2F, 0x02, 0x0F, 0x0A, 0x08, 0x1D, 0x88, 0x13, + 0xC6, 0x19, 0x9D, 0x52, 0xEF, 0x01, 0x10, 0x4A, 0xA8, 0x02, 0x08, 0x0B, 0x21, 0x00, 0x0D, 0x0A, + 0x26, 0x28, 0xA2, 0x72, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x0C, 0x0A, 0x26, 0x01, 0x06, 0x0B, + 0xE9, 0xE3, 0x40, 0xB9, 0x95, 0x01, 0x0D, 0x4A, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x00, 0x0A, + 0xC6, 0x00, 0x17, 0x0B, 0x17, 0x2E, 0x90, 0x13, 0xA1, 0x02, 0x01, 0x4A, 0x10, 0x00, 0x10, 0x0A, + 0xEF, 0x1D, 0x8F, 0x13, 0xF6, 0xD5, 0x90, 0x52, 0x10, 0x02, 0x0D, 0x4A, 0x8F, 0x02, 0x0F, 0x0B, + 0x21, 0x00, 0x11, 0x0A, 0x96, 0x2A, 0xB4, 0x72, 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x08, 0x0A, + 0x36, 0x01, 0x16, 0x0B, 0x14, 0x01, 0x11, 0x4A, 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x0C, 0x0A, + 0xD6, 0x02, 0x17, 0x0B, 0xB7, 0x2D, 0x8D, 0x13, 0x81, 0x02, 0x01, 0x4A, 0x8D, 0x01, 0x0D, 0x0A, + 0x10, 0x1E, 0x90, 0x13, 0x73, 0x32, 0x9D, 0x52, 0xAD, 0x01, 0x11, 0x4A, 0xD0, 0x00, 0x10, 0x0B, + 0x21, 0x00, 0x00, 0x0A, 0x53, 0x8E, 0xAF, 0x72, 0xAD, 0x01, 0x01, 0x4A, 0x01, 0x02, 0x0F, 0x0A, + 0xB3, 0x00, 0x13, 0x0B, 0xE6, 0x01, 0x00, 0x4A, 0xAD, 0x01, 0x01, 0x4A, 0x01, 0x02, 0x08, 0x0A, + 0x73, 0x02, 0x17, 0x0B, 0x37, 0x2E, 0x91, 0x13, 0xE9, 0x5F, 0x41, 0xB9, 0xC1, 0x00, 0x01, 0x4A, + 0x11, 0x01, 0x11, 0x0A, 0xAD, 0x1D, 0x8D, 0x13, 0x22, 0x82, 0x82, 0x52, 0x31, 0x02, 0x00, 0x4A, + 0xCD, 0x02, 0x0D, 0x0B, 0x21, 0x00, 0x0C, 0x0A, 0xC2, 0x7D, 0xB6, 0x72, 0x47, 0x85, 0x97, 0x52, + 0x31, 0x02, 0x01, 0x4A, 0xA1, 0x01, 0x10, 0x0A, 0x62, 0x00, 0x02, 0x0B, 0xE7, 0x6D, 0xAC, 0x72, + 0x16, 0x02, 0x0C, 0x4A, 0x31, 0x02, 0x01, 0x4A, 0xA1, 0x01, 0x0F, 0x0A, 0x42, 0x00, 0x17, 0x0B, + 0x27, 0x01, 0x07, 0x0B, 0x17, 0x2C, 0x80, 0x13, 0xE9, 0x8B, 0x40, 0xB9, 0xC1, 0x02, 0x01, 0x4A, + 0xE0, 0x01, 0x00, 0x0A, 0x31, 0x1E, 0x91, 0x13, 0x00, 0x00, 0x0C, 0x4A, 0x71, 0x02, 0x11, 0x0B, + 0x21, 0x00, 0x08, 0x0A, 0xA4, 0xAB, 0x98, 0x52, 0x00, 0x00, 0x01, 0x4A, 0x21, 0x02, 0x0D, 0x0A, + 0x24, 0x75, 0xA5, 0x72, 0xB3, 0x01, 0x08, 0x4A, 0x00, 0x00, 0x01, 0x4A, 0x21, 0x02, 0x10, 0x0A, + 0xE7, 0x00, 0x17, 0x0B, 0x24, 0x01, 0x04, 0x0B, 0x97, 0x2D, 0x8C, 0x13, 0xE9, 0x6F, 0x41, 0xB9, + 0x61, 0x02, 0x01, 0x4A, 0x0C, 0x02, 0x0C, 0x0A, 0x00, 0x1C, 0x80, 0x13, 0x8C, 0x01, 0x08, 0x4A, + 0x40, 0x00, 0x00, 0x0B, 0x21, 0x00, 0x0F, 0x0A, 0x84, 0x00, 0x17, 0x0B, 0xD7, 0x3E, 0x86, 0x52, + 0x8C, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x11, 0x0A, 0x17, 0x83, 0xAE, 0x72, 0x22, 0x02, 0x0F, 0x4A, + 0x8C, 0x01, 0x01, 0x4A, 0x01, 0x00, 0x0D, 0x0A, 0x37, 0x01, 0x17, 0x0B, 0x15, 0x2D, 0x88, 0x13, + 0xE9, 0xB3, 0x40, 0xB9, 0x41, 0x00, 0x01, 0x4A, 0xA8, 0x01, 0x08, 0x0A, 0x8C, 0x1D, 0x8C, 0x13, + 0x08, 0x01, 0x0F, 0x4A, 0xEC, 0x00, 0x0C, 0x0B, 0x21, 0x00, 0x10, 0x0A, 0xF5, 0x02, 0x15, 0x0B, + 0xD7, 0xC2, 0x87, 0x52, 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x00, 0x0A, 0x97, 0xCB, 0xB9, 0x72, + 0x02, 0x00, 0x10, 0x4A, 0x08, 0x01, 0x01, 0x4A, 0x81, 0x01, 0x11, 0x0A, 0x37, 0x01, 0x17, 0x0B, + 0xF4, 0x2D, 0x8F, 0x13, 0xE9, 0xEB, 0x40, 0xB9, 0x41, 0x00, 0x01, 0x4A, 0x2F, 0x02, 0x0F, 0x0A, + 0x08, 0x1D, 0x88, 0x13, 0xEF, 0x01, 0x10, 0x4A, 0x88, 0x00, 0x08, 0x0B, 0x21, 0x00, 0x0D, 0x0A, + 0xF4, 0x02, 0x14, 0x0B, 0xD7, 0x63, 0x92, 0x52, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x0C, 0x0A, + 0xF7, 0x70, 0xB3, 0x72, 0x82, 0x01, 0x0D, 0x4A, 0xEF, 0x01, 0x01, 0x4A, 0x01, 0x01, 0x00, 0x0A, + 0xEE, 0x3F, 0x41, 0xB9, 0x37, 0x01, 0x17, 0x0B, 0x06, 0x2E, 0x90, 0x13, 0xE9, 0x1B, 0x41, 0xB9, + 0x41, 0x00, 0x01, 0x4A, 0x10, 0x00, 0x10, 0x0A, 0xEF, 0x1D, 0x8F, 0x13, 0x10, 0x02, 0x0D, 0x4A, + 0xAF, 0x02, 0x0F, 0x0B, 0x21, 0x00, 0x11, 0x0A, 0xE6, 0x02, 0x06, 0x0B, 0x77, 0x46, 0x97, 0x52, + 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x08, 0x0A, 0xD7, 0xFA, 0xB5, 0x72, 0x02, 0x01, 0x11, 0x4A, + 0x10, 0x02, 0x01, 0x4A, 0xE1, 0x01, 0x0C, 0x0A, 0xEE, 0x01, 0x0E, 0x0B, 0x37, 0x01, 0x17, 0x0B, + 0xB6, 0x2D, 0x8D, 0x13, 0xE9, 0x63, 0x40, 0xB9, 0x41, 0x00, 0x01, 0x4A, 0xEE, 0x8B, 0x04, 0xB9, + 0x8D, 0x01, 0x0D, 0x0A, 0x0E, 0x1E, 0x90, 0x13, 0xAD, 0x01, 0x11, 0x4A, 0x8E, 0x02, 0x0E, 0x0B, + 0x21, 0x00, 0x00, 0x0A, 0xAD, 0x01, 0x01, 0x4A, 0xC1, 0x01, 0x0F, 0x0A, 0x33, 0x2E, 0x91, 0x13, + 0xF0, 0x01, 0x00, 0x4A, 0xAD, 0x01, 0x01, 0x4A, 0xC1, 0x01, 0x08, 0x0A, 0x33, 0x01, 0x13, 0x0B, + 0x10, 0x02, 0x01, 0x4A, 0x11, 0x01, 0x11, 0x0A, 0xE9, 0x2B, 0x41, 0xB9, 0x31, 0x02, 0x00, 0x4A, + 0x10, 0x02, 0x0C, 0x0A, 0x30, 0x02, 0x10, 0x4A, 0xF1, 0x4B, 0x41, 0xB9, 0xE1, 0x47, 0x41, 0xB9, + 0xCB, 0x01, 0x09, 0x0B, 0xE9, 0x2F, 0x41, 0xB9, 0xAD, 0x1D, 0x8D, 0x13, 0xE2, 0x43, 0x41, 0xB9, + 0xCD, 0x00, 0x0D, 0x0B, 0x11, 0x00, 0x11, 0x0B, 0xE0, 0x01, 0x00, 0x0A, 0x81, 0x01, 0x01, 0x0B, + 0x00, 0x00, 0x0C, 0x4A, 0xCC, 0x01, 0x0C, 0x4A, 0xAE, 0x01, 0x0E, 0x0A, 0x0E, 0x02, 0x0E, 0x4A, + 0xAF, 0x01, 0x0F, 0x0A, 0xAA, 0x01, 0x09, 0x0B, 0xF6, 0x02, 0x16, 0x0B, 0x8C, 0x01, 0x0F, 0x4A, + 0xEA, 0x83, 0x04, 0xB9, 0xCA, 0x1D, 0x8E, 0x13, 0x02, 0x01, 0x02, 0x0B, 0x88, 0x01, 0x08, 0x0A, + 0xCA, 0x02, 0x0A, 0x0B, 0xFF, 0x9B, 0x00, 0xF9, 0xEB, 0x87, 0x04, 0xB9, 0x08, 0x00, 0x08, 0x4A, + 0x4B, 0x01, 0x0D, 0x0A, 0xE9, 0x13, 0x41, 0xB9, 0xEC, 0x9B, 0x40, 0xF9, 0x08, 0x01, 0x0B, 0x4A, + 0xEB, 0x4F, 0x41, 0xB9, 0x08, 0x1D, 0x88, 0x13, 0x49, 0x01, 0x09, 0x0B, 0x68, 0x02, 0x08, 0x0B, + 0xE2, 0x8F, 0x04, 0xB9, 0xE8, 0x7B, 0x04, 0xB9, 0xE9, 0x7F, 0x04, 0xB9, 0xE1, 0x93, 0x04, 0xB9, + 0xF1, 0x97, 0x04, 0xB9, 0xE8, 0xC3, 0x11, 0x91, 0xE9, 0x63, 0x0E, 0x91, 0x4A, 0x01, 0x80, 0x52, + 0x08, 0x01, 0x0C, 0x8B, 0x21, 0x41, 0x2B, 0x8B, 0x00, 0xA1, 0x02, 0x91, 0x42, 0x01, 0x0B, 0x4B, + 0xE8, 0x93, 0x44, 0xB9, 0xE9, 0x97, 0x44, 0xB9, 0xEA, 0x43, 0x47, 0x39, 0xEB, 0xEF, 0x40, 0xF9, + 0x0F, 0x11, 0x1B, 0x12, 0x2E, 0x15, 0x16, 0x12, 0x30, 0x11, 0x10, 0x12, 0x11, 0x15, 0x16, 0x12, + 0x0C, 0x7D, 0x1A, 0x53, 0x2D, 0x11, 0x1B, 0x12, 0xEE, 0x01, 0x0E, 0x2A, 0x2F, 0x11, 0x0B, 0x12, + 0x30, 0x02, 0x10, 0x2A, 0x11, 0x11, 0x10, 0x12, 0x2C, 0x11, 0x1A, 0x33, 0x29, 0x15, 0x06, 0x12, + 0x0D, 0x11, 0x00, 0x33, 0x08, 0x11, 0x0B, 0x12, 0x2F, 0x02, 0x0F, 0x2A, 0xF1, 0x7B, 0x44, 0xB9, + 0x09, 0x01, 0x09, 0x2A, 0x5F, 0x01, 0x00, 0x72, 0x48, 0xFD, 0x41, 0xD3, 0xEA, 0x83, 0x44, 0xB9, + 0x08, 0x01, 0x8B, 0x9A, 0x8B, 0x01, 0x11, 0x0B, 0xEC, 0x87, 0x44, 0xB9, 0x4A, 0x15, 0x4E, 0x0B, + 0xEE, 0x8B, 0x44, 0xB9, 0xE0, 0x7F, 0x44, 0xB9, 0x8C, 0x29, 0x50, 0x0B, 0xF0, 0x03, 0x50, 0x39, + 0xCE, 0x41, 0x4F, 0x0B, 0xEF, 0x07, 0x42, 0xF9, 0x0D, 0x00, 0x0D, 0x0B, 0x1F, 0x02, 0x00, 0x72, + 0x10, 0xFE, 0x41, 0xD3, 0x0F, 0x02, 0x8F, 0x9A, 0x70, 0x7D, 0x08, 0x53, 0xF1, 0x8F, 0x44, 0xB9, + 0xF0, 0xC7, 0x0E, 0x39, 0x70, 0x7D, 0x10, 0x53, 0xEB, 0xC3, 0x0E, 0x39, 0x6B, 0x7D, 0x18, 0x53, + 0xEB, 0xCF, 0x0E, 0x39, 0xAB, 0x7D, 0x08, 0x53, 0xED, 0xD3, 0x0E, 0x39, 0xEB, 0xD7, 0x0E, 0x39, + 0xAB, 0x7D, 0x10, 0x53, 0xAD, 0x7D, 0x18, 0x53, 0xEB, 0xDB, 0x0E, 0x39, 0x4B, 0x7D, 0x08, 0x53, + 0xED, 0xDF, 0x0E, 0x39, 0x4D, 0x7D, 0x10, 0x53, 0x29, 0x56, 0x49, 0x0B, 0xEC, 0xF3, 0x0E, 0x39, + 0xEB, 0xE7, 0x0E, 0x39, 0x8B, 0x7D, 0x08, 0x53, 0xED, 0xEB, 0x0E, 0x39, 0x8D, 0x7D, 0x10, 0x53, + 0x8C, 0x7D, 0x18, 0x53, 0xEA, 0xE3, 0x0E, 0x39, 0x4A, 0x7D, 0x18, 0x53, 0xE9, 0x13, 0x0F, 0x39, + 0xED, 0xFB, 0x0E, 0x39, 0x2D, 0x7D, 0x08, 0x53, 0xEC, 0xFF, 0x0E, 0x39, 0x2C, 0x7D, 0x10, 0x53, + 0x29, 0x7D, 0x18, 0x53, 0xEE, 0x03, 0x0F, 0x39, 0xEA, 0xEF, 0x0E, 0x39, 0xCA, 0x7D, 0x08, 0x53, + 0xEB, 0xF7, 0x0E, 0x39, 0xCB, 0x7D, 0x10, 0x53, 0xCE, 0x7D, 0x18, 0x53, 0xE9, 0x1F, 0x0F, 0x39, + 0xE9, 0x17, 0x9F, 0x1A, 0xF0, 0xCB, 0x0E, 0x39, 0xEA, 0x07, 0x0F, 0x39, 0xEB, 0x0B, 0x0F, 0x39, + 0xEE, 0x0F, 0x0F, 0x39, 0xED, 0x17, 0x0F, 0x39, 0xEC, 0x1B, 0x0F, 0x39, +]; + +static C1: OnceLock> = OnceLock::new(); +static C2: OnceLock> = OnceLock::new(); +static LOGIN_C31: OnceLock> = OnceLock::new(); +static LOGIN_C32: OnceLock> = OnceLock::new(); + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + tracing::info!("starting..."); + C1.set(read_hex("BIN_C1")).expect("failed to set C1"); + C2.set(read_hex("BIN_C2")).expect("failed to set C2"); + LOGIN_C31 + .set(read_hex("BIN_LOGIN_C31")) + .expect("failed to set C31"); + LOGIN_C32 + .set(read_hex("BIN_LOGIN_C32")) + .expect("failed to set C32"); + let service = Router::new() + .route("/healthz", get(healthz)) + .route("/readyz", get(readyz)) + .route("/generate", get(generate)); + let addr = TcpListener::bind("0.0.0.0:8080") + .await + .expect("failed to bind 0.0.0.0:8080"); + tracing::info!("listening in 0.0.0.0:8080..."); + axum::serve(addr, service) + .await + .unwrap_or_else(|e| panic!("{e:?}")); +} + +fn read_hex(var: &str) -> Vec { + hex::decode(env::var(var).unwrap_or_else(|_| panic!("var: {var} not declared"))) + .unwrap_or_else(|_| panic!("failed to decode {var}")) +} + +#[derive(Debug, Deserialize)] +struct GenerateQuery { + timestamp: String, + path: String, + kind: String, +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +enum GenerateResult { + Success { success: bool, value: String }, + Failed { success: bool, message: String }, +} + +async fn generate(Query(query): Query) -> impl IntoResponse { + tracing::info!("Generated from: {query:?}"); + let (c31, c32): (&[u8], &[u8]) = match query.kind.as_str() { + "login" => ( + LOGIN_C31 + .get() + .expect("C31 is not initialized, this should not happen"), + LOGIN_C32 + .get() + .expect("C32 is not initialized, this should not happen"), + ), + _ => { + return ( + StatusCode::BAD_REQUEST, + Json(GenerateResult::Failed { + success: false, + message: format!("no kind named \"{}\"", query.kind), + }), + ); + } + }; + let Ok(timestamp) = query.timestamp.parse::() else { + return ( + StatusCode::BAD_REQUEST, + Json(GenerateResult::Failed { + success: false, + message: "timestamp is not a valid number".into(), + }), + ); + }; + let result = match chilo::generate( + &BIN, + C1.get() + .expect("C1 is not initialized, this should not happen"), + C2.get() + .expect("C2 is not initialized, this should not happen"), + c31, + c32, + timestamp, + query.path.as_bytes(), + ) { + Ok(result) => result, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(GenerateResult::Failed { + success: false, + message: format!("failed to generate value: {e}"), + }), + ); + } + }; + let base64 = base64::prelude::BASE64_STANDARD.encode(result); + ( + StatusCode::OK, + Json(GenerateResult::Success { + success: true, + value: base64, + }), + ) +} + +const fn check_ready() -> Option { + None +} + +async fn readyz() -> impl IntoResponse { + check_ready().map_or_else( + || (StatusCode::OK, Json(json!({"status":"ready"}))), + |e| { + ( + StatusCode::SERVICE_UNAVAILABLE, + Json(json!({"status": "not ready", "reason": e})), + ) + }, + ) +} + +async fn healthz() -> impl IntoResponse { + (StatusCode::OK, Json(json!({"health":"ok"}))) +} diff --git a/cuscuta-common/src/db/log/event.rs b/cuscuta-common/src/db/log/event.rs index caebaf9..a236c57 100644 --- a/cuscuta-common/src/db/log/event.rs +++ b/cuscuta-common/src/db/log/event.rs @@ -68,12 +68,11 @@ pub fn read_events( macro_rules! try_write_event { ($redis_client:expr, $worker_id:expr, $event_type:expr, $message:expr) => {{ use cuscuta_common::db::log::event::write_event; - use log; if let Some(redis_client) = $redis_client.get() && let Some(worker_id) = $worker_id.get() && let Err(ie) = write_event(redis_client, $event_type, worker_id.to_string(), $message) { - log::warn!("failed to write event: {ie}"); + tracing::warn!("failed to write event: {ie}"); } }}; } diff --git a/cuscuta-entry/Cargo.toml b/cuscuta-entry/Cargo.toml index 7cf86be..947cf91 100644 --- a/cuscuta-entry/Cargo.toml +++ b/cuscuta-entry/Cargo.toml @@ -1,26 +1,28 @@ -[package] -name = "cuscuta-entry" -version.workspace = true -edition = "2024" - -[dependencies] -cuscuta-common = { path = "../cuscuta-common" } -anyhow = { workspace = true } -axum = { workspace = true } -base64 = { workspace = true } -chrono = { workspace = true } -env_logger = { workspace = true } -itertools = "0.14.0" -log = { workspace = true } -rand = { workspace = true } -redis = { workspace = true } -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -sqlx = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true, features = ["signal"] } -tokio-util = { workspace = true } - -[lints] -workspace = true +[package] +name = "cuscuta-entry" +version.workspace = true +edition = "2024" + +[dependencies] +cuscuta-common = { path = "../cuscuta-common" } +anyhow = { workspace = true } +axum = { workspace = true } +base64 = { workspace = true } +chrono = { workspace = true } +tracing-subscriber = { workspace = true } +itertools = "0.14.0" +tracing = { workspace = true } +rand = { workspace = true } +redis = { workspace = true } +reqwest = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sqlx = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["signal"] } +tokio-util = { workspace = true } +tower = "0.5.3" +tower-http = { version = "0.7.0", features = ["trace"] } + +[lints] +workspace = true diff --git a/cuscuta-entry/src/endpoints/enqueue.rs b/cuscuta-entry/src/endpoints/enqueue.rs index ea84368..6dd8b0c 100644 --- a/cuscuta-entry/src/endpoints/enqueue.rs +++ b/cuscuta-entry/src/endpoints/enqueue.rs @@ -53,7 +53,7 @@ pub async fn enqueue(Form(form): Form) -> impl IntoResponse { }), ), Err(e) => { - log::warn!("endpoint enqueue failed: {e}"); + tracing::warn!("endpoint enqueue failed: {e}"); ( e.get_status_code(), Json(EnqueueResult::Failed { diff --git a/cuscuta-entry/src/endpoints/mod.rs b/cuscuta-entry/src/endpoints/mod.rs index bbb0947..9f73bd2 100644 --- a/cuscuta-entry/src/endpoints/mod.rs +++ b/cuscuta-entry/src/endpoints/mod.rs @@ -16,8 +16,7 @@ enum ErrorType { FailedScanRedis = -301, FailedEnqueueRedis = -302, - FailedReadEtaRedis = -303, - + // FailedReadEtaRedis = -303, InternalNoWorker = -400, BadRequestBase64 = -500, @@ -75,22 +74,22 @@ impl Error { } } -#[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss -)] -pub fn round_fixed(v: f64, n: u32) -> f64 { - let i = 10_usize.pow(n) as f64; - let x = v * i; - if v > 0_f64 { - f64::from(x.round() as u32) / i - } else { - let mr = x.trunc(); - let mf = x.fract(); - if mf.abs() >= 0.5 { - return (mr + 1_f64) / i; - } - mr / i - } -} +// #[allow( +// clippy::cast_precision_loss, +// clippy::cast_possible_truncation, +// clippy::cast_sign_loss +// )] +// pub fn round_fixed(v: f64, n: u32) -> f64 { +// let i = 10_usize.pow(n) as f64; +// let x = v * i; +// if v > 0_f64 { +// f64::from(x.round() as u32) / i +// } else { +// let mr = x.trunc(); +// let mf = x.fract(); +// if mf.abs() >= 0.5 { +// return (mr + 1_f64) / i; +// } +// mr / i +// } +// } diff --git a/cuscuta-entry/src/endpoints/query.rs b/cuscuta-entry/src/endpoints/query.rs index 317148f..ca13832 100644 --- a/cuscuta-entry/src/endpoints/query.rs +++ b/cuscuta-entry/src/endpoints/query.rs @@ -1,13 +1,10 @@ -use std::env; - use axum::{Json, extract::Query, response::IntoResponse}; use base64::Engine; use cuscuta_common::{ api::xxxxxx::{FriendInfo, SongScore}, db::{ job::{ - eta::fetch_unit_eta, - fetch::{SearchPositionResult, fetch_result, search_position}, + fetch::fetch_result, track::{JobTrackQueueStatus, JobTrackTag, fetch_all_job_track_tag}, }, redis::{job_result_friend_info_redis_key, job_result_value_redis_key}, @@ -18,8 +15,8 @@ use reqwest::StatusCode; use serde::{Deserialize, Serialize}; use crate::{ - db::{account::count_active_account, postgresql::try_open_transaction, redis::REDIS_CLIENT}, - endpoints::{Error, ErrorType, round_fixed}, + db::redis::REDIS_CLIENT, + endpoints::{Error, ErrorType}, }; #[derive(Debug, Serialize)] @@ -54,7 +51,7 @@ pub struct QueryQuery { } pub async fn query(Query(query): Query) -> impl IntoResponse { - async fn op(query: QueryQuery) -> Result { + fn op(query: QueryQuery) -> Result { let redis_client = REDIS_CLIENT .get() .ok_or(Error::NotReady(ErrorType::RedisNotReady))?; @@ -64,11 +61,11 @@ pub async fn query(Query(query): Query) -> impl IntoResponse { .map_err(|_| Error::BadRequest(ErrorType::BadRequestBase64))?, ) .map_err(|_| Error::BadRequest(ErrorType::BadRequestBase64))?; - let enable_eta = env::var("ETA_ENABLE") - .unwrap_or_else(|_| "false".to_string()) - .parse::() - .unwrap_or(false); - let (evidence_check_result, temp_track_tags) = check_evidence(redis_client, &token); + // let enable_eta = env::var("ETA_ENABLE") + // .unwrap_or_else(|_| "false".to_string()) + // .parse::() + // .unwrap_or(false); + let (evidence_check_result, _temp_track_tags) = check_evidence(redis_client, &token); let friend_info = fetch_result::(redis_client, &job_result_friend_info_redis_key(&token)) .map_or(None, |it| it.into_iter().next()); @@ -80,23 +77,23 @@ pub async fn query(Query(query): Query) -> impl IntoResponse { queueing_jobs, } => { // TODO: 这个设计只能全量查询,若需增量查询,需要改动 - let eta = if let Some(job_track_tags) = temp_track_tags - && enable_eta - { - match calc_eta_millis(redis_client, &token, &job_track_tags).await { - Ok(v) => v.map(|it| round_fixed(it / 1000.0, 2)), - Err(e) => { - log::warn!("eta: failed to calc eta: {e}"); - None - } - } - } else { - None - }; + // let eta = if let Some(job_track_tags) = temp_track_tags + // && enable_eta + // { + // match calc_eta_millis(redis_client, &token, &job_track_tags).await { + // Ok(v) => v.map(|it| round_fixed(it / 1000.0, 2)), + // Err(e) => { + // tracing::warn!("eta: failed to calc eta: {e}"); + // None + // } + // } + // } else { + // None + // }; Ok(QueryResult::SuccessPending { success: true, pending: true, - eta, + eta: Some(0.0), friend_info, total_jobs, finished_jobs, @@ -125,7 +122,7 @@ pub async fn query(Query(query): Query) -> impl IntoResponse { } } - match op(query).await { + match op(query) { Ok(token) => { let status_code = match &token { QueryResult::SuccessFinished { .. } => StatusCode::OK, @@ -135,7 +132,7 @@ pub async fn query(Query(query): Query) -> impl IntoResponse { (status_code, Json(token)) } Err(e) => { - log::warn!("endpoint enqueue failed: {e}"); + tracing::warn!("endpoint enqueue failed: {e}"); ( e.get_status_code(), Json(QueryResult::Failed { @@ -229,82 +226,83 @@ fn check_evidence( ) } -#[allow(clippy::cast_precision_loss)] -async fn calc_eta_millis( - redis_client: &Client, - _postfix: &str, - job_track_tags: &[JobTrackTag], -) -> Result, Error> { - let transaction = try_open_transaction() - .await - .map_err(|e| Error::DbExtend(ErrorType::FailedTransactionOpenDb, e))?; - let active_account_count = count_active_account(transaction) - .await - .map_err(|e| Error::Db(ErrorType::FailedCountDb, e))?; - let eta_record_trim = env::var("ETA_RECORD_TRIM") - .map_err(|_| ()) - .and_then(|it| it.parse::().map_err(|_| ())) - .unwrap_or(15); - let eta_search_limit = env::var("ETA_SEARCH_LIMIT") - .map_err(|_| ()) - .and_then(|it| it.parse::().map_err(|_| ())) - .unwrap_or(10); - let Some(estimated_unit_eta) = fetch_unit_eta(redis_client, eta_record_trim) - .map_err(|e| Error::RedisExtend(ErrorType::FailedReadEtaRedis, e))? - else { - return Ok(None); - }; - let avg_job_eta = job_track_tags - .iter() - .map(|it| it.queue.segment.len()) - .sum::() as f64 - / job_track_tags.len() as f64; - let positions = job_track_tags - .iter() - .filter_map(|it| { - let x = search_position( - redis_client, - eta_search_limit, - &it.job_essential.job_uid, - &it.queue.name, - ); - if let Err(e) = &x { - log::warn!("eta_debug: failed to search_position: {e}"); - } - x.ok() - }) - .filter(|it| it != &SearchPositionResult::QueueingNotFound) - .collect::>(); - if positions.is_empty() { - return Ok(None); - } - if positions - .iter() - .all(|it| it == &SearchPositionResult::Pending) - { - return Ok(Some(estimated_unit_eta * avg_job_eta)); - } - let max_found = positions - .iter() - .filter_map(|it| match it { - SearchPositionResult::QueueingFound(x) => Some(x), - _ => None, - }) - .max() - .copied(); - let found_counts = positions - .iter() - .filter(|it| matches!(it, SearchPositionResult::QueueingFound(_))) - .count(); - let estimated_multiply = ((found_counts as f64) / (active_account_count as f64)) - .ceil() - .max(1.0); - Ok(max_found.map(|it| { - let add = usize::from( - positions - .iter() - .any(|it| it == &SearchPositionResult::Pending), - ); - estimated_unit_eta * (it + 1 + add) as f64 * avg_job_eta * estimated_multiply - })) -} +// TODO: Deprecated, will be removed soon +// #[allow(clippy::cast_precision_loss)] +// async fn calc_eta_millis( +// redis_client: &Client, +// _postfix: &str, +// job_track_tags: &[JobTrackTag], +// ) -> Result, Error> { +// let transaction = try_open_transaction() +// .await +// .map_err(|e| Error::DbExtend(ErrorType::FailedTransactionOpenDb, e))?; +// let active_account_count = count_active_account(transaction) +// .await +// .map_err(|e| Error::Db(ErrorType::FailedCountDb, e))?; +// let eta_record_trim = env::var("ETA_RECORD_TRIM") +// .map_err(|_| ()) +// .and_then(|it| it.parse::().map_err(|_| ())) +// .unwrap_or(15); +// let eta_search_limit = env::var("ETA_SEARCH_LIMIT") +// .map_err(|_| ()) +// .and_then(|it| it.parse::().map_err(|_| ())) +// .unwrap_or(10); +// let Some(estimated_unit_eta) = fetch_unit_eta(redis_client, eta_record_trim) +// .map_err(|e| Error::RedisExtend(ErrorType::FailedReadEtaRedis, e))? +// else { +// return Ok(None); +// }; +// let avg_job_eta = job_track_tags +// .iter() +// .map(|it| it.queue.segment.len()) +// .sum::() as f64 +// / job_track_tags.len() as f64; +// let positions = job_track_tags +// .iter() +// .filter_map(|it| { +// let x = search_position( +// redis_client, +// eta_search_limit, +// &it.job_essential.job_uid, +// &it.queue.name, +// ); +// if let Err(e) = &x { +// tracing::warn!("eta_debug: failed to search_position: {e}"); +// } +// x.ok() +// }) +// .filter(|it| it != &SearchPositionResult::QueueingNotFound) +// .collect::>(); +// if positions.is_empty() { +// return Ok(None); +// } +// if positions +// .iter() +// .all(|it| it == &SearchPositionResult::Pending) +// { +// return Ok(Some(estimated_unit_eta * avg_job_eta)); +// } +// let max_found = positions +// .iter() +// .filter_map(|it| match it { +// SearchPositionResult::QueueingFound(x) => Some(x), +// _ => None, +// }) +// .max() +// .copied(); +// let found_counts = positions +// .iter() +// .filter(|it| matches!(it, SearchPositionResult::QueueingFound(_))) +// .count(); +// let estimated_multiply = ((found_counts as f64) / (active_account_count as f64)) +// .ceil() +// .max(1.0); +// Ok(max_found.map(|it| { +// let add = usize::from( +// positions +// .iter() +// .any(|it| it == &SearchPositionResult::Pending), +// ); +// estimated_unit_eta * (it + 1 + add) as f64 * avg_job_eta * estimated_multiply +// })) +// } diff --git a/cuscuta-entry/src/loop_tasks.rs b/cuscuta-entry/src/loop_tasks.rs index 88d2417..804d6e6 100644 --- a/cuscuta-entry/src/loop_tasks.rs +++ b/cuscuta-entry/src/loop_tasks.rs @@ -34,15 +34,15 @@ pub async fn sync_config(_: &CancellationToken) { Ok(()) } if CONFIG.is_initialized() { - log::trace!("config sync"); + tracing::trace!("config sync"); return; } - log::info!("sync_config: trying sync config"); + tracing::info!("sync_config: trying sync config"); if let Err(e) = try_sync() { - log::error!("sync_config: failed to sync config: {e}"); + tracing::error!("sync_config: failed to sync config: {e}"); return; } - log::info!("sync_config: config initialized"); + tracing::info!("sync_config: config initialized"); } pub async fn sync_bundle_data(_: &CancellationToken) { @@ -63,20 +63,20 @@ pub async fn sync_bundle_data(_: &CancellationToken) { Ok(bundle_data) } if BUNDLE_DATA.is_initialized() { - log::trace!("bundle data sync"); + tracing::trace!("bundle data sync"); return; } - log::info!("sync_bundle_data: trying sync bundle data"); + tracing::info!("sync_bundle_data: trying sync bundle data"); match try_sync().await { Ok(bundle_data) => { - log::info!( + tracing::info!( "sync_bundle_data: bundle data initialized: appVer:{}, ver:{}", bundle_data.application_version_number, bundle_data.version_number ); } Err(e) => { - log::error!("sync_bundle_data: failed to sync bundle data: {e}"); + tracing::error!("sync_bundle_data: failed to sync bundle data: {e}"); } } } @@ -103,22 +103,22 @@ pub async fn sync_song_list(_: &CancellationToken) { Ok((music_len, chart_len)) } if SONG_LIST.is_initialized() { - log::trace!("song list sync"); + tracing::trace!("song list sync"); return; } - log::info!("sync_song_list: trying sync song list"); + tracing::info!("sync_song_list: trying sync song list"); match try_sync().await { - Ok((music_len, chart_len)) => log::info!( + Ok((music_len, chart_len)) => tracing::info!( "sync_song_list: song list initialized (music:{music_len}, charts:{chart_len})" ), - Err(e) => log::error!("sync_song_list: failed to sync song list: {e}"), + Err(e) => tracing::error!("sync_song_list: failed to sync song list: {e}"), } } pub async fn open_redis_client(_: &CancellationToken) { fn try_connect() -> Result<(), String> { let addr = env::var("REDIS_ADDR").map_err(|_| "failed to read env: REDIS_ADDR")?; - log::debug!("redis_open: redis: {addr}"); + tracing::debug!("redis_open: redis: {addr}"); let redis = redis::Client::open(addr) .map_err(|e| format!("failed to open redis client(phase 1): {e}"))?; let mut con = redis @@ -134,19 +134,19 @@ pub async fn open_redis_client(_: &CancellationToken) { if REDIS_CLIENT.get().is_some() { return; } - log::debug!("redis_open: trying to connect to redis server..."); + tracing::debug!("redis_open: trying to connect to redis server..."); if let Err(e) = try_connect() { - log::error!("redis_open: failed to connect to redis server: {e}"); + tracing::error!("redis_open: failed to connect to redis server: {e}"); return; } - log::info!("redis_open: redis client created successfully"); + tracing::info!("redis_open: redis client created successfully"); } pub async fn open_postgresql_client(_: &CancellationToken) { async fn try_connect() -> Result<(), String> { let addr = env::var("ACCOUNTS_SQL_ADDR") .map_err(|e| format!("failed to read ACCOUNTS_SQL_ADDR: {e}"))?; - log::debug!("postgresql_open: {addr}"); + tracing::debug!("postgresql_open: {addr}"); let x = PgPoolOptions::new() .max_connections(5) .connect(addr.as_str()) @@ -160,10 +160,10 @@ pub async fn open_postgresql_client(_: &CancellationToken) { if POSTGRESQL_POOL.is_initialized() { return; } - log::debug!("postgresql_open: trying to connect to postgresql server..."); + tracing::debug!("postgresql_open: trying to connect to postgresql server..."); if let Err(e) = try_connect().await { - log::error!("postgresql_open: failed to connect to postgresql server: {e}"); + tracing::error!("postgresql_open: failed to connect to postgresql server: {e}"); return; } - log::info!("postgresql_open: postgresql pool created successfully"); + tracing::info!("postgresql_open: postgresql pool created successfully"); } diff --git a/cuscuta-entry/src/main.rs b/cuscuta-entry/src/main.rs index eb0ee9b..17f0c08 100644 --- a/cuscuta-entry/src/main.rs +++ b/cuscuta-entry/src/main.rs @@ -1,132 +1,136 @@ -//! cuscuta的entry -//! -//! # 依赖环境变量 -//! cuscuta-entry使用环境变量注入参数,这个crate依赖的环境变量有: -//! - `REDIS_STREAM_REFRESH_TTL` -//! - `GITHUB_BUNDLE_REPOSITORY` -//! - `GITHUB_BUNDLE_PATH` -//! - `GITHUB_BUNDLE_TOKEN` -//! - `GITHUB_SONG_REPOSITORY` -//! - `GITHUB_SONG_PATH` -//! - `GITHUB_SONG_TOKEN` -//! - `REDIS_ADDR` -//! - `ACCOUNTS_SQL_ADDR` - -mod data; -mod db; -mod endpoints; -mod init; -mod loop_tasks; - -use crate::{ - data::{BUNDLE_DATA, CONFIG, SONG_LIST}, - db::{postgresql::POSTGRESQL_POOL, redis::REDIS_CLIENT}, - endpoints::query::query, - enqueue::enqueue, - loop_tasks::sync_config, -}; -use cuscuta_common::quick_fetch::QuickFetch; - -use axum::{ - Json, Router, - response::IntoResponse, - routing::{get, post}, -}; -use cuscuta_common::{ - batch_check_initialized, - scheduled_job::{register_individual_job, register_job}, -}; -use reqwest::StatusCode; -use serde_json::json; -use tokio::net::TcpListener; -use tokio_util::sync::CancellationToken; - -use crate::{ - endpoints::enqueue, - init::cuscuta_init, - loop_tasks::{open_postgresql_client, open_redis_client, sync_bundle_data, sync_song_list}, -}; - -#[tokio::main] -async fn main() { - env_logger::init(); - log::info!("starting..."); - let halt_token = CancellationToken::new(); - let service = Router::new() - .route("/healthz", get(healthz)) - .route("/readyz", get(readyz)) - .route("/v1/enqueue", post(enqueue)) - .route("/v1/query", get(query)); - let addr = TcpListener::bind("0.0.0.0:8081") - .await - .expect("failed to bind 0.0.0.0:8081"); - log::info!("listening in 0.0.0.0:8081..."); - tokio::spawn(register_individual_job( - halt_token.clone(), - CancellationToken::new(), - 10, - cuscuta_init, - )); - tokio::spawn(register_job(halt_token.clone(), 10, open_redis_client)); - tokio::spawn(register_job(halt_token.clone(), 10, open_postgresql_client)); - tokio::spawn(register_job(halt_token.clone(), 10, sync_bundle_data)); - tokio::spawn(register_job(halt_token.clone(), 10, sync_song_list)); - tokio::spawn(register_job(halt_token.clone(), 10, sync_config)); - axum::serve(addr, service) - .with_graceful_shutdown(shutdown_signal(halt_token)) - .await - .unwrap_or_else(|e| panic!("{e:?}")); -} - -fn check_ready() -> Option<&'static str> { - if REDIS_CLIENT.get().is_none() { - return Some("redis client is not initialized"); - } - batch_check_initialized!(CONFIG, BUNDLE_DATA, SONG_LIST, POSTGRESQL_POOL); - None -} - -async fn readyz() -> impl IntoResponse { - check_ready().map_or_else( - || (StatusCode::OK, Json(json!({"status":"ready"}))), - |e| { - ( - StatusCode::SERVICE_UNAVAILABLE, - Json(json!({"status": "not ready", "reason": e})), - ) - }, - ) -} - -async fn healthz() -> impl IntoResponse { - (StatusCode::OK, Json(json!({"health":"ok"}))) -} - -#[allow(clippy::ignored_unit_patterns)] -async fn shutdown_signal(cancellation_token: CancellationToken) { - #[cfg(target_os = "linux")] - { - use tokio::signal::{ - self, - unix::{SignalKind, signal}, - }; - let mut sigterm = - signal(SignalKind::terminate()).expect("failed to install SIGTERM handler"); - tokio::select! { - _ = signal::ctrl_c() => {}, - _ = sigterm.recv() => {}, - _ = cancellation_token.cancelled() => {}, - } - } - #[cfg(target_os = "windows")] - { - use tokio::signal; - log::info!("咱其实挺想知道谁会在Windows上跑这个的……"); - tokio::select! { - _ = signal::ctrl_c() => {}, - _ = cancellation_token.cancelled() => {}, - } - } - log::error!("cuscuta-entry halting..."); - cancellation_token.cancel(); -} +//! cuscuta的entry +//! +//! # 依赖环境变量 +//! cuscuta-entry使用环境变量注入参数,这个crate依赖的环境变量有: +//! - `REDIS_STREAM_REFRESH_TTL` +//! - `GITHUB_BUNDLE_REPOSITORY` +//! - `GITHUB_BUNDLE_PATH` +//! - `GITHUB_BUNDLE_TOKEN` +//! - `GITHUB_SONG_REPOSITORY` +//! - `GITHUB_SONG_PATH` +//! - `GITHUB_SONG_TOKEN` +//! - `REDIS_ADDR` +//! - `ACCOUNTS_SQL_ADDR` + +mod data; +mod db; +mod endpoints; +mod init; +mod loop_tasks; + +use crate::{ + data::{BUNDLE_DATA, CONFIG, SONG_LIST}, + db::{postgresql::POSTGRESQL_POOL, redis::REDIS_CLIENT}, + endpoints::query::query, + enqueue::enqueue, + loop_tasks::sync_config, +}; +use cuscuta_common::quick_fetch::QuickFetch; + +use axum::{ + Json, Router, + response::IntoResponse, + routing::{get, post}, +}; +use cuscuta_common::{ + batch_check_initialized, + scheduled_job::{register_individual_job, register_job}, +}; +use reqwest::StatusCode; +use serde_json::json; +use tokio::net::TcpListener; +use tokio_util::sync::CancellationToken; +use tower_http::trace::TraceLayer; + +use crate::{ + endpoints::enqueue, + init::cuscuta_init, + loop_tasks::{open_postgresql_client, open_redis_client, sync_bundle_data, sync_song_list}, +}; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + tracing::info!("starting..."); + let halt_token = CancellationToken::new(); + let service = Router::new() + .route("/healthz", get(healthz)) + .route("/readyz", get(readyz)) + .route("/v1/enqueue", post(enqueue)) + .route("/v1/query", get(query)) + .layer(TraceLayer::new_for_http()); + let addr = TcpListener::bind("0.0.0.0:8081") + .await + .expect("failed to bind 0.0.0.0:8081"); + tracing::info!("listening in 0.0.0.0:8081..."); + tokio::spawn(register_individual_job( + halt_token.clone(), + CancellationToken::new(), + 10, + cuscuta_init, + )); + tokio::spawn(register_job(halt_token.clone(), 10, open_redis_client)); + tokio::spawn(register_job(halt_token.clone(), 10, open_postgresql_client)); + tokio::spawn(register_job(halt_token.clone(), 10, sync_bundle_data)); + tokio::spawn(register_job(halt_token.clone(), 10, sync_song_list)); + tokio::spawn(register_job(halt_token.clone(), 10, sync_config)); + axum::serve(addr, service) + .with_graceful_shutdown(shutdown_signal(halt_token)) + .await + .unwrap_or_else(|e| panic!("{e:?}")); +} + +fn check_ready() -> Option<&'static str> { + if REDIS_CLIENT.get().is_none() { + return Some("redis client is not initialized"); + } + batch_check_initialized!(CONFIG, BUNDLE_DATA, SONG_LIST, POSTGRESQL_POOL); + None +} + +async fn readyz() -> impl IntoResponse { + check_ready().map_or_else( + || (StatusCode::OK, Json(json!({"status":"ready"}))), + |e| { + ( + StatusCode::SERVICE_UNAVAILABLE, + Json(json!({"status": "not ready", "reason": e})), + ) + }, + ) +} + +async fn healthz() -> impl IntoResponse { + (StatusCode::OK, Json(json!({"health":"ok"}))) +} + +#[allow(clippy::ignored_unit_patterns)] +async fn shutdown_signal(cancellation_token: CancellationToken) { + #[cfg(target_os = "linux")] + { + use tokio::signal::{ + self, + unix::{SignalKind, signal}, + }; + let mut sigterm = + signal(SignalKind::terminate()).expect("failed to install SIGTERM handler"); + tokio::select! { + _ = signal::ctrl_c() => {}, + _ = sigterm.recv() => {}, + _ = cancellation_token.cancelled() => {}, + } + } + #[cfg(target_os = "windows")] + { + use tokio::signal; + tracing::info!("咱其实挺想知道谁会在Windows上跑这个的……"); + tokio::select! { + _ = signal::ctrl_c() => {}, + _ = cancellation_token.cancelled() => {}, + } + } + tracing::error!("cuscuta-entry halting..."); + cancellation_token.cancel(); +} diff --git a/cuscuta-mock/Cargo.toml b/cuscuta-mock/Cargo.toml index f8dc691..c7af9e0 100644 --- a/cuscuta-mock/Cargo.toml +++ b/cuscuta-mock/Cargo.toml @@ -8,8 +8,8 @@ cuscuta-common = { path = "../cuscuta-common" } axum = { workspace = true } base64 = { workspace = true } chrono = { workspace = true } -env_logger = { workspace = true } -log = { workspace = true } +tracing-subscriber = { workspace = true } +tracing = { workspace = true } rand = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/cuscuta-mock/src/main.rs b/cuscuta-mock/src/main.rs index 0497865..8a6cdea 100644 --- a/cuscuta-mock/src/main.rs +++ b/cuscuta-mock/src/main.rs @@ -1,456 +1,458 @@ -//! cuscuta的mock应用 -//! -//! 出于某些历史原因,它并不依赖`cuscuta-common` - -mod data; - -use std::{ - collections::HashMap, - sync::{OnceLock, RwLock}, -}; - -use axum::{ - Form, Json, Router, - extract::Query, - http::{HeaderMap, StatusCode, header}, - response::IntoResponse, - routing::{get, post}, -}; -use base64::Engine; -use chrono::Utc; -use cuscuta_common::api::xxxxxx::FriendInfo; -use rand::{RngExt, seq::SliceRandom}; -use serde_json::json; -use tokio::net::TcpListener; - -use crate::data::{ - FriendAddForm, FriendModifyResult, FriendModifyResultFailed, FriendModifyResultSuccess, - FriendRemoveForm, FriendsResult, LoginResult, LoginResultFailed, LoginResultSuccess, - RankListFetchResult, RankListFetchResultFailed, RankListFetchResultSuccess, RankListQuery, - RankListResult, check_env_bool, -}; - -static FRIEND_INFO: OnceLock>>> = OnceLock::new(); -static VALID_ACCOUNT: &[(&str, &str, &str)] = &[ - // username, password, user_id - ("123456", "123456", "123456"), - ("234567", "234567", "234567"), - ("345678", "345678", "345678"), - ("456789", "456789", "456789"), - ("nofyso", "Chensing", "114514"), -]; - -#[tokio::main] -async fn main() { - env_logger::init(); - log::info!("initializing mocker"); - initialize_friends(); - let router = Router::new() - .route("/", get(hello)) - .route("/healthz", get(healthz)) - .route("/auth/login", post(login)) - .route("/friend/me", get(list_friend)) - .route("/friend/me/add", post(add_friend)) - .route("/friend/me/delete", post(remove_friend)) - .route("/score/song/friend", get(get_rank_list)); - let addr = TcpListener::bind("0.0.0.0:8080") - .await - .expect("failed to bind 0.0.0.0:8080"); - log::info!("listening in 0.0.0.0:8080..."); - axum::serve(addr, router).await.unwrap(); -} - -async fn hello() -> impl IntoResponse { - (StatusCode::OK, "Hello") -} - -fn initialize_friends() { - let mut x = FRIEND_INFO - .get_or_init(|| RwLock::new(HashMap::new())) - .try_write() - .unwrap(); - for it in VALID_ACCOUNT { - x.insert( - it.2.to_string(), - vec![FriendInfo { - name: "nofyso".into(), - user_id: 9999999, - rating: 10, - character: 0, - is_char_uncapped: true, - is_char_uncapped_override: true, - }], - ); - } -} - -fn clone_internal_friends() -> HashMap> { - FRIEND_INFO - .get_or_init(|| RwLock::new(HashMap::new())) - .try_read() - .unwrap() - .clone() -} - -fn internal_read_friends(id: String) -> FriendModifyResult { - let x = FRIEND_INFO - .get_or_init(|| RwLock::new(HashMap::new())) - .try_read() - .unwrap(); - FriendModifyResult::Success(FriendModifyResultSuccess { - success: true, - value: FriendsResult { - friends: x.get(&id).unwrap().clone(), - }, - }) -} - -fn internal_write_friends(id: String, mut f: F) -> FriendModifyResult -where - F: FnMut(&mut HashMap>) -> Option, -{ - let mut x = FRIEND_INFO - .get_or_init(|| RwLock::new(HashMap::new())) - .try_write() - .unwrap(); - let failed = f(&mut x); - if let Some(failed) = failed { - return failed; - } - FriendModifyResult::Success(FriendModifyResultSuccess { - success: true, - value: FriendsResult { - friends: x.get(&id).unwrap().clone(), - }, - }) -} - -async fn login(headers: HeaderMap) -> impl IntoResponse { - let always_fail = check_env_bool("CUSCUTA_LOGIN_ALWAYS_FAIL"); - let auth_header = headers.get(header::AUTHORIZATION).unwrap(); - let auth_header = &auth_header.to_str().unwrap()["Basic ".len()..]; - let auth_header = base64::prelude::BASE64_STANDARD - .decode(auth_header) - .unwrap(); - let auth_header_value = str::from_utf8(&auth_header).unwrap(); - let auth_header_value: Vec<_> = auth_header_value.split(':').collect(); - let value = (auth_header_value[0], auth_header_value[1]); - let account = if always_fail { - None - } else { - VALID_ACCOUNT - .iter() - .find(|it| it.0 == value.0 && it.1 == value.1) - }; - - if let Some(account) = account { - let token = get_current_token(); - log::info!("login: success: id:{}, token:{token}", account.2); - ( - StatusCode::OK, - Json(LoginResult::Success(LoginResultSuccess { - success: true, - user_id: account.2.parse().unwrap(), - access_token: token, - token_type: "Bearer".into(), - })), - ) - } else { - log::info!("login: failed, always:{always_fail}"); - ( - StatusCode::NOT_FOUND, - Json(LoginResult::Failed(LoginResultFailed { - success: false, - error_code: 104, - })), - ) - } -} - -async fn add_friend(headers: HeaderMap, form: Form) -> impl IntoResponse { - if !check_token(&headers) { - log::warn!("add_friend: check_token failed"); - return ( - StatusCode::UNAUTHORIZED, - Json(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: 1145, - })), - ); - } - let Some(i_header) = headers.get("i") else { - return ( - StatusCode::BAD_REQUEST, - Json(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: -11, - })), - ); - }; - if form.friend_code == "123456789" { - return ( - StatusCode::NOT_FOUND, - Json(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: 404, - })), - ); - } - let i_header = i_header.to_str().unwrap().to_string(); - //TODO add random not found fail - let result = internal_write_friends( - i_header.clone(), - |it: &mut HashMap>| { - let it = it.get_mut(&i_header).unwrap(); - if it - .iter() - .any(|info| info.user_id.to_string() == form.friend_code) - { - log::warn!("add_friend: failed, duplicated addition"); - return FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: 1, - }) - .into(); - } - it.push(FriendInfo { - name: format!("a_friend_{}", form.friend_code), - user_id: form.friend_code.parse::().unwrap(), - rating: 1234, - character: 0, - is_char_uncapped: true, - is_char_uncapped_override: true, - }); - log::info!("add_friend: success, friend_code:{}", form.friend_code); - None - }, - ); - let code = match &result { - FriendModifyResult::Success(_) => StatusCode::OK, - FriendModifyResult::Failed(_) => StatusCode::BAD_REQUEST, - }; - (code, Json(result)) -} - -async fn remove_friend(headers: HeaderMap, form: Form) -> impl IntoResponse { - if !check_token(&headers) { - log::warn!("remove_friend: check_token failed"); - return ( - StatusCode::UNAUTHORIZED, - Json(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: 1145, - })), - ); - } - let Some(i_header) = headers.get("i") else { - return ( - StatusCode::BAD_REQUEST, - Json(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: -11, - })), - ); - }; - let i_header = i_header.to_str().unwrap().to_string(); - //TODO add random not found fail - let result = internal_write_friends( - i_header.clone(), - |it: &mut HashMap>| { - let it = it.get_mut(&i_header).unwrap(); - let index = it - .iter() - .enumerate() - .find(|it| it.1.user_id.to_string() == form.friend_id) - .map(|it| it.0); - match index { - Some(index) => { - log::info!("remove_friend: success, {}", form.friend_id); - it.remove(index); - None - } - None => { - log::warn!( - "remove_friend: failed, friend not found: {} in {:?}", - form.friend_id, - it - ); - Some(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: 401, - })) - } - } - }, - ); - let code = match &result { - FriendModifyResult::Success(_) => StatusCode::OK, - FriendModifyResult::Failed(_) => StatusCode::NOT_FOUND, - }; - (code, Json(result)) -} - -async fn list_friend(headers: HeaderMap) -> impl IntoResponse { - if !check_token(&headers) { - log::warn!("list_friend: check_token failed"); - return ( - StatusCode::UNAUTHORIZED, - Json(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: 1145, - })), - ); - } - let Some(i_header) = headers.get("i") else { - return ( - StatusCode::BAD_REQUEST, - Json(FriendModifyResult::Failed(FriendModifyResultFailed { - success: false, - error_code: -11, - })), - ); - }; - let i_header = i_header.to_str().unwrap().to_string(); - let result = internal_read_friends(i_header); - let status_code = match &result { - FriendModifyResult::Success(result) => { - log::info!("list_friend: success, {:?}", result.value); - StatusCode::OK - } - FriendModifyResult::Failed(_) => { - log::error!("list_friend: failed, this should never happen..."); - StatusCode::FORBIDDEN - } //this never happen... - }; - (status_code, Json(result)) -} - -async fn get_rank_list( - Query(query): Query, - headers: HeaderMap, -) -> impl IntoResponse { - if !check_token(&headers) { - return ( - StatusCode::UNAUTHORIZED, - Json(RankListFetchResult::Failed(RankListFetchResultFailed { - success: false, - error_code: 1145, - })), - ); - } - let mut rand = rand::rng(); - if rand.random_range(0..5) == 0 { - return ( - StatusCode::TOO_MANY_REQUESTS, - Json(RankListFetchResult::Failed(RankListFetchResultFailed { - success: false, - error_code: 555, - })), - ); - } - let Some(i_header) = headers.get("i") else { - return ( - StatusCode::BAD_REQUEST, - Json(RankListFetchResult::Failed(RankListFetchResultFailed { - success: false, - error_code: -11, - })), - ); - }; - let i_header = i_header.to_str().unwrap().to_string(); - let has_result = true; //rand.random::(); - let mut output_vec = Vec::::new(); - let friends = clone_internal_friends(); - let friends = friends.get(&i_header).unwrap(); - if has_result && !friends.is_empty() { - // let friend = friends.get(rand.random_range(0..friends.len())).unwrap(); - for friend in friends { - output_vec.push(RankListResult { - song_id: query.song_id.clone(), - difficulty: query.difficulty.parse().unwrap(), - user_id: friend.user_id, - score: rand.random(), - score_below_max: rand.random(), - shiny_perfect_count: rand.random(), - perfect_count: rand.random(), - near_count: rand.random(), - miss_count: rand.random(), - clear_type: rand.random(), - best_clear_type: rand.random(), - health: rand.random(), - time_played: rand.random(), - modifier: rand.random(), - name: friend.name.clone(), - character: friend.character, - is_skill_sealed: rand.random(), - is_char_uncapped: rand.random(), - icon: "".into(), - rank: rand.random(), - }); - } - } - for _ in 0..rand - .random_range(1..6usize) - .max(query.limit.parse().unwrap()) - { - output_vec.push(RankListResult { - song_id: query.song_id.clone(), - difficulty: query.difficulty.parse().unwrap(), - user_id: 12345, - score: rand.random(), - score_below_max: rand.random(), - shiny_perfect_count: rand.random(), - perfect_count: rand.random(), - near_count: rand.random(), - miss_count: rand.random(), - clear_type: rand.random(), - best_clear_type: rand.random(), - health: rand.random(), - time_played: rand.random(), - modifier: rand.random(), - name: "Random_name".into(), - character: 0, - is_skill_sealed: rand.random(), - is_char_uncapped: rand.random(), - icon: "".into(), - rank: rand.random(), - }); - } - output_vec.shuffle(&mut rand); - ( - StatusCode::OK, - Json(RankListFetchResult::Success(RankListFetchResultSuccess { - success: true, - value: output_vec, - })), - ) -} - -fn check_token(headers: &HeaderMap) -> bool { - let token = headers.get("Authorization").and_then(|it| it.to_str().ok()); - let Some(token) = token else { - return false; - }; - if token.len() <= "Bearer ".len() { - return false; - } - let token = &token["Bearer ".len()..]; - let Ok(time) = base64::prelude::BASE64_URL_SAFE.decode(token) else { - return false; - }; - let time = i64::from_le_bytes(time[..8].try_into().unwrap()); - let now = Utc::now(); - let now_time = now.timestamp_millis(); - now_time - time <= 1000 * 60 * 60 * 24 * 30 && time <= now_time -} - -fn get_current_token() -> String { - let now = Utc::now(); - let time = now.timestamp_millis(); - let buf = time.to_le_bytes(); - base64::prelude::BASE64_URL_SAFE.encode(buf) -} - -async fn healthz() -> impl IntoResponse { - (StatusCode::OK, Json(json!({"health":"ok"}))) -} +//! cuscuta的mock应用 +//! +//! 出于某些历史原因,它并不依赖`cuscuta-common` + +mod data; + +use std::{ + collections::HashMap, + sync::{OnceLock, RwLock}, +}; + +use axum::{ + Form, Json, Router, + extract::Query, + http::{HeaderMap, StatusCode, header}, + response::IntoResponse, + routing::{get, post}, +}; +use base64::Engine; +use chrono::Utc; +use cuscuta_common::api::xxxxxx::FriendInfo; +use rand::{RngExt, seq::SliceRandom}; +use serde_json::json; +use tokio::net::TcpListener; + +use crate::data::{ + FriendAddForm, FriendModifyResult, FriendModifyResultFailed, FriendModifyResultSuccess, + FriendRemoveForm, FriendsResult, LoginResult, LoginResultFailed, LoginResultSuccess, + RankListFetchResult, RankListFetchResultFailed, RankListFetchResultSuccess, RankListQuery, + RankListResult, check_env_bool, +}; + +static FRIEND_INFO: OnceLock>>> = OnceLock::new(); +static VALID_ACCOUNT: &[(&str, &str, &str)] = &[ + // username, password, user_id + ("123456", "123456", "123456"), + ("234567", "234567", "234567"), + ("345678", "345678", "345678"), + ("456789", "456789", "456789"), + ("nofyso", "Chensing", "114514"), +]; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + tracing::info!("initializing mocker"); + initialize_friends(); + let router = Router::new() + .route("/", get(hello)) + .route("/healthz", get(healthz)) + .route("/auth/login", post(login)) + .route("/friend/me", get(list_friend)) + .route("/friend/me/add", post(add_friend)) + .route("/friend/me/delete", post(remove_friend)) + .route("/score/song/friend", get(get_rank_list)); + let addr = TcpListener::bind("0.0.0.0:8080") + .await + .expect("failed to bind 0.0.0.0:8080"); + tracing::info!("listening in 0.0.0.0:8080..."); + axum::serve(addr, router).await.unwrap(); +} + +async fn hello() -> impl IntoResponse { + (StatusCode::OK, "Hello") +} + +fn initialize_friends() { + let mut x = FRIEND_INFO + .get_or_init(|| RwLock::new(HashMap::new())) + .try_write() + .unwrap(); + for it in VALID_ACCOUNT { + x.insert( + it.2.to_string(), + vec![FriendInfo { + name: "nofyso".into(), + user_id: 9999999, + rating: 10, + character: 0, + is_char_uncapped: true, + is_char_uncapped_override: true, + }], + ); + } +} + +fn clone_internal_friends() -> HashMap> { + FRIEND_INFO + .get_or_init(|| RwLock::new(HashMap::new())) + .try_read() + .unwrap() + .clone() +} + +fn internal_read_friends(id: String) -> FriendModifyResult { + let x = FRIEND_INFO + .get_or_init(|| RwLock::new(HashMap::new())) + .try_read() + .unwrap(); + FriendModifyResult::Success(FriendModifyResultSuccess { + success: true, + value: FriendsResult { + friends: x.get(&id).unwrap().clone(), + }, + }) +} + +fn internal_write_friends(id: String, mut f: F) -> FriendModifyResult +where + F: FnMut(&mut HashMap>) -> Option, +{ + let mut x = FRIEND_INFO + .get_or_init(|| RwLock::new(HashMap::new())) + .try_write() + .unwrap(); + let failed = f(&mut x); + if let Some(failed) = failed { + return failed; + } + FriendModifyResult::Success(FriendModifyResultSuccess { + success: true, + value: FriendsResult { + friends: x.get(&id).unwrap().clone(), + }, + }) +} + +async fn login(headers: HeaderMap) -> impl IntoResponse { + let always_fail = check_env_bool("CUSCUTA_LOGIN_ALWAYS_FAIL"); + let auth_header = headers.get(header::AUTHORIZATION).unwrap(); + let auth_header = &auth_header.to_str().unwrap()["Basic ".len()..]; + let auth_header = base64::prelude::BASE64_STANDARD + .decode(auth_header) + .unwrap(); + let auth_header_value = str::from_utf8(&auth_header).unwrap(); + let auth_header_value: Vec<_> = auth_header_value.split(':').collect(); + let value = (auth_header_value[0], auth_header_value[1]); + let account = if always_fail { + None + } else { + VALID_ACCOUNT + .iter() + .find(|it| it.0 == value.0 && it.1 == value.1) + }; + + if let Some(account) = account { + let token = get_current_token(); + tracing::info!("login: success: id:{}, token:{token}", account.2); + ( + StatusCode::OK, + Json(LoginResult::Success(LoginResultSuccess { + success: true, + user_id: account.2.parse().unwrap(), + access_token: token, + token_type: "Bearer".into(), + })), + ) + } else { + tracing::info!("login: failed, always:{always_fail}"); + ( + StatusCode::NOT_FOUND, + Json(LoginResult::Failed(LoginResultFailed { + success: false, + error_code: 104, + })), + ) + } +} + +async fn add_friend(headers: HeaderMap, form: Form) -> impl IntoResponse { + if !check_token(&headers) { + tracing::warn!("add_friend: check_token failed"); + return ( + StatusCode::UNAUTHORIZED, + Json(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: 1145, + })), + ); + } + let Some(i_header) = headers.get("i") else { + return ( + StatusCode::BAD_REQUEST, + Json(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: -11, + })), + ); + }; + if form.friend_code == "123456789" { + return ( + StatusCode::NOT_FOUND, + Json(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: 404, + })), + ); + } + let i_header = i_header.to_str().unwrap().to_string(); + //TODO add random not found fail + let result = internal_write_friends( + i_header.clone(), + |it: &mut HashMap>| { + let it = it.get_mut(&i_header).unwrap(); + if it + .iter() + .any(|info| info.user_id.to_string() == form.friend_code) + { + tracing::warn!("add_friend: failed, duplicated addition"); + return FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: 1, + }) + .into(); + } + it.push(FriendInfo { + name: format!("a_friend_{}", form.friend_code), + user_id: form.friend_code.parse::().unwrap(), + rating: 1234, + character: 0, + is_char_uncapped: true, + is_char_uncapped_override: true, + }); + tracing::info!("add_friend: success, friend_code:{}", form.friend_code); + None + }, + ); + let code = match &result { + FriendModifyResult::Success(_) => StatusCode::OK, + FriendModifyResult::Failed(_) => StatusCode::BAD_REQUEST, + }; + (code, Json(result)) +} + +async fn remove_friend(headers: HeaderMap, form: Form) -> impl IntoResponse { + if !check_token(&headers) { + tracing::warn!("remove_friend: check_token failed"); + return ( + StatusCode::UNAUTHORIZED, + Json(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: 1145, + })), + ); + } + let Some(i_header) = headers.get("i") else { + return ( + StatusCode::BAD_REQUEST, + Json(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: -11, + })), + ); + }; + let i_header = i_header.to_str().unwrap().to_string(); + //TODO add random not found fail + let result = internal_write_friends( + i_header.clone(), + |it: &mut HashMap>| { + let it = it.get_mut(&i_header).unwrap(); + let index = it + .iter() + .enumerate() + .find(|it| it.1.user_id.to_string() == form.friend_id) + .map(|it| it.0); + match index { + Some(index) => { + tracing::info!("remove_friend: success, {}", form.friend_id); + it.remove(index); + None + } + None => { + tracing::warn!( + "remove_friend: failed, friend not found: {} in {:?}", + form.friend_id, + it + ); + Some(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: 401, + })) + } + } + }, + ); + let code = match &result { + FriendModifyResult::Success(_) => StatusCode::OK, + FriendModifyResult::Failed(_) => StatusCode::NOT_FOUND, + }; + (code, Json(result)) +} + +async fn list_friend(headers: HeaderMap) -> impl IntoResponse { + if !check_token(&headers) { + tracing::warn!("list_friend: check_token failed"); + return ( + StatusCode::UNAUTHORIZED, + Json(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: 1145, + })), + ); + } + let Some(i_header) = headers.get("i") else { + return ( + StatusCode::BAD_REQUEST, + Json(FriendModifyResult::Failed(FriendModifyResultFailed { + success: false, + error_code: -11, + })), + ); + }; + let i_header = i_header.to_str().unwrap().to_string(); + let result = internal_read_friends(i_header); + let status_code = match &result { + FriendModifyResult::Success(result) => { + tracing::info!("list_friend: success, {:?}", result.value); + StatusCode::OK + } + FriendModifyResult::Failed(_) => { + tracing::error!("list_friend: failed, this should never happen..."); + StatusCode::FORBIDDEN + } //this never happen... + }; + (status_code, Json(result)) +} + +async fn get_rank_list( + Query(query): Query, + headers: HeaderMap, +) -> impl IntoResponse { + if !check_token(&headers) { + return ( + StatusCode::UNAUTHORIZED, + Json(RankListFetchResult::Failed(RankListFetchResultFailed { + success: false, + error_code: 1145, + })), + ); + } + let mut rand = rand::rng(); + if rand.random_range(0..5) == 0 { + return ( + StatusCode::TOO_MANY_REQUESTS, + Json(RankListFetchResult::Failed(RankListFetchResultFailed { + success: false, + error_code: 555, + })), + ); + } + let Some(i_header) = headers.get("i") else { + return ( + StatusCode::BAD_REQUEST, + Json(RankListFetchResult::Failed(RankListFetchResultFailed { + success: false, + error_code: -11, + })), + ); + }; + let i_header = i_header.to_str().unwrap().to_string(); + let has_result = true; //rand.random::(); + let mut output_vec = Vec::::new(); + let friends = clone_internal_friends(); + let friends = friends.get(&i_header).unwrap(); + if has_result && !friends.is_empty() { + // let friend = friends.get(rand.random_range(0..friends.len())).unwrap(); + for friend in friends { + output_vec.push(RankListResult { + song_id: query.song_id.clone(), + difficulty: query.difficulty.parse().unwrap(), + user_id: friend.user_id, + score: rand.random(), + score_below_max: rand.random(), + shiny_perfect_count: rand.random(), + perfect_count: rand.random(), + near_count: rand.random(), + miss_count: rand.random(), + clear_type: rand.random(), + best_clear_type: rand.random(), + health: rand.random(), + time_played: rand.random(), + modifier: rand.random(), + name: friend.name.clone(), + character: friend.character, + is_skill_sealed: rand.random(), + is_char_uncapped: rand.random(), + icon: "".into(), + rank: rand.random(), + }); + } + } + for _ in 0..rand + .random_range(1..6usize) + .max(query.limit.parse().unwrap()) + { + output_vec.push(RankListResult { + song_id: query.song_id.clone(), + difficulty: query.difficulty.parse().unwrap(), + user_id: 12345, + score: rand.random(), + score_below_max: rand.random(), + shiny_perfect_count: rand.random(), + perfect_count: rand.random(), + near_count: rand.random(), + miss_count: rand.random(), + clear_type: rand.random(), + best_clear_type: rand.random(), + health: rand.random(), + time_played: rand.random(), + modifier: rand.random(), + name: "Random_name".into(), + character: 0, + is_skill_sealed: rand.random(), + is_char_uncapped: rand.random(), + icon: "".into(), + rank: rand.random(), + }); + } + output_vec.shuffle(&mut rand); + ( + StatusCode::OK, + Json(RankListFetchResult::Success(RankListFetchResultSuccess { + success: true, + value: output_vec, + })), + ) +} + +fn check_token(headers: &HeaderMap) -> bool { + let token = headers.get("Authorization").and_then(|it| it.to_str().ok()); + let Some(token) = token else { + return false; + }; + if token.len() <= "Bearer ".len() { + return false; + } + let token = &token["Bearer ".len()..]; + let Ok(time) = base64::prelude::BASE64_URL_SAFE.decode(token) else { + return false; + }; + let time = i64::from_le_bytes(time[..8].try_into().unwrap()); + let now = Utc::now(); + let now_time = now.timestamp_millis(); + now_time - time <= 1000 * 60 * 60 * 24 * 30 && time <= now_time +} + +fn get_current_token() -> String { + let now = Utc::now(); + let time = now.timestamp_millis(); + let buf = time.to_le_bytes(); + base64::prelude::BASE64_URL_SAFE.encode(buf) +} + +async fn healthz() -> impl IntoResponse { + (StatusCode::OK, Json(json!({"health":"ok"}))) +} diff --git a/cuscuta-worker/Cargo.toml b/cuscuta-worker/Cargo.toml index 7a17f9f..6d978aa 100644 --- a/cuscuta-worker/Cargo.toml +++ b/cuscuta-worker/Cargo.toml @@ -8,8 +8,8 @@ cuscuta-common = { path = "../cuscuta-common" } anyhow = { workspace = true } axum = { workspace = true } chrono = { workspace = true } -env_logger = { workspace = true } -log = { workspace = true } +tracing-subscriber = { workspace = true } +tracing = { workspace = true } rand = { workspace = true } redis = { workspace = true } reqwest = { workspace = true } diff --git a/cuscuta-worker/src/db/account.rs b/cuscuta-worker/src/db/account.rs index ebe0a6e..813de0e 100644 --- a/cuscuta-worker/src/db/account.rs +++ b/cuscuta-worker/src/db/account.rs @@ -28,16 +28,16 @@ pub async fn perform_login( { api::chilo::ChiloResult::Success { value, .. } => value, api::chilo::ChiloResult::Failed { message, .. } => { - log::warn!("login_interface: failed to generate challenge: {message}"); + tracing::warn!("login_interface: failed to generate challenge: {message}"); return Err(api::Error::BadStatus( StatusCode::INTERNAL_SERVER_ERROR, message, )); } }; - log::info!("login_interface: challenge: {random_challenge}"); - log::info!("login_interface: email: {}", account_row.account_email); - log::info!("login_interface: pw: {}", account_row.account_password); + tracing::info!("login_interface: challenge: {random_challenge}"); + tracing::info!("login_interface: email: {}", account_row.account_email); + tracing::info!("login_interface: pw: {}", account_row.account_password); api_login( bundle_data, &account_row.account_email, @@ -136,7 +136,7 @@ pub mod auto { .temp_token .clone() .expect("this should not happen #2"); - log::info!( + tracing::info!( "check_token: fetch friends: {token}, {user_id}, {}, {bundle_data:?}", current_row.account_email ); diff --git a/cuscuta-worker/src/init.rs b/cuscuta-worker/src/init.rs index 8d5e294..eb1f118 100644 --- a/cuscuta-worker/src/init.rs +++ b/cuscuta-worker/src/init.rs @@ -55,7 +55,7 @@ pub async fn cuscuta_init(service_token: &CancellationToken, init_token: &Cancel } } async fn try_init() -> Result<(), Error> { - log::info!("init: cuscuta-worker initializing..."); + tracing::info!("init: cuscuta-worker initializing..."); let bundle_data = BUNDLE_DATA .try_read(std::clone::Clone::clone) .map_err(|e| (Level::Retry, format!("failed to read BUNDLE_DATA: {e}")))?; @@ -86,7 +86,7 @@ pub async fn cuscuta_init(service_token: &CancellationToken, init_token: &Cancel ) })? .ok_or_else(|| (Level::Halt, "no account found".to_string()))?; - log::info!("init: locked {}", account_row.id); + tracing::info!("init: locked {}", account_row.id); ACCOUNT_ROW .try_write(|_| Some(account_row.clone())) .map_err(|e| (Level::Retry, format!("failed to write ACCOUNT_ROW: {e}")))?; @@ -94,7 +94,7 @@ pub async fn cuscuta_init(service_token: &CancellationToken, init_token: &Cancel account_row, friends, } = get_friend_result(&bundle_data, &account_row).await?; - log::info!("init: found {} existing friends", friends.friends.len()); + tracing::info!("init: found {} existing friends", friends.friends.len()); for friend in friends.friends { api_delete_friend( &bundle_data, @@ -111,7 +111,7 @@ pub async fn cuscuta_init(service_token: &CancellationToken, init_token: &Cancel ) .await .map_err(|e| (Level::Halt, format!("failed to clean friends: {e}")))?; - log::info!("init: removed friend: {friend:?}"); + tracing::info!("init: removed friend: {friend:?}"); } ACCOUNT_ROW .try_write(|_| Some(account_row.clone())) @@ -131,31 +131,31 @@ pub async fn cuscuta_init(service_token: &CancellationToken, init_token: &Cancel .try_write(|_| None) .map_err(|e| (Level::Retry, format!("failed to clear account row: {e}")))?; } else { - log::info!("init_resume: no account found, skip"); + tracing::info!("init_resume: no account found, skip"); } Ok(()) } if let Err(Error { level, description }) = try_init().await { if level == Level::Halt { - log::error!("init: serious error occurred in initializing: {description}, halting"); + tracing::error!("init: serious error occurred in initializing: {description}, halting"); init_token.cancel(); service_token.cancel(); return; } - log::warn!("init: failed to initialize temporary, level:{level:?}, desc:{description}"); - log::info!("init_resume: resuming states"); + tracing::warn!("init: failed to initialize temporary, level:{level:?}, desc:{description}"); + tracing::info!("init_resume: resuming states"); let mut resume_retries = 0; loop { if let Err(Error { level, description }) = try_failed_resume().await { if resume_retries >= 5 || level == Level::Halt { - log::error!( + tracing::error!( "init_resume: serious error occurred in initialize resuming: {description}, halting" ); init_token.cancel(); service_token.cancel(); return; } - log::warn!( + tracing::warn!( "init_resume: failed to resume temporary, level:{level:?}, desc:{description}" ); } else { @@ -165,10 +165,10 @@ pub async fn cuscuta_init(service_token: &CancellationToken, init_token: &Cancel } return; } - log::info!(" ┏━╸╻ ╻┏━┓┏━╸╻ ╻╺┳╸┏━┓ "); - log::info!(" ┃ ┃ ┃┗━┓┃ ┃ ┃ ┃ ┣━┫ "); - log::info!(" ┗━╸┗━┛┗━┛┗━╸┗━┛ ╹ ╹ ╹ "); - log::info!("init: let the cuscuta spread..."); + tracing::info!(" ┏━╸╻ ╻┏━┓┏━╸╻ ╻╺┳╸┏━┓ "); + tracing::info!(" ┃ ┃ ┃┗━┓┃ ┃ ┃ ┃ ┣━┫ "); + tracing::info!(" ┗━╸┗━┛┗━┛┗━╸┗━┛ ╹ ╹ ╹ "); + tracing::info!("init: let the cuscuta spread..."); init_token.cancel(); } diff --git a/cuscuta-worker/src/loop_tasks.rs b/cuscuta-worker/src/loop_tasks.rs index 5371b3b..7fdb3ac 100644 --- a/cuscuta-worker/src/loop_tasks.rs +++ b/cuscuta-worker/src/loop_tasks.rs @@ -37,20 +37,20 @@ pub async fn sync_bundle_data(_: &CancellationToken) { Ok(bundle_data) } if BUNDLE_DATA.is_initialized() { - log::trace!("bundle data sync"); + tracing::trace!("bundle data sync"); return; } - log::info!("sync_bundle_data: trying sync bundle data"); + tracing::info!("sync_bundle_data: trying sync bundle data"); match try_sync().await { Ok(bundle_data) => { - log::info!( + tracing::info!( "sync_bundle_data: bundle data initialized: appVer:{}, ver:{}", bundle_data.application_version_number, bundle_data.version_number ); } Err(e) => { - log::error!("sync_bundle_data: failed to sync bundle data: {e}"); + tracing::error!("sync_bundle_data: failed to sync bundle data: {e}"); } } } @@ -91,15 +91,15 @@ pub async fn sync_config(_: &CancellationToken) { Ok(()) } if CONFIG.is_initialized() { - log::trace!("config sync"); + tracing::trace!("config sync"); return; } - log::info!("sync_config: trying sync config"); + tracing::info!("sync_config: trying sync config"); if let Err(e) = try_sync() { - log::error!("sync_config: failed to sync config: {e}"); + tracing::error!("sync_config: failed to sync config: {e}"); return; } - log::info!("sync_config: config initialized"); + tracing::info!("sync_config: config initialized"); } pub async fn sync_song_list(_: &CancellationToken) { @@ -124,22 +124,22 @@ pub async fn sync_song_list(_: &CancellationToken) { Ok((music_len, chart_len)) } if SONG_LIST.is_initialized() { - log::trace!("song list sync"); + tracing::trace!("song list sync"); return; } - log::info!("sync_song_list: trying sync song list"); + tracing::info!("sync_song_list: trying sync song list"); match try_sync().await { - Ok((music_len, chart_len)) => log::info!( + Ok((music_len, chart_len)) => tracing::info!( "sync_song_list: song list initialized (music:{music_len}, charts:{chart_len})" ), - Err(e) => log::error!("sync_song_list: failed to sync song list: {e}"), + Err(e) => tracing::error!("sync_song_list: failed to sync song list: {e}"), } } pub async fn open_redis_client(_: &CancellationToken) { fn try_connect() -> Result<(), String> { let addr = env::var("REDIS_ADDR").map_err(|_| "failed to read env: REDIS_ADDR")?; - log::debug!("redis_open: redis: {addr}"); + tracing::debug!("redis_open: redis: {addr}"); let redis = redis::Client::open(addr) .map_err(|e| format!("failed to open redis client(phase 1): {e}"))?; let mut con = redis @@ -155,19 +155,19 @@ pub async fn open_redis_client(_: &CancellationToken) { if REDIS_CLIENT.get().is_some() { return; } - log::debug!("redis_open: trying to connect to redis server..."); + tracing::debug!("redis_open: trying to connect to redis server..."); if let Err(e) = try_connect() { - log::error!("redis_open: failed to connect to redis server: {e}"); + tracing::error!("redis_open: failed to connect to redis server: {e}"); return; } - log::info!("redis_open: redis client created successfully"); + tracing::info!("redis_open: redis client created successfully"); } pub async fn open_postgresql_client(_: &CancellationToken) { async fn try_connect() -> Result<(), String> { let addr = env::var("ACCOUNTS_SQL_ADDR") .map_err(|e| format!("failed to read ACCOUNTS_SQL_ADDR: {e}"))?; - log::debug!("postgresql_open: {addr}"); + tracing::debug!("postgresql_open: {addr}"); let x = PgPoolOptions::new() .max_connections(5) .connect(addr.as_str()) @@ -181,12 +181,12 @@ pub async fn open_postgresql_client(_: &CancellationToken) { if POSTGRESQL_POOL.is_initialized() { return; } - log::debug!("postgresql_open: trying to connect to postgresql server..."); + tracing::debug!("postgresql_open: trying to connect to postgresql server..."); if let Err(e) = try_connect().await { - log::error!("postgresql_open: failed to connect to postgresql server: {e}"); + tracing::error!("postgresql_open: failed to connect to postgresql server: {e}"); return; } - log::info!("postgresql_open: postgresql pool created successfully"); + tracing::info!("postgresql_open: postgresql pool created successfully"); } pub async fn update_lease_time(_: &CancellationToken) { @@ -209,19 +209,19 @@ pub async fn update_lease_time(_: &CancellationToken) { .map_err(|e| format!("failed to update lease time: {e}"))?; Ok(lease_time) } - log::debug!("update_lease_time: trying update lease time"); + tracing::debug!("update_lease_time: trying update lease time"); match try_update().await { Ok(lease_time) => { if let Some(lease_time) = lease_time { - log::debug!("update_lease_time: updated lease time: {lease_time}"); + tracing::debug!("update_lease_time: updated lease time: {lease_time}"); } else { - log::warn!( + tracing::warn!( "update_lease_time: failed to update account lease time: no returns found, id may not exist" ); } } Err(e) => { - log::warn!("update_lease_time: failed to update account lease time: {e}"); + tracing::warn!("update_lease_time: failed to update account lease time: {e}"); } } } diff --git a/cuscuta-worker/src/main.rs b/cuscuta-worker/src/main.rs index 9cd65f6..c1082a6 100644 --- a/cuscuta-worker/src/main.rs +++ b/cuscuta-worker/src/main.rs @@ -1,186 +1,188 @@ -//! cuscuta的worker -//! -//! # 依赖环境变量 -//! cuscuta-worker使用环境变量注入参数,这个crate依赖的环境变量有: -//! - `WORKER_MAX_JOBS` -//! - `WORKER_MAX_RETRIES` -//! - `WORKER_EXPONENTIAL_BACKOFF_BASE_MILLIS` -//! - `WORKER_EXPONENTIAL_BACKOFF_MULTIPLIER` -//! - `WORKER_EXPONENTIAL_BACKOFF_MAX_DELAY_MILLIS` -//! - `WORKER_ACCOUNT_LEASE_TIME_SECS` -//! - `WORKER_ACCOUNT_LEASE_TIME_REFRESH_GAP_SECS` -//! - `REDIS_STREAM_REFRESH_TTL` -//! - `GITHUB_BUNDLE_REPOSITORY` -//! - `GITHUB_BUNDLE_PATH` -//! - `GITHUB_BUNDLE_TOKEN` -//! - `GITHUB_SONG_REPOSITORY` -//! - `GITHUB_SONG_PATH` -//! - `GITHUB_SONG_TOKEN` -//! - `REDIS_ADDR` -//! - `ACCOUNTS_SQL_ADDR` -//! - `API_CHILO` -//! - `API_LOGIN` -//! - `API_LIST_FRIENDS` -//! - `API_ADD_FRIENDS` -//! - `API_DELETE_FRIENDS` -//! - `API_GET_RANK` -//! - -mod data; -mod db; -mod init; -mod loop_tasks; -mod worker; - -use std::env; - -use axum::{Json, Router, http::StatusCode, response::IntoResponse, routing::get}; -use cuscuta_common::{ - batch_check_initialized, - db::account::try_release_account, - quick_fetch::QuickFetch, - scheduled_job::{register_individual_job, register_job}, -}; -use serde_json::json; -use tokio::net::TcpListener; -use tokio_util::sync::CancellationToken; - -use crate::{ - data::{ACCOUNT_ROW, BUNDLE_DATA, CONFIG, SONG_LIST}, - db::{ - postgresql::{POSTGRESQL_POOL, try_open_transaction}, - redis::REDIS_CLIENT, - }, - init::cuscuta_init, - loop_tasks::{ - open_postgresql_client, open_redis_client, sync_bundle_data, sync_config, sync_song_list, - update_lease_time, - }, - worker::{resume_state, worker_loop}, -}; - -#[tokio::main] -async fn main() { - env_logger::init(); - log::info!("starting..."); - let halt_token = CancellationToken::new(); - let service = Router::new() - .route("/healthz", get(healthz)) - .route("/readyz", get(readyz)); - let addr = TcpListener::bind("0.0.0.0:8080") - .await - .expect("failed to bind 0.0.0.0:8080"); - log::info!("listening in 0.0.0.0:8080..."); - tokio::spawn(register_individual_job( - halt_token.clone(), - CancellationToken::new(), - 10, - cuscuta_init, - )); - tokio::spawn(register_job(halt_token.clone(), 10, open_redis_client)); - tokio::spawn(register_job(halt_token.clone(), 10, open_postgresql_client)); - tokio::spawn(register_job(halt_token.clone(), 10, sync_config)); - tokio::spawn(register_job(halt_token.clone(), 10, sync_bundle_data)); - tokio::spawn(register_job(halt_token.clone(), 10, sync_song_list)); - tokio::spawn(register_job( - halt_token.clone(), - env::var("WORKER_ACCOUNT_LEASE_TIME_REFRESH_GAP_SECS") - .map_err(|_| ()) - .and_then(|it| it.parse().map_err(|_| ())) - .unwrap_or(30), - update_lease_time, - )); - tokio::spawn(start_loop(halt_token.clone())); - axum::serve(addr, service) - .with_graceful_shutdown(shutdown_signal(halt_token)) - .await - .unwrap_or_else(|e| panic!("{e:?}")); -} - -async fn start_loop(cancellation_token: CancellationToken) { - let worker_loop_result = worker_loop(&cancellation_token).await; - if let Some(e) = &worker_loop_result.error { - log::error!("worker_loop_shell: serious error occurred in worker loop, halting: {e}"); - } - log::warn!("worker_loop_shell: worker loop halted, trying resuming states..."); - log::warn!( - "worker_loop_shell: friends: {}, pending jobs: {}, last_cursor: {}", - worker_loop_result.friends.len(), - worker_loop_result.jobs.len(), - worker_loop_result.cursor - ); - resume_state(worker_loop_result); - cancellation_token.cancel(); -} - -#[allow(clippy::ignored_unit_patterns)] -async fn shutdown_signal(cancellation_token: CancellationToken) { - #[cfg(target_os = "linux")] - { - use tokio::signal::{ - self, - unix::{SignalKind, signal}, - }; - let mut sigterm = - signal(SignalKind::terminate()).expect("failed to install SIGTERM handler"); - tokio::select! { - _ = signal::ctrl_c() => {}, - _ = sigterm.recv() => {}, - _ = cancellation_token.cancelled() => {}, - } - } - #[cfg(target_os = "windows")] - { - use tokio::signal; - log::info!("咱其实挺想知道谁会在Windows上跑这个的……"); - tokio::select! { - _ = signal::ctrl_c() => {}, - _ = cancellation_token.cancelled() => {}, - } - } - log::error!("cuscuta halting..."); - cancellation_token.cancel(); - halt_progress().await; -} - -async fn halt_progress() { - async fn reset_account_state() -> Result<(), String> { - let account_row = ACCOUNT_ROW - .try_read(std::clone::Clone::clone) - .map_err(|e| format!("failed to fetch account: {e}"))?; - let transaction = try_open_transaction() - .await - .map_err(|e| format!("failed to begin transaction: {e}"))?; - try_release_account(transaction, account_row.id) - .await - .map_err(|e| format!("failed to release account: {e}"))?; - Ok(()) - } - if let Err(e) = reset_account_state().await { - log::error!("halt_release: failed to release account: {e}"); - } -} - -fn check_ready() -> Option<&'static str> { - if REDIS_CLIENT.get().is_none() { - return Some("redis client is not initialized"); - } - batch_check_initialized!(CONFIG, BUNDLE_DATA, SONG_LIST, ACCOUNT_ROW, POSTGRESQL_POOL); - None -} - -async fn readyz() -> impl IntoResponse { - check_ready().map_or_else( - || (StatusCode::OK, Json(json!({"status":"ready"}))), - |e| { - ( - StatusCode::SERVICE_UNAVAILABLE, - Json(json!({"status": "not ready", "reason": e})), - ) - }, - ) -} - -async fn healthz() -> impl IntoResponse { - (StatusCode::OK, Json(json!({"health":"ok"}))) -} +//! cuscuta的worker +//! +//! # 依赖环境变量 +//! cuscuta-worker使用环境变量注入参数,这个crate依赖的环境变量有: +//! - `WORKER_MAX_JOBS` +//! - `WORKER_MAX_RETRIES` +//! - `WORKER_EXPONENTIAL_BACKOFF_BASE_MILLIS` +//! - `WORKER_EXPONENTIAL_BACKOFF_MULTIPLIER` +//! - `WORKER_EXPONENTIAL_BACKOFF_MAX_DELAY_MILLIS` +//! - `WORKER_ACCOUNT_LEASE_TIME_SECS` +//! - `WORKER_ACCOUNT_LEASE_TIME_REFRESH_GAP_SECS` +//! - `REDIS_STREAM_REFRESH_TTL` +//! - `GITHUB_BUNDLE_REPOSITORY` +//! - `GITHUB_BUNDLE_PATH` +//! - `GITHUB_BUNDLE_TOKEN` +//! - `GITHUB_SONG_REPOSITORY` +//! - `GITHUB_SONG_PATH` +//! - `GITHUB_SONG_TOKEN` +//! - `REDIS_ADDR` +//! - `ACCOUNTS_SQL_ADDR` +//! - `API_CHILO` +//! - `API_LOGIN` +//! - `API_LIST_FRIENDS` +//! - `API_ADD_FRIENDS` +//! - `API_DELETE_FRIENDS` +//! - `API_GET_RANK` +//! + +mod data; +mod db; +mod init; +mod loop_tasks; +mod worker; + +use std::env; + +use axum::{Json, Router, http::StatusCode, response::IntoResponse, routing::get}; +use cuscuta_common::{ + batch_check_initialized, + db::account::try_release_account, + quick_fetch::QuickFetch, + scheduled_job::{register_individual_job, register_job}, +}; +use serde_json::json; +use tokio::net::TcpListener; +use tokio_util::sync::CancellationToken; + +use crate::{ + data::{ACCOUNT_ROW, BUNDLE_DATA, CONFIG, SONG_LIST}, + db::{ + postgresql::{POSTGRESQL_POOL, try_open_transaction}, + redis::REDIS_CLIENT, + }, + init::cuscuta_init, + loop_tasks::{ + open_postgresql_client, open_redis_client, sync_bundle_data, sync_config, sync_song_list, + update_lease_time, + }, + worker::{resume_state, worker_loop}, +}; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + tracing::info!("starting..."); + let halt_token = CancellationToken::new(); + let service = Router::new() + .route("/healthz", get(healthz)) + .route("/readyz", get(readyz)); + let addr = TcpListener::bind("0.0.0.0:8080") + .await + .expect("failed to bind 0.0.0.0:8080"); + tracing::info!("listening in 0.0.0.0:8080..."); + tokio::spawn(register_individual_job( + halt_token.clone(), + CancellationToken::new(), + 10, + cuscuta_init, + )); + tokio::spawn(register_job(halt_token.clone(), 10, open_redis_client)); + tokio::spawn(register_job(halt_token.clone(), 10, open_postgresql_client)); + tokio::spawn(register_job(halt_token.clone(), 10, sync_config)); + tokio::spawn(register_job(halt_token.clone(), 10, sync_bundle_data)); + tokio::spawn(register_job(halt_token.clone(), 10, sync_song_list)); + tokio::spawn(register_job( + halt_token.clone(), + env::var("WORKER_ACCOUNT_LEASE_TIME_REFRESH_GAP_SECS") + .map_err(|_| ()) + .and_then(|it| it.parse().map_err(|_| ())) + .unwrap_or(30), + update_lease_time, + )); + tokio::spawn(start_loop(halt_token.clone())); + axum::serve(addr, service) + .with_graceful_shutdown(shutdown_signal(halt_token)) + .await + .unwrap_or_else(|e| panic!("{e:?}")); +} + +async fn start_loop(cancellation_token: CancellationToken) { + let worker_loop_result = worker_loop(&cancellation_token).await; + if let Some(e) = &worker_loop_result.error { + tracing::error!("worker_loop_shell: serious error occurred in worker loop, halting: {e}"); + } + tracing::warn!("worker_loop_shell: worker loop halted, trying resuming states..."); + tracing::warn!( + "worker_loop_shell: friends: {}, pending jobs: {}, last_cursor: {}", + worker_loop_result.friends.len(), + worker_loop_result.jobs.len(), + worker_loop_result.cursor + ); + resume_state(worker_loop_result); + cancellation_token.cancel(); +} + +#[allow(clippy::ignored_unit_patterns)] +async fn shutdown_signal(cancellation_token: CancellationToken) { + #[cfg(target_os = "linux")] + { + use tokio::signal::{ + self, + unix::{SignalKind, signal}, + }; + let mut sigterm = + signal(SignalKind::terminate()).expect("failed to install SIGTERM handler"); + tokio::select! { + _ = signal::ctrl_c() => {}, + _ = sigterm.recv() => {}, + _ = cancellation_token.cancelled() => {}, + } + } + #[cfg(target_os = "windows")] + { + use tokio::signal; + tracing::info!("咱其实挺想知道谁会在Windows上跑这个的……"); + tokio::select! { + _ = signal::ctrl_c() => {}, + _ = cancellation_token.cancelled() => {}, + } + } + tracing::error!("cuscuta halting..."); + cancellation_token.cancel(); + halt_progress().await; +} + +async fn halt_progress() { + async fn reset_account_state() -> Result<(), String> { + let account_row = ACCOUNT_ROW + .try_read(std::clone::Clone::clone) + .map_err(|e| format!("failed to fetch account: {e}"))?; + let transaction = try_open_transaction() + .await + .map_err(|e| format!("failed to begin transaction: {e}"))?; + try_release_account(transaction, account_row.id) + .await + .map_err(|e| format!("failed to release account: {e}"))?; + Ok(()) + } + if let Err(e) = reset_account_state().await { + tracing::error!("halt_release: failed to release account: {e}"); + } +} + +fn check_ready() -> Option<&'static str> { + if REDIS_CLIENT.get().is_none() { + return Some("redis client is not initialized"); + } + batch_check_initialized!(CONFIG, BUNDLE_DATA, SONG_LIST, ACCOUNT_ROW, POSTGRESQL_POOL); + None +} + +async fn readyz() -> impl IntoResponse { + check_ready().map_or_else( + || (StatusCode::OK, Json(json!({"status":"ready"}))), + |e| { + ( + StatusCode::SERVICE_UNAVAILABLE, + Json(json!({"status": "not ready", "reason": e})), + ) + }, + ) +} + +async fn healthz() -> impl IntoResponse { + (StatusCode::OK, Json(json!({"health":"ok"}))) +} diff --git a/cuscuta-worker/src/worker/clean.rs b/cuscuta-worker/src/worker/clean.rs index fb54e21..30970c0 100644 --- a/cuscuta-worker/src/worker/clean.rs +++ b/cuscuta-worker/src/worker/clean.rs @@ -83,7 +83,7 @@ pub async fn clean_jobs( .collect(), }, ) { - log::warn!("job_clean: failed to write track tag: {e}"); + tracing::warn!("job_clean: failed to write track tag: {e}"); continue; } if let Some(friend_info) = friend_info @@ -121,7 +121,7 @@ pub async fn clean_jobs( WorkerEventType::Warn, format!("job finished with error: {failure_info:?}") ); - log::warn!( + tracing::warn!( "job: {finished_job:?} finished with error: {finished_job:?} : {failure_info:?}" ); } else { @@ -129,7 +129,7 @@ pub async fn clean_jobs( WorkerEventType::Trace, format!("job finished: {finished_job:?}") ); - log::info!("job: {finished_job:?} finished"); + tracing::info!("job: {finished_job:?} finished"); } finished_job.state = JobState::Cleaned; } diff --git a/cuscuta-worker/src/worker/mod.rs b/cuscuta-worker/src/worker/mod.rs index 7d47b67..3e0229a 100644 --- a/cuscuta-worker/src/worker/mod.rs +++ b/cuscuta-worker/src/worker/mod.rs @@ -147,7 +147,7 @@ async fn internal_loop( .await? else { if let Err(e) = update_worker_status(redis_client, worker_id, *cursor, None, current_jobs) { - log::warn!("worker_loop: failed to update worker status #1: {e}"); + tracing::warn!("worker_loop: failed to update worker status #1: {e}"); } sleep(Duration::from_secs(1)).await; return Ok(()); @@ -159,10 +159,10 @@ async fn internal_loop( Some(¤t_segments), current_jobs, ) { - log::warn!("worker_loop: failed to update worker status #2: {e}"); + tracing::warn!("worker_loop: failed to update worker status #2: {e}"); } if current_jobs.is_empty() { - log::debug!("worker_loop: no jobs, skip"); + tracing::debug!("worker_loop: no jobs, skip"); sleep(Duration::from_secs(2)).await; return Ok(()); } @@ -264,7 +264,7 @@ pub fn resume_state(worker_result: WorkerResult) { .unwrap_or(300); let mut connection = redis_client.get_connection().map_err(Error::Redis)?; for job in worker_result.jobs { - log::info!("resuming_jobs: reenqueueing job: {job:?}"); + tracing::info!("resuming_jobs: reenqueueing job: {job:?}"); connection .xack(&job.sub_queue.name, "default_group", &[&job.job_id]) .map_err(Error::Redis)?; @@ -298,6 +298,6 @@ pub fn resume_state(worker_result: WorkerResult) { Ok(()) } if let Err(e) = resume_jobs(worker_result) { - log::error!("resuming: failed to resume jobs: {e}"); + tracing::error!("resuming: failed to resume jobs: {e}"); } } diff --git a/cuscuta-worker/src/worker/pending_friend.rs b/cuscuta-worker/src/worker/pending_friend.rs index a0264f9..4edda1a 100644 --- a/cuscuta-worker/src/worker/pending_friend.rs +++ b/cuscuta-worker/src/worker/pending_friend.rs @@ -136,9 +136,9 @@ async fn try_modify_remote_friend( match result { Err(e) => { if let api::Error::BadStatus(code, message) = &e { - log::warn!("pending_friends: failed to call friend_add: {message}"); + tracing::warn!("pending_friends: failed to call friend_add: {message}"); if *code == 400 { - log::warn!( + tracing::warn!( "pending_friends: friend is already exist but cache is out-of-date! trying readd" ); xxxxxx_safe_call_ex( @@ -160,11 +160,11 @@ async fn try_modify_remote_friend( .await .map(|it| it.friends) } else { - log::warn!("pending_friends: failed to add friend: {e}: code: {code}"); + tracing::warn!("pending_friends: failed to add friend: {e}: code: {code}"); Err(e) } } else { - log::warn!("pending_friends: unexpected error: {e}"); + tracing::warn!("pending_friends: unexpected error: {e}"); Err(e) } } diff --git a/cuscuta-worker/src/worker/pending_gather.rs b/cuscuta-worker/src/worker/pending_gather.rs index 72c2788..6a97f95 100644 --- a/cuscuta-worker/src/worker/pending_gather.rs +++ b/cuscuta-worker/src/worker/pending_gather.rs @@ -38,7 +38,7 @@ pub fn process_job_with_result(jobs: &mut [Job], scores: &[SongScore]) -> Vec<(S }; } } - log::debug!( + tracing::debug!( "linked {} results for {:?}", job_links.len(), scores.first().map(|it| it.song_id.clone()) diff --git a/cuscuta-worker/src/worker/pull.rs b/cuscuta-worker/src/worker/pull.rs index 7b400a8..0d04956 100644 --- a/cuscuta-worker/src/worker/pull.rs +++ b/cuscuta-worker/src/worker/pull.rs @@ -67,7 +67,7 @@ pub async fn scan_sub_queue_and_pull_job( }; if let Some(new_jobs) = new_jobs { for it in new_jobs { - log::info!("worker_loop: pulled job: {it:?}"); + tracing::info!("worker_loop: pulled job: {it:?}"); worker_write_event!(WorkerEventType::Trace, format!("pulled: {it:?}")); update_job_track_info( redis_client, @@ -175,7 +175,7 @@ fn pull_jobs( ) { Ok(o) => o, Err(e) => { - log::warn!("worker_loop_pull_jobs: failed to claim jobs: {e}"); + tracing::warn!("worker_loop_pull_jobs: failed to claim jobs: {e}"); Vec::new() } }; @@ -188,7 +188,7 @@ fn pull_jobs( ) { Ok(o) => o, Err(e) => { - log::warn!("worker_loop_pull_jobs: failed to fetch jobs: {e}"); + tracing::warn!("worker_loop_pull_jobs: failed to fetch jobs: {e}"); Vec::new() } }; diff --git a/cuscutactl/Cargo.toml b/cuscutactl/Cargo.toml index c73d461..079e74a 100644 --- a/cuscutactl/Cargo.toml +++ b/cuscutactl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cuscutactl" -version = "0.1.1" +version = "0.1.2" edition = "2024" [dependencies] diff --git a/cuscutactl/src/stats.rs b/cuscutactl/src/stats.rs index f2b8385..57d489e 100644 --- a/cuscutactl/src/stats.rs +++ b/cuscutactl/src/stats.rs @@ -46,6 +46,9 @@ pub fn event(redis_url: &str, show_level: ShowLevel, limit: usize) -> anyhow::Re .collect::>(), ); lines += len; + if out.len() >= limit { + break; + } } for it in out { let opt = Utc.timestamp_opt(it.timestamp / 1000, 0);