From c6de0847fd9aa7be30a27e333ff64f66a556649e Mon Sep 17 00:00:00 2001 From: wany <13995595726@qq.com> Date: Mon, 27 Nov 2023 10:41:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A9=E5=8F=B0=E5=B1=B1=E6=B0=B4=E5=BA=93?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=B0=B4=E8=B4=A8=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 212 ++++++++++++++++++ src/main/java/com/whdc/TtsskApplication.java | 36 +++ src/main/java/com/whdc/entity/WqR.java | 52 +++++ src/main/java/com/whdc/entity/WqReal.java | 51 +++++ src/main/java/com/whdc/mapper/WqRMapper.java | 17 ++ .../java/com/whdc/mapper/WqRealMapper.java | 17 ++ src/main/java/com/whdc/mqtt/Config.java | 9 + src/main/java/com/whdc/mqtt/MQTTConfig.java | 92 ++++++++ src/main/java/com/whdc/mqtt/Pub.java | 54 +++++ src/main/java/com/whdc/mqtt/Sub.java | 84 +++++++ .../java/com/whdc/service/WqRService.java | 16 ++ .../com/whdc/service/impl/WqRServiceImp.java | 20 ++ src/main/resources/application.yml | 67 ++++++ src/main/resources/logback-spring.xml | 206 +++++++++++++++++ src/main/resources/tpl.xlsx | Bin 0 -> 9289 bytes 15 files changed, 933 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/whdc/TtsskApplication.java create mode 100644 src/main/java/com/whdc/entity/WqR.java create mode 100644 src/main/java/com/whdc/entity/WqReal.java create mode 100644 src/main/java/com/whdc/mapper/WqRMapper.java create mode 100644 src/main/java/com/whdc/mapper/WqRealMapper.java create mode 100644 src/main/java/com/whdc/mqtt/Config.java create mode 100644 src/main/java/com/whdc/mqtt/MQTTConfig.java create mode 100644 src/main/java/com/whdc/mqtt/Pub.java create mode 100644 src/main/java/com/whdc/mqtt/Sub.java create mode 100644 src/main/java/com/whdc/service/WqRService.java create mode 100644 src/main/java/com/whdc/service/impl/WqRServiceImp.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/tpl.xlsx diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..89893da --- /dev/null +++ b/pom.xml @@ -0,0 +1,212 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.1 + + + + com.whdc + ttssk-service + 1.0 + 天台山水库 + + + 8 + 8 + 1.8 + target/release + + + + + + ali-maven + https://maven.aliyun.com/nexus/content/groups/public + + true + + + true + always + fail + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-aop + + + com.baomidou + mybatis-plus-boot-starter + 3.5.2 + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-data-redis + + + + mysql + mysql-connector-java + 8.0.16 + + + + + + cn.dev33 + sa-token-spring-boot-starter + 1.30.0 + + + cn.dev33 + sa-token-dao-redis-jackson + 1.30.0 + + + + org.apache.commons + commons-pool2 + + + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + 3.0.3 + + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + + + org.projectlombok + lombok + + + + + + com.alibaba + fastjson + 1.2.80 + + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.0 + + + + com.alibaba + druid + 1.2.8 + + + + + + + com.github.jeffreyning + mybatisplus-plus + 1.7.0-RELEASE + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + false + + + true + + lib/ + + com.whdc.SpringApplication + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-lib + package + + copy-dependencies + + + ${copy.jar.directory}/lib + false + false + runtime + + + + + + + + maven-antrun-plugin + + + copy + package + + + + + + + + + + + run + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/whdc/TtsskApplication.java b/src/main/java/com/whdc/TtsskApplication.java new file mode 100644 index 0000000..4bb1050 --- /dev/null +++ b/src/main/java/com/whdc/TtsskApplication.java @@ -0,0 +1,36 @@ +package com.whdc; + +import com.github.jeffreyning.mybatisplus.conf.EnableMPP; +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.BeansException; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +/** + * @author 李赛 + * @date 2022-06-26 0:17 + */ +@Slf4j +@EnableMPP +@EnableWebMvc +@EnableKnife4j +@EnableScheduling +@SpringBootApplication +@MapperScan("com.whdc.mapper") +public class TtsskApplication { + public static void main(String[] args) { + try { + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>> 启动程序 <<<<<<<<<<<<<<<<<<<<<<<<<<<"); + + SpringApplication.run(TtsskApplication.class, args); + } catch (BeansException e) { + e.printStackTrace(); + log.error(e.getMessage(), e); + } + } + +} diff --git a/src/main/java/com/whdc/entity/WqR.java b/src/main/java/com/whdc/entity/WqR.java new file mode 100644 index 0000000..9657184 --- /dev/null +++ b/src/main/java/com/whdc/entity/WqR.java @@ -0,0 +1,52 @@ +package com.whdc.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.Date; + +/** + *

+ * 水质信息表 + *

+ * + * @author wanyan + * @since 2023-11-01 + */ +@Getter +@Setter +@TableName("wq_r") +@Data +public class WqR { + + @TableId(value = "stcd") + private String stcd; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("tm") + private Date tm; + + @TableField("chlorophyll") + private BigDecimal chlorophyll; + + @TableField("blueAlgae") + private BigDecimal blueAlgae; + + @TableField("greenAlgae") + private BigDecimal greenAlgae; + + @TableField("diatom") + private BigDecimal diatom; + + @TableField("cryptomonas") + private BigDecimal cryptomonas; + + +} diff --git a/src/main/java/com/whdc/entity/WqReal.java b/src/main/java/com/whdc/entity/WqReal.java new file mode 100644 index 0000000..b72d45a --- /dev/null +++ b/src/main/java/com/whdc/entity/WqReal.java @@ -0,0 +1,51 @@ +package com.whdc.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.Date; + +/** + *

+ * 水质实时信息表 + *

+ * + * @author wanyan + * @since 2023-11-01 + */ +@Getter +@Setter +@TableName("wq_real") +@Data +public class WqReal { + + @TableId(value = "stcd") + private String stcd; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("tm") + private Date tm; + + @TableField("chlorophyll") + private BigDecimal chlorophyll; + + @TableField("blueAlgae") + private BigDecimal blueAlgae; + + @TableField("greenAlgae") + private BigDecimal greenAlgae; + + @TableField("diatom") + private BigDecimal diatom; + + @TableField("cryptomonas") + private BigDecimal cryptomonas; + + +} diff --git a/src/main/java/com/whdc/mapper/WqRMapper.java b/src/main/java/com/whdc/mapper/WqRMapper.java new file mode 100644 index 0000000..52916a8 --- /dev/null +++ b/src/main/java/com/whdc/mapper/WqRMapper.java @@ -0,0 +1,17 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.entity.WqR; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 水质信息表 Mapper 接口 + *

+ * + * @author wanyan + * @since 2023-11-01 + */ +@Mapper +public interface WqRMapper extends BaseMapper { +} diff --git a/src/main/java/com/whdc/mapper/WqRealMapper.java b/src/main/java/com/whdc/mapper/WqRealMapper.java new file mode 100644 index 0000000..e49243a --- /dev/null +++ b/src/main/java/com/whdc/mapper/WqRealMapper.java @@ -0,0 +1,17 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.entity.WqReal; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 水质实时信息表 Mapper 接口 + *

+ * + * @author wanyan + * @since 2023-11-01 + */ +@Mapper +public interface WqRealMapper extends BaseMapper { +} diff --git a/src/main/java/com/whdc/mqtt/Config.java b/src/main/java/com/whdc/mqtt/Config.java new file mode 100644 index 0000000..0635776 --- /dev/null +++ b/src/main/java/com/whdc/mqtt/Config.java @@ -0,0 +1,9 @@ +package com.whdc.mqtt; + +public class Config { + public static final String USERNAME = "wy"; + public static final char[] PASSWORD = "Gsiot_890".toCharArray(); + + public static final String TOPIC_WQ_R = "/Algae/#"; + +} diff --git a/src/main/java/com/whdc/mqtt/MQTTConfig.java b/src/main/java/com/whdc/mqtt/MQTTConfig.java new file mode 100644 index 0000000..5667de2 --- /dev/null +++ b/src/main/java/com/whdc/mqtt/MQTTConfig.java @@ -0,0 +1,92 @@ +package com.whdc.mqtt; + +import com.alibaba.fastjson.JSON; +import com.whdc.entity.WqR; +import com.whdc.entity.WqReal; +import com.whdc.mapper.WqRMapper; +import com.whdc.mapper.WqRealMapper; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.Date; + +@Configuration +@Slf4j +public class MQTTConfig { + + @Autowired + private WqRMapper wqRMapper; + + @Autowired + private WqRealMapper wqRealMapper; + + @Value("${mqtt.broker}") + private String broker; + @Value("${mqtt.clientId}") + private String clientId; + + @Value("${enableMqttListening}") + private Boolean enable; + + private static Sub sub; + + @PostConstruct + public void initMQTT() { + if (!enable) { + log.debug("开发环境,不开启mqtt"); + return; + } + log.debug("开启mqtt broker: %s, clientId: %s", broker, clientId); + + Pub.BROKER = broker; + sub = new Sub(broker, clientId, Config.TOPIC_WQ_R); + sub.setOnMessageListener(new Sub.OnMessageListener() { + @Override + public void onMessage(String message) { + log.debug("mqtt: " + message); + System.out.println(message); + try { + WqR wqr = JSON.parseObject(message, WqR.class); + Date now = new Date(); + wqr.setStcd("WQ"); + wqr.setTm(now); + wqRMapper.insert(wqr); + WqReal wqReal = wqRealMapper.selectById("WQ"); + if(wqReal == null){ + wqReal = new WqReal(); + } + wqReal.setChlorophyll(wqr.getChlorophyll()); + wqReal.setBlueAlgae(wqr.getBlueAlgae()); + wqReal.setGreenAlgae(wqr.getGreenAlgae()); + wqReal.setDiatom(wqr.getDiatom()); + wqReal.setCryptomonas(wqr.getCryptomonas()); + wqReal.setTm(now); + if (wqReal.getStcd() == null) { + wqReal.setStcd("WQ"); + wqRealMapper.insert(wqReal); + } else { + wqRealMapper.updateById(wqReal); + } + + } catch (Throwable e) { + e.printStackTrace(); + } + } + }); + new Thread(() -> { + try { + Thread.sleep(10 * 1000); + sub.connect(true); + } catch (MqttException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }).start(); + } + +} diff --git a/src/main/java/com/whdc/mqtt/Pub.java b/src/main/java/com/whdc/mqtt/Pub.java new file mode 100644 index 0000000..8a68b5b --- /dev/null +++ b/src/main/java/com/whdc/mqtt/Pub.java @@ -0,0 +1,54 @@ +package com.whdc.mqtt; + +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + +public class Pub { + private final String clientId; + private final String topic; + public static String BROKER; + private final int qos; + private MqttClient client; + + public Pub(String clientId, String topic) { + this.clientId = clientId; + this.topic = topic; + this.qos = 0; + } + + public Pub(String clientId, String topic, int qos) { + this.clientId = clientId; + this.topic = topic; + this.qos = qos; + } + + public void connect() throws MqttException { + this.client = new MqttClient("tcp://120.24.5.249:3189", "mqttx_2b072a3c", new MemoryPersistence()); + MqttConnectOptions connOpts = new MqttConnectOptions(); + connOpts.setUserName(Config.USERNAME); + connOpts.setPassword(Config.PASSWORD); + connOpts.setCleanSession(true); + this.client.connect(connOpts); + } + + public void close() { + try { + this.client.disconnectForcibly(200); + this.client.close(true); + } catch (MqttException e) { + e.printStackTrace(); + } + } + + public void pub(String msg) throws MqttException { + MqttMessage message = new MqttMessage(msg.getBytes()); + message.setQos(qos); + this.client.publish(topic, message); + } + + + +} diff --git a/src/main/java/com/whdc/mqtt/Sub.java b/src/main/java/com/whdc/mqtt/Sub.java new file mode 100644 index 0000000..9bca41e --- /dev/null +++ b/src/main/java/com/whdc/mqtt/Sub.java @@ -0,0 +1,84 @@ +package com.whdc.mqtt; + +import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + + +public class Sub { + private final String clientId; + private final String topic; + private final String broker; + private OnMessageListener onMessageListener; + private MqttClient client; + + public Sub(String broker, String clientId, String topic) { + this.clientId = clientId; + this.topic = topic; + this.broker = broker; + } + + public void setOnMessageListener(OnMessageListener listener) { + this.onMessageListener = listener; + } + + public void connect() throws MqttException { + connect(true); + } + + public void connect(boolean autoReconnect) throws MqttException { + this.client = new MqttClient(broker, this.clientId, new MemoryPersistence()); + MqttConnectOptions connOpts = new MqttConnectOptions(); + connOpts.setUserName(Config.USERNAME); + connOpts.setPassword(Config.PASSWORD); + connOpts.setCleanSession(true); + connOpts.setAutomaticReconnect(autoReconnect); + + this.client.setCallback(new MqttCallback() { + @Override + public void connectionLost(Throwable throwable) { + throwable.printStackTrace(); + } + + @Override + public void messageArrived(String topic, MqttMessage mqttMessage) { + byte[] payload = mqttMessage.getPayload(); + String msg = new String(payload); + try { + Sub.this.onMessageListener.onMessage(msg); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + @Override + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { + } + }); + + this.client.connect(connOpts); + this.client.subscribe(this.topic, 0); + } + + public void close() { + try { + this.client.disconnectForcibly(200); + this.client.close(true); + } catch (MqttException e) { + } + } + + public interface OnMessageListener { + void onMessage(String message); + } + + public static void main(String[] args) throws MqttException { + Sub sub = new Sub("tcp://120.24.5.249:3189","mqttx_2b072a3c", Config.TOPIC_WQ_R); + sub.setOnMessageListener(new OnMessageListener() { + @Override + public void onMessage(String message) { + System.out.println("received msg: " + message); + } + }); + sub.connect(true); + } +} diff --git a/src/main/java/com/whdc/service/WqRService.java b/src/main/java/com/whdc/service/WqRService.java new file mode 100644 index 0000000..9064945 --- /dev/null +++ b/src/main/java/com/whdc/service/WqRService.java @@ -0,0 +1,16 @@ +package com.whdc.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.whdc.entity.WqR; + +/** + *

+ * 水质信息表 服务类 + *

+ * + * @author wanyan + * @since 2023-11-01 + */ +public interface WqRService extends IService { + +} diff --git a/src/main/java/com/whdc/service/impl/WqRServiceImp.java b/src/main/java/com/whdc/service/impl/WqRServiceImp.java new file mode 100644 index 0000000..94c95ff --- /dev/null +++ b/src/main/java/com/whdc/service/impl/WqRServiceImp.java @@ -0,0 +1,20 @@ +package com.whdc.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.whdc.entity.WqR; +import com.whdc.mapper.WqRMapper; +import com.whdc.service.WqRService; +import org.springframework.stereotype.Service; + +/** + *

+ * 水质信息表 服务实现类 + *

+ * + * @author wanyan + * @since 2023-11-01 + */ +@Service +public class WqRServiceImp extends ServiceImpl implements WqRService { + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..f1ea230 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,67 @@ +server: + port: 12303 + + +spring: + #数据库配置 + datasource: + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://rm-wz9n28sq10rz5b0u2o.mysql.rds.aliyuncs.com:3306/ttssk?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&useInformationSchema=true&serverTimezone=GMT%2B8&autoReconnect=true + username: fxkh + password: Fxkh_789_@ + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + + #jpa配置 + jpa: + properties: + hibernate: + dialect: org.hibernate.dialect.DmDialect + +knife4j: + enable: true + setting: + enableFooter: false + enableFooterCustom: true + footerCustomContent: Copyright 2018-[湖北纬皓端成科技有限公司](http://www.wavehorizon.cn) + + documents: # 文档配置,可配置多个分组 + - group: doc-knife4j-1.0.0 + name: MarkDown文档 + locations: classpath:markdown/* + - group: v1.0 + name: v1.0 + + +logging: +# level: +# org.springframework.boot.autoconfigure: error #spring的自动装配日志只打error,否则debug输出的会打印很多自动装配的log信息到控制台 +# com.whdc.zhzmkzapi.mapper: error + config: classpath:logback-spring.xml +mybatis: + mapper-locations: classpath:mapper/*.xml +# Sa-Token配置 +sa-token: + # token 名称 (同时也是cookie名称) + token-name: token + # token 有效期, 24小时,单位s, -1代表永不过期 + timeout: 86400 + # token 临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 + activity-timeout: -1 + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: true + # token风格 + token-style: simple-uuid + # 是否输出操作日志 + is-log: false + + +mqtt: + broker: tcp://120.24.5.249:3189 + clientId: mqttx_2b172a3c + +enableMqttListening: true \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..e14da74 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,206 @@ + + + + + + + + logback + + + + + + + + + + + + + + + + + + + + + + + + + + debug + + + ${CONSOLE_LOG_PATTERN} + + UTF-8 + + + + + + + + ${log.path}/web_debug.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + ${log.path}/web-debug-%d{yyyy-MM-dd}.%i.log + + ${maxFileSize} + + + ${maxHistory} + + + + debug + ACCEPT + DENY + + + + + + + ${log.path}/web_info.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + ${log.path}/web-info-%d{yyyy-MM-dd}.%i.log + + ${maxFileSize} + + + ${maxHistory} + + + + info + ACCEPT + DENY + + + + + + + ${log.path}/web_warn.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + ${log.path}/web-warn-%d{yyyy-MM-dd}.%i.log + + ${maxFileSize} + + + ${maxHistory} + + + + warn + ACCEPT + DENY + + + + + + + ${log.path}/web_error.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + ${log.path}/web-error-%d{yyyy-MM-dd}.%i.log + + ${maxFileSize} + + + ${maxHistory} + + + + ERROR + ACCEPT + DENY + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/tpl.xlsx b/src/main/resources/tpl.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..454f79054881ae76f5a619ca756aa8e137c021b1 GIT binary patch literal 9289 zcmaJ{1y~hZ*Cqs{B&55$j&wxn*rekf*;ACO`DXLWx5`gS~_~8&4SZWdy7;7G!z9K@W(%8LcjyokI zXsDr<))@lR%6_yqOLc{1$eyl#yk#am)vVBQSR~V>8h?Clx#h8#SNa3$ubRrydSIJZZzWn9>qN@VYnhG?8xH2YTw9AEk zI2`92P2gg>Tm*!$Q^+-_eQ&N_tZ~{*eD@TRvf_H(plK-tp?pH-JA)&WpIui$;c|vv zRvzZ97MwxF$nUt0IU}n0HCq+l-%a0`P~o21$Pp(agx;y}^LV&|n^C_M-y}G7LB!f()!G5*knyo`$wjzgFx|Au%Q!}50pL?UVdwHxR#@H`5J#90)S+N050@{%Kx4E1hcfd8wMc~d zfyF#TR*Sq3#6ErwWU%K~f+C55Z>?f{ynJTnGowU^20wmeo(N-1<)(?E0 zlbc4E7UMPF`_*CjMHrx7V$`r#EzNAzzPe=S#n0_JJdXz zx93Y-7NB>9j26~_MbpBbWLMrQgX`3jm1{{r0ySPf-VhhZjDIA4{^3)w)$a~ksN=<= z0yzi;GSs}zRv;ZX>;<0tLrA`#1*grcj6p; z!8?RyCz1$|v^nfn1}_gvduDn*f0{2s@|heQmAfJ~cA1>OcA>4Ml6rwxZa~5WrM5&S zSppaUQ)%s>sa0+I{GP)f8_sV0cBfi*Rn?XK&>wTRt05iG@!`a{cN#tLYa$?B0zu-< z2js$&BvIf>J?EcJBFPo69 zY4KxHH+C4utG7g_NL(}rUX+(oQjc?`OmB7RZbUVie6lhpy>O?($q?Fg#nozi z1ImfgKGrBWk0K2@ck!+&TE2O=WP#wDayV{WVyS}Tcb23|Qlhp}G$B;jVV0TEC&3&@ z%+5hbWz$EY#llsLs@b1j_QDlJHP0NYIa^s>i#z}&aE;HeQ?hG-+{MjTrjgpc%+#?{ zTTCwv))a6HQbb3?hhE`w-1O?iDK;JKMp$bI&A~I)cvJ)31*OyOSY%$ZRl=3;Bz8`N zcg6&6uWOmSDap&x2UMdwMUUvF+NNYuyAw@Qkf*IquO_xwqAYTnjZNF8Z}&6Ps^qAK zD-(O0cw-W4As)d?ldjG}go-7bc)XWVMx%DF5x~4R(_2CT&tz-6E{~|Hk~t;wNwL^! z`X=?}uHCj#syEhYb}`dg->rUBd_DM?ZYjK08Q-)w`jn)^_sUigNeT)(3b>9|D|z7P z2^rP)$I(??G6^io$(;Q~csm}jB;H0nxZ{za&!nN*dKP%R5BN7%;@vyjP7PGAglBtH zc+YRxQ3Lp;x?M803vkve^ouA@oPgBsz%0G_s>Jw@o_p>^x_P5k~&cCDk~426`mEVU$>S(!*Hy2n<&OS5ol;yy`Kc>`#aS2uCsk!;Xr$p6Qc40 zC5AzlRd~(;YiN>fpgd;8UW{v1g z%|vRoe7i6Q9%q+HT8}Kl1h;0NXWSD$19+q*Eldi?m{Fu%BT3u1army)yqfwbC<&)w zqUi4)6MtG&WhouMKW}*aKO*|6q5ws-ow0$1!5k5tkHbBM=l0Y zv>#8V>KtNfctF}bVx%HR2-7O|HZ(km&n--v2N3D7&^POuw>!n94ZC=3-=(92{_}Q~ zg=Nm(rTN$pG+HOLV_C8zs4JQ2A484183MC>x!-0Ngn6Y*~<^$0KGhhPv0n90xi zdpgAHDZ>3)Ma%0%d;InQ-a~+0XePN1#;&z&IOKMP)(}g1X1>;;)<*mWZ5%*l=fqds zI$gbZ^ZG?89#cQO&%A z`FNXd_aC(8Th$*f**FN<&P#pR)7w1tAo=hvh=N}%c)_{ZLM|FtIJzRjSn)$OReD=Z zzPJsm0#W&nm{)n1u&TQ@uA9>K#FoEgY#231n61w~MTaZMC}802j5RVLgaQL1N}YLHqQbx1uENDKxvKa33!wOBWxGH*e?K+0;;6PVaZC zo0~mO+u1$uPx@Bx<(GmG2}fha&muL_DS7;|lWJ<*dW1EGbyME&s4P8mbJaj1vSvG4 zhF~T<2&^G?qu>{CYgxU957?zbu@&KE<`Y+daq8maUTMSif^lJ>ZCiE5xTVCj#l3vC zYP&eco5Bf8Fkl-gyJI^o6)1EHXisVb^acvv;HROisLtC;eyT|wk+<&6h2zcLdj8f* zyvuF{?R(#rZ%$-_Uj$LsOwiyovt37$)wnzGfJ8_A{RS}~#Q^LoAeA=dMkCy&Z_g`D zH||P29D}671~Q*$6rh8LNJ5f5yb;o;pg_fSdd<_BGTZYy?Hn~_ zIdtYSY@cp#Q{Icgl=E% zb8fXS)D)ry{FC^#vL+7s4w%eyIi{{0i{V1fo zrB^X0$S7#GoIHCN7C0XHYPwdG^lRNT-C98SFfF8>H{TT_!T066Med(XciPh&k5U0equ)reZtobcO)8 zGq^Y^wmX?va!iHuGU;G1+!5+AqU_-39mX?=XD5X$Uj~c=mz5Wx1g_rq+Vd;FW&uXp zvG?k8k!O4zwG7)5$Baln@>6zN#5w)wQ%RmCR3|23=S^YGkoumg%&@)$66*~tq5X~s z3n|*|7RFnH8 zF&oy&g|g=qvui8AscXB(x5)FW_>QJ)Ll{e%uN{hJ&iG$;!_Qc^XC_NEX&0jwNYa{{ zy-S;J+A}a4Dq9~$Fcxb_d$YySHVS1=41^!u?|t1{baR^86mpyN*@4sBJ}6ZW*R4Fte5`lMj7*hbhiXzXvUxUd53@q%C@XU}%xcGbx*@ zlOf@`vHczUm^s04J{wMDh~afR6IL?yDdH@Gjwn1$tA=Z$|0p&}2rs zEi8SoRjO|L)#dV>s3`Qg=>@Ke4=oqB6dK~oo3q^kOeH(*b6$3}sAdTxE{oOS=y4OV zZqFcx?YV?Q?lJHOMQWkyTna_j4*aw^?!(>n+mD(gVPwg(thLq^3@X+9%g9sKD}CW>t)W2QYZZ4X^5B^wZbkZhBN z;SD*I8AlJm?$wq^jJk{St*u&0#-s?F3H}EWrZ>joM;#H5?8?8%oF#<7^g4A!Pu`&# zl07~mFn`i-t8f@T<^D2C?sqFFiJcR>w{Q_}<_MW6elzp{eog2Ups%(K1-{2+VP%hJ zwz#X#;6^8cHsUD=tFHeAcOudUa%wOb<3q8|lArnu^6yk@snhX0{7OCMaHgVahE)b# z8YA)6UGs{)jQNI17&345r&who4dz)yv~XL&-ei#yUhIW4?{#{IZa$;Cd-ihLvLQ2& zqjFb&CVTP>C{voJv~k+7A>v?djnEjkhqi`DoY{jZC>+V`8>mabMo~EDY$1$xZ7%$b z6Ld_dEL%mq)Zp&Y!;av`alw|m;5D2oE@KtUOQVG&J}KQ zvD{)Kd02sz4qrZKrfPSAgnYGrz6Ni5ZL94K<|lHmaxDF{B)oS-ggWD_cRi9w^mk5+x5m#LCGcbJ-wn|tGEMI@Uo)I7X$i~g3|o(DCv+LC3oaX@o12 zRiGjJicQ0a5ekP5=43z^Q7>W{#bNq|CH2J8d{+vi>^m);FYLU9t z&}tvLpvF<5QqUS$zeVE`dEGF-GO*1z5$~*73%1p%Tr058sOYGnGhc4qR-iUtU$$1A z`|jf^XJdxcMjqsGdN4wxd8E<4c*F9$L;-{>JZpnDx za{9^98+S%#xVAZbjK5~%J>n%PX(G;^IM`f{ZIdBKeMkvrA(M#0qKo@`y8 zp5`8A$X2o2X>c^+Z-!BS2)JBqaO1e?9AZpSY&YF{!Zdp2ZaPh@`QiOteax|#XV_NH5%fq|zzQV`zNZ`Txc870uC6Os z-1mUb^M;y=z3DPGLDbyBmLc_~2{LB|#mH*XK(7a)=R0iL+5R)>O_RG@hwuB0iLscj6Zpp#|uGmK=D%$KW_$#0XW*!08|I*LDQg zp5#M5w2Hz;(H_C^)1AS6X+!W$lzCcSx4Fn)gv!1>KQ4(``thp1%;m(Ifx+N07m(ZR z)^lxn*L|QS#9o8%IUN}@yqJlEAaK^Me8}P0==IgOr+2fux7~nA7Cf}QVPzkj2L<{u zJL8aLjqs4otZS$w-*T*f@W#JnIu7yk_qpl3t>TTLaFf_^;4URH_DMS$L)Y)z)~pbK*OkN4T4vCzl$MAeTlzt^I{Fh!Im zpeC;*sLA^TeSlgLw#GU@1O2ylKod)&A0??@w44-#09Mzbz@knf(OKkYD3r>QfOI{| zTd)w>x}|}cAuNe!BEHT{eh=op$ReJ;U*eUS^&8Jr=WH(5-WeB1PWgs24<9-&1SGtZ z+>6AU>f(CKiW5*Kjs_w9R!uECiDOQ@WRPBvooRvRGx`7lt7$6Y z9(?W$tZCC?exs9Ig6yS^0|kaAY?O5*_H{qHPSs6gA0~4}MDI5SxfN5&N83#IrMQCC zEF}0cSd+z%2affvfFVs-Frp&uez6Dop0wXx^IYX5`KkXt5Beef>g?)TS(!b3D1=4z zgWwTlkxTJAA6C~L4hv^R85&}KJ0BWw1B(?2z43@12Z?J}iXchVugSR)2O5m;#LT8J z{qq`W`K4dI-=Zn4m_iCcH>nIK&3mpi7s?*R&=m zgf0$!xtRG`V&5k&sPD^a86uUEIdTju&+u0>%WMZGRE%Xl1ntSXHW+Pux;hp?2%IEw z4|6-*42_AIC5MK<-YWN~csoKwCQ>~YMXn5*_mbI%M{|jg@BtSO>4-9iIjS!j?s$PO z^R`OoP4`x67ZZv`Wv>r}3dhhQLvRHM!W)go>M5d%1=m!ES3R+OP&#l(Y<>DB7w`na zFKuK5sgU4hBvfbt$g;yUCIvF@-LWu?%AH>mvdQwjeE|Fad+*R7qiO>U%>QhuyK*=* zJc9mP1jPjW-!u$Ql*Q4*k{wLQT~C*m(i;GO^pI? z=*H?`B9g8*J}$cH*W=C;X#sM=8$Q^-`M_`0SC{xT#}IMz$5@`O~;rMRGa$^hkZ!8ZG55`%Xl=(g@^bcsdto z0$uB&Z8-cIuC1&Jc88XDlRgU#Gt;gqMt!n3k3gjC?Kv4b3?Ww13@y=qMRb zRBT9(D5Bw)_%?WMU8C8CE~#5XmN1w+eejxpbab<=HEKk?qF$6CDe zYgTP>;0b-924MZkHyNRArP1inR>OU&jl*ZsK{a5?{Cfq&+fQa->Ya_zBEPpR_7V&?4u;3)37 zOqk3TA7`%ZQQbfAECfU^d=f48ibZBF7@@zr4RF&%dsr+c&uSBN@7XEH)= zV)z)*$$$-Yl(`faCRXE>&H5j$;k;utTO&}QBZAt)Phgw6kd>vKfu)_cqO-Mut>#m} z7svL?LIBYGFDaVZv(%a2TI+=Byk9oRCtHCWyJ%rKi%!HYZR5g#0>kbYse-3e)nT0Dq!L zf||!V{3|%^mlJTjL2362k`bmMCuAUlVT+Y(BR4~%)RIDy8X_AWPpcxGG)?NeGuKtJ z4+6?OmKaq<$wA1W@gz}io9uSH6f9xUl{Q$Xw!GN&eU3l-I&3($R2_t62B`@EFaV8k zQfxIRKsj{3W=b1zSazO+|RpZCG-4=rud3(mL~Y%iQQMO8J^HJrAd>apEH%S(Yn zp#69k{@e!er)NOU0Rsmk0?n}m|LUio0{$J%drCP!$J362JKSFh=y#=mrl2RDpGt}F z{^I!&GXG22Q%wFGPs*OgKXy9&uJ6yi4o}2C^-=zl?LRvo{xs2_Q2sA&f6ycSx6;3W z{Xg0N1e$-bQ-XlaU+jOw&j0JCp8(aLhh`LpQyB?ke74E(3?sS